Merge remote-tracking branch 'upstream/master' into feat-log-and-logwatch
# Conflicts: # data/web/admin.php # docker-compose.yml
This commit is contained in:
commit
04c5c616c8
|
@ -3,4 +3,10 @@ data/conf/sogo/sieve.creds
|
|||
data/conf/dovecot/dovecot-master.passwd
|
||||
mailcow.conf
|
||||
mailcow.conf_backup
|
||||
data/conf/nginx/listen.active
|
||||
data/conf/nginx/listen*active
|
||||
data/conf/nginx/server_name*active
|
||||
data/conf/postfix/sql
|
||||
data/conf/dovecot/sql
|
||||
data/web/inc/vars.local.inc.php
|
||||
site/
|
||||
data/assets/ssl
|
||||
|
|
|
@ -2,4 +2,4 @@
|
|||
|
||||
[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=JWBSYHF4SMC68)
|
||||
|
||||
Please see the official documentation for instructions => [mailcow.email/dockerized](https://mailcow.email/dockerized)
|
||||
Please see [the official documentation](https://andryyy.github.io/mailcow-dockerized/) for instructions.
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
FROM debian:latest
|
||||
MAINTAINER https://m-ko.de Markus Kosmal <code@cnfg.io>
|
||||
|
||||
# Debian Base to use
|
||||
ENV DEBIAN_VERSION jessie
|
||||
|
||||
# initial install of av daemon
|
||||
RUN echo "deb http://http.debian.net/debian/ $DEBIAN_VERSION main contrib non-free" > /etc/apt/sources.list && \
|
||||
echo "deb http://http.debian.net/debian/ $DEBIAN_VERSION-updates main contrib non-free" >> /etc/apt/sources.list && \
|
||||
echo "deb http://security.debian.org/ $DEBIAN_VERSION/updates main contrib non-free" >> /etc/apt/sources.list && \
|
||||
apt-get update && \
|
||||
DEBIAN_FRONTEND=noninteractive apt-get install --no-install-recommends -y -qq \
|
||||
clamav-daemon \
|
||||
clamav-freshclam \
|
||||
libclamunrar7 \
|
||||
wget && \
|
||||
apt-get clean && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# initial update of av databases
|
||||
RUN wget -O /var/lib/clamav/main.cvd http://db.local.clamav.net/main.cvd && \
|
||||
wget -O /var/lib/clamav/daily.cvd http://db.local.clamav.net/daily.cvd && \
|
||||
wget -O /var/lib/clamav/bytecode.cvd http://db.local.clamav.net/bytecode.cvd && \
|
||||
chown clamav:clamav /var/lib/clamav/*.cvd
|
||||
|
||||
# permission juggling
|
||||
RUN mkdir /var/run/clamav && \
|
||||
chown clamav:clamav /var/run/clamav && \
|
||||
chmod 750 /var/run/clamav
|
||||
|
||||
# av configuration update
|
||||
RUN sed -i 's/^Foreground .*$/Foreground true/g' /etc/clamav/clamd.conf && \
|
||||
echo "TCPSocket 3310" >> /etc/clamav/clamd.conf && \
|
||||
sed -i 's/^Foreground .*$/Foreground true/g' /etc/clamav/freshclam.conf
|
||||
|
||||
# volume provision
|
||||
VOLUME ["/var/lib/clamav"]
|
||||
|
||||
# port provision
|
||||
EXPOSE 3310
|
||||
|
||||
# av daemon bootstrapping
|
||||
COPY bootstrap.sh /
|
||||
CMD ["/bootstrap.sh"]
|
|
@ -0,0 +1,35 @@
|
|||
#!/bin/bash
|
||||
# bootstrap clam av service and clam av database updater shell script
|
||||
# presented by mko (Markus Kosmal<code@cnfg.io>)
|
||||
set -m
|
||||
|
||||
# start clam service itself and the updater in background as daemon
|
||||
freshclam -d &
|
||||
clamd &
|
||||
|
||||
# recognize PIDs
|
||||
pidlist=`jobs -p`
|
||||
|
||||
# initialize latest result var
|
||||
latest_exit=0
|
||||
|
||||
# define shutdown helper
|
||||
function shutdown() {
|
||||
trap "" SUBS
|
||||
|
||||
for single in $pidlist; do
|
||||
if ! kill -0 $pidlist 2>/dev/null; then
|
||||
wait $pidlist
|
||||
exitcode=$?
|
||||
fi
|
||||
done
|
||||
|
||||
kill $pidlist 2>/dev/null
|
||||
}
|
||||
|
||||
# run shutdown
|
||||
trap terminate SUBS
|
||||
wait
|
||||
|
||||
# return received result
|
||||
exit $latest_exit
|
|
@ -1,4 +1,4 @@
|
|||
From ubuntu:xenial
|
||||
FROM ubuntu:xenial
|
||||
MAINTAINER Andre Peters <andre.peters@servercow.de>
|
||||
|
||||
ENV DEBIAN_FRONTEND noninteractive
|
||||
|
@ -19,13 +19,46 @@ RUN apt-get -y install dovecot-common \
|
|||
dovecot-mysql \
|
||||
dovecot-pop3d \
|
||||
dovecot-dev \
|
||||
syslog-ng \
|
||||
syslog-ng-core \
|
||||
ca-certificates \
|
||||
supervisor \
|
||||
wget \
|
||||
curl \
|
||||
build-essential \
|
||||
autotools-dev \
|
||||
automake
|
||||
automake \
|
||||
libauthen-ntlm-perl \
|
||||
libcrypt-ssleay-perl \
|
||||
libdigest-hmac-perl \
|
||||
libfile-copy-recursive-perl \
|
||||
libio-compress-perl \
|
||||
libio-socket-inet6-perl \
|
||||
libio-socket-ssl-perl \
|
||||
libio-tee-perl \
|
||||
libmodule-scandeps-perl \
|
||||
libnet-ssleay-perl \
|
||||
libpar-packer-perl \
|
||||
libreadonly-perl \
|
||||
libterm-readkey-perl \
|
||||
libtest-pod-perl \
|
||||
libtest-simple-perl \
|
||||
libunicode-string-perl \
|
||||
liburi-perl \
|
||||
libdbi-perl \
|
||||
liblockfile-simple-perl \
|
||||
libdbd-mysql-perl \
|
||||
libipc-run-perl \
|
||||
make \
|
||||
cpanminus
|
||||
|
||||
RUN sed -i -E 's/^(\s*)system\(\);/\1unix-stream("\/dev\/log");/' /etc/syslog-ng/syslog-ng.conf
|
||||
RUN cpanm Data::Uniqid Mail::IMAPClient String::Util
|
||||
RUN echo '* * * * * root /usr/local/bin/imapsync_cron.pl' > /etc/cron.d/imapsync
|
||||
RUN echo '30 3 * * * vmail /usr/bin/doveadm quota recalc -A' > /etc/cron.d/dovecot-sync
|
||||
|
||||
WORKDIR /tmp
|
||||
|
||||
RUN wget http://hg.dovecot.org/dovecot-antispam-plugin/archive/tip.tar.gz -O - | tar xvz \
|
||||
&& cd /tmp/dovecot-antispam* \
|
||||
&& ./autogen.sh \
|
||||
|
@ -33,10 +66,15 @@ RUN wget http://hg.dovecot.org/dovecot-antispam-plugin/archive/tip.tar.gz -O - |
|
|||
&& make \
|
||||
&& make install
|
||||
|
||||
COPY ./imapsync /usr/local/bin/imapsync
|
||||
COPY ./postlogin.sh /usr/local/bin/postlogin.sh
|
||||
COPY ./imapsync_cron.pl /usr/local/bin/imapsync_cron.pl
|
||||
COPY ./rspamd-pipe /usr/local/bin/rspamd-pipe
|
||||
COPY ./docker-entrypoint.sh /
|
||||
COPY ./supervisord.conf /etc/supervisor/supervisord.conf
|
||||
|
||||
RUN chmod +x /usr/local/bin/rspamd-pipe
|
||||
RUN chmod +x /usr/local/bin/imapsync_cron.pl
|
||||
|
||||
RUN groupadd -g 5000 vmail
|
||||
RUN useradd -g vmail -u 5000 vmail -d /var/vmail
|
||||
|
@ -44,6 +82,6 @@ RUN useradd -g vmail -u 5000 vmail -d /var/vmail
|
|||
EXPOSE 24 10001
|
||||
|
||||
ENTRYPOINT ["/docker-entrypoint.sh"]
|
||||
CMD ["/usr/sbin/dovecot", "-F"]
|
||||
CMD exec /usr/bin/supervisord -c /etc/supervisor/supervisord.conf
|
||||
|
||||
RUN apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
|
||||
|
|
|
@ -1,11 +1,43 @@
|
|||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# Set config parameters, escape " in db password
|
||||
# Hard-code env vars to imapsync due to cron not passing them to the perl script
|
||||
sed -i "/^\$DBUSER/c\\\$DBUSER='${DBUSER}';" /usr/local/bin/imapsync_cron.pl
|
||||
sed -i "/^\$DBPASS/c\\\$DBPASS='${DBPASS}';" /usr/local/bin/imapsync_cron.pl
|
||||
sed -i "/^\$DBNAME/c\\\$DBNAME='${DBNAME}';" /usr/local/bin/imapsync_cron.pl
|
||||
|
||||
[[ ! -d /etc/dovecot/sql/ ]] && mkdir -p /etc/dovecot/sql/
|
||||
|
||||
# Set Dovecot sql config parameters, escape " in db password
|
||||
DBPASS=$(echo ${DBPASS} | sed 's/"/\\"/g')
|
||||
sed -i "/^connect/c\connect = \"host=mysql dbname=${DBNAME} user=${DBUSER} password=${DBPASS}\"" /etc/dovecot/sql/*
|
||||
|
||||
cat <<EOF > /etc/dovecot/sql/dovecot-dict-sql.conf
|
||||
connect = "host=mysql dbname=${DBNAME} user=${DBNAME} password=${DBPASS}"
|
||||
map {
|
||||
pattern = priv/quota/storage
|
||||
table = quota2
|
||||
username_field = username
|
||||
value_field = bytes
|
||||
}
|
||||
map {
|
||||
pattern = priv/quota/messages
|
||||
table = quota2
|
||||
username_field = username
|
||||
value_field = messages
|
||||
}
|
||||
EOF
|
||||
|
||||
cat <<EOF > /etc/dovecot/sql/dovecot-mysql.conf
|
||||
driver = mysql
|
||||
connect = "host=mysql dbname=${DBNAME} user=${DBNAME} password=${DBPASS}"
|
||||
default_pass_scheme = SSHA256
|
||||
password_query = SELECT password FROM mailbox WHERE username = '%u' AND domain IN (SELECT domain FROM domain WHERE domain='%d' AND active='1')
|
||||
user_query = SELECT CONCAT('maildir:/var/vmail/',maildir) AS mail, 5000 AS uid, 5000 AS gid, concat('*:bytes=', quota) AS quota_rule FROM mailbox WHERE username = '%u' AND active = '1'
|
||||
iterate_query = SELECT username FROM mailbox WHERE active='1';
|
||||
EOF
|
||||
|
||||
[[ ! -d /var/vmail/sieve ]] && mkdir -p /var/vmail/sieve
|
||||
[[ ! -d /etc/sogo ]] && mkdir -p /etc/sogo
|
||||
cat /etc/dovecot/sieve_after > /var/vmail/sieve/global.sieve
|
||||
sievec /var/vmail/sieve/global.sieve
|
||||
chown -R vmail:vmail /var/vmail/sieve
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,72 @@
|
|||
#!/usr/bin/perl
|
||||
|
||||
use DBI;
|
||||
use File::Temp qw/ mkstemp /;
|
||||
use LockFile::Simple qw(lock trylock unlock);
|
||||
use Data::Dumper qw(Dumper);
|
||||
use IPC::Run 'run';
|
||||
use String::Util 'trim';
|
||||
|
||||
$DBNAME = '';
|
||||
$DBUSER = '';
|
||||
$DBPASS = '';
|
||||
|
||||
$run_dir="/tmp";
|
||||
$dsn = "DBI:mysql:database=" . $DBNAME . ";host=mysql";
|
||||
$lock_file = $run_dir . "/imapsync_busy";
|
||||
$lockmgr = LockFile::Simple->make(-autoclean => 1, -max => 1);
|
||||
$lockmgr->lock($lock_file) || die "can't lock ${lock_file}";
|
||||
$dbh = DBI->connect($dsn, $DBUSER, $DBPASS);
|
||||
open my $file, '<', "/etc/sogo/sieve.creds";
|
||||
my $creds = <$file>;
|
||||
close $file;
|
||||
my ($master_user, $master_pass) = split /:/, $creds;
|
||||
my $sth = $dbh->prepare("SELECT id, user1, user2, host1, authmech1, password1, exclude, port1, enc1, delete2duplicates, maxage, subfolder2 FROM imapsync WHERE active = 1 AND (UNIX_TIMESTAMP(NOW()) - UNIX_TIMESTAMP(last_run) > mins_interval * 60 OR last_run IS NULL)");
|
||||
$sth->execute();
|
||||
my $row;
|
||||
|
||||
while ($row = $sth->fetchrow_arrayref()) {
|
||||
|
||||
$id = @$row[0];
|
||||
$user1 = @$row[1];
|
||||
$user2 = @$row[2];
|
||||
$host1 = @$row[3];
|
||||
$authmech1 = @$row[4];
|
||||
$password1 = @$row[5];
|
||||
$exclude = @$row[6];
|
||||
$port1 = @$row[7];
|
||||
$enc1 = @$row[8];
|
||||
$delete2duplicates = @$row[9];
|
||||
$maxage = @$row[10];
|
||||
$subfolder2 = @$row[11];
|
||||
|
||||
if ($enc1 eq "TLS") { $enc1 = "--tls1"; } elsif ($enc1 eq "SSL") { $enc1 = "--ssl1"; } else { undef $enc1; }
|
||||
|
||||
run [ "/usr/local/bin/imapsync",
|
||||
"--timeout1", "10",
|
||||
"--tmpdir", "/tmp",
|
||||
"--subscribeall",
|
||||
($exclude eq "" ? () : ("--exclude", $exclude)),
|
||||
($subfolder2 eq "" ? () : ('--subfolder2', $subfolder2)),
|
||||
($maxage eq "0" ? () : ('--maxage', $maxage)),
|
||||
($delete2duplicates ne "1" ? () : ('--delete2duplicates')),
|
||||
(!defined($enc1) ? () : ($enc1)),
|
||||
"--host1", $host1,
|
||||
"--user1", $user1,
|
||||
"--password1", $password1,
|
||||
"--port1", $port1,
|
||||
"--host2", "localhost",
|
||||
"--user2", $user2 . '*' . trim($master_user),
|
||||
"--password2", trim($master_pass),
|
||||
'--no-modulesversion'], ">", \my $stdout;
|
||||
|
||||
$update = $dbh->prepare("UPDATE imapsync SET returned_text = ?, last_run = NOW() WHERE id = ?");
|
||||
$update->bind_param( 1, ${stdout} );
|
||||
$update->bind_param( 2, ${id} );
|
||||
$update->execute();
|
||||
}
|
||||
|
||||
$sth->finish();
|
||||
$dbh->disconnect();
|
||||
|
||||
$lockmgr->unlock($lock_file);
|
|
@ -0,0 +1,4 @@
|
|||
#!/bin/sh
|
||||
|
||||
export MASTER_USER=$USER
|
||||
exec "$@"
|
|
@ -0,0 +1,21 @@
|
|||
[supervisord]
|
||||
nodaemon=true
|
||||
|
||||
[program:syslog-ng]
|
||||
command=/usr/sbin/syslog-ng --foreground --no-caps
|
||||
redirect_stderr=true
|
||||
autostart=true
|
||||
stdout_syslog=true
|
||||
|
||||
[program:dovecot]
|
||||
command=/usr/sbin/dovecot -F
|
||||
autorestart=true
|
||||
|
||||
[program:logfiles]
|
||||
command=/usr/bin/tail -f /var/log/mail.log /var/log/syslog
|
||||
stdout_logfile=/dev/fd/1
|
||||
stdout_logfile_maxbytes=0
|
||||
|
||||
[program:cron]
|
||||
command=/usr/sbin/cron -f
|
||||
autorestart=true
|
|
@ -1,26 +0,0 @@
|
|||
FROM ubuntu:xenial
|
||||
MAINTAINER Andre Peters <andre.peters@debinux.de>
|
||||
|
||||
ENV DEBIAN_FRONTEND noninteractive
|
||||
ENV LC_ALL C
|
||||
|
||||
RUN dpkg-divert --local --rename --add /sbin/initctl \
|
||||
&& ln -sf /bin/true /sbin/initctl \
|
||||
&& dpkg-divert --local --rename --add /usr/bin/ischroot \
|
||||
&& ln -sf /bin/true /usr/bin/ischroot
|
||||
|
||||
RUN echo 'deb http://repo.powerdns.com/ubuntu xenial-rec-40 main' > /etc/apt/sources.list.d/pdns.list
|
||||
|
||||
RUN echo 'Package: pdns-*\n\
|
||||
Pin: origin repo.powerdns.com\n\
|
||||
Pin-Priority: 600\n' > /etc/apt/preferences.d/pdns
|
||||
|
||||
RUN apt-key adv --fetch-keys http://repo.powerdns.com/FD380FBB-pub.asc \
|
||||
&& apt-get update \
|
||||
&& apt-get install -y --force-yes pdns-recursor
|
||||
|
||||
CMD ["/usr/sbin/pdns_recursor"]
|
||||
|
||||
EXPOSE 53/udp
|
||||
|
||||
RUN apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
|
|
@ -8,6 +8,7 @@ RUN apt-get update \
|
|||
|
||||
RUN docker-php-ext-configure intl
|
||||
RUN docker-php-ext-install intl pdo pdo_mysql xmlrpc
|
||||
RUN pear install channel://pear.php.net/Net_IDNA2-0.1.1 Auth_SASL Net_IMAP NET_SMTP Net_IDNA2 Mail_mime
|
||||
|
||||
COPY ./docker-entrypoint.sh /
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
From ubuntu:xenial
|
||||
FROM ubuntu:xenial
|
||||
MAINTAINER Andre Peters <andre.peters@servercow.de>
|
||||
|
||||
ENV DEBIAN_FRONTEND noninteractive
|
||||
|
@ -13,6 +13,7 @@ RUN apt-get update
|
|||
RUN apt-get install -y --no-install-recommends supervisor \
|
||||
postfix \
|
||||
sasl2-bin \
|
||||
libsasl2-modules \
|
||||
postfix \
|
||||
postfix-mysql \
|
||||
postfix-pcre \
|
||||
|
|
|
@ -2,10 +2,101 @@
|
|||
|
||||
trap "postfix stop" EXIT
|
||||
|
||||
sed -i "/^user/c\user = ${DBUSER}" /opt/postfix/conf/sql/*
|
||||
sed -i "/^password/c\password = ${DBPASS}" /opt/postfix/conf/sql/*
|
||||
sed -i "/^dbname/c\dbname = ${DBNAME}" /opt/postfix/conf/sql/*
|
||||
[[ ! -d /opt/postfix/conf/sql/ ]] && mkdir -p /opt/postfix/conf/sql/
|
||||
|
||||
postfix -c /opt/postfix/conf start
|
||||
cat <<EOF > /opt/postfix/conf/sql/mysql_relay_recipient_maps.cf
|
||||
user = ${DBUSER}
|
||||
password = ${DBPASS}
|
||||
hosts = mysql
|
||||
dbname = ${DBNAME}
|
||||
query = SELECT DISTINCT CASE WHEN '%d' IN (SELECT domain FROM domain WHERE relay_all_recipients=1 AND domain='%d' AND backupmx=1) THEN '%s' ELSE (SELECT goto FROM alias WHERE address='%s' AND active='1') END AS result;
|
||||
EOF
|
||||
|
||||
sleep infinity
|
||||
cat <<EOF > /opt/postfix/conf/sql/mysql_tls_enforce_in_policy.cf
|
||||
user = ${DBUSER}
|
||||
password = ${DBPASS}
|
||||
hosts = mysql
|
||||
dbname = ${DBNAME}
|
||||
query = SELECT IF( EXISTS( SELECT 'TLS_ACTIVE' FROM alias LEFT OUTER JOIN mailbox ON mailbox.username = alias.address WHERE (address='%s' OR address IN (SELECT CONCAT('%u', '@', target_domain) FROM alias_domain WHERE alias_domain='%d')) AND mailbox.tls_enforce_in = '1' AND mailbox.active = '1'), 'reject_plaintext_session', 'DUNNO') AS 'tls_enforce_in';
|
||||
EOF
|
||||
|
||||
cat <<EOF > /opt/postfix/conf/sql/mysql_tls_enforce_out_policy.cf
|
||||
user = ${DBUSER}
|
||||
password = ${DBPASS}
|
||||
hosts = mysql
|
||||
dbname = ${DBNAME}
|
||||
query = SELECT IF( EXISTS( SELECT 'TLS_ACTIVE' FROM alias LEFT OUTER JOIN mailbox ON mailbox.username = alias.address WHERE (address='%s' OR address IN (SELECT CONCAT('%u', '@', target_domain) FROM alias_domain WHERE alias_domain='%d')) AND mailbox.tls_enforce_out = '1' AND mailbox.active = '1'), 'smtp_enforced_tls:', 'DUNNO') AS 'tls_enforce_out';
|
||||
EOF
|
||||
|
||||
cat <<EOF > /opt/postfix/conf/sql/mysql_virtual_alias_domain_catchall_maps.cf
|
||||
user = ${DBUSER}
|
||||
password = ${DBPASS}
|
||||
hosts = mysql
|
||||
dbname = ${DBNAME}
|
||||
query = SELECT goto FROM alias,alias_domain WHERE alias_domain.alias_domain = '%d' and alias.address = CONCAT('@', alias_domain.target_domain) AND alias.active = 1 AND alias_domain.active='1'
|
||||
EOF
|
||||
|
||||
cat <<EOF > /opt/postfix/conf/sql/mysql_virtual_alias_domain_maps.cf
|
||||
user = ${DBUSER}
|
||||
password = ${DBPASS}
|
||||
hosts = mysql
|
||||
dbname = ${DBNAME}
|
||||
query = SELECT username FROM mailbox,alias_domain WHERE alias_domain.alias_domain = '%d' and mailbox.username = CONCAT('%u', '@', alias_domain.target_domain) AND mailbox.active = 1 AND alias_domain.active='1'
|
||||
EOF
|
||||
|
||||
cat <<EOF > /opt/postfix/conf/sql/mysql_virtual_alias_maps.cf
|
||||
user = ${DBUSER}
|
||||
password = ${DBPASS}
|
||||
hosts = mysql
|
||||
dbname = ${DBNAME}
|
||||
query = SELECT goto FROM alias WHERE address='%s' AND active='1';
|
||||
EOF
|
||||
|
||||
cat <<EOF > /opt/postfix/conf/sql/mysql_virtual_domains_maps.cf
|
||||
user = ${DBUSER}
|
||||
password = ${DBPASS}
|
||||
hosts = mysql
|
||||
dbname = ${DBNAME}
|
||||
query = SELECT alias_domain from alias_domain WHERE alias_domain='%s' AND active='1' UNION SELECT domain FROM domain WHERE domain='%s' AND active = '1' AND backupmx = '0'
|
||||
EOF
|
||||
|
||||
cat <<EOF > /opt/postfix/conf/sql/mysql_virtual_mailbox_maps.cf
|
||||
user = ${DBUSER}
|
||||
password = ${DBPASS}
|
||||
hosts = mysql
|
||||
dbname = ${DBNAME}
|
||||
query = SELECT maildir FROM mailbox WHERE username='%s' AND active = '1'
|
||||
EOF
|
||||
|
||||
cat <<EOF > /opt/postfix/conf/sql/mysql_virtual_relay_domain_maps.cf
|
||||
user = ${DBUSER}
|
||||
password = ${DBPASS}
|
||||
hosts = mysql
|
||||
dbname = ${DBNAME}
|
||||
query = SELECT domain FROM domain WHERE domain='%s' AND backupmx = '1' AND active = '1'
|
||||
EOF
|
||||
|
||||
cat <<EOF > /opt/postfix/conf/sql/mysql_virtual_sender_acl.cf
|
||||
user = ${DBUSER}
|
||||
password = ${DBPASS}
|
||||
hosts = mysql
|
||||
dbname = ${DBNAME}
|
||||
query = SELECT goto FROM alias WHERE address='%s' AND active='1' AND domain IN(SELECT domain FROM domain WHERE domain='%d' AND active='1') UNION SELECT logged_in_as FROM sender_acl WHERE send_as='@%d' OR send_as='%s' OR send_as IN ( SELECT CONCAT ('@',target_domain) FROM alias_domain WHERE alias_domain = '%d') OR send_as IN ( SELECT CONCAT ('%u','@',target_domain) FROM alias_domain WHERE alias_domain = '%d' ) AND logged_in_as NOT IN (SELECT goto FROM alias WHERE address='%s') UNION SELECT username FROM mailbox,alias_domain WHERE alias_domain.alias_domain = '%d' AND mailbox.username = CONCAT('%u','@',alias_domain.target_domain) AND mailbox.active ='1' AND alias_domain.active='1'
|
||||
EOF
|
||||
|
||||
cat <<EOF > /opt/postfix/conf/sql/mysql_virtual_spamalias_maps.cf
|
||||
user = ${DBUSER}
|
||||
password = ${DBPASS}
|
||||
hosts = mysql
|
||||
dbname = ${DBNAME}
|
||||
query = SELECT goto FROM spamalias WHERE address='%s' AND validity >= UNIX_TIMESTAMP()
|
||||
EOF
|
||||
|
||||
postconf -c /opt/postfix/conf
|
||||
if [[ $? != 0 ]]; then
|
||||
echo "Postfix configuration error, refusing to start."
|
||||
exit 1
|
||||
else
|
||||
postfix -c /opt/postfix/conf start
|
||||
sleep 126144000
|
||||
fi
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
FROM ubuntu:xenial
|
||||
MAINTAINER Andre Peters <andre.peters@debinux.de>
|
||||
MAINTAINER Andre Peters <andre.peters@servercow.de>
|
||||
|
||||
ENV DEBIAN_FRONTEND noninteractive
|
||||
ENV LC_ALL C
|
||||
|
@ -9,16 +9,19 @@ RUN dpkg-divert --local --rename --add /sbin/initctl \
|
|||
&& dpkg-divert --local --rename --add /usr/bin/ischroot \
|
||||
&& ln -sf /bin/true /usr/bin/ischroot
|
||||
|
||||
RUN apt-key adv --fetch-keys http://rspamd.com/apt-stable/gpg.key \
|
||||
&& echo "deb http://rspamd.com/apt-stable/ xenial main" > /etc/apt/sources.list.d/rspamd.list \
|
||||
RUN apt-key adv --fetch-keys http://rspamd.com/apt/gpg.key \
|
||||
&& echo "deb http://rspamd.com/apt/ xenial main" > /etc/apt/sources.list.d/rspamd.list \
|
||||
&& apt-get update \
|
||||
&& apt-get -y install rspamd ca-certificates
|
||||
&& apt-get -y install rspamd ca-certificates python-pip
|
||||
|
||||
RUN echo '.include $LOCAL_CONFDIR/local.d/rspamd.conf.local' > /etc/rspamd/rspamd.conf.local
|
||||
# "Hardcoded" - we need them
|
||||
RUN echo 'settings = "http://nginx:8081/settings.php";' > /etc/rspamd/modules.d/settings.conf
|
||||
|
||||
CMD ["/usr/bin/rspamd","-f", "-u", "_rspamd", "-g", "_rspamd"]
|
||||
COPY settings.conf /etc/rspamd/modules.d/settings.conf
|
||||
COPY antivirus.conf /etc/rspamd/modules.d/antivirus.conf
|
||||
|
||||
RUN pip install -U oletools
|
||||
|
||||
CMD /usr/bin/rspamd -f -u _rspamd -g _rspamd
|
||||
|
||||
RUN apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
|
||||
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
antivirus {
|
||||
.include(try=true,priority=1,duplicate=merge) "$LOCAL_CONFDIR/local.d/antivirus.conf"
|
||||
.include(try=true,priority=10) "$LOCAL_CONFDIR/override.d/antivirus.conf"
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
settings = "http://nginx:8081/settings.php";
|
|
@ -11,7 +11,14 @@ RUN dpkg-divert --local --rename --add /sbin/initctl \
|
|||
&& ln -sf /bin/true /usr/bin/ischroot
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y --no-install-recommends apt-transport-https ca-certificates wget syslog-ng syslog-ng-core supervisor mysql-client cron \
|
||||
&& apt-get install -y --no-install-recommends apt-transport-https \
|
||||
ca-certificates \
|
||||
wget \
|
||||
syslog-ng \
|
||||
syslog-ng-core \
|
||||
supervisor \
|
||||
mysql-client \
|
||||
cron \
|
||||
&& dpkgArch="$(dpkg --print-architecture | awk -F- '{ print $NF }')" \
|
||||
&& wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch" \
|
||||
&& wget -O /usr/local/bin/gosu.asc "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch.asc" \
|
||||
|
@ -35,8 +42,9 @@ RUN echo '0 0 * * * sogo /usr/sbin/sogo-tool update-autoreply -p /etc/sogo/s
|
|||
COPY ./reconf-domains.sh /
|
||||
COPY supervisord.conf /etc/supervisor/supervisord.conf
|
||||
|
||||
EXPOSE 20000
|
||||
EXPOSE 9191
|
||||
#EXPOSE 20000
|
||||
#EXPOSE 9191
|
||||
#EXPOSE 9192
|
||||
|
||||
CMD exec /usr/bin/supervisord -c /etc/supervisor/supervisord.conf
|
||||
|
||||
|
|
|
@ -1,22 +1,20 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Wait for MySQL to warm-up
|
||||
while mysqladmin ping --host mysql --silent; do
|
||||
|
||||
# Recreate view
|
||||
|
||||
mysql --host mysql -u ${DBUSER} -p${DBPASS} ${DBNAME} -e "DROP VIEW IF EXISTS sogo_view"
|
||||
|
||||
mysql --host mysql -u ${DBUSER} -p${DBPASS} ${DBNAME} << EOF
|
||||
CREATE VIEW sogo_view (c_uid, domain, c_name, c_password, c_cn, mail, aliases, ad_aliases, senderacl, home) AS
|
||||
SELECT mailbox.username, mailbox.domain, mailbox.username, mailbox.password, mailbox.name, mailbox.username, IFNULL(ga.aliases, ''), IFNULL(gda.ad_alias, ''), IFNULL(gs.send_as, ''), CONCAT('/var/vmail/', maildir) FROM mailbox
|
||||
CREATE VIEW sogo_view (c_uid, domain, c_name, c_password, c_cn, mail, aliases, ad_aliases, home, kind, multiple_bookings) AS
|
||||
SELECT mailbox.username, mailbox.domain, mailbox.username, mailbox.password, mailbox.name, mailbox.username, IFNULL(ga.aliases, ''), IFNULL(gda.ad_alias, ''), CONCAT('/var/vmail/', maildir), mailbox.kind, mailbox.multiple_bookings FROM mailbox
|
||||
LEFT OUTER JOIN grouped_mail_aliases ga ON ga.username = mailbox.username
|
||||
LEFT OUTER JOIN grouped_sender_acl gs ON gs.username = mailbox.username
|
||||
LEFT OUTER JOIN grouped_domain_alias_address gda ON gda.username = mailbox.username
|
||||
WHERE mailbox.active = '1';
|
||||
EOF
|
||||
|
||||
# Wait for MySQL to warm-up
|
||||
while ! mysqladmin ping --host mysql --silent; do
|
||||
sleep 1
|
||||
done
|
||||
|
||||
mkdir -p /var/lib/sogo/GNUstep/Defaults/
|
||||
|
||||
|
@ -32,8 +30,6 @@ cat <<EOF > /var/lib/sogo/GNUstep/Defaults/sogod.plist
|
|||
<string>mysql://${DBUSER}:${DBPASS}@mysql:3306/${DBNAME}/sogo_cache_folder</string>
|
||||
<key>OCSEMailAlarmsFolderURL</key>
|
||||
<string>mysql://${DBUSER}:${DBPASS}@mysql:3306/${DBNAME}/sogo_alarms_folder</string>
|
||||
<key>DomainFieldName</key>
|
||||
<string>domain</string>
|
||||
<key>OCSFolderInfoURL</key>
|
||||
<string>mysql://${DBUSER}:${DBPASS}@mysql:3306/${DBNAME}/sogo_folder_info</string>
|
||||
<key>OCSSessionsFolderURL</key>
|
||||
|
@ -51,11 +47,10 @@ EOF
|
|||
# Generate multi-domain setup
|
||||
while read line
|
||||
do
|
||||
DOMAIN_SANE=$(echo ${line} | tr '-' 'b' | tr '.' 'p' | tr -cd '[[:alnum:]]')
|
||||
echo " <key>${line}</key>
|
||||
<dict>
|
||||
<key>SOGoMailDomain</key>
|
||||
<string>${DOMAIN_SANE}</string>
|
||||
<string>${line}</string>
|
||||
<key>SOGoUserSources</key>
|
||||
<array>
|
||||
<dict>
|
||||
|
@ -63,14 +58,13 @@ while read line
|
|||
<array>
|
||||
<string>aliases</string>
|
||||
<string>ad_aliases</string>
|
||||
<string>senderacl</string>
|
||||
</array>
|
||||
<key>KindFieldName</key>
|
||||
<string>kind</string>
|
||||
<key>DomainFieldName</key>
|
||||
<string>domain</string>
|
||||
<key>MultipleBookingsFieldName</key>
|
||||
<string>multiple_bookings</string>
|
||||
<key>IMAPLoginFieldName</key>
|
||||
<string>c_uid</string>
|
||||
<key>canAuthenticate</key>
|
||||
<string>YES</string>
|
||||
<key>displayName</key>
|
||||
|
@ -99,4 +93,6 @@ echo ' </dict>
|
|||
chown sogo:sogo -R /var/lib/sogo/
|
||||
chmod 600 /var/lib/sogo/GNUstep/Defaults/sogod.plist
|
||||
|
||||
sleep infinite
|
||||
sleep 99999
|
||||
|
||||
done
|
||||
|
|
|
@ -7,9 +7,6 @@ redirect_stderr=true
|
|||
autostart=true
|
||||
stdout_syslog=true
|
||||
|
||||
[group:sogo-group]
|
||||
programs=reconf-domains,sogo
|
||||
|
||||
[program:sogo]
|
||||
command=/usr/sbin/sogod
|
||||
user=sogo
|
||||
|
@ -30,6 +27,12 @@ stdout_logfile_maxbytes=0
|
|||
command=/usr/sbin/cron -f
|
||||
autorestart=true
|
||||
|
||||
[program:sogo-webres]
|
||||
command=/usr/bin/python -u -m SimpleHTTPServer 9192
|
||||
directory=/usr/lib/GNUstep/SOGo/
|
||||
user=sogo
|
||||
autorestart=true
|
||||
|
||||
[inet_http_server]
|
||||
port=9191
|
||||
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
acl internal_networks {
|
||||
127.0.0.0/8;
|
||||
192.168.0.0/16;
|
||||
172.16.0.0/12;
|
||||
10.0.0.0/8;
|
||||
};
|
||||
|
||||
options {
|
||||
directory "/var/bind";
|
||||
allow-recursion { internal_networks; };
|
||||
listen-on { any; };
|
||||
listen-on-v6 { none; };
|
||||
pid-file "/var/run/named/named.pid";
|
||||
allow-transfer { none; };
|
||||
dnssec-enable yes;
|
||||
dnssec-validation yes;
|
||||
dnssec-lookaside auto;
|
||||
};
|
||||
|
||||
include "/etc/bind/bind.keys";
|
|
@ -1,6 +1,6 @@
|
|||
auth_mechanisms = plain login
|
||||
#mail_debug = yes
|
||||
log_path = /dev/stdout
|
||||
log_path = /var/log/mail.log
|
||||
disable_plaintext_auth = yes
|
||||
# Uncomment on NFS share
|
||||
#mmap_disable = yes
|
||||
|
@ -21,6 +21,7 @@ ssl_dh_parameters_length = 2048
|
|||
log_timestamp = "%Y-%m-%d %H:%M:%S "
|
||||
recipient_delimiter = +
|
||||
auth_master_user_separator = *
|
||||
mail_prefetch_count = 30
|
||||
passdb {
|
||||
driver = passwd-file
|
||||
args = /etc/dovecot/dovecot-master.passwd
|
||||
|
@ -130,6 +131,9 @@ namespace inbox {
|
|||
auto = subscribe
|
||||
special_use = \Junk
|
||||
}
|
||||
mailbox "Junk-E-mail" {
|
||||
special_use = \Junk
|
||||
}
|
||||
mailbox "Junk E-mail" {
|
||||
special_use = \Junk
|
||||
}
|
||||
|
@ -182,6 +186,9 @@ service managesieve-login {
|
|||
process_min_avail = 2
|
||||
vsz_limit = 128M
|
||||
}
|
||||
service imap {
|
||||
executable = imap imap-postlogin
|
||||
}
|
||||
service managesieve {
|
||||
process_limit = 256
|
||||
}
|
||||
|
@ -236,3 +243,8 @@ remote 127.0.0.1 {
|
|||
}
|
||||
submission_host = postfix:588
|
||||
mail_max_userip_connections = 500
|
||||
service imap-postlogin {
|
||||
executable = script-login /usr/local/bin/postlogin.sh
|
||||
unix_listener imap-postlogin {
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
connect = "host=mysql dbname=mailcow user=mailcow password=mysafepasswd"
|
||||
|
||||
map {
|
||||
pattern = priv/quota/storage
|
||||
table = quota2
|
||||
username_field = username
|
||||
value_field = bytes
|
||||
}
|
||||
map {
|
||||
pattern = priv/quota/messages
|
||||
table = quota2
|
||||
username_field = username
|
||||
value_field = messages
|
||||
}
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
driver = mysql
|
||||
connect = "host=mysql dbname=mailcow user=mailcow password=mysafepasswd"
|
||||
default_pass_scheme = SSHA256
|
||||
password_query = SELECT password FROM mailbox WHERE username = '%u' AND domain IN (SELECT domain FROM domain WHERE domain='%d' AND active='1')
|
||||
user_query = SELECT CONCAT('maildir:/var/vmail/',maildir) AS mail, 5000 AS uid, 5000 AS gid, concat('*:bytes=', quota) AS quota_rule FROM mailbox WHERE username = '%u' AND active = '1'
|
||||
iterate_query = SELECT username FROM mailbox WHERE active='1';
|
|
@ -0,0 +1 @@
|
|||
server_name logs.servercow.de autodiscover.* autoconfig.*;
|
|
@ -1,5 +1,9 @@
|
|||
proxy_cache_path /tmp levels=1:2 keys_zone=sogo:10m inactive=24h max_size=1g;
|
||||
server {
|
||||
listen 443;
|
||||
include /etc/nginx/conf.d/listen_ssl.active;
|
||||
include /etc/nginx/mime.types;
|
||||
charset utf-8;
|
||||
override_charset on;
|
||||
ssl on;
|
||||
ssl_certificate /etc/ssl/mail/cert.pem;
|
||||
ssl_certificate_key /etc/ssl/mail/key.pem;
|
||||
|
@ -9,13 +13,23 @@ server {
|
|||
add_header Strict-Transport-Security "max-age=15768000; includeSubDomains";
|
||||
ssl_ecdh_curve secp384r1;
|
||||
index index.php index.html;
|
||||
server_name _ autodiscover.* autoconfig.*;
|
||||
include /etc/nginx/conf.d/server_name.active;
|
||||
error_log /var/log/nginx/error.log;
|
||||
access_log /var/log/nginx/access.log;
|
||||
root /web;
|
||||
|
||||
location ^~ /.well-known/acme-challenge/ {
|
||||
allow all;
|
||||
default_type "text/plain";
|
||||
}
|
||||
|
||||
# If behind reverse proxy, forwards the correct IP
|
||||
set_real_ip_from 172.22.1.1;
|
||||
real_ip_header X-Forwarded-For;
|
||||
real_ip_recursive on;
|
||||
|
||||
location = /principals/ {
|
||||
rewrite ^ https://$host/SOGo/dav;
|
||||
rewrite ^ $scheme://$host:$server_port/SOGo/dav;
|
||||
allow all;
|
||||
}
|
||||
|
||||
|
@ -24,7 +38,7 @@ server {
|
|||
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
||||
fastcgi_pass phpfpm:9000;
|
||||
fastcgi_index index.php;
|
||||
include fastcgi_params;
|
||||
include /etc/nginx/fastcgi_params;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||
fastcgi_param PATH_INFO $fastcgi_path_info;
|
||||
fastcgi_param PHP_VALUE "max_execution_time = 1200
|
||||
|
@ -35,9 +49,10 @@ server {
|
|||
|
||||
rewrite ^(/save.+)$ /rspamd$1 last;
|
||||
location /rspamd/ {
|
||||
proxy_pass http://rspamd:11334/;
|
||||
proxy_pass http://172.22.1.253:11334/;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
add_header Strict-Transport-Security "max-age=31536000; includeSubdomains";
|
||||
add_header X-Content-Type-Options nosniff;
|
||||
add_header X-Frame-Options SAMEORIGIN;
|
||||
|
@ -48,16 +63,24 @@ server {
|
|||
deny all;
|
||||
}
|
||||
|
||||
if ($host ~* autodiscover\.(.*)) {
|
||||
rewrite ^(.*) /autodiscover.php last;
|
||||
location ~ /(?:a|A)utodiscover/(?:a|A)utodiscover.xml {
|
||||
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
||||
fastcgi_pass phpfpm:9000;
|
||||
include /etc/nginx/fastcgi_params;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||
try_files /autodiscover.php =404;
|
||||
}
|
||||
|
||||
if ($host ~* autoconfig\.(.*)) {
|
||||
rewrite ^(.*) /autoconfig.php last;
|
||||
location ~ /(?:m|M)ail/(?:c|C)onfig-v1.1.xml {
|
||||
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
||||
fastcgi_pass phpfpm:9000;
|
||||
include /etc/nginx/fastcgi_params;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||
try_files /autoconfig.php =404;
|
||||
}
|
||||
|
||||
location ^~ /Microsoft-Server-ActiveSync {
|
||||
proxy_pass http://sogo:20000/SOGo/Microsoft-Server-ActiveSync;
|
||||
proxy_pass http://172.22.1.252:20000/SOGo/Microsoft-Server-ActiveSync;
|
||||
proxy_connect_timeout 1000;
|
||||
proxy_next_upstream timeout error;
|
||||
proxy_send_timeout 1000;
|
||||
|
@ -72,45 +95,212 @@ server {
|
|||
proxy_set_header x-webobjects-server-protocol HTTP/1.0;
|
||||
proxy_set_header x-webobjects-remote-host $remote_addr;
|
||||
proxy_set_header x-webobjects-server-name $server_name;
|
||||
proxy_set_header x-webobjects-server-url $scheme://$host;
|
||||
proxy_set_header x-webobjects-server-url $scheme://$host:$server_port;
|
||||
proxy_set_header x-webobjects-server-port $server_port;
|
||||
client_body_buffer_size 128k;
|
||||
client_max_body_size 100m;
|
||||
}
|
||||
|
||||
location ^~ /SOGo {
|
||||
proxy_pass http://sogo:20000;
|
||||
proxy_pass http://172.22.1.252:20000;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header x-webobjects-server-protocol HTTP/1.0;
|
||||
proxy_set_header x-webobjects-remote-host $remote_addr;
|
||||
proxy_set_header x-webobjects-server-name $server_name;
|
||||
proxy_set_header x-webobjects-server-url $scheme://$host;
|
||||
proxy_set_header x-webobjects-server-url $scheme://$host:$server_port;
|
||||
proxy_set_header x-webobjects-server-port $server_port;
|
||||
#proxy_connect_timeout 90;
|
||||
#proxy_send_timeout 90;
|
||||
#proxy_read_timeout 90;
|
||||
#proxy_buffer_size 4k;
|
||||
#proxy_buffers 4 32k;
|
||||
#proxy_busy_buffers_size 64k;
|
||||
#proxy_temp_file_write_size 64k;
|
||||
client_body_buffer_size 128k;
|
||||
client_max_body_size 100m;
|
||||
break;
|
||||
}
|
||||
|
||||
location /SOGo.woa/WebServerResources/ {
|
||||
alias /usr/lib/GNUstep/SOGo/WebServerResources/;
|
||||
proxy_pass http://172.22.1.252:9192/WebServerResources/;
|
||||
proxy_set_header Host $host;
|
||||
proxy_cache sogo;
|
||||
proxy_cache_valid 200 1d;
|
||||
proxy_cache_use_stale error timeout invalid_header updating http_500 http_502 http_503 http_504;
|
||||
#alias /usr/lib/GNUstep/SOGo/WebServerResources/;
|
||||
allow all;
|
||||
}
|
||||
|
||||
location /.woa/WebServerResources/ {
|
||||
proxy_pass http://172.22.1.252:9192/WebServerResources/;
|
||||
proxy_set_header Host $host;
|
||||
proxy_cache sogo;
|
||||
proxy_cache_valid 200 1d;
|
||||
proxy_cache_use_stale error timeout invalid_header updating http_500 http_502 http_503 http_504;
|
||||
#alias /usr/lib/GNUstep/SOGo/WebServerResources/;
|
||||
allow all;
|
||||
}
|
||||
|
||||
location /SOGo/WebServerResources/ {
|
||||
alias /usr/lib/GNUstep/SOGo/WebServerResources/;
|
||||
proxy_pass http://172.22.1.252:9192/WebServerResources/;
|
||||
proxy_set_header Host $host;
|
||||
proxy_cache sogo;
|
||||
proxy_cache_valid 200 1d;
|
||||
proxy_cache_use_stale error timeout invalid_header updating http_500 http_502 http_503 http_504;
|
||||
#alias /usr/lib/GNUstep/SOGo/WebServerResources/;
|
||||
allow all;
|
||||
}
|
||||
|
||||
location (^/SOGo/so/ControlPanel/Products/[^/]*UI/Resources/.*\.(jpg|png|gif|css|js)$ {
|
||||
alias /usr/lib/GNUstep/SOGo/$1.SOGo/Resources/$2;
|
||||
proxy_pass http://172.22.1.252:9192/$1.SOGo/Resources/$2;
|
||||
proxy_set_header Host $host;
|
||||
proxy_cache sogo;
|
||||
proxy_cache_valid 200 1d;
|
||||
proxy_cache_use_stale error timeout invalid_header updating http_500 http_502 http_503 http_504;
|
||||
#alias /usr/lib/GNUstep/SOGo/$1.SOGo/Resources/$2;
|
||||
}
|
||||
}
|
||||
server {
|
||||
include /etc/nginx/conf.d/listen_plain.active;
|
||||
include /etc/nginx/mime.types;
|
||||
charset utf-8;
|
||||
override_charset on;
|
||||
index index.php index.html;
|
||||
include /etc/nginx/conf.d/server_name.active;
|
||||
error_log /var/log/nginx/error.log;
|
||||
access_log /var/log/nginx/access.log;
|
||||
root /web;
|
||||
|
||||
location ^~ /.well-known/acme-challenge/ {
|
||||
allow all;
|
||||
default_type "text/plain";
|
||||
}
|
||||
|
||||
# If behind reverse proxy, forwards the correct IP
|
||||
set_real_ip_from 172.22.1.1;
|
||||
real_ip_header X-Forwarded-For;
|
||||
real_ip_recursive on;
|
||||
|
||||
location = /principals/ {
|
||||
rewrite ^ $scheme://$host:$server_port/SOGo/dav;
|
||||
allow all;
|
||||
}
|
||||
|
||||
location ~ \.php$ {
|
||||
try_files $uri =404;
|
||||
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
||||
fastcgi_pass phpfpm:9000;
|
||||
fastcgi_index index.php;
|
||||
include /etc/nginx/fastcgi_params;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||
fastcgi_param PATH_INFO $fastcgi_path_info;
|
||||
fastcgi_param PHP_VALUE "max_execution_time = 1200
|
||||
max_input_time = 1200
|
||||
memory_limit = 64M";
|
||||
fastcgi_read_timeout 1200;
|
||||
}
|
||||
|
||||
rewrite ^(/save.+)$ /rspamd$1 last;
|
||||
location /rspamd/ {
|
||||
proxy_pass http://172.22.1.253:11334/;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
add_header Strict-Transport-Security "max-age=31536000; includeSubdomains";
|
||||
add_header X-Content-Type-Options nosniff;
|
||||
add_header X-Frame-Options SAMEORIGIN;
|
||||
add_header X-XSS-Protection "1; mode=block";
|
||||
}
|
||||
|
||||
location ^~ /inc/init.sql {
|
||||
deny all;
|
||||
}
|
||||
|
||||
location ~ /(?:a|A)utodiscover/(?:a|A)utodiscover.xml {
|
||||
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
||||
fastcgi_pass phpfpm:9000;
|
||||
include /etc/nginx/fastcgi_params;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||
try_files /autodiscover.php =404;
|
||||
}
|
||||
|
||||
location ~ /(?:m|M)ail/(?:c|C)onfig-v1.1.xml {
|
||||
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
||||
fastcgi_pass phpfpm:9000;
|
||||
include /etc/nginx/fastcgi_params;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||
try_files /autoconfig.php =404;
|
||||
}
|
||||
|
||||
location ^~ /Microsoft-Server-ActiveSync {
|
||||
proxy_pass http://172.22.1.252:20000/SOGo/Microsoft-Server-ActiveSync;
|
||||
proxy_connect_timeout 1000;
|
||||
proxy_next_upstream timeout error;
|
||||
proxy_send_timeout 1000;
|
||||
proxy_read_timeout 1000;
|
||||
proxy_buffer_size 8k;
|
||||
proxy_buffers 4 32k;
|
||||
proxy_temp_file_write_size 64k;
|
||||
proxy_busy_buffers_size 64k;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header x-webobjects-server-protocol HTTP/1.0;
|
||||
proxy_set_header x-webobjects-remote-host $remote_addr;
|
||||
proxy_set_header x-webobjects-server-name $server_name;
|
||||
proxy_set_header x-webobjects-server-url $scheme://$host:$server_port;
|
||||
proxy_set_header x-webobjects-server-port $server_port;
|
||||
client_body_buffer_size 128k;
|
||||
client_max_body_size 100m;
|
||||
}
|
||||
|
||||
location ^~ /SOGo {
|
||||
proxy_pass http://172.22.1.252:20000;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header x-webobjects-server-protocol HTTP/1.0;
|
||||
proxy_set_header x-webobjects-remote-host $remote_addr;
|
||||
proxy_set_header x-webobjects-server-name $server_name;
|
||||
proxy_set_header x-webobjects-server-url $scheme://$host:$server_port;
|
||||
proxy_set_header x-webobjects-server-port $server_port;
|
||||
client_body_buffer_size 128k;
|
||||
client_max_body_size 100m;
|
||||
break;
|
||||
}
|
||||
|
||||
location /SOGo.woa/WebServerResources/ {
|
||||
proxy_pass http://172.22.1.252:9192/WebServerResources/;
|
||||
proxy_set_header Host $host;
|
||||
proxy_cache sogo;
|
||||
proxy_cache_valid 200 1d;
|
||||
proxy_cache_use_stale error timeout invalid_header updating http_500 http_502 http_503 http_504;
|
||||
#alias /usr/lib/GNUstep/SOGo/WebServerResources/;
|
||||
allow all;
|
||||
}
|
||||
|
||||
location /.woa/WebServerResources/ {
|
||||
proxy_pass http://172.22.1.252:9192/WebServerResources/;
|
||||
proxy_set_header Host $host;
|
||||
proxy_cache sogo;
|
||||
proxy_cache_valid 200 1d;
|
||||
proxy_cache_use_stale error timeout invalid_header updating http_500 http_502 http_503 http_504;
|
||||
#alias /usr/lib/GNUstep/SOGo/WebServerResources/;
|
||||
allow all;
|
||||
}
|
||||
|
||||
location /SOGo/WebServerResources/ {
|
||||
proxy_pass http://172.22.1.252:9192/WebServerResources/;
|
||||
proxy_set_header Host $host;
|
||||
proxy_cache sogo;
|
||||
proxy_cache_valid 200 1d;
|
||||
proxy_cache_use_stale error timeout invalid_header updating http_500 http_502 http_503 http_504;
|
||||
#alias /usr/lib/GNUstep/SOGo/WebServerResources/;
|
||||
allow all;
|
||||
}
|
||||
|
||||
location (^/SOGo/so/ControlPanel/Products/[^/]*UI/Resources/.*\.(jpg|png|gif|css|js)$ {
|
||||
proxy_pass http://172.22.1.252:9192/$1.SOGo/Resources/$2;
|
||||
proxy_set_header Host $host;
|
||||
proxy_cache sogo;
|
||||
proxy_cache_valid 200 1d;
|
||||
proxy_cache_use_stale error timeout invalid_header updating http_500 http_502 http_503 http_504;
|
||||
#alias /usr/lib/GNUstep/SOGo/$1.SOGo/Resources/$2;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
listen ${HTTP_PORT};
|
|
@ -0,0 +1 @@
|
|||
listen ${HTTPS_PORT};
|
|
@ -0,0 +1 @@
|
|||
server_name ${MAILCOW_HOSTNAME} autodiscover.* autoconfig.*;
|
|
@ -1 +0,0 @@
|
|||
addNTA("mailcow-network", "nta for local")
|
|
@ -1,41 +0,0 @@
|
|||
allow-from=127.0.0.0/8, 192.168.0.0/16, 172.16.0.0/12, 10.0.0.0/8
|
||||
config-dir=/etc/powerdns
|
||||
daemon=no
|
||||
disable-syslog=yes
|
||||
dnssec=process
|
||||
dnssec-log-bogus=yes
|
||||
dont-query=10.0.0.0/8, 100.64.0.0/10, 169.254.0.0/16, 192.168.0.0/16, 172.16.0.0/12, ::1/128, fc00::/7, fe80::/10, 0.0.0.0/8, 192.0.0.0/24, 192.0.2.0/24, 198.51.100.0/24, 203.0.113.0/24, 240.0.0.0/4, ::/96, ::ffff:0:0/96, 100::/64, 2001:db8::/32
|
||||
export-etc-hosts=off
|
||||
# forward-zones=
|
||||
forward-zones-recurse=mailcow-network.=127.0.0.11
|
||||
local-address=0.0.0.0
|
||||
local-port=53
|
||||
loglevel=6
|
||||
# lowercase-outgoing=no
|
||||
lua-config-file=/etc/powerdns/pdns_custom.lua
|
||||
# max-cache-entries=1000000
|
||||
# max-cache-ttl=86400
|
||||
# max-mthreads=2048
|
||||
# max-negative-ttl=3600
|
||||
# max-packetcache-entries=500000
|
||||
# max-qperq=50
|
||||
# max-tcp-clients=128
|
||||
# max-tcp-per-client=0
|
||||
# max-total-msec=7000
|
||||
# minimum-ttl-override=0
|
||||
# network-timeout=1500
|
||||
# packetcache-servfail-ttl=60
|
||||
# packetcache-ttl=3600
|
||||
quiet=yes
|
||||
# security-poll-suffix=secpoll.powerdns.com.
|
||||
# serve-rfc1918=yes
|
||||
# server-down-max-fails=64
|
||||
# server-down-throttle-time=60
|
||||
setgid=pdns
|
||||
setuid=pdns
|
||||
# spoof-nearmiss-max=20
|
||||
# stack-size=200000
|
||||
# threads=2
|
||||
# trace=off
|
||||
version-string=PowerDNS Recursor
|
||||
webserver=no
|
|
@ -56,7 +56,7 @@ smtpd_error_sleep_time = 10s
|
|||
smtpd_hard_error_limit = ${stress?1}${stress:5}
|
||||
smtpd_helo_required = yes
|
||||
smtpd_proxy_timeout = 600s
|
||||
smtpd_recipient_restrictions = check_recipient_access proxy:mysql:/opt/postfix/conf/sql/mysql_tls_enforce_in_policy.cf, permit_sasl_authenticated, permit_mynetworks, reject_invalid_helo_hostname, reject_unknown_reverse_client_hostname, reject_unauth_destination
|
||||
smtpd_recipient_restrictions = permit_sasl_authenticated, permit_mynetworks, check_recipient_access proxy:mysql:/opt/postfix/conf/sql/mysql_tls_enforce_in_policy.cf, reject_invalid_helo_hostname, reject_unknown_reverse_client_hostname, reject_unauth_destination
|
||||
smtpd_sasl_auth_enable = yes
|
||||
smtpd_sasl_authenticated_header = yes
|
||||
smtpd_sasl_path = inet:dovecot:10001
|
||||
|
@ -83,10 +83,11 @@ virtual_alias_maps = proxy:mysql:/opt/postfix/conf/sql/mysql_virtual_alias_maps.
|
|||
virtual_gid_maps = static:5000
|
||||
virtual_mailbox_base = /var/vmail/
|
||||
virtual_mailbox_domains = proxy:mysql:/opt/postfix/conf/sql/mysql_virtual_domains_maps.cf
|
||||
virtual_mailbox_maps = proxy:mysql:/opt/postfix/conf/sql/mysql_virtual_mailbox_maps.cf, proxy:mysql:/opt/postfix/conf/sql/mysql_virtual_alias_domain_mailbox_maps.cf
|
||||
virtual_mailbox_maps = proxy:mysql:/opt/postfix/conf/sql/mysql_virtual_mailbox_maps.cf
|
||||
virtual_minimum_uid = 104
|
||||
virtual_transport = lmtp:inet:dovecot:24
|
||||
virtual_uid_maps = static:5000
|
||||
smtpd_milters = inet:rmilter:9900
|
||||
non_smtpd_milters = inet:rmilter:9900
|
||||
milter_mail_macros = i {mail_addr} {client_addr} {client_name} {auth_authen}
|
||||
mydestination = localhost.localdomain, localhost
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
user = mailcow
|
||||
password = mysafepasswd
|
||||
hosts = mysql
|
||||
dbname = mailcow
|
||||
query = SELECT DISTINCT CASE WHEN '%d' IN (SELECT domain FROM domain WHERE relay_all_recipients=1 AND domain='%d' AND backupmx=1) THEN '%s' ELSE (SELECT goto FROM alias WHERE address='%s' AND active='1') END AS result;
|
|
@ -1,5 +0,0 @@
|
|||
user = mailcow
|
||||
password = mysafepasswd
|
||||
hosts = mysql
|
||||
dbname = mailcow
|
||||
query = SELECT IF( EXISTS( SELECT 'TLS_ACTIVE' FROM alias LEFT OUTER JOIN mailbox ON mailbox.username = alias.address WHERE (address='%s' OR address IN (SELECT CONCAT('%u', '@', target_domain) FROM alias_domain WHERE alias_domain='%d')) AND mailbox.tls_enforce_in = '1' AND mailbox.active = '1'), 'reject_plaintext_session', 'DUNNO') AS 'tls_enforce_in';
|
|
@ -1,5 +0,0 @@
|
|||
user = mailcow
|
||||
password = mysafepasswd
|
||||
hosts = mysql
|
||||
dbname = mailcow
|
||||
query = SELECT IF( EXISTS( SELECT 'TLS_ACTIVE' FROM alias LEFT OUTER JOIN mailbox ON mailbox.username = alias.address WHERE (address='%s' OR address IN (SELECT CONCAT('%u', '@', target_domain) FROM alias_domain WHERE alias_domain='%d')) AND mailbox.tls_enforce_out = '1' AND mailbox.active = '1'), 'smtp_enforced_tls:', 'DUNNO') AS 'tls_enforce_out';
|
|
@ -1,6 +0,0 @@
|
|||
user = mailcow
|
||||
password = mysafepasswd
|
||||
hosts = mysql
|
||||
dbname = mailcow
|
||||
query = SELECT goto FROM alias,alias_domain WHERE alias_domain.alias_domain = '%d' and alias.address = CONCAT('@', alias_domain.target_domain) AND alias.active = 1 AND alias_domain.active='1'
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
user = mailcow
|
||||
password = mysafepasswd
|
||||
hosts = mysql
|
||||
dbname = mailcow
|
||||
query = SELECT maildir FROM mailbox,alias_domain WHERE alias_domain.alias_domain = '%d' and mailbox.username = CONCAT('%u', '@', alias_domain.target_domain) AND mailbox.active = 1 AND alias_domain.active='1'
|
|
@ -1,5 +0,0 @@
|
|||
user = mailcow
|
||||
password = mysafepasswd
|
||||
hosts = mysql
|
||||
dbname = mailcow
|
||||
query = SELECT goto FROM alias,alias_domain WHERE alias_domain.alias_domain = '%d' and alias.address = CONCAT('%u', '@', alias_domain.target_domain) AND alias.active = 1 AND alias_domain.active='1'
|
|
@ -1,5 +0,0 @@
|
|||
user = mailcow
|
||||
password = mysafepasswd
|
||||
hosts = mysql
|
||||
dbname = mailcow
|
||||
query = SELECT goto FROM alias WHERE address='%s' AND active='1';
|
|
@ -1,5 +0,0 @@
|
|||
user = mailcow
|
||||
password = mysafepasswd
|
||||
hosts = mysql
|
||||
dbname = mailcow
|
||||
query = SELECT alias_domain from alias_domain WHERE alias_domain='%s' AND active='1' UNION SELECT domain FROM domain WHERE domain='%s' AND active = '1' AND backupmx = '0'
|
|
@ -1,5 +0,0 @@
|
|||
user = mailcow
|
||||
password = mysafepasswd
|
||||
hosts = mysql
|
||||
dbname = mailcow
|
||||
query = SELECT maildir FROM mailbox WHERE username='%s' AND active = '1'
|
|
@ -1,5 +0,0 @@
|
|||
user = mailcow
|
||||
password = mysafepasswd
|
||||
hosts = mysql
|
||||
dbname = mailcow
|
||||
query = SELECT domain FROM domain WHERE domain='%s' AND backupmx = '1' AND active = '1'
|
|
@ -1,5 +0,0 @@
|
|||
user = mailcow
|
||||
password = mysafepasswd
|
||||
hosts = mysql
|
||||
dbname = mailcow
|
||||
query = SELECT goto FROM alias WHERE address='%s' AND active='1' AND domain IN (SELECT domain FROM domain WHERE domain='%d' AND active='1') UNION SELECT logged_in_as FROM sender_acl WHERE send_as='@%d' OR send_as='%s' AND logged_in_as NOT IN (SELECT goto FROM alias WHERE address='%s') UNION SELECT goto FROM alias,alias_domain WHERE alias_domain.alias_domain = '%d' AND alias.address = CONCAT('%u', '@', alias_domain.target_domain) AND alias.active ='1' AND alias_domain.active='1'
|
|
@ -1,5 +0,0 @@
|
|||
user = mailcow
|
||||
password = mysafepasswd
|
||||
hosts = mysql
|
||||
dbname = mailcow
|
||||
query = SELECT goto FROM spamalias WHERE address='%s' AND validity >= UNIX_TIMESTAMP()
|
|
@ -1,22 +1,34 @@
|
|||
<?php
|
||||
ini_set('error_reporting', 0);
|
||||
header('Content-Type: text/plain');
|
||||
require_once "vars.inc.php";
|
||||
ini_set('error_reporting', 0);
|
||||
$has_object = 0;
|
||||
header('Content-Type: text/plain');
|
||||
$dsn = $database_type . ':host=' . $database_host . ';dbname=' . $database_name;
|
||||
$opt = [
|
||||
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
||||
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
||||
PDO::ATTR_EMULATE_PREPARES => false,
|
||||
];
|
||||
$pdo = new PDO($dsn, $database_user, $database_pass, $opt);
|
||||
$stmt = $pdo->query("SELECT `domain` FROM `domain`");
|
||||
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
while ($row = array_shift($rows)) {
|
||||
echo strtolower(trim($row['domain'])) . PHP_EOL;
|
||||
try {
|
||||
$pdo = new PDO($dsn, $database_user, $database_pass, $opt);
|
||||
$stmt = $pdo->query("SELECT `domain` FROM `domain`");
|
||||
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
while ($row = array_shift($rows)) {
|
||||
$has_object = 1;
|
||||
echo strtolower(trim($row['domain'])) . PHP_EOL;
|
||||
}
|
||||
$stmt = $pdo->query("SELECT `alias_domain` FROM `alias_domain`");
|
||||
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
while ($row = array_shift($rows)) {
|
||||
$has_object = 1;
|
||||
echo strtolower(trim($row['alias_domain'])) . PHP_EOL;
|
||||
}
|
||||
if ($has_object == 0) {
|
||||
echo "dummy@domain.local";
|
||||
}
|
||||
}
|
||||
$stmt = $pdo->query("SELECT `alias_domain` FROM `alias_domain`");
|
||||
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
while ($row = array_shift($rows)) {
|
||||
echo strtolower(trim($row['alias_domain'])) . PHP_EOL;
|
||||
catch (PDOException $e) {
|
||||
echo "dummy@domain.local";
|
||||
exit;
|
||||
}
|
||||
?>
|
|
@ -1,19 +1,34 @@
|
|||
<?php
|
||||
ini_set('error_reporting', 0);
|
||||
/*
|
||||
The match section performs AND operation on different matches: for example, if you have from and rcpt in the same rule,
|
||||
then the rule matches only when from AND rcpt match. For similar matches, the OR rule applies: if you have multiple rcpt matches,
|
||||
then any of these will trigger the rule. If a rule is triggered then no more rules are matched.
|
||||
*/
|
||||
function parse_email($email) {
|
||||
if(!filter_var($email, FILTER_VALIDATE_EMAIL)) return false;
|
||||
$a = strrpos($email, '@');
|
||||
return array('local' => substr($email, 0, $a), 'domain' => substr($email, $a));
|
||||
}
|
||||
header('Content-Type: text/plain');
|
||||
require_once "vars.inc.php";
|
||||
|
||||
ini_set('error_reporting', 0);
|
||||
|
||||
$dsn = $database_type . ':host=' . $database_host . ';dbname=' . $database_name;
|
||||
$opt = [
|
||||
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
||||
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
||||
PDO::ATTR_EMULATE_PREPARES => false,
|
||||
];
|
||||
$pdo = new PDO($dsn, $database_user, $database_pass, $opt);
|
||||
try {
|
||||
$pdo = new PDO($dsn, $database_user, $database_pass, $opt);
|
||||
$stmt = $pdo->query("SELECT * FROM `filterconf`");
|
||||
}
|
||||
catch (PDOException $e) {
|
||||
echo 'settings { }';
|
||||
exit;
|
||||
}
|
||||
|
||||
?>
|
||||
settings {
|
||||
<?php
|
||||
|
@ -33,19 +48,40 @@ while ($row = array_shift($rows)) {
|
|||
$spamscore = $stmt->fetchAll(PDO::FETCH_COLUMN|PDO::FETCH_GROUP);
|
||||
|
||||
$stmt = $pdo->prepare("SELECT GROUP_CONCAT(REPLACE(`value`, '*', '.*') SEPARATOR '|') AS `value` FROM `filterconf`
|
||||
WHERE `object`= :object
|
||||
WHERE (`object`= :object OR `object`= :object_domain)
|
||||
AND (`option` = 'blacklist_from' OR `option` = 'whitelist_from')");
|
||||
$stmt->execute(array(':object' => $row['object']));
|
||||
$grouped_lists = $stmt->fetchAll(PDO::FETCH_COLUMN);
|
||||
$value_sane = preg_replace("/\.\./", ".", (preg_replace("/\*/", ".*", $grouped_lists[0])));
|
||||
$stmt->execute(array(':object' => $row['object'], ':object_domain' => substr(strrchr($row['object'], "@"), 1)));
|
||||
$grouped_lists = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
array_filter($grouped_lists);
|
||||
while ($grouped_list = array_shift($grouped_lists)) {
|
||||
$value_sane = preg_replace("/\.\./", ".", (preg_replace("/\*/", ".*", $grouped_list['value'])));
|
||||
if (!empty($value_sane)) {
|
||||
?>
|
||||
from = "/^((?!<?=$value_sane;?>).)*$/";
|
||||
<?php
|
||||
}
|
||||
}
|
||||
$local = parse_email($row['object'])['local'];
|
||||
$domain = parse_email($row['object'])['domain'];
|
||||
if (!empty($local) && !empty($local)) {
|
||||
?>
|
||||
rcpt = "/<?=$local;?>\+.*<?=$domain;?>/";
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
rcpt = "<?=$row['object'];?>";
|
||||
<?php
|
||||
$stmt = $pdo->prepare("SELECT `address` FROM `alias` WHERE `goto` = :object_goto AND `address` NOT LIKE '@%' AND `address` != :object_address");
|
||||
$stmt->execute(array(':object_goto' => $row['object'], ':object_address' => $row['object']));
|
||||
$stmt = $pdo->prepare("SELECT `address` FROM `alias` WHERE `goto` LIKE :object_goto AND `address` NOT LIKE '@%' AND `address` != :object_address");
|
||||
$stmt->execute(array(':object_goto' => '%' . $row['object'] . '%', ':object_address' => $row['object']));
|
||||
$rows_aliases_1 = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
while ($row_aliases_1 = array_shift($rows_aliases_1)) {
|
||||
$local = parse_email($row_aliases_1['address'])['local'];
|
||||
$domain = parse_email($row_aliases_1['address'])['domain'];
|
||||
if (!empty($local) && !empty($local)) {
|
||||
?>
|
||||
rcpt = "/<?=$local;?>\+.*<?=$domain;?>/";
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
rcpt = "<?=$row_aliases_1['address'];?>";
|
||||
<?php
|
||||
|
@ -55,10 +91,20 @@ while ($row = array_shift($rows)) {
|
|||
WHERE `mailbox`.`username` = :object");
|
||||
$stmt->execute(array(':object' => $row['object']));
|
||||
$rows_aliases_2 = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
while ($row_aliases_2 = array_filter(array_shift($rows_aliases_2))) {
|
||||
array_filter($rows_aliases_2);
|
||||
while ($row_aliases_2 = array_shift($rows_aliases_2)) {
|
||||
if (!empty($row_aliases_2['aliases'])) {
|
||||
$local = parse_email($row_aliases_2['aliases'])['local'];
|
||||
$domain = parse_email($row_aliases_2['aliases'])['domain'];
|
||||
if (!empty($local) && !empty($local)) {
|
||||
?>
|
||||
rcpt = "/<?=$local;?>\+.*<?=$domain;?>/";
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
rcpt = "<?=$row_aliases_2['aliases'];?>";
|
||||
<?php
|
||||
}
|
||||
}
|
||||
?>
|
||||
apply "default" {
|
||||
|
@ -68,6 +114,7 @@ while ($row = array_shift($rows)) {
|
|||
"add header" = <?=$spamscore['lowspamlevel'][0];?>;
|
||||
}
|
||||
}
|
||||
}
|
||||
<?php
|
||||
}
|
||||
|
||||
|
@ -81,7 +128,6 @@ while ($row = array_shift($rows)) {
|
|||
$username_sane = preg_replace("/[^a-zA-Z0-9]+/", "", $row['object']);
|
||||
?>
|
||||
whitelist_<?=$username_sane;?> {
|
||||
priority = high;
|
||||
<?php
|
||||
$stmt = $pdo->prepare("SELECT GROUP_CONCAT(REPLACE(`value`, '*', '.*') SEPARATOR '|') AS `value` FROM `filterconf`
|
||||
WHERE `object`= :object
|
||||
|
@ -94,27 +140,47 @@ while ($row = array_shift($rows)) {
|
|||
<?php
|
||||
if (!filter_var(trim($row['object']), FILTER_VALIDATE_EMAIL)) {
|
||||
?>
|
||||
rcpt = "/.*@<?=$username_userpref['username'];?>/";
|
||||
priority = medium;
|
||||
rcpt = "/.*@<?=$row['object'];?>/";
|
||||
<?php
|
||||
$stmt = $pdo->prepare("SELECT `alias_domain` FROM `alias_domain`
|
||||
WHERE `target_domain` = :object");
|
||||
$stmt->execute(array(':object' => $row['object']));
|
||||
$rows_domain_aliases = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
array_filter($rows_domain_aliases);
|
||||
while ($row_domain_aliases = array_shift($rows_domain_aliases)) {
|
||||
?>
|
||||
rcpt = "<?=$row_domain_aliases['alias_domain'];?>";
|
||||
rcpt = "/.*@<?=$row_domain_aliases['alias_domain'];?>/";
|
||||
<?php
|
||||
}
|
||||
}
|
||||
else {
|
||||
?>
|
||||
priority = high;
|
||||
<?php
|
||||
$local = parse_email($row['object'])['local'];
|
||||
$domain = parse_email($row['object'])['domain'];
|
||||
if (!empty($local) && !empty($local)) {
|
||||
?>
|
||||
rcpt = "/<?=$local;?>\+.*<?=$domain;?>/";
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
rcpt = "<?=$row['object'];?>";
|
||||
<?php
|
||||
}
|
||||
$stmt = $pdo->prepare("SELECT `address` FROM `alias` WHERE `goto` = :object_goto AND `address` NOT LIKE '@%' AND `address` != :object_address");
|
||||
$stmt->execute(array(':object_goto' => $row['object'], ':object_address' => $row['object']));
|
||||
$stmt = $pdo->prepare("SELECT `address` FROM `alias` WHERE `goto` LIKE :object_goto AND `address` NOT LIKE '@%' AND `address` != :object_address");
|
||||
$stmt->execute(array(':object_goto' => '%' . $row['object'] . '%', ':object_address' => $row['object']));
|
||||
$rows_aliases_wl_1 = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
array_filter($rows_aliases_wl_1);
|
||||
while ($row_aliases_wl_1 = array_shift($rows_aliases_wl_1)) {
|
||||
$local = parse_email($row_aliases_wl_1['address'])['local'];
|
||||
$domain = parse_email($row_aliases_wl_1['address'])['domain'];
|
||||
if (!empty($local) && !empty($local)) {
|
||||
?>
|
||||
rcpt = "/<?=$local;?>\+.*<?=$domain;?>/";
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
rcpt = "<?=$row_aliases_wl_1['address'];?>";
|
||||
<?php
|
||||
|
@ -124,10 +190,20 @@ while ($row = array_shift($rows)) {
|
|||
WHERE `mailbox`.`username` = :object");
|
||||
$stmt->execute(array(':object' => $row['object']));
|
||||
$rows_aliases_wl_2 = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
while ($row_aliases_wl_2 = array_filter(array_shift($rows_aliases_wl_2))) {
|
||||
array_filter($rows_aliases_wl_2);
|
||||
while ($row_aliases_wl_2 = array_shift($rows_aliases_wl_2)) {
|
||||
if (!empty($row_aliases_wl_2['aliases'])) {
|
||||
$local = parse_email($row_aliases_wl_2['aliases'])['local'];
|
||||
$domain = parse_email($row_aliases_wl_2['aliases'])['domain'];
|
||||
if (!empty($local) && !empty($local)) {
|
||||
?>
|
||||
rcpt = "/<?=$local;?>\+.*<?=$domain;?>/";
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
rcpt = "<?=$row_aliases_wl_2['aliases'];?>";
|
||||
<?php
|
||||
}
|
||||
}
|
||||
?>
|
||||
apply "default" {
|
||||
|
@ -147,7 +223,6 @@ while ($row = array_shift($rows)) {
|
|||
$username_sane = preg_replace("/[^a-zA-Z0-9]+/", "", $row['object']);
|
||||
?>
|
||||
blacklist_<?=$username_sane;?> {
|
||||
priority = medium;
|
||||
<?php
|
||||
$stmt = $pdo->prepare("SELECT GROUP_CONCAT(REPLACE(`value`, '*', '.*') SEPARATOR '|') AS `value` FROM `filterconf`
|
||||
WHERE `object`= :object
|
||||
|
@ -160,40 +235,70 @@ while ($row = array_shift($rows)) {
|
|||
<?php
|
||||
if (!filter_var(trim($row['object']), FILTER_VALIDATE_EMAIL)) {
|
||||
?>
|
||||
rcpt = "/.*@<?=$username_userpref['username'];?>/";
|
||||
priority = medium;
|
||||
rcpt = "/.*@<?=$row['object'];?>/";
|
||||
<?php
|
||||
$stmt = $pdo->prepare("SELECT `alias_domain` FROM `alias_domain`
|
||||
WHERE `target_domain` = :object");
|
||||
$stmt->execute(array(':object' => $row['object']));
|
||||
$rows_domain_aliases = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
array_filter($rows_domain_aliases);
|
||||
while ($row_domain_aliases = array_shift($rows_domain_aliases)) {
|
||||
?>
|
||||
rcpt = "<?=$row_domain_aliases['alias_domain'];?>";
|
||||
rcpt = "/.*@<?=$row_domain_aliases['alias_domain'];?>/";
|
||||
<?php
|
||||
}
|
||||
}
|
||||
else {
|
||||
?>
|
||||
priority = high;
|
||||
<?php
|
||||
$local = parse_email($row['object'])['local'];
|
||||
$domain = parse_email($row['object'])['domain'];
|
||||
if (!empty($local) && !empty($local)) {
|
||||
?>
|
||||
rcpt = "/<?=$local;?>\+.*<?=$domain;?>/";
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
rcpt = "<?=$row['object'];?>";
|
||||
<?php
|
||||
}
|
||||
$stmt = $pdo->prepare("SELECT `address` FROM `alias` WHERE `goto` = :object_goto AND `address` NOT LIKE '@%' AND `address` != :object_address");
|
||||
$stmt->execute(array(':object_goto' => $row['object'], ':object_address' => $row['object']));
|
||||
$rows_aliases_wl_1 = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
while ($row_aliases_wl_1 = array_shift($rows_aliases_wl_1)) {
|
||||
$stmt = $pdo->prepare("SELECT `address` FROM `alias` WHERE `goto` LIKE :object_goto AND `address` NOT LIKE '@%' AND `address` != :object_address");
|
||||
$stmt->execute(array(':object_goto' => '%' . $row['object'] . '%', ':object_address' => $row['object']));
|
||||
$rows_aliases_bl_1 = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
array_filter($rows_aliases_bl_1);
|
||||
while ($row_aliases_bl_1 = array_shift($rows_aliases_bl_1)) {
|
||||
$local = parse_email($row_aliases_bl_1['address'])['local'];
|
||||
$domain = parse_email($row_aliases_bl_1['address'])['domain'];
|
||||
if (!empty($local) && !empty($local)) {
|
||||
?>
|
||||
rcpt = "<?=$row_aliases_wl_1['address'];?>";
|
||||
rcpt = "/<?=$local;?>\+.*<?=$domain;?>/";
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
rcpt = "<?=$row_aliases_bl_1['address'];?>";
|
||||
<?php
|
||||
}
|
||||
$stmt = $pdo->prepare("SELECT CONCAT(`local_part`, '@', `alias_domain`.`alias_domain`) AS `aliases` FROM `mailbox`
|
||||
LEFT OUTER JOIN `alias_domain` on `mailbox`.`domain` = `alias_domain`.`target_domain`
|
||||
WHERE `mailbox`.`username` = :object");
|
||||
$stmt->execute(array(':object' => $row['object']));
|
||||
$rows_aliases_wl_2 = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
while ($row_aliases_wl_2 = array_filter(array_shift($rows_aliases_wl_2))) {
|
||||
$rows_aliases_bl_2 = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
array_filter($rows_aliases_bl_2);
|
||||
while ($row_aliases_bl_2 = array_shift($rows_aliases_bl_2)) {
|
||||
if (!empty($row_aliases_bl_2['aliases'])) {
|
||||
$local = parse_email($row_aliases_bl_2['aliases'])['local'];
|
||||
$domain = parse_email($row_aliases_bl_2['aliases'])['domain'];
|
||||
if (!empty($local) && !empty($local)) {
|
||||
?>
|
||||
rcpt = "<?=$row_aliases_wl_2['aliases'];?>";
|
||||
rcpt = "/<?=$local;?>\+.*<?=$domain;?>/";
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
rcpt = "<?=$row_aliases_bl_2['aliases'];?>";
|
||||
<?php
|
||||
}
|
||||
}
|
||||
?>
|
||||
apply "default" {
|
||||
|
@ -203,4 +308,4 @@ while ($row = array_shift($rows)) {
|
|||
<?php
|
||||
}
|
||||
?>
|
||||
}
|
||||
}
|
|
@ -1,17 +1,41 @@
|
|||
<?php
|
||||
ini_set('error_reporting', 0);
|
||||
header('Content-Type: text/plain');
|
||||
require_once "vars.inc.php";
|
||||
ini_set('error_reporting', 0);
|
||||
$has_object = 0;
|
||||
header('Content-Type: text/plain');
|
||||
$dsn = $database_type . ':host=' . $database_host . ';dbname=' . $database_name;
|
||||
$opt = [
|
||||
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
||||
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
||||
PDO::ATTR_EMULATE_PREPARES => false,
|
||||
];
|
||||
$pdo = new PDO($dsn, $database_user, $database_pass, $opt);
|
||||
$stmt = $pdo->query("SELECT `username` FROM `mailbox` WHERE `wants_tagged_subject` = '1'");
|
||||
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
while ($row = array_shift($rows)) {
|
||||
echo strtolower(trim($row['username'])) . PHP_EOL;
|
||||
try {
|
||||
$pdo = new PDO($dsn, $database_user, $database_pass, $opt);
|
||||
$stmt = $pdo->query("SELECT `username` FROM `mailbox` WHERE `wants_tagged_subject` = '1'");
|
||||
$rows_a = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
while ($row_a = array_shift($rows_a)) {
|
||||
$stmt = $pdo->prepare("SELECT `address` FROM `alias` WHERE `goto` REGEXP :username AND goto != `address` AND `address` NOT LIKE '@%'");
|
||||
$stmt->execute(array(':username' => '(^|,)'.$row_a['username'].'($|,)'));
|
||||
$rows_a_a = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
while ($row_a_a = array_shift($rows_a_a)) {
|
||||
echo strtolower(trim($row_a_a['address'])) . PHP_EOL;
|
||||
}
|
||||
$has_object = 1;
|
||||
echo strtolower(trim($row_a['username'])) . PHP_EOL;
|
||||
}
|
||||
$stmt = $pdo->query("SELECT CONCAT(`mailbox`.`local_part`, '@', `alias_domain`.`alias_domain`) AS `tag_ad` FROM `mailbox`
|
||||
INNER JOIN `alias_domain` ON `mailbox`.`domain` = `alias_domain`.`target_domain` WHERE `mailbox`.`wants_tagged_subject` = '1';");
|
||||
$rows_b = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
while ($row_b = array_shift($rows_b)) {
|
||||
$has_object = 1;
|
||||
echo strtolower(trim($row_b['tag_ad'])) . PHP_EOL;
|
||||
}
|
||||
if ($has_object == 0) {
|
||||
echo "dummy@domain.local";
|
||||
}
|
||||
}
|
||||
?>
|
||||
catch (PDOException $e) {
|
||||
echo "dummy@domain.local";
|
||||
exit;
|
||||
}
|
||||
?>
|
||||
|
|
|
@ -11,42 +11,51 @@ rspamd_config.MAILCOW_MOO = function (task)
|
|||
return true
|
||||
end
|
||||
|
||||
local modify_subject_map = rspamd_config:add_map({
|
||||
url = 'http://nginx:8081/tags.php',
|
||||
modify_subject_map = rspamd_config:add_map({
|
||||
url = 'http://172.22.1.251:8081/tags.php',
|
||||
type = 'map',
|
||||
description = 'Map of users to use subject tags for'
|
||||
})
|
||||
|
||||
local auth_domain_map = rspamd_config:add_map({
|
||||
url = 'http://nginx:8081/authoritative.php',
|
||||
auth_domain_map = rspamd_config:add_map({
|
||||
url = 'http://172.22.1.251:8081/authoritative.php',
|
||||
type = 'map',
|
||||
description = 'Map of domains we are authoritative for'
|
||||
})
|
||||
|
||||
rspamd_config.ADD_DELIMITER_TAG = {
|
||||
callback = function(task)
|
||||
local util = require("rspamd_util")
|
||||
local rspamd_logger = require "rspamd_logger"
|
||||
local user_tagged = task:get_recipients(1)[1]['user']
|
||||
local domain = task:get_recipients(1)[1]['domain']
|
||||
local user, tag = user_tagged:match("([^+]+)+(.*)")
|
||||
local authdomain = auth_domain_map:get_key(domain)
|
||||
rspamd_config:register_post_filter(function(task)
|
||||
local util = require("rspamd_util")
|
||||
local rspamd_logger = require "rspamd_logger"
|
||||
|
||||
if tag and authdomain then
|
||||
rspamd_logger.infox("Domain %s is part of mailcow, start reading tag settings", domain)
|
||||
local user_untagged = user .. '@' .. domain
|
||||
rspamd_logger.infox("Querying tag settings for user %1", user_untagged)
|
||||
if modify_subject_map:get_key(user_untagged) then
|
||||
rspamd_logger.infox("User wants subject modified for tagged mail")
|
||||
local tagged_rcpt = task:get_symbol("TAGGED_RCPT")
|
||||
local user = task:get_recipients(0)[1]['user']
|
||||
local domain = task:get_recipients(0)[1]['domain']
|
||||
local rcpt = user .. '@' .. domain
|
||||
local authdomain = auth_domain_map:get_key(domain)
|
||||
|
||||
if tagged_rcpt then
|
||||
local tag = tagged_rcpt[1].options[1]
|
||||
rspamd_logger.infox("found tag: %s", tag)
|
||||
local action = task:get_metric_action('default')
|
||||
rspamd_logger.infox("metric action now: %s", action)
|
||||
|
||||
if action ~= 'no action' and action ~= 'greylist' then
|
||||
rspamd_logger.infox("skipping tag handler for action: %s", action)
|
||||
return false
|
||||
end
|
||||
|
||||
if authdomain then
|
||||
rspamd_logger.infox("found mailcow domain %s", domain)
|
||||
rspamd_logger.infox("querying tag settings for user %s", rcpt)
|
||||
|
||||
if modify_subject_map:get_key(rcpt) then
|
||||
rspamd_logger.infox("user wants subject modified for tagged mail")
|
||||
local sbj = task:get_header('Subject')
|
||||
if tag then
|
||||
rspamd_logger.infox("Found tag %1, will modify subject header", tag)
|
||||
new_sbj = '=?UTF-8?B?' .. tostring(util.encode_base64('[' .. tag .. '] ' .. sbj)) .. '?='
|
||||
task:set_rmilter_reply({
|
||||
remove_headers = {['Subject'] = 1},
|
||||
add_headers = {['Subject'] = new_sbj}
|
||||
})
|
||||
end
|
||||
else
|
||||
rspamd_logger.infox("Add X-Moo-Tag header")
|
||||
task:set_rmilter_reply({
|
||||
|
@ -54,8 +63,44 @@ rspamd_config.ADD_DELIMITER_TAG = {
|
|||
})
|
||||
end
|
||||
else
|
||||
rspamd_logger.infox("Skip delimiter handling for untagged message or authenticated user")
|
||||
rspamd_logger.infox("skip delimiter handling for unknown domain")
|
||||
end
|
||||
return false
|
||||
end
|
||||
end)
|
||||
|
||||
rspamd_config.MRAPTOR = {
|
||||
callback = function(task)
|
||||
local parts = task:get_parts()
|
||||
local rspamd_logger = require "rspamd_logger"
|
||||
local rspamd_regexp = require "rspamd_regexp"
|
||||
|
||||
if parts then
|
||||
for _,p in ipairs(parts) do
|
||||
local mtype,subtype = p:get_type()
|
||||
local re = rspamd_regexp.create_cached('/(office|word|excel)/i')
|
||||
if re:match(subtype) then
|
||||
local content = tostring(p:get_content())
|
||||
local filename = p:get_filename()
|
||||
|
||||
local file = os.tmpname()
|
||||
f = io.open(file, "a+")
|
||||
f:write(content)
|
||||
f:close()
|
||||
|
||||
local scan = assert(io.popen('PATH=/usr/bin:/usr/local/bin mraptor ' .. file .. '> /dev/null 2>&1; echo $?', 'r'))
|
||||
local result = scan:read('*all')
|
||||
local exit_code = string.match(result, "%d+")
|
||||
rspamd_logger.infox(exit_code)
|
||||
scan:close()
|
||||
|
||||
if exit_code == "20" then
|
||||
rspamd_logger.infox("Reject dangerous macro in office file " .. filename)
|
||||
task:set_pre_result(rspamd_actions['reject'], 'Dangerous macro in office file ' .. filename)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
}
|
||||
|
|
204
data/web/add.php
204
data/web/add.php
|
@ -1,6 +1,6 @@
|
|||
<?php
|
||||
require_once("inc/prerequisites.inc.php");
|
||||
$AuthUsers = array("admin", "domainadmin");
|
||||
$AuthUsers = array("admin", "domainadmin", "user");
|
||||
if (!isset($_SESSION['mailcow_cc_role']) OR !in_array($_SESSION['mailcow_cc_role'], $AuthUsers)) {
|
||||
header('Location: /');
|
||||
exit();
|
||||
|
@ -77,7 +77,7 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm
|
|||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-2 col-sm-10">
|
||||
<button type="submit" name="trigger_mailbox_action" value="adddomain" class="btn btn-success"><?=$lang['add']['save'];?></button>
|
||||
<button type="submit" name="mailbox_add_domain" class="btn btn-success"><?=$lang['add']['save'];?></button>
|
||||
</div>
|
||||
</div>
|
||||
<p><span class="glyphicon glyphicon-exclamation-sign text-danger"></span> <?=$lang['add']['restart_sogo_hint'];?></p>
|
||||
|
@ -112,7 +112,7 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm
|
|||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-2 col-sm-10">
|
||||
<button type="submit" name="trigger_mailbox_action" value="addalias" class="btn btn-success "><?=$lang['add']['save'];?></button>
|
||||
<button type="submit" name="mailbox_add_alias" class="btn btn-success "><?=$lang['add']['save'];?></button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
@ -132,29 +132,12 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm
|
|||
<div class="form-group">
|
||||
<label class="control-label col-sm-2" for="target_domain"><?=$lang['add']['target_domain'];?></label>
|
||||
<div class="col-sm-10">
|
||||
<select name="target_domain" id="target_domain" title="<?=$lang['add']['select'];?>">
|
||||
<?php
|
||||
try {
|
||||
$stmt = $pdo->prepare("SELECT `domain` FROM `domain`
|
||||
WHERE `domain` IN (
|
||||
SELECT `domain` FROM `domain_admins`
|
||||
WHERE `username`= :username
|
||||
AND `active`='1'
|
||||
)
|
||||
OR 'admin' = :admin");
|
||||
$stmt->execute(array(':username' => $_SESSION['mailcow_cc_username'], ':admin' => $_SESSION['mailcow_cc_role']));
|
||||
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
}
|
||||
catch(PDOException $e) {
|
||||
$_SESSION['return'] = array(
|
||||
'type' => 'danger',
|
||||
'msg' => 'MySQL: '.$e
|
||||
);
|
||||
}
|
||||
while ($row = array_shift($rows)) {
|
||||
echo "<option>".htmlspecialchars($row['domain'])."</option>";
|
||||
}
|
||||
?>
|
||||
<select name="target_domain" id="target_domain" title="<?=$lang['add']['select'];?>" required>
|
||||
<?php
|
||||
foreach (mailbox_get_domains() as $domain) {
|
||||
echo "<option>".htmlspecialchars($domain)."</option>";
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -167,7 +150,7 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm
|
|||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-2 col-sm-10">
|
||||
<button type="submit" name="trigger_mailbox_action" value="addaliasdomain" class="btn btn-success "><?=$lang['add']['save'];?></button>
|
||||
<button type="submit" name="mailbox_add_alias_domain" class="btn btn-success "><?=$lang['add']['save'];?></button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
@ -188,30 +171,13 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm
|
|||
<div class="col-sm-10">
|
||||
<select id="addSelectDomain" name="domain" id="domain" title="<?=$lang['add']['select'];?>" required>
|
||||
<?php
|
||||
try {
|
||||
$stmt = $pdo->prepare("SELECT `domain` FROM `domain`
|
||||
WHERE `domain` IN (
|
||||
SELECT `domain` FROM `domain_admins`
|
||||
WHERE `username`= :username
|
||||
AND `active`='1'
|
||||
)
|
||||
OR 'admin' = :admin");
|
||||
$stmt->execute(array(':username' => $_SESSION['mailcow_cc_username'], ':admin' => $_SESSION['mailcow_cc_role']));
|
||||
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
}
|
||||
catch(PDOException $e) {
|
||||
$_SESSION['return'] = array(
|
||||
'type' => 'danger',
|
||||
'msg' => 'MySQL: '.$e
|
||||
);
|
||||
}
|
||||
while ($row = array_shift($rows)) {
|
||||
echo "<option>".htmlspecialchars($row['domain'])."</option>";
|
||||
foreach (mailbox_get_domains() as $domain) {
|
||||
echo "<option>".htmlspecialchars($domain)."</option>";
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2" for="name"><?=$lang['add']['full_name'];?></label>
|
||||
<div class="col-sm-10">
|
||||
|
@ -247,7 +213,61 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm
|
|||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-2 col-sm-10">
|
||||
<button type="submit" name="trigger_mailbox_action" value="addmailbox" class="btn btn-success "><?=$lang['add']['save'];?></button>
|
||||
<button type="submit" name="mailbox_add_mailbox" class="btn btn-success "><?=$lang['add']['save'];?></button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<?php
|
||||
}
|
||||
elseif (isset($_GET['resource'])) {
|
||||
?>
|
||||
<h4><?=$lang['add']['resource'];?></h4>
|
||||
<form class="form-horizontal" role="form" method="post" action="<?=($FORM_ACTION == "previous") ? $_SESSION['return_to'] : null;?>">
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2" for="description"><?=$lang['add']['description'];?></label>
|
||||
<div class="col-sm-10">
|
||||
<input type="text" class="form-control" name="description" id="description" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2" for="domain"><?=$lang['add']['domain'];?>:</label>
|
||||
<div class="col-sm-10">
|
||||
<select name="domain" id="domain" title="<?=$lang['add']['select'];?>" required>
|
||||
<?php
|
||||
foreach (mailbox_get_domains() as $domain) {
|
||||
echo "<option>".htmlspecialchars($domain)."</option>";
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2" for="domain"><?=$lang['add']['kind'];?>:</label>
|
||||
<div class="col-sm-10">
|
||||
<select name="kind" id="kind" title="<?=$lang['add']['select'];?>" required>
|
||||
<option value="location">Location</option>
|
||||
<option value="group">Group</option>
|
||||
<option value="thing">Thing</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-2 col-sm-10">
|
||||
<div class="checkbox">
|
||||
<label><input type="checkbox" name="active" checked> <?=$lang['add']['active'];?></label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-2 col-sm-10">
|
||||
<div class="checkbox">
|
||||
<label><input type="checkbox" name="multiple_bookings" checked> <?=$lang['add']['multiple_bookings'];?></label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-2 col-sm-10">
|
||||
<button type="submit" name="mailbox_add_resource" class="btn btn-success "><?=$lang['add']['save'];?></button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
@ -259,6 +279,94 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm
|
|||
<?php
|
||||
}
|
||||
}
|
||||
elseif (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "user")) {
|
||||
if (isset($_GET['syncjob'])) {
|
||||
?>
|
||||
<h4><?=$lang['add']['syncjob'];?></h4>
|
||||
<p><?=$lang['add']['syncjob_hint'];?></p>
|
||||
<form class="form-horizontal" role="form" method="post" action="<?=($FORM_ACTION == "previous") ? $_SESSION['return_to'] : null;?>">
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2" for="host1"><?=$lang['add']['hostname'];?></label>
|
||||
<div class="col-sm-10">
|
||||
<input type="text" class="form-control" name="host1" id="host1" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2" for="port1">Port</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="number" class="form-control" name="port1" id="port1" min="1" max="65535" value="143" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2" for="user1"><?=$lang['add']['username'];?></label>
|
||||
<div class="col-sm-10">
|
||||
<input type="text" class="form-control" name="user1" id="user1" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2" for="password1"><?=$lang['add']['password'];?></label>
|
||||
<div class="col-sm-10">
|
||||
<input type="text" class="form-control" name="password1" id="password1" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2" for="enc1"><?=$lang['add']['enc_method'];?></label>
|
||||
<div class="col-sm-10">
|
||||
<select name="enc1" id="enc1" title="<?=$lang['add']['select'];?>" required>
|
||||
<option selected>TLS</option>
|
||||
<option>SSL</option>
|
||||
<option>PLAIN</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2" for="mins_interval"><?=$lang['add']['mins_interval'];?></label>
|
||||
<div class="col-sm-10">
|
||||
<input type="number" class="form-control" name="mins_interval" min="10" max="3600" value="20" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2" for="subfolder2"><?=$lang['edit']['subfolder2'];?></label>
|
||||
<div class="col-sm-10">
|
||||
<input type="text" class="form-control" name="subfolder2" id="subfolder2" value="External">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2" for="maxage"><?=$lang['edit']['maxage'];?></label>
|
||||
<div class="col-sm-10">
|
||||
<input type="number" class="form-control" name="maxage" id="maxage" min="0" max="32000" value="0">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2" for="exclude"><?=$lang['add']['exclude'];?></label>
|
||||
<div class="col-sm-10">
|
||||
<input type="text" class="form-control" name="exclude" id="exclude" value="(?i)spam|(?i)junk">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-2 col-sm-10">
|
||||
<div class="checkbox">
|
||||
<label><input type="checkbox" name="delete2duplicates" checked> <?=$lang['add']['delete2duplicates'];?></label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-2 col-sm-10">
|
||||
<div class="checkbox">
|
||||
<label><input type="checkbox" name="active" checked> <?=$lang['add']['active'];?></label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-2 col-sm-10">
|
||||
<button type="submit" name="add_syncjob" value="1" class="btn btn-success "><?=$lang['add']['save'];?></button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
|
||||
else {
|
||||
?>
|
||||
<div class="alert alert-danger" role="alert"><?=$lang['danger']['access_denied'];?></div>
|
||||
|
|
|
@ -4,251 +4,302 @@ require_once("inc/prerequisites.inc.php");
|
|||
if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == "admin") {
|
||||
require_once("inc/header.inc.php");
|
||||
$_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
|
||||
$tfa_data = get_tfa();
|
||||
?>
|
||||
<div class="container">
|
||||
<h4><span class="glyphicon glyphicon-user" aria-hidden="true"></span> <?=$lang['admin']['access'];?></h4>
|
||||
<h4><span class="glyphicon glyphicon-user" aria-hidden="true"></span> <?=$lang['admin']['access'];?></h4>
|
||||
|
||||
<div class="panel-group" id="accordion_access">
|
||||
<div class="panel panel-danger">
|
||||
<div class="panel-heading"><?=$lang['admin']['admin_details'];?></div>
|
||||
<div class="panel-body">
|
||||
<form class="form-horizontal" autocapitalize="none" autocorrect="off" role="form" method="post">
|
||||
<?php
|
||||
try {
|
||||
$stmt = $pdo->prepare("SELECT `username` FROM `admin`
|
||||
WHERE `superadmin`='1' and active='1'");
|
||||
$stmt->execute();
|
||||
$AdminData = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
}
|
||||
catch(PDOException $e) {
|
||||
$_SESSION['return'] = array(
|
||||
'type' => 'danger',
|
||||
'msg' => 'MySQL: '.$e
|
||||
);
|
||||
}
|
||||
?>
|
||||
<input type="hidden" name="admin_user_now" value="<?=htmlspecialchars($AdminData['username']);?>">
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2" for="admin_user"><?=$lang['admin']['admin'];?>:</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="text" class="form-control" name="admin_user" id="admin_user" value="<?=htmlspecialchars($AdminData['username']);?>" required>
|
||||
↳ <kbd>a-z A-Z - _ .</kbd>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2" for="admin_pass"><?=$lang['admin']['password'];?>:</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="password" class="form-control" name="admin_pass" id="admin_pass" placeholder="<?=$lang['admin']['unchanged_if_empty'];?>">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2" for="admin_pass2"><?=$lang['admin']['password_repeat'];?>:</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="password" class="form-control" name="admin_pass2" id="admin_pass2">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-2 col-sm-10">
|
||||
<button type="submit" name="trigger_set_admin" class="btn btn-default"><?=$lang['admin']['save'];?></button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel panel-default">
|
||||
<div style="cursor:pointer;" class="panel-heading" data-toggle="collapse" data-parent="#accordion_access" data-target="#collapseDomAdmins">
|
||||
<span class="accordion-toggle"><?=$lang['admin']['domain_admins'];?></span>
|
||||
</div>
|
||||
<div id="collapseDomAdmins" class="panel-collapse collapse">
|
||||
<div class="panel-body">
|
||||
<form method="post">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped sortable-theme-bootstrap" data-sortable id="domainadminstable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="sort-table" style="min-width: 100px;"><?=$lang['admin']['username'];?></th>
|
||||
<th class="sort-table" style="min-width: 166px;"><?=$lang['admin']['admin_domains'];?></th>
|
||||
<th class="sort-table" style="min-width: 76px;"><?=$lang['admin']['active'];?></th>
|
||||
<th style="text-align: right; min-width: 200px;" data-sortable="false"><?=$lang['admin']['action'];?></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php
|
||||
try {
|
||||
$stmt = $pdo->query("SELECT DISTINCT
|
||||
`username`,
|
||||
CASE WHEN `active`='1' THEN '".$lang['admin']['yes']."' ELSE '".$lang['admin']['no']."' END AS `active`
|
||||
FROM `domain_admins`
|
||||
WHERE `username` IN (
|
||||
SELECT `username` FROM `admin`
|
||||
WHERE `superadmin`!='1'
|
||||
)");
|
||||
$rows_username = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
}
|
||||
catch(PDOException $e) {
|
||||
$_SESSION['return'] = array(
|
||||
'type' => 'danger',
|
||||
'msg' => 'MySQL: '.$e
|
||||
);
|
||||
}
|
||||
if(!empty($rows_username)):
|
||||
while ($row_user_state = array_shift($rows_username)):
|
||||
?>
|
||||
<tr id="data">
|
||||
<td><?=htmlspecialchars(strtolower($row_user_state['username']));?></td>
|
||||
<td>
|
||||
<?php
|
||||
try {
|
||||
$stmt = $pdo->prepare("SELECT `domain` FROM `domain_admins` WHERE `username` = :username");
|
||||
$stmt->execute(array('username' => $row_user_state['username']));
|
||||
$rows_domain = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
}
|
||||
catch(PDOException $e) {
|
||||
$_SESSION['return'] = array(
|
||||
'type' => 'danger',
|
||||
'msg' => 'MySQL: '.$e
|
||||
);
|
||||
}
|
||||
while ($row_domain = array_shift($rows_domain)) {
|
||||
echo htmlspecialchars($row_domain['domain']).'<br />';
|
||||
}
|
||||
?>
|
||||
</td>
|
||||
<td><?=$row_user_state['active'];?></td>
|
||||
<td style="text-align: right;">
|
||||
<div class="btn-group">
|
||||
<a href="edit.php?domainadmin=<?=$row_user_state['username'];?>" class="btn btn-xs btn-default"><span class="glyphicon glyphicon-pencil"></span> <?=$lang['admin']['edit'];?></a>
|
||||
<a href="delete.php?domainadmin=<?=$row_user_state['username'];?>" class="btn btn-xs btn-danger"><span class="glyphicon glyphicon-trash"></span> <?=$lang['admin']['remove'];?></a>
|
||||
</div>
|
||||
</td>
|
||||
</td>
|
||||
</tr>
|
||||
<div class="panel-group" id="accordion_access">
|
||||
<div class="panel panel-danger">
|
||||
<div class="panel-heading"><?=$lang['admin']['admin_details'];?></div>
|
||||
<div class="panel-body">
|
||||
<form class="form-horizontal" autocapitalize="none" autocorrect="off" role="form" method="post">
|
||||
<?php $admindetails = get_admin_details(); ?>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-3" for="admin_user"><?=$lang['admin']['admin'];?>:</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" class="form-control" name="admin_user" id="admin_user" value="<?=htmlspecialchars($admindetails['username']);?>" required>
|
||||
↳ <kbd>a-z A-Z - _ .</kbd>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-3" for="admin_pass"><?=$lang['admin']['password'];?>:</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="password" class="form-control" name="admin_pass" id="admin_pass" placeholder="<?=$lang['admin']['unchanged_if_empty'];?>">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-3" for="admin_pass2"><?=$lang['admin']['password_repeat'];?>:</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="password" class="form-control" name="admin_pass2" id="admin_pass2">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-3 col-sm-9">
|
||||
<button type="submit" name="edit_admin_account" class="btn btn-default"><?=$lang['admin']['save'];?></button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<hr>
|
||||
<div class="row">
|
||||
<div class="col-sm-3 col-xs-5 text-right"><?=$lang['tfa']['tfa'];?>:</div>
|
||||
<div class="col-sm-9 col-xs-7">
|
||||
<p id="tfa_pretty"><?=$tfa_data['pretty'];?></p>
|
||||
<div id="tfa_additional">
|
||||
<?php if($tfa_data['additional']):
|
||||
foreach ($tfa_data['additional'] as $key_info): ?>
|
||||
<form style="display:inline;" method="post">
|
||||
<input type="hidden" name="unset_tfa_key" value="<?=$key_info['id'];?>" />
|
||||
<div style="padding:4px;margin:4px" class="label label-<?=($_SESSION['tfa_id'] == $key_info['id']) ? 'success' : 'default'; ?>">
|
||||
<?=$key_info['key_id'];?>
|
||||
<a href="#" style="font-weight:bold;color:white" onClick="$(this).closest('form').submit()">[<?=strtolower($lang['admin']['remove']);?>]</a>
|
||||
</div>
|
||||
</form>
|
||||
<?php endforeach;
|
||||
endif;?>
|
||||
</div>
|
||||
<br />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-3 col-xs-5 text-right"><?=$lang['tfa']['set_tfa'];?>:</div>
|
||||
<div class="col-sm-9 col-xs-7">
|
||||
<select data-width="auto" id="selectTFA" class="selectpicker" title="<?=$lang['tfa']['select'];?>">
|
||||
<option value="yubi_otp"><?=$lang['tfa']['yubi_otp'];?></option>
|
||||
<option value="u2f"><?=$lang['tfa']['u2f'];?></option>
|
||||
<option value="none"><?=$lang['tfa']['none'];?></option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel panel-default">
|
||||
<div style="cursor:pointer;" class="panel-heading" data-toggle="collapse" data-parent="#accordion_access" data-target="#collapseDomAdmins">
|
||||
<span class="accordion-toggle"><?=$lang['admin']['domain_admins'];?></span>
|
||||
</div>
|
||||
<div id="collapseDomAdmins" class="panel-collapse collapse">
|
||||
<div class="panel-body">
|
||||
<form method="post">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped sortable-theme-bootstrap" data-sortable id="domainadminstable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="sort-table" style="min-width: 100px;"><?=$lang['admin']['username'];?></th>
|
||||
<th class="sort-table" style="min-width: 166px;"><?=$lang['admin']['admin_domains'];?></th>
|
||||
<th class="sort-table" style="min-width: 76px;"><?=$lang['admin']['active'];?></th>
|
||||
<th class="sort-table" style="min-width: 76px;"><?=$lang['tfa']['tfa'];?></th>
|
||||
<th style="text-align: right; min-width: 200px;" data-sortable="false"><?=$lang['admin']['action'];?></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php
|
||||
foreach (get_domain_admins() as $domain_admin) {
|
||||
$da_data = get_domain_admin_details($domain_admin);
|
||||
if (!empty($da_data)):
|
||||
?>
|
||||
<tr id="data">
|
||||
<td><?=htmlspecialchars(strtolower($domain_admin));?></td>
|
||||
<td>
|
||||
<?php
|
||||
foreach ($da_data['selected_domains'] as $domain) {
|
||||
echo htmlspecialchars($domain).'<br />';
|
||||
}
|
||||
?>
|
||||
</td>
|
||||
<td><?=$da_data['active'];?></td>
|
||||
<td><?=empty($da_data['tfa_active_int']) ? "✘" : "✔";?></td>
|
||||
<td style="text-align: right;">
|
||||
<div class="btn-group">
|
||||
<a href="edit.php?domainadmin=<?=$domain_admin;?>" class="btn btn-xs btn-default"><span class="glyphicon glyphicon-pencil"></span> <?=$lang['admin']['edit'];?></a>
|
||||
<a href="delete.php?domainadmin=<?=$domain_admin;?>" class="btn btn-xs btn-danger"><span class="glyphicon glyphicon-trash"></span> <?=$lang['admin']['remove'];?></a>
|
||||
</div>
|
||||
</td>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<?php
|
||||
endwhile;
|
||||
else:
|
||||
?>
|
||||
<tr id="no-data"><td colspan="4" style="text-align: center; font-style: italic;"><?=$lang['admin']['no_record'];?></td></tr>
|
||||
<?php
|
||||
endif;
|
||||
?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</form>
|
||||
<small>
|
||||
<legend><?=$lang['admin']['add_domain_admin'];?></legend>
|
||||
<form class="form-horizontal" role="form" method="post">
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2" for="username"><?=$lang['admin']['username'];?>:</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="text" class="form-control" name="username" id="username" required>
|
||||
↳ <kbd>a-z A-Z - _ .</kbd>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2" for="name"><?=$lang['admin']['admin_domains'];?>:</label>
|
||||
<div class="col-sm-10">
|
||||
<select title="<?=$lang['admin']['search_domain_da'];?>" style="width:100%" name="domain[]" size="5" multiple>
|
||||
<?php
|
||||
try {
|
||||
$stmt = $pdo->query("SELECT domain FROM domain");
|
||||
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
}
|
||||
catch(PDOException $e) {
|
||||
$_SESSION['return'] = array(
|
||||
'type' => 'danger',
|
||||
'msg' => 'MySQL: '.$e
|
||||
);
|
||||
}
|
||||
while ($row = array_shift($rows)) {
|
||||
echo "<option>".htmlspecialchars($row['domain'])."</option>";
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2" for="password"><?=$lang['admin']['password'];?>:</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="password" class="form-control" name="password" id="password" placeholder="">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2" for="password2"><?=$lang['admin']['password_repeat'];?>:</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="password" class="form-control" name="password2" id="password2" placeholder="">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-2 col-sm-10">
|
||||
<div class="checkbox">
|
||||
<label><input type="checkbox" name="active" checked> <?=$lang['admin']['active'];?></label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-2 col-sm-10">
|
||||
<button type="submit" name="trigger_add_domain_admin" class="btn btn-default"><?=$lang['admin']['add'];?></button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
else:
|
||||
?>
|
||||
<tr id="no-data"><td colspan="4" style="text-align: center; font-style: italic;"><?=$lang['admin']['no_record'];?></td></tr>
|
||||
<?php
|
||||
endif;
|
||||
}
|
||||
?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</form>
|
||||
<small>
|
||||
<legend><?=$lang['admin']['add_domain_admin'];?></legend>
|
||||
<form class="form-horizontal" role="form" method="post">
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2" for="username"><?=$lang['admin']['username'];?>:</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="text" class="form-control" name="username" id="username" required>
|
||||
↳ <kbd>a-z A-Z - _ .</kbd>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2" for="name"><?=$lang['admin']['admin_domains'];?>:</label>
|
||||
<div class="col-sm-10">
|
||||
<select title="<?=$lang['admin']['search_domain_da'];?>" style="width:100%" name="domain[]" size="5" multiple>
|
||||
<?php
|
||||
foreach (mailbox_get_domains() as $domain) {
|
||||
echo "<option>".htmlspecialchars($domain)."</option>";
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2" for="password"><?=$lang['admin']['password'];?>:</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="password" class="form-control" name="password" id="password" placeholder="">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2" for="password2"><?=$lang['admin']['password_repeat'];?>:</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="password" class="form-control" name="password2" id="password2" placeholder="">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-2 col-sm-10">
|
||||
<div class="checkbox">
|
||||
<label><input type="checkbox" name="active" checked> <?=$lang['admin']['active'];?></label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-2 col-sm-10">
|
||||
<button type="submit" name="add_domain_admin" class="btn btn-default"><?=$lang['admin']['add'];?></button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h4><span class="glyphicon glyphicon-wrench" aria-hidden="true"></span> <?=$lang['admin']['configuration'];?></h4>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading"><?=$lang['admin']['dkim_keys'];?></div>
|
||||
<div id="collapseDKIM" class="panel-collapse">
|
||||
<div class="panel-body">
|
||||
<?php
|
||||
$dnstxt_folder = scandir($GLOBALS["MC_DKIM_TXTS"]);
|
||||
$dnstxt_files = array_diff($dnstxt_folder, array('.', '..'));
|
||||
foreach($dnstxt_files as $file) {
|
||||
$pubKey = file_get_contents($GLOBALS["MC_DKIM_TXTS"]."/".$file);
|
||||
$domain = substr($file, 0, -5);
|
||||
?>
|
||||
<div class="row">
|
||||
<div class="col-xs-2">
|
||||
<p>Domain: <strong><?=htmlspecialchars($domain);?></strong> (dkim._domainkey)</p>
|
||||
</div>
|
||||
<div class="col-xs-9">
|
||||
<pre>v=DKIM1;k=rsa;t=s;s=email;p=<?=$pubKey;?></pre>
|
||||
</div>
|
||||
<div class="col-xs-1">
|
||||
<form class="form-inline" role="form" method="post">
|
||||
<a href="#" onclick="$(this).closest('form').submit()"><span class="glyphicon glyphicon-remove-circle"></span></a>
|
||||
<input type="hidden" name="delete_dkim_record" value="<?=htmlspecialchars($file);?>">
|
||||
<input type="hidden" name="dkim[domain]" value="<?=$domain;?>">
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
<legend><?=$lang['admin']['dkim_add_key'];?></legend>
|
||||
<form class="form-inline" role="form" method="post">
|
||||
<div class="form-group">
|
||||
<label for="dkim_domain">Domain</label>
|
||||
<input class="form-control" id="dkim_domain" name="dkim[domain]" placeholder="example.org" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<select class="form-control" id="dkim_key_size" name="dkim[key_size]" title="<?=$lang['admin']['dkim_key_length'];?>" required>
|
||||
<option>1024</option>
|
||||
<option>2048</option>
|
||||
</select>
|
||||
</div>
|
||||
<button type="submit" name="add_dkim_record" class="btn btn-default"><span class="glyphicon glyphicon-plus"></span> <?=$lang['admin']['add'];?></button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h4><span class="glyphicon glyphicon-dashboard" aria-hidden="true"></span> <?=$lang['admin']['maintenance'];?></h4>
|
||||
<div class="panel-group" id="accordion_maint">
|
||||
<h4><span class="glyphicon glyphicon-wrench" aria-hidden="true"></span> <?=$lang['admin']['configuration'];?></h4>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading"><?=$lang['admin']['dkim_keys'];?></div>
|
||||
<div id="collapseDKIM" class="panel-collapse">
|
||||
<div class="panel-body">
|
||||
<p style="margin-bottom:40px"><?=$lang['admin']['dkim_key_hint'];?></p>
|
||||
<?php
|
||||
foreach(mailbox_get_domains() as $domain) {
|
||||
if (!empty($dkim = dkim_get_key_details($domain))) {
|
||||
?>
|
||||
<div class="row">
|
||||
<div class="col-xs-3">
|
||||
<p>Domain: <strong><?=htmlspecialchars($domain);?></strong><br />
|
||||
<span class="label label-success"><?=$lang['admin']['dkim_key_valid'];?></span>
|
||||
<span class="label label-info"><?=$dkim['length'];?> bit</span>
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-xs-8">
|
||||
<pre><?=$dkim['dkim_txt'];?></pre>
|
||||
</div>
|
||||
<div class="col-xs-1">
|
||||
<form class="form-inline" method="post">
|
||||
<input type="hidden" name="domain" value="<?=$domain;?>">
|
||||
<input type="hidden" name="dkim_delete_key" value="1">
|
||||
<a href="#" onclick="$(this).closest('form').submit()" data-toggle="tooltip" data-placement="top" title="<?=$lang['user']['delete_now'];?>"><span class="glyphicon glyphicon-remove"></span></a>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
else {
|
||||
?>
|
||||
<div class="row">
|
||||
<div class="col-xs-3">
|
||||
<p>Domain: <strong><?=htmlspecialchars($domain);?></strong><br /><span class="label label-danger"><?=$lang['admin']['dkim_key_missing'];?></span></p>
|
||||
</div>
|
||||
<div class="col-xs-8"><pre>-</pre></div>
|
||||
<div class="col-xs-1"> </div>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
foreach(mailbox_get_alias_domains($domain) as $alias_domain) {
|
||||
if (!empty($dkim = dkim_get_key_details($alias_domain))) {
|
||||
?>
|
||||
<div class="row">
|
||||
<div class="col-xs-offset-1 col-xs-2">
|
||||
<p><small>↳ Alias-Domain: <strong><?=htmlspecialchars($alias_domain);?></strong><br /></small>
|
||||
<span class="label label-success"><?=$lang['admin']['dkim_key_valid'];?></span>
|
||||
<span class="label label-info"><?=$dkim['length'];?> bit</span>
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-xs-8">
|
||||
<pre><?=$dkim['dkim_txt'];?></pre>
|
||||
</div>
|
||||
<div class="col-xs-1">
|
||||
<form class="form-inline" method="post">
|
||||
<input type="hidden" name="domain" value="<?=$alias_domain;?>">
|
||||
<input type="hidden" name="dkim_delete_key" value="1">
|
||||
<a href="#" onclick="$(this).closest('form').submit()" data-toggle="tooltip" data-placement="top" title="<?=$lang['user']['delete_now'];?>"><span class="glyphicon glyphicon-remove"></span></a>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
else {
|
||||
?>
|
||||
<div class="row">
|
||||
<div class="col-xs-2 col-xs-offset-1">
|
||||
<p><small>↳ Alias-Domain: <strong><?=htmlspecialchars($alias_domain);?></strong><br /></small><span class="label label-danger"><?=$lang['admin']['dkim_key_missing'];?></span></p>
|
||||
</div>
|
||||
<div class="col-xs-8"><pre>-</pre></div>
|
||||
<div class="col-xs-1"> </div>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach(dkim_get_blind_keys() as $blind) {
|
||||
if (!empty($dkim = dkim_get_key_details($blind))) {
|
||||
?>
|
||||
<div class="row">
|
||||
<div class="col-xs-3">
|
||||
<p>Domain: <strong><?=htmlspecialchars($blind);?></strong><br /><span class="label label-warning"><?=$lang['admin']['dkim_key_unused'];?></span></p>
|
||||
</div>
|
||||
<div class="col-xs-8">
|
||||
<pre><?=$dkim['dkim_txt'];?></pre>
|
||||
</div>
|
||||
<div class="col-xs-1">
|
||||
<form class="form-inline" method="post">
|
||||
<input type="hidden" name="domain" value="<?=$blind;?>">
|
||||
<input type="hidden" name="dkim_delete_key" value="1">
|
||||
<a href="#" onclick="$(this).closest('form').submit()" data-toggle="tooltip" data-placement="top" title="<?=$lang['user']['delete_now'];?>"><span class="glyphicon glyphicon-remove"></span></a>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
?>
|
||||
<legend style="margin-top:40px"><?=$lang['admin']['dkim_add_key'];?></legend>
|
||||
<form class="form-inline" role="form" method="post">
|
||||
<div class="form-group">
|
||||
<label for="domain">Domain</label>
|
||||
<input class="form-control" id="domain" name="domain" placeholder="example.org" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<select data-width="200px" class="form-control" id="key_size" name="key_size" title="<?=$lang['admin']['dkim_key_length'];?>" required>
|
||||
<option data-subtext="bits">1024</option>
|
||||
<option data-subtext="bits">2048</option>
|
||||
</select>
|
||||
</div>
|
||||
<button type="submit" name="dkim_add_key" class="btn btn-default"><span class="glyphicon glyphicon-plus"></span> <?=$lang['admin']['add'];?></button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel-group" id="accordion_maint">
|
||||
<div class="panel panel-default">
|
||||
<div style="cursor:pointer;" class="panel-heading" data-toggle="collapse" data-parent="#accordion_maint" data-target="#collapseSysinfo">
|
||||
<span class="accordion-toggle"><?=$lang['admin']['sys_info'];?></span>
|
||||
|
@ -268,10 +319,10 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div> <!-- /container -->
|
||||
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.11.4/jquery-ui.min.js" integrity="sha384-YWP9O4NjmcGo4oEJFXvvYSEzuHIvey+LbXkBNJ1Kd0yfugEZN9NCQNpRYBVC1RvA" crossorigin="anonymous"></script>
|
||||
<script src="js/sorttable.js"></script>
|
||||
<script src="js/admin.js"></script>
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
<?php
|
||||
header("Content-Type: application/xml");
|
||||
require_once "inc/vars.inc.php";
|
||||
require_once 'inc/vars.inc.php';
|
||||
require_once 'inc/functions.inc.php';
|
||||
|
||||
ini_set('error_reporting', '0');
|
||||
$config = array(
|
||||
'useEASforOutlook' => 'yes',
|
||||
'autodiscoverType' => 'activesync',
|
||||
|
@ -15,22 +17,44 @@ $config = array(
|
|||
'ssl' => 'on'
|
||||
),
|
||||
'activesync' => array(
|
||||
'url' => 'https://' . $mailcow_hostname . '/Microsoft-Server-ActiveSync'
|
||||
'url' => 'https://'.$mailcow_hostname.'/Microsoft-Server-ActiveSync'
|
||||
)
|
||||
);
|
||||
// If useEASforOutlook == no, the autodiscoverType option will be replaced to imap.
|
||||
|
||||
if(file_exists('inc/vars.local.inc.php')) {
|
||||
include_once 'inc/vars.local.inc.php';
|
||||
}
|
||||
|
||||
/* ---------- DO NOT MODIFY ANYTHING BEYOND THIS LINE. IGNORE AT YOUR OWN RISK. ---------- */
|
||||
|
||||
if ($config['useEASforOutlook'] == 'no') {
|
||||
if (strpos($_SERVER['HTTP_USER_AGENT'], 'Outlook')) {
|
||||
$config['autodiscoverType'] = 'imap';
|
||||
}
|
||||
}
|
||||
// Workaround for short open tags
|
||||
echo '<?xml version="1.0" encoding="utf-8" ?>';
|
||||
?>
|
||||
<Autodiscover xmlns="http://schemas.microsoft.com/exchange/autodiscover/responseschema/2006">
|
||||
<?php
|
||||
$data = trim(file_get_contents("php://input"));
|
||||
if(!$data) {
|
||||
|
||||
$dsn = "$database_type:host=$database_host;dbname=$database_name";
|
||||
$opt = [
|
||||
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
||||
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
||||
PDO::ATTR_EMULATE_PREPARES => false,
|
||||
];
|
||||
$pdo = new PDO($dsn, $database_user, $database_pass, $opt);
|
||||
$login_user = strtolower(trim($_SERVER['PHP_AUTH_USER']));
|
||||
$as = check_login($login_user, $_SERVER['PHP_AUTH_PW']);
|
||||
|
||||
if (!isset($_SERVER['PHP_AUTH_USER']) OR $as !== "user") {
|
||||
header('WWW-Authenticate: Basic realm=""');
|
||||
header('HTTP/1.0 401 Unauthorized');
|
||||
exit;
|
||||
} else {
|
||||
if (isset($_SERVER['PHP_AUTH_USER']) && isset($_SERVER['PHP_AUTH_PW'])) {
|
||||
if ($as === "user") {
|
||||
header("Content-Type: application/xml");
|
||||
echo '<?xml version="1.0" encoding="utf-8" ?><Autodiscover xmlns="http://schemas.microsoft.com/exchange/autodiscover/responseschema/2006">';
|
||||
|
||||
$data = trim(file_get_contents("php://input"));
|
||||
if(!$data) {
|
||||
list($usec, $sec) = explode(' ', microtime());
|
||||
echo '<Response>';
|
||||
echo '<Error Time="' . date('H:i:s', $sec) . substr($usec, 0, strlen($usec) - 2) . '" Id="2477272013">';
|
||||
|
@ -38,84 +62,81 @@ if(!$data) {
|
|||
echo '</Response>';
|
||||
echo '</Autodiscover>';
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
$discover = new SimpleXMLElement($data);
|
||||
$email = $discover->Request->EMailAddress;
|
||||
|
||||
$discover = new SimpleXMLElement($data);
|
||||
$email = $discover->Request->EMailAddress;
|
||||
|
||||
if ($config['autodiscoverType'] == 'imap') {
|
||||
?>
|
||||
<Response xmlns="http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a">
|
||||
<Account>
|
||||
<AccountType>email</AccountType>
|
||||
<Action>settings</Action>
|
||||
<Protocol>
|
||||
<Type>IMAP</Type>
|
||||
<Server><?php echo $config['imap']['server']; ?></Server>
|
||||
<Port><?php echo $config['imap']['port']; ?></Port>
|
||||
<DomainRequired>off</DomainRequired>
|
||||
<LoginName><?php echo $email; ?></LoginName>
|
||||
<SPA>off</SPA>
|
||||
<SSL><?php echo $config['imap']['ssl']; ?></SSL>
|
||||
<AuthRequired>on</AuthRequired>
|
||||
</Protocol>
|
||||
<Protocol>
|
||||
<Type>SMTP</Type>
|
||||
<Server><?php echo $config['smtp']['server']; ?></Server>
|
||||
<Port><?php echo $config['smtp']['port']; ?></Port>
|
||||
<DomainRequired>off</DomainRequired>
|
||||
<LoginName><?php echo $email; ?></LoginName>
|
||||
<SPA>off</SPA>
|
||||
<SSL><?php echo $config['smtp']['ssl']; ?></SSL>
|
||||
<AuthRequired>on</AuthRequired>
|
||||
<UsePOPAuth>on</UsePOPAuth>
|
||||
<SMTPLast>off</SMTPLast>
|
||||
</Protocol>
|
||||
</Account>
|
||||
</Response>
|
||||
<?php
|
||||
}
|
||||
else if ($config['autodiscoverType'] == 'activesync') {
|
||||
$dsn = "$database_type:host=$database_host;dbname=$database_name";
|
||||
$opt = [
|
||||
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
||||
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
||||
PDO::ATTR_EMULATE_PREPARES => false,
|
||||
];
|
||||
$pdo = new PDO($dsn, $database_user, $database_pass, $opt);
|
||||
$username = trim($email);
|
||||
try {
|
||||
$stmt = $pdo->prepare("SELECT `name` FROM `mailbox` WHERE `username`= :username");
|
||||
$stmt->execute(array(':username' => $username));
|
||||
$MailboxData = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
}
|
||||
catch(PDOException $e) {
|
||||
die("Failed to determine name from SQL");
|
||||
}
|
||||
if (!empty($MailboxData['name'])) {
|
||||
$displayname = utf8_encode($MailboxData['name']);
|
||||
}
|
||||
else {
|
||||
$displayname = $email;
|
||||
}
|
||||
?>
|
||||
<Response xmlns="http://schemas.microsoft.com/exchange/autodiscover/mobilesync/responseschema/2006">
|
||||
<Culture>en:en</Culture>
|
||||
<User>
|
||||
<DisplayName><?php echo $displayname; ?></DisplayName>
|
||||
<EMailAddress><?php echo $email; ?></EMailAddress>
|
||||
</User>
|
||||
<Action>
|
||||
<Settings>
|
||||
<Server>
|
||||
<Type>MobileSync</Type>
|
||||
<Url><?php echo $config['activesync']['url']; ?></Url>
|
||||
<Name><?php echo $config['activesync']['url']; ?></Name>
|
||||
</Server>
|
||||
</Settings>
|
||||
</Action>
|
||||
</Response>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
if ($config['autodiscoverType'] == 'imap') {
|
||||
?>
|
||||
<Response xmlns="http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a">
|
||||
<Account>
|
||||
<AccountType>email</AccountType>
|
||||
<Action>settings</Action>
|
||||
<Protocol>
|
||||
<Type>IMAP</Type>
|
||||
<Server><?php echo $config['imap']['server']; ?></Server>
|
||||
<Port><?php echo $config['imap']['port']; ?></Port>
|
||||
<DomainRequired>off</DomainRequired>
|
||||
<LoginName><?php echo $email; ?></LoginName>
|
||||
<SPA>off</SPA>
|
||||
<SSL><?php echo $config['imap']['ssl']; ?></SSL>
|
||||
<AuthRequired>on</AuthRequired>
|
||||
</Protocol>
|
||||
<Protocol>
|
||||
<Type>SMTP</Type>
|
||||
<Server><?php echo $config['smtp']['server']; ?></Server>
|
||||
<Port><?php echo $config['smtp']['port']; ?></Port>
|
||||
<DomainRequired>off</DomainRequired>
|
||||
<LoginName><?php echo $email; ?></LoginName>
|
||||
<SPA>off</SPA>
|
||||
<SSL><?php echo $config['smtp']['ssl']; ?></SSL>
|
||||
<AuthRequired>on</AuthRequired>
|
||||
<UsePOPAuth>on</UsePOPAuth>
|
||||
<SMTPLast>off</SMTPLast>
|
||||
</Protocol>
|
||||
</Account>
|
||||
</Response>
|
||||
<?php
|
||||
}
|
||||
else if ($config['autodiscoverType'] == 'activesync') {
|
||||
$username = trim($email);
|
||||
try {
|
||||
$stmt = $pdo->prepare("SELECT `name` FROM `mailbox` WHERE `username`= :username");
|
||||
$stmt->execute(array(':username' => $username));
|
||||
$MailboxData = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
}
|
||||
catch(PDOException $e) {
|
||||
die("Failed to determine name from SQL");
|
||||
}
|
||||
if (!empty($MailboxData['name'])) {
|
||||
$displayname = utf8_encode($MailboxData['name']);
|
||||
}
|
||||
else {
|
||||
$displayname = $email;
|
||||
}
|
||||
?>
|
||||
<Response xmlns="http://schemas.microsoft.com/exchange/autodiscover/mobilesync/responseschema/2006">
|
||||
<Culture>en:en</Culture>
|
||||
<User>
|
||||
<DisplayName><?php echo $displayname; ?></DisplayName>
|
||||
<EMailAddress><?php echo $email; ?></EMailAddress>
|
||||
</User>
|
||||
<Action>
|
||||
<Settings>
|
||||
<Server>
|
||||
<Type>MobileSync</Type>
|
||||
<Url><?php echo $config['activesync']['url']; ?></Url>
|
||||
<Name><?php echo $config['activesync']['url']; ?></Name>
|
||||
</Server>
|
||||
</Settings>
|
||||
</Action>
|
||||
</Response>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
</Autodiscover>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
|
|
@ -6,7 +6,7 @@ if (!isset($_SESSION['mailcow_cc_role']) OR !in_array($_SESSION['mailcow_cc_role
|
|||
exit();
|
||||
}
|
||||
if ($_GET['ACTION'] == "start") {
|
||||
$request = xmlrpc_encode_request("supervisor.startProcessGroup", 'sogo-group', array('encoding'=>'utf-8'));
|
||||
$request = xmlrpc_encode_request("supervisor.startProcess", 'reconf-domains', array('encoding'=>'utf-8'));
|
||||
$context = stream_context_create(array('http' => array(
|
||||
'method' => "POST",
|
||||
'header' => "Content-Length: " . strlen($request),
|
||||
|
@ -18,11 +18,25 @@ if ($_GET['ACTION'] == "start") {
|
|||
echo '<b><span class="pull-right text-warning">' . $response['faultString'] . '</span></b>';
|
||||
}
|
||||
else {
|
||||
echo '<b><span class="pull-right text-success">OK</span></b>';
|
||||
sleep(4);
|
||||
$request = xmlrpc_encode_request("supervisor.startProcess", 'sogo', array('encoding'=>'utf-8'));
|
||||
$context = stream_context_create(array('http' => array(
|
||||
'method' => "POST",
|
||||
'header' => "Content-Length: " . strlen($request),
|
||||
'content' => $request
|
||||
)));
|
||||
$file = @file_get_contents("http://sogo:9191/RPC2", false, $context) or die("Cannot connect to $remote_server:$listener_port");
|
||||
$response = xmlrpc_decode($file);
|
||||
if (isset($response['faultString'])) {
|
||||
echo '<b><span class="pull-right text-warning">' . $response['faultString'] . '</span></b>';
|
||||
}
|
||||
else {
|
||||
echo '<b><span class="pull-right text-success">OK</span></b>';
|
||||
}
|
||||
}
|
||||
}
|
||||
elseif ($_GET['ACTION'] == "stop") {
|
||||
$request = xmlrpc_encode_request("supervisor.stopProcessGroup", 'sogo-group', array('encoding'=>'utf-8'));
|
||||
$request = xmlrpc_encode_request("supervisor.stopProcess", 'sogo', array('encoding'=>'utf-8'));
|
||||
$context = stream_context_create(array('http' => array(
|
||||
'method' => "POST",
|
||||
'header' => "Content-Length: " . strlen($request),
|
||||
|
@ -34,7 +48,21 @@ elseif ($_GET['ACTION'] == "stop") {
|
|||
echo '<b><span class="pull-right text-warning">' . $response['faultString'] . '</span></b>';
|
||||
}
|
||||
else {
|
||||
echo '<b><span class="pull-right text-success">OK</span></b>';
|
||||
sleep(1);
|
||||
$request = xmlrpc_encode_request("supervisor.stopProcess", 'reconf-domains', array('encoding'=>'utf-8'));
|
||||
$context = stream_context_create(array('http' => array(
|
||||
'method' => "POST",
|
||||
'header' => "Content-Length: " . strlen($request),
|
||||
'content' => $request
|
||||
)));
|
||||
$file = @file_get_contents("http://sogo:9191/RPC2", false, $context) or die("Cannot connect to $remote_server:$listener_port");
|
||||
$response = xmlrpc_decode($file);
|
||||
if (isset($response['faultString'])) {
|
||||
echo '<b><span class="pull-right text-warning">' . $response['faultString'] . '</span></b>';
|
||||
}
|
||||
else {
|
||||
echo '<b><span class="pull-right text-success">OK</span></b>';
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,19 @@
|
|||
.panel-heading div {
|
||||
margin-top: -18px;
|
||||
font-size: 15px;
|
||||
}
|
||||
.panel-heading div span {
|
||||
margin-left:5px;
|
||||
}
|
||||
.panel-body {
|
||||
display: none;
|
||||
}
|
||||
.clickable {
|
||||
cursor: pointer;
|
||||
}
|
||||
.progress {
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
.table>thead>tr>th {
|
||||
vertical-align: top !important;
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
#maxmsgsize { min-width: 80px; }
|
||||
#slider1 .slider-selection {
|
||||
background: #FFD700;
|
||||
}
|
||||
#slider1 .slider-track-high {
|
||||
background: #FF4500;
|
||||
}
|
||||
#slider1 .slider-track-low {
|
||||
background: #66CD00;
|
||||
}
|
||||
.striped:nth-child(odd) {
|
||||
background-color: #fff;
|
||||
}
|
||||
.striped:nth-child(even) {
|
||||
background-color: #fafafa;
|
||||
border:1px solid white;
|
||||
}
|
||||
.btn {
|
||||
text-transform: none;
|
||||
}
|
||||
.glyphicon-spin {
|
||||
font-size:12px;
|
||||
-webkit-animation: spin 2000ms infinite linear;
|
||||
animation: spin 2000ms infinite linear;
|
||||
}
|
||||
@-webkit-keyframes spin {
|
||||
0% {
|
||||
-webkit-transform: rotate(0deg);
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
-webkit-transform: rotate(359deg);
|
||||
transform: rotate(359deg);
|
||||
}
|
||||
}
|
||||
@keyframes spin {
|
||||
0% {
|
||||
-webkit-transform: rotate(0deg);
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
-webkit-transform: rotate(359deg);
|
||||
transform: rotate(359deg);
|
||||
}
|
||||
}
|
||||
pre{white-space:pre-wrap;white-space:-moz-pre-wrap;white-space:-pre-wrap;white-space:-o-pre-wrap;word-wrap:break-word;}
|
|
@ -0,0 +1,79 @@
|
|||
ul[id*="sortable"] { word-wrap: break-word; list-style-type: none; float: left; padding: 0 15px 0 0; width: 48%; cursor:move}
|
||||
ul[id$="sortable-active"] li {cursor:move; }
|
||||
ul[id$="sortable-inactive"] li {cursor:move }
|
||||
.list-heading { cursor:default !important}
|
||||
.ui-state-disabled { cursor:no-drop; color:#ccc; }
|
||||
.ui-state-highlight {background: #F5F5F5 !important; height: 41px !important; cursor:move }
|
||||
table[data-sortable] {
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
}
|
||||
table[data-sortable] th {
|
||||
vertical-align: bottom;
|
||||
font-weight: bold;
|
||||
}
|
||||
table[data-sortable] th, table[data-sortable] td {
|
||||
text-align: left;
|
||||
padding: 10px;
|
||||
}
|
||||
table[data-sortable] th:not([data-sortable="false"]) {
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
-o-user-select: none;
|
||||
user-select: none;
|
||||
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||
-webkit-touch-callout: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
table[data-sortable] th:after {
|
||||
content: "";
|
||||
visibility: hidden;
|
||||
display: inline-block;
|
||||
vertical-align: inherit;
|
||||
height: 0;
|
||||
width: 0;
|
||||
border-width: 5px;
|
||||
border-style: solid;
|
||||
border-color: transparent;
|
||||
margin-right: 1px;
|
||||
margin-left: 10px;
|
||||
float: right;
|
||||
}
|
||||
table[data-sortable] th[data-sortable="false"]:after {
|
||||
display: none;
|
||||
}
|
||||
table[data-sortable] th[data-sorted="true"]:after {
|
||||
visibility: visible;
|
||||
}
|
||||
table[data-sortable] th[data-sorted-direction="descending"]:after {
|
||||
border-top-color: inherit;
|
||||
margin-top: 8px;
|
||||
}
|
||||
table[data-sortable] th[data-sorted-direction="ascending"]:after {
|
||||
border-bottom-color: inherit;
|
||||
margin-top: 3px;
|
||||
}
|
||||
table[data-sortable].sortable-theme-bootstrap thead th {
|
||||
border-bottom: 2px solid #e0e0e0;
|
||||
}
|
||||
table[data-sortable].sortable-theme-bootstrap th[data-sorted="true"] {
|
||||
color: #3a87ad;
|
||||
background: #d9edf7;
|
||||
border-bottom-color: #bce8f1;
|
||||
}
|
||||
table[data-sortable].sortable-theme-bootstrap th[data-sorted="true"][data-sorted-direction="descending"]:after {
|
||||
border-top-color: #3a87ad;
|
||||
}
|
||||
table[data-sortable].sortable-theme-bootstrap th[data-sorted="true"][data-sorted-direction="ascending"]:after {
|
||||
border-bottom-color: #3a87ad;
|
||||
}
|
||||
table[data-sortable].sortable-theme-bootstrap.sortable-theme-bootstrap-striped tbody > tr:nth-child(odd) > td {
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
#data td, #no-data td {
|
||||
vertical-align: middle;
|
||||
}
|
||||
.sort-table:hover {
|
||||
border-bottom-color: #00B7DC !important;
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
<?php
|
||||
require_once("inc/prerequisites.inc.php");
|
||||
$AuthUsers = array("admin", "domainadmin");
|
||||
$AuthUsers = array("admin", "domainadmin", "user");
|
||||
if (!isset($_SESSION['mailcow_cc_role']) OR !in_array($_SESSION['mailcow_cc_role'], $AuthUsers)) {
|
||||
header('Location: /');
|
||||
exit();
|
||||
|
@ -30,7 +30,7 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm
|
|||
<input type="hidden" name="domain" value="<?php echo htmlspecialchars($domain) ?>">
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-1 col-sm-10">
|
||||
<button type="submit" name="trigger_mailbox_action" value="deletedomain" class="btn btn-default btn-sm"><?=$lang['delete']['remove_button'];?></button>
|
||||
<button type="submit" name="mailbox_delete_domain" class="btn btn-default btn-sm"><?=$lang['delete']['remove_button'];?></button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
@ -49,7 +49,7 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm
|
|||
<input type="hidden" name="address" value="<?php echo htmlspecialchars($_GET["alias"]) ?>">
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-1 col-sm-10">
|
||||
<button type="submit" name="trigger_mailbox_action" value="deletealias" class="btn btn-default btn-sm"><?=$lang['delete']['remove_button'];?></button>
|
||||
<button type="submit" name="mailbox_delete_alias" class="btn btn-default btn-sm"><?=$lang['delete']['remove_button'];?></button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
@ -66,27 +66,16 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm
|
|||
isset($_GET["aliasdomain"]) &&
|
||||
is_valid_domain_name($_GET["aliasdomain"]) &&
|
||||
!empty($_GET["aliasdomain"])) {
|
||||
$alias_domain = strtolower(trim($_GET["aliasdomain"]));
|
||||
try {
|
||||
$stmt = $pdo->prepare("SELECT `target_domain` FROM `alias_domain`
|
||||
WHERE `alias_domain`= :alias_domain");
|
||||
$stmt->execute(array(':alias_domain' => $alias_domain));
|
||||
$DomainData = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
}
|
||||
catch(PDOException $e) {
|
||||
$_SESSION['return'] = array(
|
||||
'type' => 'danger',
|
||||
'msg' => 'MySQL: '.$e
|
||||
);
|
||||
}
|
||||
if (hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $DomainData['target_domain'])) {
|
||||
$alias_domain = $_GET["aliasdomain"];
|
||||
$result = mailbox_get_alias_domain_details($alias_domain);
|
||||
if (!empty($result)) {
|
||||
?>
|
||||
<div class="alert alert-warning" role="alert"><?=sprintf($lang['delete']['remove_domainalias_warning'], htmlspecialchars($_GET["aliasdomain"]));?></div>
|
||||
<form class="form-horizontal" role="form" method="post" action="/mailbox.php">
|
||||
<input type="hidden" name="alias_domain" value="<?php echo htmlspecialchars($alias_domain) ?>">
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-1 col-sm-10">
|
||||
<button type="submit" name="trigger_mailbox_action" value="deletealiasdomain" class="btn btn-default btn-sm"><?=$lang['delete']['remove_button'];?></button>
|
||||
<button type="submit" name="mailbox_delete_alias_domain" class="btn btn-default btn-sm"><?=$lang['delete']['remove_button'];?></button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
@ -102,7 +91,7 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm
|
|||
elseif (isset($_GET["domainadmin"]) &&
|
||||
ctype_alnum(str_replace(array('_', '.', '-'), '', $_GET["domainadmin"])) &&
|
||||
!empty($_GET["domainadmin"]) &&
|
||||
$_SESSION['mailcow_cc_role'] == "admin") {
|
||||
$_SESSION['mailcow_cc_role'] == "admin") {
|
||||
$domain_admin = $_GET["domainadmin"];
|
||||
?>
|
||||
<div class="alert alert-warning" role="alert"><?=sprintf($lang['delete']['remove_domainadmin_warning'], htmlspecialchars($_GET["domainadmin"]));?></div>
|
||||
|
@ -110,7 +99,7 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm
|
|||
<input type="hidden" name="username" value="<?=htmlspecialchars($domain_admin);?>">
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-1 col-sm-10">
|
||||
<button type="submit" name="trigger_delete_domain_admin" class="btn btn-default btn-sm"><?=$lang['delete']['remove_button'];?></button>
|
||||
<button type="submit" name="delete_domain_admin" class="btn btn-default btn-sm"><?=$lang['delete']['remove_button'];?></button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
@ -121,16 +110,74 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm
|
|||
filter_var($_GET["mailbox"], FILTER_VALIDATE_EMAIL) &&
|
||||
!empty($_GET["mailbox"])) {
|
||||
$mailbox = $_GET["mailbox"];
|
||||
$domain = substr(strrchr($mailbox, "@"), 1);
|
||||
if (hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $domain)) {
|
||||
if (hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $mailbox)) {
|
||||
?>
|
||||
<div class="alert alert-warning" role="alert"><?=sprintf($lang['delete']['remove_mailbox_warning'], htmlspecialchars($_GET["mailbox"]));?></div>
|
||||
<div class="alert alert-warning" role="alert"><?=sprintf($lang['delete']['remove_mailbox_warning'], htmlspecialchars($mailbox));?></div>
|
||||
<p><?=$lang['delete']['remove_mailbox_details'];?></p>
|
||||
<form class="form-horizontal" role="form" method="post" action="/mailbox.php">
|
||||
<input type="hidden" name="username" value="<?=htmlspecialchars($mailbox);?>">
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-1 col-sm-10">
|
||||
<button type="submit" name="trigger_mailbox_action" value="deletemailbox" class="btn btn-default btn-sm"><?=$lang['delete']['remove_button'];?></button>
|
||||
<button type="submit" name="mailbox_delete_mailbox" class="btn btn-default btn-sm"><?=$lang['delete']['remove_button'];?></button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<?php
|
||||
}
|
||||
else {
|
||||
?>
|
||||
<div class="alert alert-info" role="alert"><?=$lang['info']['no_action'];?></div>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
// DELETE RESOURCE
|
||||
elseif (isset($_GET["resource"]) &&
|
||||
filter_var($_GET["resource"], FILTER_VALIDATE_EMAIL) &&
|
||||
!empty($_GET["resource"])) {
|
||||
$resource = $_GET["resource"];
|
||||
if (hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $resource)) {
|
||||
?>
|
||||
<div class="alert alert-warning" role="alert"><?=sprintf($lang['delete']['remove_resource_warning'], htmlspecialchars($resource));?></div>
|
||||
<p><?=$lang['delete']['remove_resource_details'];?></p>
|
||||
<form class="form-horizontal" role="form" method="post" action="/mailbox.php">
|
||||
<input type="hidden" name="name" value="<?=htmlspecialchars($resource);?>">
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-1 col-sm-10">
|
||||
<button type="submit" name="mailbox_delete_resource" class="btn btn-default btn-sm"><?=$lang['delete']['remove_button'];?></button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<?php
|
||||
}
|
||||
else {
|
||||
?>
|
||||
<div class="alert alert-info" role="alert"><?=$lang['info']['no_action'];?></div>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
else {
|
||||
?>
|
||||
<div class="alert alert-info" role="alert"><?=$lang['info']['no_action'];?></div>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
elseif (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "user")) {
|
||||
// DELETE SYNCJOB
|
||||
if (isset($_GET["syncjob"]) &&
|
||||
is_numeric($_GET["syncjob"]) &&
|
||||
filter_var($_SESSION['mailcow_cc_username'], FILTER_VALIDATE_EMAIL)) {
|
||||
$id = $_GET["syncjob"];
|
||||
$result = get_syncjob_details($id);
|
||||
if (!empty($result)) {
|
||||
?>
|
||||
<div class="alert alert-warning" role="alert"><?=sprintf($lang['delete']['remove_syncjob_warning'], htmlspecialchars($result['user2']));?></div>
|
||||
<p><?=$lang['delete']['remove_syncjob_details'];?></p>
|
||||
<form class="form-horizontal" role="form" method="post" action="/user.php">
|
||||
<input type="hidden" name="username" value="<?=htmlspecialchars($mailbox);?>">
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-1 col-sm-10">
|
||||
<input type="hidden" name="id" value="<?=$_GET["syncjob"];?>">
|
||||
<button type="submit" name="delete_syncjob" value="1" class="btn btn-default btn-sm"><?=$lang['delete']['remove_button'];?></button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<?php
|
||||
require_once("inc/prerequisites.inc.php");
|
||||
$AuthUsers = array("admin", "domainadmin");
|
||||
$AuthUsers = array("admin", "domainadmin", "user");
|
||||
if (!isset($_SESSION['mailcow_cc_role']) OR !in_array($_SESSION['mailcow_cc_role'], $AuthUsers)) {
|
||||
header('Location: /');
|
||||
exit();
|
||||
|
@ -20,34 +20,8 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm
|
|||
if (isset($_GET["alias"]) &&
|
||||
!empty($_GET["alias"])) {
|
||||
$alias = $_GET["alias"];
|
||||
$domain = substr(strrchr($alias, "@"), 1);
|
||||
try {
|
||||
$stmt = $pdo->prepare("SELECT * FROM `alias`
|
||||
WHERE `address`= :address
|
||||
AND `goto` != :goto
|
||||
AND (
|
||||
`domain` IN (
|
||||
SELECT `domain` FROM `domain_admins`
|
||||
WHERE `active`='1'
|
||||
AND `username`= :username
|
||||
)
|
||||
OR 'admin'= :admin
|
||||
)");
|
||||
$stmt->execute(array(
|
||||
':address' => $alias,
|
||||
':goto' => $alias,
|
||||
':username' => $_SESSION['mailcow_cc_username'],
|
||||
':admin' => $_SESSION['mailcow_cc_role']
|
||||
));
|
||||
$result = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
}
|
||||
catch(PDOException $e) {
|
||||
$_SESSION['return'] = array(
|
||||
'type' => 'danger',
|
||||
'msg' => 'MySQL: '.$e
|
||||
);
|
||||
}
|
||||
if ($result !== false) {
|
||||
$result = mailbox_get_alias_details($alias);
|
||||
if (!empty($result)) {
|
||||
?>
|
||||
<h4><?=$lang['edit']['alias'];?></h4>
|
||||
<br />
|
||||
|
@ -62,13 +36,13 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm
|
|||
<div class="form-group">
|
||||
<div class="col-sm-offset-2 col-sm-10">
|
||||
<div class="checkbox">
|
||||
<label><input type="checkbox" name="active" <?php if (isset($result['active']) && $result['active']=="1") { echo "checked"; }; ?>> <?=$lang['edit']['active'];?></label>
|
||||
<label><input type="checkbox" name="active" <?php if (isset($result['active_int']) && $result['active_int']=="1") { echo "checked"; }; ?>> <?=$lang['edit']['active'];?></label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-2 col-sm-10">
|
||||
<button type="submit" name="trigger_mailbox_action" value="editalias" class="btn btn-success btn-sm"><?=$lang['edit']['save'];?></button>
|
||||
<button type="submit" name="mailbox_edit_alias" class="btn btn-success btn-sm"><?=$lang['edit']['save'];?></button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
@ -86,68 +60,34 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm
|
|||
$_GET["domainadmin"] != 'admin' &&
|
||||
$_SESSION['mailcow_cc_role'] == "admin") {
|
||||
$domain_admin = $_GET["domainadmin"];
|
||||
try {
|
||||
$stmt = $pdo->prepare("SELECT * FROM `domain_admins` WHERE `username`= :domain_admin");
|
||||
$stmt->execute(array(
|
||||
':domain_admin' => $domain_admin
|
||||
));
|
||||
$result = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
}
|
||||
catch(PDOException $e) {
|
||||
$_SESSION['return'] = array(
|
||||
'type' => 'danger',
|
||||
'msg' => 'MySQL: '.$e
|
||||
);
|
||||
}
|
||||
if ($result !== false) {
|
||||
$result = get_domain_admin_details($domain_admin);
|
||||
if (!empty($result)) {
|
||||
?>
|
||||
<h4><?=$lang['edit']['domain_admin'];?></h4>
|
||||
<br />
|
||||
<form class="form-horizontal" role="form" method="post" action="<?=($FORM_ACTION == "previous") ? $_SESSION['return_to'] : null;?>">
|
||||
<input type="hidden" name="username" value="<?=htmlspecialchars($domain_admin);?>">
|
||||
<input type="hidden" name="username_now" value="<?=htmlspecialchars($domain_admin);?>">
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2" for="username"><?=$lang['edit']['username'];?></label>
|
||||
<div class="col-sm-10">
|
||||
<input class="form-control" type="text" name="username" value="<?=htmlspecialchars($domain_admin);?>" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2" for="domain"><?=$lang['edit']['domains'];?></label>
|
||||
<div class="col-sm-10">
|
||||
<select id="domain" name="domain[]" multiple>
|
||||
<?php
|
||||
try {
|
||||
$stmt = $pdo->prepare("SELECT `domain` FROM `domain`
|
||||
WHERE `domain` IN (
|
||||
SELECT `domain` FROM `domain_admins`
|
||||
WHERE `username`= :domain_admin)");
|
||||
$stmt->execute(array(':domain_admin' => $domain_admin));
|
||||
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
}
|
||||
catch(PDOException $e) {
|
||||
$_SESSION['return'] = array(
|
||||
'type' => 'danger',
|
||||
'msg' => 'MySQL: '.$e
|
||||
);
|
||||
}
|
||||
while ($row_selected = array_shift($rows)):
|
||||
foreach ($result['selected_domains'] as $domain):
|
||||
?>
|
||||
<option selected><?=htmlspecialchars($row_selected['domain']);?></option>
|
||||
<option selected><?=htmlspecialchars($domain);?></option>
|
||||
<?php
|
||||
endwhile;
|
||||
try {
|
||||
$stmt = $pdo->prepare("SELECT `domain` FROM `domain`
|
||||
WHERE `domain` NOT IN (
|
||||
SELECT `domain` FROM `domain_admins`
|
||||
WHERE `username`= :domain_admin)");
|
||||
$stmt->execute(array(':domain_admin' => $domain_admin));
|
||||
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
}
|
||||
catch(PDOException $e) {
|
||||
$_SESSION['return'] = array(
|
||||
'type' => 'danger',
|
||||
'msg' => 'MySQL: '.$e
|
||||
);
|
||||
}
|
||||
while ($row_unselected = array_shift($rows)):
|
||||
endforeach;
|
||||
foreach ($result['unselected_domains'] as $domain):
|
||||
?>
|
||||
<option><?=htmlspecialchars($row_unselected['domain']);?></option>
|
||||
<option><?=htmlspecialchars($domain);?></option>
|
||||
<?php
|
||||
endwhile;
|
||||
endforeach;
|
||||
?>
|
||||
</select>
|
||||
</div>
|
||||
|
@ -167,13 +107,20 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm
|
|||
<div class="form-group">
|
||||
<div class="col-sm-offset-2 col-sm-10">
|
||||
<div class="checkbox">
|
||||
<label><input type="checkbox" name="active" <?php if (isset($result['active']) && $result['active']=="1") { echo "checked"; }; ?>> <?=$lang['edit']['active'];?></label>
|
||||
<label><input type="checkbox" name="active" <?php if (isset($result['active_int']) && $result['active_int']=="1") { echo "checked"; }; ?>> <?=$lang['edit']['active'];?></label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-2 col-sm-10">
|
||||
<button type="submit" name="trigger_edit_domain_admin" class="btn btn-success btn-sm"><?=$lang['edit']['save'];?></button>
|
||||
<div class="checkbox">
|
||||
<label><input type="checkbox" name="disable_tfa"> <?=$lang['tfa']['disable_tfa'];?></label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-2 col-sm-10">
|
||||
<button type="submit" name="edit_domain_admin" class="btn btn-success btn-sm"><?=$lang['edit']['save'];?></button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
@ -189,29 +136,8 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm
|
|||
is_valid_domain_name($_GET["domain"]) &&
|
||||
!empty($_GET["domain"])) {
|
||||
$domain = $_GET["domain"];
|
||||
try {
|
||||
$stmt = $pdo->prepare("SELECT * FROM `domain` WHERE `domain`='".$domain."'
|
||||
AND (
|
||||
`domain` IN (
|
||||
SELECT `domain` from `domain_admins`
|
||||
WHERE `active`='1'
|
||||
AND `username` = :username
|
||||
)
|
||||
OR 'admin'= :admin
|
||||
)");
|
||||
$stmt->execute(array(
|
||||
':username' => $_SESSION['mailcow_cc_username'],
|
||||
':admin' => $_SESSION['mailcow_cc_role']
|
||||
));
|
||||
$result = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
}
|
||||
catch(PDOException $e) {
|
||||
$_SESSION['return'] = array(
|
||||
'type' => 'danger',
|
||||
'msg' => 'MySQL: '.$e
|
||||
);
|
||||
}
|
||||
if ($result !== false) {
|
||||
$result = mailbox_get_domain_details($domain);
|
||||
if (!empty($result)) {
|
||||
?>
|
||||
<h4><?=$lang['edit']['domain'];?></h4>
|
||||
<form class="form-horizontal" role="form" method="post" action="<?=($FORM_ACTION == "previous") ? $_SESSION['return_to'] : null;?>">
|
||||
|
@ -228,34 +154,34 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm
|
|||
<div class="form-group">
|
||||
<label class="control-label col-sm-2" for="aliases"><?=$lang['edit']['max_aliases'];?></label>
|
||||
<div class="col-sm-10">
|
||||
<input type="number" class="form-control" name="aliases" id="aliases" value="<?=intval($result['aliases']);?>">
|
||||
<input type="number" class="form-control" name="aliases" id="aliases" value="<?=intval($result['max_num_aliases_for_domain']);?>">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2" for="mailboxes"><?=$lang['edit']['max_mailboxes'];?></label>
|
||||
<div class="col-sm-10">
|
||||
<input type="number" class="form-control" name="mailboxes" id="mailboxes" value="<?=intval($result['mailboxes']);?>">
|
||||
<input type="number" class="form-control" name="mailboxes" id="mailboxes" value="<?=intval($result['max_num_mboxes_for_domain']);?>">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2" for="maxquota"><?=$lang['edit']['max_quota'];?></label>
|
||||
<div class="col-sm-10">
|
||||
<input type="number" class="form-control" name="maxquota" id="maxquota" value="<?=intval($result['maxquota']);?>">
|
||||
<input type="number" class="form-control" name="maxquota" id="maxquota" value="<?=intval($result['max_new_mailbox_quota'] / 1048576);?>">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2" for="quota"><?=$lang['edit']['domain_quota'];?></label>
|
||||
<div class="col-sm-10">
|
||||
<input type="number" class="form-control" name="quota" id="quota" value="<?=intval($result['quota']);?>">
|
||||
<input type="number" class="form-control" name="quota" id="quota" value="<?=intval($result['max_quota_for_domain'] / 1048576);?>">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2"><?=$lang['edit']['backup_mx_options'];?></label>
|
||||
<div class="col-sm-10">
|
||||
<div class="checkbox">
|
||||
<label><input type="checkbox" name="backupmx" <?php if (isset($result['backupmx']) && $result['backupmx']=="1") { echo "checked"; }; ?>> <?=$lang['edit']['relay_domain'];?></label>
|
||||
<label><input type="checkbox" name="backupmx" <?=(isset($result['backupmx_int']) && $result['backupmx_int']=="1") ? "checked" : null;?>> <?=$lang['edit']['relay_domain'];?></label>
|
||||
<br />
|
||||
<label><input type="checkbox" name="relay_all_recipients" <?php if (isset($result['relay_all_recipients']) && $result['relay_all_recipients']=="1") { echo "checked"; }; ?>> <?=$lang['edit']['relay_all'];?></label>
|
||||
<label><input type="checkbox" name="relay_all_recipients" <?=(isset($result['relay_all_recipients_int']) && $result['relay_all_recipients_int']=="1") ? "checked" : null;?>> <?=$lang['edit']['relay_all'];?></label>
|
||||
<p><?=$lang['edit']['relay_all_info'];?></p>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -266,37 +192,148 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm
|
|||
<div class="form-group">
|
||||
<div class="col-sm-offset-2 col-sm-10">
|
||||
<div class="checkbox">
|
||||
<label><input type="checkbox" name="active" <?php if (isset($result['active']) && $result['active']=="1") { echo "checked "; }; if ($_SESSION['mailcow_cc_role']=="domainadmin") { echo "disabled"; }; ?>> <?=$lang['edit']['active'];?></label>
|
||||
<label><input type="checkbox" name="active" <?=(isset($result['active_int']) && $result['active_int']=="1") ? "checked" : null;?> <?=($_SESSION['mailcow_cc_role'] == "admin") ? null : "disabled";?>> <?=$lang['edit']['active'];?></label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-2 col-sm-10">
|
||||
<button type="submit" name="trigger_mailbox_action" value="editdomain" class="btn btn-success btn-sm"><?=$lang['edit']['save'];?></button>
|
||||
<button type="submit" name="mailbox_edit_domain" class="btn btn-success btn-sm"><?=$lang['edit']['save'];?></button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<?php
|
||||
if (file_exists($GLOBALS["MC_DKIM_TXTS"]. "/" . $domain . "." . "dkim")) {
|
||||
$pubKey = file_get_contents($GLOBALS["MC_DKIM_TXTS"]. "/" . $domain . "." . "dkim");
|
||||
if (!empty($dkim = dkim_get_key_details($domain))) {
|
||||
?>
|
||||
<div class="row">
|
||||
<div class="col-xs-2">
|
||||
<p>Domain: <strong><?=htmlspecialchars($domain);?></strong> (dkim._domainkey)</p>
|
||||
</div>
|
||||
<div class="col-xs-9">
|
||||
<pre>v=DKIM1;k=rsa;t=s;s=email;p=<?=$pubKey;?></pre>
|
||||
</div>
|
||||
<div class="col-xs-1">
|
||||
<form class="form-inline" role="form" method="post">
|
||||
<a href="#" onclick="$(this).closest('form').submit()"><span class="glyphicon glyphicon-remove-circle"></span></a>
|
||||
<input type="hidden" name="delete_dkim_record" value="<?=htmlspecialchars($file);?>">
|
||||
<input type="hidden" name="dkim[domain]" value="<?=$domain;?>">
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="row">
|
||||
<div class="col-xs-2">
|
||||
<p>Domain: <strong><?=htmlspecialchars($result['domain_name']);?></strong> (dkim._domainkey)</p>
|
||||
</div>
|
||||
<div class="col-xs-10">
|
||||
<pre><?=$dkim['dkim_txt'];?></pre>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
<hr>
|
||||
<div class="row">
|
||||
<div class="col-sm-6">
|
||||
<h4><span class="glyphicon glyphicon-thumbs-up" aria-hidden="true"></span> <?=$lang['user']['spamfilter_wl'];?></h4>
|
||||
<p><?=$lang['user']['spamfilter_wl_desc'];?></p>
|
||||
<div class="row">
|
||||
<div class="col-sm-6"><b><?=$lang['user']['spamfilter_table_rule'];?></b></div>
|
||||
<div class="col-sm-6"><b><?=$lang['user']['spamfilter_table_action'];?></b></div>
|
||||
</div>
|
||||
<?php
|
||||
$get_policy_list = get_policy_list($domain);
|
||||
if (empty($get_policy_list['whitelist'])):
|
||||
?>
|
||||
<div class="row">
|
||||
<div class="col-sm-12"><i><?=$lang['user']['spamfilter_table_empty'];?></i></div>
|
||||
</div>
|
||||
<?php
|
||||
else:
|
||||
foreach($get_policy_list['whitelist'] as $wl):
|
||||
?>
|
||||
<div class="row striped">
|
||||
<form class="form-inline" method="post">
|
||||
<div class="col-xs-6"><code><?=$wl['value'];?></code></div>
|
||||
<div class="col-xs-6">
|
||||
<?php
|
||||
if ($wl['object'] == $domain):
|
||||
?>
|
||||
<input type="hidden" name="delete_prefid" value="<?=$wl['prefid'];?>">
|
||||
<input type="hidden" name="delete_policy_list_item">
|
||||
<input type="hidden" name="domain" value="<?=$domain;?>">
|
||||
<a href="#" onclick="$(this).closest('form').submit()" data-toggle="tooltip" data-placement="left" title="<?=$lang['user']['delete_now'];?>"><span class="glyphicon glyphicon-remove"></span></a>
|
||||
<?php
|
||||
else:
|
||||
?>
|
||||
<span style="cursor:not-allowed"><?=$lang['user']['spamfilter_table_domain_policy'];?></span>
|
||||
<?php
|
||||
endif;
|
||||
?>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<?php
|
||||
endforeach;
|
||||
endif;
|
||||
?>
|
||||
<hr style="margin:5px 0px 7px 0px">
|
||||
<div class="row">
|
||||
<form class="form-inline" method="post">
|
||||
<div class="col-xs-6">
|
||||
<input type="text" class="form-control input-sm" name="object_from" id="object_from" placeholder="*@example.org" required>
|
||||
<input type="hidden" name="object_list" value="wl">
|
||||
<input type="hidden" name="domain" value="<?=$domain;?>">
|
||||
</div>
|
||||
<div class="col-xs-6">
|
||||
<button type="submit" name="add_policy_list_item" class="btn btn-xs btn-default"><?=$lang['user']['spamfilter_table_add'];?></button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<h4><span class="glyphicon glyphicon-thumbs-down" aria-hidden="true"></span> <?=$lang['user']['spamfilter_bl'];?></h4>
|
||||
<p><?=$lang['user']['spamfilter_bl_desc'];?></p>
|
||||
<div class="row">
|
||||
<div class="col-sm-6"><b><?=$lang['user']['spamfilter_table_rule'];?></b></div>
|
||||
<div class="col-sm-6"><b><?=$lang['user']['spamfilter_table_action'];?></b></div>
|
||||
</div>
|
||||
<?php
|
||||
if (empty($get_policy_list['blacklist'])):
|
||||
?>
|
||||
<div class="row">
|
||||
<div class="col-sm-12"><i><?=$lang['user']['spamfilter_table_empty'];?></i></div>
|
||||
</div>
|
||||
<?php
|
||||
else:
|
||||
foreach($get_policy_list['blacklist'] as $bl):
|
||||
?>
|
||||
<div class="row striped">
|
||||
<form class="form-inline" method="post">
|
||||
<div class="col-xs-6"><code><?=$bl['value'];?></code></div>
|
||||
<div class="col-xs-6">
|
||||
<input type="hidden" name="delete_prefid" value="<?=$bl['prefid'];?>">
|
||||
<?php
|
||||
if ($bl['object'] == $domain):
|
||||
?>
|
||||
<input type="hidden" name="delete_policy_list_item">
|
||||
<input type="hidden" name="domain" value="<?=$domain;?>">
|
||||
<a href="#" onclick="$(this).closest('form').submit()" data-toggle="tooltip" data-placement="left" title="<?=$lang['user']['delete_now'];?>"><span class="glyphicon glyphicon-remove"></span></a>
|
||||
<?php
|
||||
else:
|
||||
?>
|
||||
<span style="cursor:not-allowed"><?=$lang['user']['spamfilter_table_domain_policy'];?></span>
|
||||
<?php
|
||||
endif;
|
||||
?>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<?php
|
||||
endforeach;
|
||||
endif;
|
||||
?>
|
||||
<hr style="margin:5px 0px 7px 0px">
|
||||
<div class="row">
|
||||
<form class="form-inline" method="post">
|
||||
<div class="col-xs-6">
|
||||
<input type="text" class="form-control input-sm" name="object_from" id="object_from" placeholder="*@example.org" required>
|
||||
<input type="hidden" name="object_list" value="bl">
|
||||
<input type="hidden" name="domain" value="<?=$domain;?>">
|
||||
</div>
|
||||
<div class="col-xs-6">
|
||||
<button type="submit" name="add_policy_list_item" class="btn btn-xs btn-default"><?=$lang['user']['spamfilter_table_add'];?></button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
else {
|
||||
?>
|
||||
|
@ -308,31 +345,8 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm
|
|||
is_valid_domain_name($_GET["aliasdomain"]) &&
|
||||
!empty($_GET["aliasdomain"])) {
|
||||
$alias_domain = $_GET["aliasdomain"];
|
||||
try {
|
||||
$stmt = $pdo->prepare("SELECT * FROM `alias_domain`
|
||||
WHERE `alias_domain`= :alias_domain
|
||||
AND (
|
||||
`target_domain` IN (
|
||||
SELECT `domain` FROM `domain_admins`
|
||||
WHERE `active`='1'
|
||||
AND `username`= :username
|
||||
)
|
||||
OR 'admin'= :admin
|
||||
)");
|
||||
$stmt->execute(array(
|
||||
':alias_domain' => $alias_domain,
|
||||
':username' => $_SESSION['mailcow_cc_username'],
|
||||
':admin' => $_SESSION['mailcow_cc_role']
|
||||
));
|
||||
$result = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
}
|
||||
catch(PDOException $e) {
|
||||
$_SESSION['return'] = array(
|
||||
'type' => 'danger',
|
||||
'msg' => 'MySQL: '.$e
|
||||
);
|
||||
}
|
||||
if ($result !== false) {
|
||||
$result = mailbox_get_alias_domain_details($alias_domain);
|
||||
if (!empty($result)) {
|
||||
?>
|
||||
<h4><?=$lang['edit']['edit_alias_domain'];?></h4>
|
||||
<form class="form-horizontal" role="form" method="post" action="<?=($FORM_ACTION == "previous") ? $_SESSION['return_to'] : null;?>">
|
||||
|
@ -346,46 +360,29 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm
|
|||
<div class="form-group">
|
||||
<div class="col-sm-offset-2 col-sm-10">
|
||||
<div class="checkbox">
|
||||
<label><input type="checkbox" name="active" <?= (isset($result['active']) && $result['active']=="1") ? "checked" : null ?>> <?=$lang['edit']['active'];?></label>
|
||||
<label><input type="checkbox" name="active" <?=(isset($result['active_int']) && $result['active_int']=="1") ? "checked" : null ?>> <?=$lang['edit']['active'];?></label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-2 col-sm-10">
|
||||
<button type="submit" name="trigger_mailbox_action" value="editaliasdomain" class="btn btn-success btn-sm"><?=$lang['edit']['save'];?></button>
|
||||
<button type="submit" name="mailbox_edit_alias_domain" class="btn btn-success btn-sm"><?=$lang['edit']['save'];?></button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<?php
|
||||
$dnstxt_folder = scandir($GLOBALS["MC_DKIM_TXTS"]);
|
||||
$dnstxt_files = array_diff($dnstxt_folder, array('.', '..'));
|
||||
foreach($dnstxt_files as $file) {
|
||||
if (explode("_", $file)[1] == $domain) {
|
||||
$str = file_get_contents($GLOBALS["MC_DKIM_TXTS"]."/".$file);
|
||||
$str = preg_replace('/\r|\t|\n/', '', $str);
|
||||
preg_match('/\(.*\)/im', $str, $matches);
|
||||
if(isset($matches[0])) {
|
||||
$str = str_replace(array(' ', '"', '(', ')'), '', $matches[0]);
|
||||
}
|
||||
if (!empty($dkim = dkim_get_key_details($alias_domain))) {
|
||||
?>
|
||||
<div class="row">
|
||||
<div class="col-xs-2">
|
||||
<p class="text-right"><?=$lang['edit']['dkim_signature'];?></p>
|
||||
</div>
|
||||
<div class="col-xs-10">
|
||||
<div class="col-md-2"><b><?=$lang['edit']['dkim_txt_name'];?></b></div>
|
||||
<div class="col-md-10">
|
||||
<pre><?=htmlspecialchars(explode("_", $file)[0]);?>._domainkey</pre>
|
||||
</div>
|
||||
<div class="col-md-2"><b><?=$lang['edit']['dkim_txt_value'];?></b></div>
|
||||
<div class="col-md-10">
|
||||
<pre><?=htmlspecialchars($str);?></pre>
|
||||
<?=$lang['edit']['dkim_record_info'];?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="row">
|
||||
<div class="col-xs-2">
|
||||
<p>Domain: <strong><?=htmlspecialchars($result['alias_domain']);?></strong> (dkim._domainkey)</p>
|
||||
</div>
|
||||
<div class="col-xs-10">
|
||||
<pre><?=$dkim['dkim_txt'];?></pre>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
@ -395,99 +392,232 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm
|
|||
}
|
||||
}
|
||||
elseif (isset($_GET['mailbox']) && filter_var($_GET["mailbox"], FILTER_VALIDATE_EMAIL) && !empty($_GET["mailbox"])) {
|
||||
$mailbox = $_GET["mailbox"];
|
||||
try {
|
||||
$stmt = $pdo->prepare("SELECT `username`, `domain`, `name`, `quota`, `active` FROM `mailbox` WHERE `username` = :username1");
|
||||
$stmt->execute(array(
|
||||
':username1' => $mailbox,
|
||||
));
|
||||
$result = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
}
|
||||
catch(PDOException $e) {
|
||||
$_SESSION['return'] = array(
|
||||
'type' => 'danger',
|
||||
'msg' => 'MySQL: '.$e
|
||||
);
|
||||
}
|
||||
if ($result !== false && hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $result['domain'])) {
|
||||
$left_m = remaining_specs($result['domain'], $_GET['mailbox'])['left_m'];
|
||||
?>
|
||||
<h4><?=$lang['edit']['mailbox'];?></h4>
|
||||
$mailbox = $_GET["mailbox"];
|
||||
$result = mailbox_get_mailbox_details($mailbox);
|
||||
if (!empty($result)) {
|
||||
?>
|
||||
<h4><?=$lang['edit']['mailbox'];?></h4>
|
||||
<form class="form-horizontal" role="form" method="post" action="<?=($FORM_ACTION == "previous") ? $_SESSION['return_to'] : null;?>">
|
||||
<input type="hidden" name="username" value="<?=htmlspecialchars($result['username']);?>">
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2" for="name"><?=$lang['edit']['full_name'];?>:</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="text" class="form-control" name="name" id="name" value="<?=htmlspecialchars($result['name'], ENT_QUOTES, 'UTF-8');?>">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2" for="quota"><?=$lang['edit']['quota_mb'];?>:
|
||||
<br /><span id="quotaBadge" class="badge">max. <?=intval($result['max_new_quota'] / 1048576)?> MiB</span>
|
||||
</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="number" name="quota" id="quota" id="destroyable" style="width:100%" min="1" max="<?=intval($result['max_new_quota'] / 1048576);?>" value="<?=intval($result['quota']) / 1048576;?>" class="form-control">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2" for="sender_acl"><?=$lang['edit']['sender_acl'];?>:</label>
|
||||
<div class="col-sm-10">
|
||||
<select data-width="100%" style="width:100%" id="sender_acl" name="sender_acl[]" size="10" multiple>
|
||||
<?php
|
||||
$sender_acl_handles = mailbox_get_sender_acl_handles($mailbox);
|
||||
|
||||
foreach ($sender_acl_handles['sender_acl_domains']['ro'] as $domain):
|
||||
?>
|
||||
<option data-subtext="Admin" value="<?=htmlspecialchars($domain);?>" disabled selected><?=htmlspecialchars(sprintf($lang['edit']['dont_check_sender_acl'], $domain));?></option>
|
||||
<?php
|
||||
endforeach;
|
||||
|
||||
foreach ($sender_acl_handles['sender_acl_addresses']['ro'] as $domain):
|
||||
?>
|
||||
<option data-subtext="Admin" disabled selected><?=htmlspecialchars($alias);?></option>
|
||||
<?php
|
||||
endforeach;
|
||||
|
||||
foreach ($sender_acl_handles['fixed_sender_aliases'] as $alias):
|
||||
?>
|
||||
<option data-subtext="Alias" disabled selected><?=htmlspecialchars($alias);?></option>
|
||||
<?php
|
||||
endforeach;
|
||||
|
||||
foreach ($sender_acl_handles['sender_acl_domains']['rw'] as $domain):
|
||||
?>
|
||||
<option value="<?=htmlspecialchars($domain);?>" selected><?=htmlspecialchars(sprintf($lang['edit']['dont_check_sender_acl'], $domain));?></option>
|
||||
<?php
|
||||
endforeach;
|
||||
|
||||
foreach ($sender_acl_handles['sender_acl_domains']['selectable'] as $domain):
|
||||
?>
|
||||
<option value="<?=htmlspecialchars($domain);?>"><?=htmlspecialchars(sprintf($lang['edit']['dont_check_sender_acl'], $domain));?></option>
|
||||
<?php
|
||||
endforeach;
|
||||
|
||||
foreach ($sender_acl_handles['sender_acl_addresses']['rw'] as $address):
|
||||
?>
|
||||
<option selected><?=htmlspecialchars($address);?></option>
|
||||
<?php
|
||||
endforeach;
|
||||
|
||||
foreach ($sender_acl_handles['sender_acl_addresses']['selectable'] as $address):
|
||||
?>
|
||||
<option><?=htmlspecialchars($address);?></option>
|
||||
<?php
|
||||
endforeach;
|
||||
|
||||
?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2" for="password"><?=$lang['edit']['password'];?></label>
|
||||
<div class="col-sm-10">
|
||||
<input type="password" class="form-control" name="password" id="password" placeholder="<?=$lang['edit']['unchanged_if_empty'];?>">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2" for="password2"><?=$lang['edit']['password_repeat'];?></label>
|
||||
<div class="col-sm-10">
|
||||
<input type="password" class="form-control" name="password2" id="password2">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-2 col-sm-10">
|
||||
<div class="checkbox">
|
||||
<label><input type="checkbox" name="active" <?=($result['active_int']=="1") ? "checked" : null;?>> <?=$lang['edit']['active'];?></label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-2 col-sm-10">
|
||||
<button type="submit" name="mailbox_edit_mailbox" class="btn btn-success btn-sm"><?=$lang['edit']['save'];?></button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
elseif (isset($_GET['resource']) && filter_var($_GET["resource"], FILTER_VALIDATE_EMAIL) && !empty($_GET["resource"])) {
|
||||
$resource = $_GET["resource"];
|
||||
$result = mailbox_get_resource_details($resource);
|
||||
if (!empty($result)) {
|
||||
?>
|
||||
<h4><?=$lang['edit']['resource'];?></h4>
|
||||
<form class="form-horizontal" role="form" method="post" action="<?=($FORM_ACTION == "previous") ? $_SESSION['return_to'] : null;?>">
|
||||
<input type="hidden" name="username" value="<?=htmlspecialchars($result['username']);?>">
|
||||
<input type="hidden" name="name" value="<?=htmlspecialchars($result['name']);?>">
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2" for="name"><?=$lang['edit']['full_name'];?>:</label>
|
||||
<label class="control-label col-sm-2" for="description"><?=$lang['add']['description'];?></label>
|
||||
<div class="col-sm-10">
|
||||
<input type="text" class="form-control" name="name" id="name" value="<?=htmlspecialchars($result['name'], ENT_QUOTES, 'UTF-8');?>">
|
||||
<input type="text" class="form-control" name="description" id="description" value="<?=htmlspecialchars($result['description'], ENT_QUOTES, 'UTF-8');?>" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2" for="quota"><?=$lang['edit']['quota_mb'];?>:
|
||||
<br /><span id="quotaBadge" class="badge">max. <?=intval($left_m)?> MiB</span>
|
||||
</label>
|
||||
<label class="control-label col-sm-2" for="domain"><?=$lang['edit']['kind'];?>:</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="number" name="quota" id="quota" id="destroyable" style="width:100%" min="1" max="<?=intval($left_m);?>" value="<?=intval($result['quota']) / 1048576;?>" class="form-control">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2" for="sender_acl"><?=$lang['edit']['sender_acl'];?>:</label>
|
||||
<div class="col-sm-10">
|
||||
<select style="width:100%" id="sender_acl" name="sender_acl[]" size="10" multiple>
|
||||
<?php
|
||||
$rows = get_sender_acl_handles($mailbox, "preselected");
|
||||
while ($row_goto_from_alias = array_shift($rows)):
|
||||
?>
|
||||
<option disabled selected><?=htmlspecialchars($row_goto_from_alias['address']);?></option>
|
||||
<?php
|
||||
endwhile;
|
||||
|
||||
// All manual selected
|
||||
$rows = get_sender_acl_handles($mailbox, "selected");
|
||||
while ($row_selected_sender_acl = array_shift($rows)):
|
||||
if (!filter_var($row_selected_sender_acl['send_as'], FILTER_VALIDATE_EMAIL)):
|
||||
?>
|
||||
<option data-divider="true"></option>
|
||||
<option value="<?=htmlspecialchars($row_selected_sender_acl['send_as']);?>" selected><?=htmlspecialchars(sprintf($lang['edit']['dont_check_sender_acl'], str_replace('@', '', $row_selected_sender_acl['send_as'])));?></option>
|
||||
<option data-divider="true"></option>
|
||||
<?php
|
||||
else:
|
||||
?>
|
||||
<option selected><?=htmlspecialchars($row_selected_sender_acl['send_as']);?></option>
|
||||
<?php
|
||||
endif;
|
||||
endwhile;
|
||||
|
||||
// Unselected domains
|
||||
$rows = get_sender_acl_handles($mailbox, "unselected-domains");
|
||||
while ($row_unselected_sender_acl = array_shift($rows)):
|
||||
?>
|
||||
<option data-divider="true"></option>
|
||||
<option value="@<?=htmlspecialchars($row_unselected_sender_acl['domain']);?>"><?=htmlspecialchars(sprintf($lang['edit']['dont_check_sender_acl'], $row_unselected_sender_acl['domain']));?></option>
|
||||
<option data-divider="true"></option>
|
||||
<?php
|
||||
endwhile;
|
||||
|
||||
// Unselected addresses
|
||||
$rows = get_sender_acl_handles($mailbox, "unselected-addresses");
|
||||
while ($row_unselected_sender_acl = array_shift($rows)):
|
||||
?>
|
||||
<option><?=htmlspecialchars($row_unselected_sender_acl['address']);?></option>
|
||||
<?php
|
||||
endwhile;
|
||||
?>
|
||||
<select name="kind" id="kind" title="<?=$lang['edit']['select'];?>" required>
|
||||
<option value="location" <?=($result['kind'] == "location") ? "selected" : null;?>>Location</option>
|
||||
<option value="group" <?=($result['kind'] == "group") ? "selected" : null;?>>Group</option>
|
||||
<option value="thing" <?=($result['kind'] == "thing") ? "selected" : null;?>>Thing</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2" for="password"><?=$lang['edit']['password'];?></label>
|
||||
<div class="col-sm-10">
|
||||
<input type="password" class="form-control" name="password" id="password" placeholder="<?=$lang['edit']['unchanged_if_empty'];?>">
|
||||
<div class="col-sm-offset-2 col-sm-10">
|
||||
<div class="checkbox">
|
||||
<label><input type="checkbox" name="active" <?=($result['active_int']=="1") ? "checked" : null;?>> <?=$lang['edit']['active'];?></label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2" for="password2"><?=$lang['edit']['password_repeat'];?></label>
|
||||
<div class="col-sm-offset-2 col-sm-10">
|
||||
<div class="checkbox">
|
||||
<label><input type="checkbox" name="multiple_bookings" <?=($result['multiple_bookings_int']=="1") ? "checked" : null;?>> <?=$lang['edit']['multiple_bookings'];?></label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-2 col-sm-10">
|
||||
<button type="submit" name="mailbox_edit_resource" class="btn btn-success btn-sm"><?=$lang['edit']['save'];?></button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<?php
|
||||
}
|
||||
else {
|
||||
?>
|
||||
<div class="alert alert-info" role="alert"><?=$lang['info']['no_action'];?></div>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
else {
|
||||
?>
|
||||
<div class="alert alert-info" role="alert"><?=$lang['info']['no_action'];?></div>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
elseif (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "user")) {
|
||||
if (isset($_GET['syncjob']) &&
|
||||
is_numeric($_GET['syncjob'])) {
|
||||
$id = $_GET["syncjob"];
|
||||
$result = get_syncjob_details($id);
|
||||
if (!empty($result)) {
|
||||
?>
|
||||
<h4><?=$lang['edit']['syncjob'];?></h4>
|
||||
<form class="form-horizontal" role="form" method="post" action="<?=($FORM_ACTION == "previous") ? $_SESSION['return_to'] : null;?>">
|
||||
<input type="hidden" name="id" value="<?=htmlspecialchars($result['id']);?>">
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2" for="host1"><?=$lang['edit']['hostname'];?></label>
|
||||
<div class="col-sm-10">
|
||||
<input type="password" class="form-control" name="password2" id="password2">
|
||||
<input type="text" class="form-control" name="host1" id="host1" value="<?=htmlspecialchars($result['host1'], ENT_QUOTES, 'UTF-8');?>">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2" for="port1">Port</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="number" class="form-control" name="port1" id="port1" min="1" max="65535" value="<?=htmlspecialchars($result['port1'], ENT_QUOTES, 'UTF-8');?>">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2" for="user1"><?=$lang['edit']['username'];?></label>
|
||||
<div class="col-sm-10">
|
||||
<input type="text" class="form-control" name="user1" id="user1" value="<?=htmlspecialchars($result['user1'], ENT_QUOTES, 'UTF-8');?>">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2" for="password1"><?=$lang['edit']['password'];?></label>
|
||||
<div class="col-sm-10">
|
||||
<input type="text" class="form-control" name="password1" id="password1" value="<?=htmlspecialchars($result['password1'], ENT_QUOTES, 'UTF-8');?>">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2" for="enc1"><?=$lang['edit']['encryption'];?>:</label>
|
||||
<div class="col-sm-10">
|
||||
<select id="enc1" name="enc1">
|
||||
<option <?=($result['enc1'] == "TLS") ? "selected" : null;?>>TLS</option>
|
||||
<option <?=($result['enc1'] == "SSL") ? "selected" : null;?>>SSL</option>
|
||||
<option <?=($result['enc1'] == "PLAIN") ? "selected" : null;?>>PLAIN</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2" for="mins_interval"><?=$lang['edit']['mins_interval'];?></label>
|
||||
<div class="col-sm-10">
|
||||
<input type="number" class="form-control" name="mins_interval" min="10" max="3600" value="<?=htmlspecialchars($result['mins_interval'], ENT_QUOTES, 'UTF-8');?>" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2" for="subfolder2"><?=$lang['edit']['subfolder2'];?></label>
|
||||
<div class="col-sm-10">
|
||||
<input type="text" class="form-control" name="subfolder2" id="subfolder2" value="<?=htmlspecialchars($result['subfolder2'], ENT_QUOTES, 'UTF-8');?>">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2" for="maxage"><?=$lang['edit']['maxage'];?></label>
|
||||
<div class="col-sm-10">
|
||||
<input type="number" class="form-control" name="maxage" id="maxage" value="<?=htmlspecialchars($result['maxage'], ENT_QUOTES, 'UTF-8');?>">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2" for="exclude"><?=$lang['edit']['exclude'];?></label>
|
||||
<div class="col-sm-10">
|
||||
<input type="text" class="form-control" name="exclude" id="exclude" value="<?=htmlspecialchars($result['exclude'], ENT_QUOTES, 'UTF-8');?>">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
|
@ -499,7 +629,7 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm
|
|||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-2 col-sm-10">
|
||||
<button type="submit" name="trigger_mailbox_action" value="editmailbox" class="btn btn-success btn-sm"><?=$lang['edit']['save'];?></button>
|
||||
<button type="submit" name="edit_syncjob" value="1" class="btn btn-success btn-sm"><?=$lang['edit']['save'];?></button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
include("inc/tfa_modals.php");
|
||||
if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == "admin"):
|
||||
?>
|
||||
<div id="RestartSOGo" class="modal fade" role="dialog">
|
||||
|
@ -21,10 +22,12 @@ if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == "admi
|
|||
<?php
|
||||
endif;
|
||||
?>
|
||||
<div style="margin-bottom:100px"></div>
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.6/js/bootstrap.min.js"></script>
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/bootstrap-switch/3.3.2/js/bootstrap-switch.min.js"></script>
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/bootstrap-slider/7.0.2/bootstrap-slider.min.js"></script>
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/bootstrap-select/1.9.4/js/bootstrap-select.js"></script>
|
||||
<script src="/js/bootstrap-switch.min.js"></script>
|
||||
<script src="/js/bootstrap-slider.min.js"></script>
|
||||
<script src="/js/bootstrap-select.min.js"></script>
|
||||
<script src="/js/u2f-api.js"></script>
|
||||
<script>
|
||||
// Select language and reopen active URL without POST
|
||||
function setLang(sel) {
|
||||
|
@ -33,6 +36,94 @@ function setLang(sel) {
|
|||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
// Confirm TFA modal
|
||||
<?php if (isset($_SESSION['pending_tfa_method'])):?>
|
||||
$('#ConfirmTFAModal').modal({
|
||||
backdrop: 'static',
|
||||
keyboard: false
|
||||
});
|
||||
$('#ConfirmTFAModal').on('shown.bs.modal', function(){
|
||||
$(this).find('#token').focus();
|
||||
// If U2F
|
||||
if(document.getElementById("u2f_auth_data") !== null) {
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
cache: false,
|
||||
dataType: 'script',
|
||||
url: "json_api.php",
|
||||
data: {
|
||||
'action':'get_u2f_auth_challenge',
|
||||
'object':'<?=(isset($_SESSION['pending_mailcow_cc_username'])) ? $_SESSION['pending_mailcow_cc_username'] : null;?>',
|
||||
},
|
||||
success: function(data){
|
||||
data;
|
||||
}
|
||||
});
|
||||
setTimeout(function() {
|
||||
console.log("sign: ", req);
|
||||
u2f.sign(req, function(data) {
|
||||
var form = document.getElementById('u2f_auth_form');
|
||||
var auth = document.getElementById('u2f_auth_data');
|
||||
console.log("Authenticate callback", data);
|
||||
auth.value = JSON.stringify(data);
|
||||
form.submit();
|
||||
});
|
||||
}, 1000);
|
||||
}
|
||||
});
|
||||
<?php endif; ?>
|
||||
|
||||
// Set TFA modals
|
||||
|
||||
$('#selectTFA').change(function () {
|
||||
if ($(this).val() == "yubi_otp") {
|
||||
$('#YubiOTPModal').modal('show');
|
||||
$("option:selected").prop("selected", false);
|
||||
}
|
||||
if ($(this).val() == "u2f") {
|
||||
$('#U2FModal').modal('show');
|
||||
$("option:selected").prop("selected", false);
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
cache: false,
|
||||
dataType: 'script',
|
||||
url: "json_api.php",
|
||||
data: {
|
||||
'action':'get_u2f_reg_challenge',
|
||||
'object':'<?=(isset($_SESSION['mailcow_cc_username'])) ? $_SESSION['mailcow_cc_username'] : null;?>',
|
||||
},
|
||||
success: function(data){
|
||||
data;
|
||||
}
|
||||
});
|
||||
setTimeout(function() {
|
||||
console.log("Register: ", req);
|
||||
u2f.register([req], sigs, function(data) {
|
||||
var form = document.getElementById('u2f_reg_form');
|
||||
var reg = document.getElementById('u2f_register_data');
|
||||
console.log("Register callback", data);
|
||||
if (data.errorCode && data.errorCode != 0) {
|
||||
var u2f_return_code = document.getElementById('u2f_return_code');
|
||||
u2f_return_code.style.display = u2f_return_code.style.display === 'none' ? '' : null;
|
||||
if (data.errorCode == "4") { data.errorCode = "4 - The presented device is not eligible for this request. For a registration request this may mean that the token is already registered, and for a sign request it may mean that the token does not know the presented key handle"; }
|
||||
u2f_return_code.innerHTML = 'Error code: ' + data.errorCode;
|
||||
return;
|
||||
}
|
||||
reg.value = JSON.stringify(data);
|
||||
form.submit();
|
||||
});
|
||||
}, 1000);
|
||||
}
|
||||
if ($(this).val() == "none") {
|
||||
$('#DisableTFAModal').modal('show');
|
||||
$("option:selected").prop("selected", false);
|
||||
}
|
||||
});
|
||||
|
||||
// Activate tooltips
|
||||
$(function () {
|
||||
$('[data-toggle="tooltip"]').tooltip()
|
||||
})
|
||||
// Hide alerts after n seconds
|
||||
$("#alert-fade").fadeTo(7000, 500).slideUp(500, function(){
|
||||
$("#alert-fade").alert('close');
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -12,162 +12,16 @@
|
|||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.0/jquery.min.js" integrity="sha384-XxcvoeNF5V0ZfksTnV+bejnCsJjOOIzN6UVwF85WBsAnU3zeYh5bloN+L4WLgeNE" crossorigin="anonymous"></script>
|
||||
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.6/css/bootstrap.min.css">
|
||||
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/bootswatch/3.3.6/<?=strtolower(trim($DEFAULT_THEME));?>/bootstrap.min.css">
|
||||
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/bootstrap-select/1.9.4/css/bootstrap-select.min.css">
|
||||
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/bootstrap-slider/7.0.2/css/bootstrap-slider.min.css">
|
||||
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/bootstrap-switch/3.3.2/css/bootstrap3/bootstrap-switch.min.css">
|
||||
<link rel="stylesheet" href="/css/bootstrap-select.min.css">
|
||||
<link rel="stylesheet" href="/css/bootstrap-slider.min.css">
|
||||
<link rel="stylesheet" href="/css/bootstrap-switch.min.css">
|
||||
<link rel="stylesheet" href="//fonts.googleapis.com/css?family=Source+Sans+Pro:400,600,700&subset=latin,latin-ext">
|
||||
<link rel="stylesheet" href="/inc/languages.min.css">
|
||||
<link rel="stylesheet" href="/css/mailcow.css">
|
||||
<link rel="stylesheet" href="/css/tables.css">
|
||||
<?=(preg_match("/mailbox.php/i", $_SERVER['REQUEST_URI'])) ? '<link rel="stylesheet" href="/css/mailbox.css">' : null;?>
|
||||
<link rel="shortcut icon" href="/favicon.png" type="image/png">
|
||||
<link rel="icon" href="/favicon.png" type="image/png">
|
||||
<style>
|
||||
#maxmsgsize { min-width: 80px; }
|
||||
ul[id*="sortable"] { word-wrap: break-word; list-style-type: none; float: left; padding: 0 15px 0 0; width: 48%; cursor:move}
|
||||
ul[id$="sortable-active"] li {cursor:move; }
|
||||
ul[id$="sortable-inactive"] li {cursor:move }
|
||||
.list-heading { cursor:default !important}
|
||||
.ui-state-disabled { cursor:no-drop; color:#ccc; }
|
||||
.ui-state-highlight {background: #F5F5F5 !important; height: 41px !important; cursor:move }
|
||||
#slider1 .slider-selection {
|
||||
background: #FFD700;
|
||||
}
|
||||
#slider1 .slider-track-high {
|
||||
background: #FF4500;
|
||||
}
|
||||
#slider1 .slider-track-low {
|
||||
background: #66CD00;
|
||||
}
|
||||
table[data-sortable] {
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
}
|
||||
table[data-sortable] th {
|
||||
vertical-align: bottom;
|
||||
font-weight: bold;
|
||||
}
|
||||
table[data-sortable] th, table[data-sortable] td {
|
||||
text-align: left;
|
||||
padding: 10px;
|
||||
}
|
||||
table[data-sortable] th:not([data-sortable="false"]) {
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
-o-user-select: none;
|
||||
user-select: none;
|
||||
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||
-webkit-touch-callout: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
table[data-sortable] th:after {
|
||||
content: "";
|
||||
visibility: hidden;
|
||||
display: inline-block;
|
||||
vertical-align: inherit;
|
||||
height: 0;
|
||||
width: 0;
|
||||
border-width: 5px;
|
||||
border-style: solid;
|
||||
border-color: transparent;
|
||||
margin-right: 1px;
|
||||
margin-left: 10px;
|
||||
float: right;
|
||||
}
|
||||
table[data-sortable] th[data-sortable="false"]:after {
|
||||
display: none;
|
||||
}
|
||||
table[data-sortable] th[data-sorted="true"]:after {
|
||||
visibility: visible;
|
||||
}
|
||||
table[data-sortable] th[data-sorted-direction="descending"]:after {
|
||||
border-top-color: inherit;
|
||||
margin-top: 8px;
|
||||
}
|
||||
table[data-sortable] th[data-sorted-direction="ascending"]:after {
|
||||
border-bottom-color: inherit;
|
||||
margin-top: 3px;
|
||||
}
|
||||
table[data-sortable].sortable-theme-bootstrap thead th {
|
||||
border-bottom: 2px solid #e0e0e0;
|
||||
}
|
||||
table[data-sortable].sortable-theme-bootstrap th[data-sorted="true"] {
|
||||
color: #3a87ad;
|
||||
background: #d9edf7;
|
||||
border-bottom-color: #bce8f1;
|
||||
}
|
||||
table[data-sortable].sortable-theme-bootstrap th[data-sorted="true"][data-sorted-direction="descending"]:after {
|
||||
border-top-color: #3a87ad;
|
||||
}
|
||||
table[data-sortable].sortable-theme-bootstrap th[data-sorted="true"][data-sorted-direction="ascending"]:after {
|
||||
border-bottom-color: #3a87ad;
|
||||
}
|
||||
table[data-sortable].sortable-theme-bootstrap.sortable-theme-bootstrap-striped tbody > tr:nth-child(odd) > td {
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
.btn {
|
||||
text-transform: none;
|
||||
}
|
||||
#data td, #no-data td {
|
||||
vertical-align: middle;
|
||||
}
|
||||
.sort-table:hover {
|
||||
border-bottom-color: #00B7DC !important;
|
||||
}
|
||||
.striped:nth-child(odd) {
|
||||
background-color: #fff;
|
||||
}
|
||||
.striped:nth-child(even) {
|
||||
background-color: #fafafa;
|
||||
border:1px solid white;
|
||||
}
|
||||
.glyphicon-spin {
|
||||
-webkit-animation: spin 1000ms infinite linear;
|
||||
animation: spin 1000ms infinite linear;
|
||||
}
|
||||
@-webkit-keyframes spin {
|
||||
0% {
|
||||
-webkit-transform: rotate(0deg);
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
-webkit-transform: rotate(359deg);
|
||||
transform: rotate(359deg);
|
||||
}
|
||||
}
|
||||
@keyframes spin {
|
||||
0% {
|
||||
-webkit-transform: rotate(0deg);
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
-webkit-transform: rotate(359deg);
|
||||
transform: rotate(359deg);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<?php
|
||||
if (preg_match("/mailbox.php/i", $_SERVER['REQUEST_URI'])):
|
||||
?>
|
||||
<style>
|
||||
.panel-heading div {
|
||||
margin-top: -18px;
|
||||
font-size: 15px;
|
||||
}
|
||||
.panel-heading div span {
|
||||
margin-left:5px;
|
||||
}
|
||||
.panel-body {
|
||||
display: none;
|
||||
}
|
||||
.clickable {
|
||||
cursor: pointer;
|
||||
}
|
||||
.progress {
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
</style>
|
||||
<?php
|
||||
endif;
|
||||
?>
|
||||
</head>
|
||||
<body style="padding-top:70px">
|
||||
<nav class="navbar navbar-default navbar-fixed-top" role="navigation">
|
||||
|
@ -191,6 +45,7 @@ endif;
|
|||
<ul class="dropdown-menu" role="menu">
|
||||
<li <?=($_SESSION['mailcow_locale'] == 'de') ? 'class="active"' : ''?>> <a href="?<?= http_build_query(array_merge($_GET, array("lang" => "de"))) ?>"><span class="lang-xs lang-lbl-full" lang="de"></span></a></li>
|
||||
<li <?=($_SESSION['mailcow_locale'] == 'en') ? 'class="active"' : ''?>> <a href="?<?= http_build_query(array_merge($_GET, array("lang" => "en"))) ?>"><span class="lang-xs lang-lbl-full" lang="en"></span></a></li>
|
||||
<li <?=($_SESSION['mailcow_locale'] == 'es') ? 'class="active"' : ''?>> <a href="?<?= http_build_query(array_merge($_GET, array("lang" => "es"))) ?>"><span class="lang-xs lang-lbl-full" lang="es"></span></a></li>
|
||||
<li <?=($_SESSION['mailcow_locale'] == 'nl') ? 'class="active"' : ''?>> <a href="?<?= http_build_query(array_merge($_GET, array("lang" => "nl"))) ?>"><span class="lang-xs lang-lbl-full" lang="nl"></span></a></li>
|
||||
<li <?=($_SESSION['mailcow_locale'] == 'pt') ? 'class="active"' : ''?>> <a href="?<?= http_build_query(array_merge($_GET, array("lang" => "pt"))) ?>"><span class="lang-xs lang-lbl-full" lang="pt"></span></a></li>
|
||||
</ul>
|
||||
|
@ -214,11 +69,11 @@ endif;
|
|||
<li <?=(preg_match("/mailbox/i", $_SERVER['REQUEST_URI'])) ? 'class="active"' : ''?>><a href="/mailbox.php"><?=$lang['header']['mailboxes'];?></a></li>
|
||||
<?php
|
||||
}
|
||||
if ($_SESSION['mailcow_cc_role'] == "user") {
|
||||
if ($_SESSION['mailcow_cc_role'] != "admin") {
|
||||
?>
|
||||
<li <?=(preg_match("/user/i", $_SERVER['REQUEST_URI'])) ? 'class="active"' : ''?>><a href="/user.php"><?=$lang['header']['user_settings'];?></a></li>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
</ul>
|
||||
|
@ -232,10 +87,14 @@ endif;
|
|||
?>
|
||||
<?php
|
||||
}
|
||||
if (isset($_SESSION['mailcow_cc_username'])):
|
||||
if (!isset($_SESSION["dual-login"]) && isset($_SESSION['mailcow_cc_username'])):
|
||||
?>
|
||||
<li><a style="border-left:1px solid #E7E7E7" href="#" onclick="logout.submit()"><?=sprintf($lang['header']['logged_in_as_logout'], $_SESSION['mailcow_cc_username']);?></a></li>
|
||||
<?php
|
||||
elseif (isset($_SESSION["dual-login"])):
|
||||
?>
|
||||
<li><a style="border-left:1px solid #E7E7E7" href="#" onclick="logout.submit()"><?=sprintf($lang['header']['logged_in_as_logout_dual'], $_SESSION['mailcow_cc_username'], $_SESSION["dual-login"]["username"]);?></a></li>
|
||||
<?php
|
||||
endif;
|
||||
?>
|
||||
</ul>
|
||||
|
|
|
@ -1,107 +1,145 @@
|
|||
CREATE TABLE IF NOT EXISTS `admin` (
|
||||
`username` varchar(255) NOT NULL,
|
||||
`password` varchar(255) NOT NULL,
|
||||
`superadmin` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`created` datetime NOT NULL DEFAULT '2016-01-01 00:00:00',
|
||||
`modified` datetime NOT NULL DEFAULT '2016-01-01 00:00:00',
|
||||
`active` tinyint(1) NOT NULL DEFAULT '1',
|
||||
`username` VARCHAR(255) NOT NULL,
|
||||
`password` VARCHAR(255) NOT NULL,
|
||||
`superadmin` TINYINT(1) NOT NULL DEFAULT '0',
|
||||
`created` DATETIME NOT NULL DEFAULT '2016-01-01 00:00:00',
|
||||
`modified` DATETIME NOT NULL DEFAULT '2016-01-01 00:00:00',
|
||||
`active` TINYINT(1) NOT NULL DEFAULT '1',
|
||||
PRIMARY KEY (`username`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `alias` (
|
||||
`address` varchar(255) NOT NULL,
|
||||
`goto` text NOT NULL,
|
||||
`domain` varchar(255) NOT NULL,
|
||||
`created` datetime NOT NULL DEFAULT '2016-01-01 00:00:00',
|
||||
`modified` datetime NOT NULL DEFAULT '2016-01-01 00:00:00',
|
||||
`active` tinyint(1) NOT NULL DEFAULT '1',
|
||||
`address` VARCHAR(255) NOT NULL,
|
||||
`goto` TEXT NOT NULL,
|
||||
`domain` VARCHAR(255) NOT NULL,
|
||||
`created` DATETIME NOT NULL DEFAULT '2016-01-01 00:00:00',
|
||||
`modified` DATETIME NOT NULL DEFAULT '2016-01-01 00:00:00',
|
||||
`active` TINYINT(1) NOT NULL DEFAULT '1',
|
||||
PRIMARY KEY (`address`),
|
||||
KEY `domain` (`domain`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `sender_acl` (
|
||||
`logged_in_as` varchar(255) NOT NULL,
|
||||
`send_as` varchar(255) NOT NULL
|
||||
`logged_in_as` VARCHAR(255) NOT NULL,
|
||||
`send_as` VARCHAR(255) NOT NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `spamalias` (
|
||||
`address` varchar(255) NOT NULL,
|
||||
`goto` text NOT NULL,
|
||||
`validity` int(11) NOT NULL,
|
||||
`address` VARCHAR(255) NOT NULL,
|
||||
`goto` TEXT NOT NULL,
|
||||
`validity` INT(11) NOT NULL,
|
||||
PRIMARY KEY (`address`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `alias_domain` (
|
||||
`alias_domain` varchar(255) NOT NULL,
|
||||
`target_domain` varchar(255) NOT NULL,
|
||||
`created` datetime NOT NULL DEFAULT '2016-01-01 00:00:00',
|
||||
`modified` datetime NOT NULL DEFAULT '2016-01-01 00:00:00',
|
||||
`active` tinyint(1) NOT NULL DEFAULT '1',
|
||||
`alias_domain` VARCHAR(255) NOT NULL,
|
||||
`target_domain` VARCHAR(255) NOT NULL,
|
||||
`created` DATETIME NOT NULL DEFAULT '2016-01-01 00:00:00',
|
||||
`modified` DATETIME NOT NULL DEFAULT '2016-01-01 00:00:00',
|
||||
`active` TINYINT(1) NOT NULL DEFAULT '1',
|
||||
PRIMARY KEY (`alias_domain`),
|
||||
KEY `active` (`active`),
|
||||
KEY `target_domain` (`target_domain`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `domain` (
|
||||
`domain` varchar(255) NOT NULL,
|
||||
`description` varchar(255),
|
||||
`aliases` int(10) NOT NULL DEFAULT '0',
|
||||
`mailboxes` int(10) NOT NULL DEFAULT '0',
|
||||
`maxquota` bigint(20) NOT NULL DEFAULT '0',
|
||||
`quota` bigint(20) NOT NULL DEFAULT '0',
|
||||
`transport` varchar(255) NOT NULL,
|
||||
`backupmx` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`relay_all_recipients` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`created` datetime NOT NULL DEFAULT '2016-01-01 00:00:00',
|
||||
`modified` datetime NOT NULL DEFAULT '2016-01-01 00:00:00',
|
||||
`active` tinyint(1) NOT NULL DEFAULT '1',
|
||||
`domain` VARCHAR(255) NOT NULL,
|
||||
`description` VARCHAR(255),
|
||||
`aliases` INT(10) NOT NULL DEFAULT '0',
|
||||
`mailboxes` INT(10) NOT NULL DEFAULT '0',
|
||||
`maxquota` BIGINT(20) NOT NULL DEFAULT '0',
|
||||
`quota` BIGINT(20) NOT NULL DEFAULT '0',
|
||||
`transport` VARCHAR(255) NOT NULL,
|
||||
`backupmx` TINYINT(1) NOT NULL DEFAULT '0',
|
||||
`relay_all_recipients` TINYINT(1) NOT NULL DEFAULT '0',
|
||||
`created` DATETIME NOT NULL DEFAULT '2016-01-01 00:00:00',
|
||||
`modified` DATETIME NOT NULL DEFAULT '2016-01-01 00:00:00',
|
||||
`active` TINYINT(1) NOT NULL DEFAULT '1',
|
||||
PRIMARY KEY (`domain`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `domain_admins` (
|
||||
`username` varchar(255) NOT NULL,
|
||||
`domain` varchar(255) NOT NULL,
|
||||
`created` datetime NOT NULL DEFAULT '2016-01-01 00:00:00',
|
||||
`active` tinyint(1) NOT NULL DEFAULT '1',
|
||||
`username` VARCHAR(255) NOT NULL,
|
||||
`domain` VARCHAR(255) NOT NULL,
|
||||
`created` DATETIME NOT NULL DEFAULT '2016-01-01 00:00:00',
|
||||
`active` TINYINT(1) NOT NULL DEFAULT '1',
|
||||
KEY `username` (`username`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `mailbox` (
|
||||
`username` varchar(255) NOT NULL,
|
||||
`password` varchar(255) NOT NULL,
|
||||
`name` varchar(255),
|
||||
`maildir` varchar(255) NOT NULL,
|
||||
`quota` bigint(20) NOT NULL DEFAULT '0',
|
||||
`local_part` varchar(255) NOT NULL,
|
||||
`domain` varchar(255) NOT NULL,
|
||||
`created` datetime NOT NULL DEFAULT '2016-01-01 00:00:00',
|
||||
`modified` datetime NOT NULL DEFAULT '2016-01-01 00:00:00',
|
||||
`tls_enforce_in` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`tls_enforce_out` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`kind` varchar(100) NOT NULL DEFAULT '',
|
||||
`multiple_bookings` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`wants_tagged_subject` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`active` tinyint(1) NOT NULL DEFAULT '1',
|
||||
`username` VARCHAR(255) NOT NULL,
|
||||
`password` VARCHAR(255) NOT NULL,
|
||||
`name` VARCHAR(255),
|
||||
`maildir` VARCHAR(255) NOT NULL,
|
||||
`quota` BIGINT(20) NOT NULL DEFAULT '0',
|
||||
`local_part` VARCHAR(255) NOT NULL,
|
||||
`domain` VARCHAR(255) NOT NULL,
|
||||
`created` DATETIME NOT NULL DEFAULT '2016-01-01 00:00:00',
|
||||
`modified` DATETIME NOT NULL DEFAULT '2016-01-01 00:00:00',
|
||||
`tls_enforce_in` TINYINT(1) NOT NULL DEFAULT '0',
|
||||
`tls_enforce_out` TINYINT(1) NOT NULL DEFAULT '0',
|
||||
`kind` VARCHAR(100) NOT NULL DEFAULT '',
|
||||
`multiple_bookings` TINYINT(1) NOT NULL DEFAULT '0',
|
||||
`wants_tagged_subject` TINYINT(1) NOT NULL DEFAULT '0',
|
||||
`active` TINYINT(1) NOT NULL DEFAULT '1',
|
||||
PRIMARY KEY (`username`),
|
||||
KEY `domain` (`domain`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `quota2` (
|
||||
`username` varchar(100) NOT NULL,
|
||||
`bytes` bigint(20) NOT NULL DEFAULT '0',
|
||||
`messages` int(11) NOT NULL DEFAULT '0',
|
||||
`username` VARCHAR(100) NOT NULL,
|
||||
`bytes` BIGINT(20) NOT NULL DEFAULT '0',
|
||||
`messages` INT(11) NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY (`username`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `filterconf` (
|
||||
`object` varchar(100) NOT NULL DEFAULT '',
|
||||
`option` varchar(50) NOT NULL DEFAULT '',
|
||||
`value` varchar(100) NOT NULL DEFAULT '',
|
||||
`prefid` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`object` VARCHAR(100) NOT NULL DEFAULT '',
|
||||
`option` VARCHAR(50) NOT NULL DEFAULT '',
|
||||
`value` VARCHAR(100) NOT NULL DEFAULT '',
|
||||
`prefid` INT(11) NOT NULL AUTO_INCREMENT,
|
||||
PRIMARY KEY (`prefid`),
|
||||
KEY `object` (`object`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `imapsync` (
|
||||
`id` INT NOT NULL AUTO_INCREMENT,
|
||||
`user2` VARCHAR(255) NOT NULL,
|
||||
`host1` VARCHAR(255) NOT NULL,
|
||||
`authmech1` ENUM('PLAIN','LOGIN','CRAM-MD5') DEFAULT 'PLAIN',
|
||||
`regextrans2` VARCHAR(255) DEFAULT '',
|
||||
`authmd51` TINYINT(1) NOT NULL DEFAULT 0,
|
||||
`domain2` VARCHAR(255) NOT NULL DEFAULT '',
|
||||
`subfolder2` VARCHAR(255) NOT NULL DEFAULT '',
|
||||
`user1` VARCHAR(255) NOT NULL,
|
||||
`password1` VARCHAR(255) NOT NULL,
|
||||
`exclude` VARCHAR(500) NOT NULL DEFAULT '',
|
||||
`maxage` SMALLINT NOT NULL DEFAULT '0',
|
||||
`mins_interval` VARCHAR(50) NOT NULL,
|
||||
`port1` SMALLINT NOT NULL,
|
||||
`enc1` ENUM('TLS','SSL','PLAIN') DEFAULT 'TLS',
|
||||
`delete2duplicates` TINYINT(1) NOT NULL DEFAULT '1',
|
||||
`returned_text` TEXT,
|
||||
`last_run` TIMESTAMP NULL DEFAULT NULL,
|
||||
`created` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`modified` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`active` TINYINT(1) NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `tfa` (
|
||||
`id` INT NOT NULL AUTO_INCREMENT,
|
||||
`username` VARCHAR(255) NOT NULL,
|
||||
`authmech` ENUM('yubi_otp', 'u2f', 'hotp', 'totp'),
|
||||
`secret` VARCHAR(255) DEFAULT NULL,
|
||||
`keyHandle` VARCHAR(255) DEFAULT NULL,
|
||||
`publicKey` VARCHAR(255) DEFAULT NULL,
|
||||
`counter` INT NOT NULL DEFAULT '0',
|
||||
`certificate` TEXT,
|
||||
`active` TINYINT(1) NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;
|
||||
|
||||
DROP VIEW IF EXISTS grouped_mail_aliases;
|
||||
DROP VIEW IF EXISTS grouped_sender_acl;
|
||||
DROP VIEW IF EXISTS grouped_domain_alias_address;
|
||||
|
@ -123,80 +161,80 @@ SELECT username, IFNULL(GROUP_CONCAT(local_part, '@', alias_domain SEPARATOR ' '
|
|||
LEFT OUTER JOIN alias_domain on target_domain=domain GROUP BY username;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS sogo_acl (
|
||||
c_folder_id integer NOT NULL,
|
||||
c_folder_id INTEGER NOT NULL,
|
||||
c_object character varying(255) NOT NULL,
|
||||
c_uid character varying(255) NOT NULL,
|
||||
c_role character varying(80) NOT NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS sogo_alarms_folder (
|
||||
c_path varchar(255) NOT NULL,
|
||||
c_name varchar(255) NOT NULL,
|
||||
c_uid varchar(255) NOT NULL,
|
||||
c_recurrence_id int(11) DEFAULT NULL,
|
||||
c_alarm_number int(11) NOT NULL,
|
||||
c_alarm_date int(11) NOT NULL
|
||||
c_path VARCHAR(255) NOT NULL,
|
||||
c_name VARCHAR(255) NOT NULL,
|
||||
c_uid VARCHAR(255) NOT NULL,
|
||||
c_recurrence_id INT(11) DEFAULT NULL,
|
||||
c_alarm_number INT(11) NOT NULL,
|
||||
c_alarm_date INT(11) NOT NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS sogo_cache_folder (
|
||||
c_uid varchar(255) NOT NULL,
|
||||
c_path varchar(255) NOT NULL,
|
||||
c_parent_path varchar(255) DEFAULT NULL,
|
||||
c_type tinyint(3) unsigned NOT NULL,
|
||||
c_creationdate int(11) NOT NULL,
|
||||
c_lastmodified int(11) NOT NULL,
|
||||
c_version int(11) NOT NULL DEFAULT '0',
|
||||
c_deleted tinyint(4) NOT NULL DEFAULT '0',
|
||||
c_content longtext,
|
||||
c_uid VARCHAR(255) NOT NULL,
|
||||
c_path VARCHAR(255) NOT NULL,
|
||||
c_parent_path VARCHAR(255) DEFAULT NULL,
|
||||
c_type TINYINT(3) unsigned NOT NULL,
|
||||
c_creationdate INT(11) NOT NULL,
|
||||
c_lastmodified INT(11) NOT NULL,
|
||||
c_version INT(11) NOT NULL DEFAULT '0',
|
||||
c_deleted TINYINT(4) NOT NULL DEFAULT '0',
|
||||
c_content longTEXT,
|
||||
PRIMARY KEY (c_uid,c_path)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS sogo_folder_info (
|
||||
c_folder_id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||
c_path varchar(255) NOT NULL,
|
||||
c_path1 varchar(255) NOT NULL,
|
||||
c_path2 varchar(255) DEFAULT NULL,
|
||||
c_path3 varchar(255) DEFAULT NULL,
|
||||
c_path4 varchar(255) DEFAULT NULL,
|
||||
c_foldername varchar(255) NOT NULL,
|
||||
c_location integer NULL,
|
||||
c_quick_location varchar(2048) DEFAULT NULL,
|
||||
c_acl_location varchar(2048) DEFAULT NULL,
|
||||
c_folder_type varchar(255) NOT NULL,
|
||||
c_folder_id BIGINT(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||
c_path VARCHAR(255) NOT NULL,
|
||||
c_path1 VARCHAR(255) NOT NULL,
|
||||
c_path2 VARCHAR(255) DEFAULT NULL,
|
||||
c_path3 VARCHAR(255) DEFAULT NULL,
|
||||
c_path4 VARCHAR(255) DEFAULT NULL,
|
||||
c_foldername VARCHAR(255) NOT NULL,
|
||||
c_location INTeger NULL,
|
||||
c_quick_location VARCHAR(2048) DEFAULT NULL,
|
||||
c_acl_location VARCHAR(2048) DEFAULT NULL,
|
||||
c_folder_type VARCHAR(255) NOT NULL,
|
||||
PRIMARY KEY (c_path),
|
||||
UNIQUE KEY c_folder_id (c_folder_id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS sogo_quick_appointment (
|
||||
c_folder_id integer NOT NULL,
|
||||
c_folder_id INTeger NOT NULL,
|
||||
c_name character varying(255) NOT NULL,
|
||||
c_uid character varying(255) NOT NULL,
|
||||
c_startdate integer,
|
||||
c_enddate integer,
|
||||
c_cycleenddate integer,
|
||||
c_startdate INTeger,
|
||||
c_enddate INTeger,
|
||||
c_cycleenddate INTeger,
|
||||
c_title character varying(1000) NOT NULL,
|
||||
c_participants text,
|
||||
c_isallday integer,
|
||||
c_iscycle integer,
|
||||
c_cycleinfo text,
|
||||
c_classification integer NOT NULL,
|
||||
c_isopaque integer NOT NULL,
|
||||
c_status integer NOT NULL,
|
||||
c_priority integer,
|
||||
c_participants TEXT,
|
||||
c_isallday INTeger,
|
||||
c_iscycle INTeger,
|
||||
c_cycleinfo TEXT,
|
||||
c_classification INTeger NOT NULL,
|
||||
c_isopaque INTeger NOT NULL,
|
||||
c_status INTeger NOT NULL,
|
||||
c_priority INTeger,
|
||||
c_location character varying(255),
|
||||
c_orgmail character varying(255),
|
||||
c_partmails text,
|
||||
c_partstates text,
|
||||
c_partmails TEXT,
|
||||
c_partstates TEXT,
|
||||
c_category character varying(255),
|
||||
c_sequence integer,
|
||||
c_sequence INTeger,
|
||||
c_component character varying(10) NOT NULL,
|
||||
c_nextalarm integer,
|
||||
c_description text,
|
||||
c_nextalarm INTeger,
|
||||
c_description TEXT,
|
||||
CONSTRAINT sogo_quick_appointment_pkey PRIMARY KEY (c_folder_id, c_name)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS sogo_quick_contact (
|
||||
c_folder_id integer NOT NULL,
|
||||
c_folder_id INTeger NOT NULL,
|
||||
c_name character varying(255) NOT NULL,
|
||||
c_givenname character varying(255),
|
||||
c_cn character varying(255),
|
||||
|
@ -213,31 +251,31 @@ CREATE TABLE IF NOT EXISTS sogo_quick_contact (
|
|||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS sogo_sessions_folder (
|
||||
c_id varchar(255) NOT NULL,
|
||||
c_value varchar(255) NOT NULL,
|
||||
c_creationdate int(11) NOT NULL,
|
||||
c_lastseen int(11) NOT NULL,
|
||||
c_id VARCHAR(255) NOT NULL,
|
||||
c_value VARCHAR(255) NOT NULL,
|
||||
c_creationdate INT(11) NOT NULL,
|
||||
c_lastseen INT(11) NOT NULL,
|
||||
PRIMARY KEY (c_id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS sogo_store (
|
||||
c_folder_id integer NOT NULL,
|
||||
c_folder_id INTeger NOT NULL,
|
||||
c_name character varying(255) NOT NULL,
|
||||
c_content mediumtext NOT NULL,
|
||||
c_creationdate integer NOT NULL,
|
||||
c_lastmodified integer NOT NULL,
|
||||
c_version integer NOT NULL,
|
||||
c_deleted integer,
|
||||
c_content mediumTEXT NOT NULL,
|
||||
c_creationdate INTeger NOT NULL,
|
||||
c_lastmodified INTeger NOT NULL,
|
||||
c_version INTeger NOT NULL,
|
||||
c_deleted INTeger,
|
||||
CONSTRAINT sogo_store_pkey PRIMARY KEY (c_folder_id, c_name)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS sogo_user_profile (
|
||||
c_uid varchar(255) NOT NULL,
|
||||
c_defaults text,
|
||||
c_settings text,
|
||||
c_uid VARCHAR(255) NOT NULL,
|
||||
c_defaults TEXT,
|
||||
c_settings TEXT,
|
||||
PRIMARY KEY (c_uid)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;
|
||||
|
||||
REPLACE INTO admin (username, password, superadmin, created, modified, active) VALUES ('admin', '{SSHA256}K8eVJ6YsZbQCfuJvSUbaQRLr0HPLz5rC9IAp0PAFl0tmNDBkMDc0NDAyOTAxN2Rk', 1, NOW(), NOW(), 1);
|
||||
DELETE FROM domain_admins WHERE domain='all';
|
||||
INSERT INTO domain_admins (username, domain, created, active) VALUES ('admin', 'ALL', NOW(), 1);
|
||||
INSERT INTO `admin` (username, password, superadmin, created, modified, active) SELECT 'admin', '{SSHA256}K8eVJ6YsZbQCfuJvSUbaQRLr0HPLz5rC9IAp0PAFl0tmNDBkMDc0NDAyOTAxN2Rk', 1, NOW(), NOW(), 1 WHERE NOT EXISTS (SELECT * FROM `admin`);
|
||||
DELETE FROM `domain_admins`;
|
||||
INSERT INTO `domain_admins` (username, domain, created, active) SELECT `username`, 'ALL', NOW(), 1 FROM `admin` WHERE superadmin='1' AND `username` NOT IN (SELECT `username` FROM `domain_admins`);
|
||||
|
|
|
@ -0,0 +1,506 @@
|
|||
<?php
|
||||
/* Copyright (c) 2014 Yubico AB
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer in the documentation and/or other materials provided
|
||||
* with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
namespace u2flib_server;
|
||||
|
||||
/** Constant for the version of the u2f protocol */
|
||||
const U2F_VERSION = "U2F_V2";
|
||||
|
||||
/** Error for the authentication message not matching any outstanding
|
||||
* authentication request */
|
||||
const ERR_NO_MATCHING_REQUEST = 1;
|
||||
|
||||
/** Error for the authentication message not matching any registration */
|
||||
const ERR_NO_MATCHING_REGISTRATION = 2;
|
||||
|
||||
/** Error for the signature on the authentication message not verifying with
|
||||
* the correct key */
|
||||
const ERR_AUTHENTICATION_FAILURE = 3;
|
||||
|
||||
/** Error for the challenge in the registration message not matching the
|
||||
* registration challenge */
|
||||
const ERR_UNMATCHED_CHALLENGE = 4;
|
||||
|
||||
/** Error for the attestation signature on the registration message not
|
||||
* verifying */
|
||||
const ERR_ATTESTATION_SIGNATURE = 5;
|
||||
|
||||
/** Error for the attestation verification not verifying */
|
||||
const ERR_ATTESTATION_VERIFICATION = 6;
|
||||
|
||||
/** Error for not getting good random from the system */
|
||||
const ERR_BAD_RANDOM = 7;
|
||||
|
||||
/** Error when the counter is lower than expected */
|
||||
const ERR_COUNTER_TOO_LOW = 8;
|
||||
|
||||
/** Error decoding public key */
|
||||
const ERR_PUBKEY_DECODE = 9;
|
||||
|
||||
/** Error user-agent returned error */
|
||||
const ERR_BAD_UA_RETURNING = 10;
|
||||
|
||||
/** Error old OpenSSL version */
|
||||
const ERR_OLD_OPENSSL = 11;
|
||||
|
||||
/** @internal */
|
||||
const PUBKEY_LEN = 65;
|
||||
|
||||
class U2F
|
||||
{
|
||||
/** @var string */
|
||||
private $appId;
|
||||
|
||||
/** @var null|string */
|
||||
private $attestDir;
|
||||
|
||||
/** @internal */
|
||||
private $FIXCERTS = array(
|
||||
'349bca1031f8c82c4ceca38b9cebf1a69df9fb3b94eed99eb3fb9aa3822d26e8',
|
||||
'dd574527df608e47ae45fbba75a2afdd5c20fd94a02419381813cd55a2a3398f',
|
||||
'1d8764f0f7cd1352df6150045c8f638e517270e8b5dda1c63ade9c2280240cae',
|
||||
'd0edc9a91a1677435a953390865d208c55b3183c6759c9b5a7ff494c322558eb',
|
||||
'6073c436dcd064a48127ddbf6032ac1a66fd59a0c24434f070d4e564c124c897',
|
||||
'ca993121846c464d666096d35f13bf44c1b05af205f9b4a1e00cf6cc10c5e511'
|
||||
);
|
||||
|
||||
/**
|
||||
* @param string $appId Application id for the running application
|
||||
* @param string|null $attestDir Directory where trusted attestation roots may be found
|
||||
* @throws Error If OpenSSL older than 1.0.0 is used
|
||||
*/
|
||||
public function __construct($appId, $attestDir = null)
|
||||
{
|
||||
if(OPENSSL_VERSION_NUMBER < 0x10000000) {
|
||||
throw new Error('OpenSSL has to be at least version 1.0.0, this is ' . OPENSSL_VERSION_TEXT, ERR_OLD_OPENSSL);
|
||||
}
|
||||
$this->appId = $appId;
|
||||
$this->attestDir = $attestDir;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to get a registration request to send to a user.
|
||||
* Returns an array of one registration request and a array of sign requests.
|
||||
*
|
||||
* @param array $registrations List of current registrations for this
|
||||
* user, to prevent the user from registering the same authenticator several
|
||||
* times.
|
||||
* @return array An array of two elements, the first containing a
|
||||
* RegisterRequest the second being an array of SignRequest
|
||||
* @throws Error
|
||||
*/
|
||||
public function getRegisterData(array $registrations = array())
|
||||
{
|
||||
$challenge = $this->createChallenge();
|
||||
$request = new RegisterRequest($challenge, $this->appId);
|
||||
$signs = $this->getAuthenticateData($registrations);
|
||||
return array($request, $signs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to verify and unpack a registration message.
|
||||
*
|
||||
* @param RegisterRequest $request this is a reply to
|
||||
* @param object $response response from a user
|
||||
* @param bool $includeCert set to true if the attestation certificate should be
|
||||
* included in the returned Registration object
|
||||
* @return Registration
|
||||
* @throws Error
|
||||
*/
|
||||
public function doRegister($request, $response, $includeCert = true)
|
||||
{
|
||||
if( !is_object( $request ) ) {
|
||||
throw new \InvalidArgumentException('$request of doRegister() method only accepts object.');
|
||||
}
|
||||
|
||||
if( !is_object( $response ) ) {
|
||||
throw new \InvalidArgumentException('$response of doRegister() method only accepts object.');
|
||||
}
|
||||
|
||||
if( property_exists( $response, 'errorCode') && $response->errorCode !== 0 ) {
|
||||
throw new Error('User-agent returned error. Error code: ' . $response->errorCode, ERR_BAD_UA_RETURNING );
|
||||
}
|
||||
|
||||
if( !is_bool( $includeCert ) ) {
|
||||
throw new \InvalidArgumentException('$include_cert of doRegister() method only accepts boolean.');
|
||||
}
|
||||
|
||||
$rawReg = $this->base64u_decode($response->registrationData);
|
||||
$regData = array_values(unpack('C*', $rawReg));
|
||||
$clientData = $this->base64u_decode($response->clientData);
|
||||
$cli = json_decode($clientData);
|
||||
|
||||
if($cli->challenge !== $request->challenge) {
|
||||
throw new Error('Registration challenge does not match', ERR_UNMATCHED_CHALLENGE );
|
||||
}
|
||||
|
||||
$registration = new Registration();
|
||||
$offs = 1;
|
||||
$pubKey = substr($rawReg, $offs, PUBKEY_LEN);
|
||||
$offs += PUBKEY_LEN;
|
||||
// decode the pubKey to make sure it's good
|
||||
$tmpKey = $this->pubkey_to_pem($pubKey);
|
||||
if($tmpKey === null) {
|
||||
throw new Error('Decoding of public key failed', ERR_PUBKEY_DECODE );
|
||||
}
|
||||
$registration->publicKey = base64_encode($pubKey);
|
||||
$khLen = $regData[$offs++];
|
||||
$kh = substr($rawReg, $offs, $khLen);
|
||||
$offs += $khLen;
|
||||
$registration->keyHandle = $this->base64u_encode($kh);
|
||||
|
||||
// length of certificate is stored in byte 3 and 4 (excluding the first 4 bytes)
|
||||
$certLen = 4;
|
||||
$certLen += ($regData[$offs + 2] << 8);
|
||||
$certLen += $regData[$offs + 3];
|
||||
|
||||
$rawCert = $this->fixSignatureUnusedBits(substr($rawReg, $offs, $certLen));
|
||||
$offs += $certLen;
|
||||
$pemCert = "-----BEGIN CERTIFICATE-----\r\n";
|
||||
$pemCert .= chunk_split(base64_encode($rawCert), 64);
|
||||
$pemCert .= "-----END CERTIFICATE-----";
|
||||
if($includeCert) {
|
||||
$registration->certificate = base64_encode($rawCert);
|
||||
}
|
||||
if($this->attestDir) {
|
||||
if(openssl_x509_checkpurpose($pemCert, -1, $this->get_certs()) !== true) {
|
||||
throw new Error('Attestation certificate can not be validated', ERR_ATTESTATION_VERIFICATION );
|
||||
}
|
||||
}
|
||||
|
||||
if(!openssl_pkey_get_public($pemCert)) {
|
||||
throw new Error('Decoding of public key failed', ERR_PUBKEY_DECODE );
|
||||
}
|
||||
$signature = substr($rawReg, $offs);
|
||||
|
||||
$dataToVerify = chr(0);
|
||||
$dataToVerify .= hash('sha256', $request->appId, true);
|
||||
$dataToVerify .= hash('sha256', $clientData, true);
|
||||
$dataToVerify .= $kh;
|
||||
$dataToVerify .= $pubKey;
|
||||
|
||||
if(openssl_verify($dataToVerify, $signature, $pemCert, 'sha256') === 1) {
|
||||
return $registration;
|
||||
} else {
|
||||
throw new Error('Attestation signature does not match', ERR_ATTESTATION_SIGNATURE );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to get an authentication request.
|
||||
*
|
||||
* @param array $registrations An array of the registrations to create authentication requests for.
|
||||
* @return array An array of SignRequest
|
||||
* @throws Error
|
||||
*/
|
||||
public function getAuthenticateData(array $registrations)
|
||||
{
|
||||
$sigs = array();
|
||||
foreach ($registrations as $reg) {
|
||||
if( !is_object( $reg ) ) {
|
||||
throw new \InvalidArgumentException('$registrations of getAuthenticateData() method only accepts array of object.');
|
||||
}
|
||||
|
||||
$sig = new SignRequest();
|
||||
$sig->appId = $this->appId;
|
||||
$sig->keyHandle = $reg->keyHandle;
|
||||
$sig->challenge = $this->createChallenge();
|
||||
$sigs[] = $sig;
|
||||
}
|
||||
return $sigs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to verify an authentication response
|
||||
*
|
||||
* @param array $requests An array of outstanding authentication requests
|
||||
* @param array $registrations An array of current registrations
|
||||
* @param object $response A response from the authenticator
|
||||
* @return Registration
|
||||
* @throws Error
|
||||
*
|
||||
* The Registration object returned on success contains an updated counter
|
||||
* that should be saved for future authentications.
|
||||
* If the Error returned is ERR_COUNTER_TOO_LOW this is an indication of
|
||||
* token cloning or similar and appropriate action should be taken.
|
||||
*/
|
||||
public function doAuthenticate(array $requests, array $registrations, $response)
|
||||
{
|
||||
if( !is_object( $response ) ) {
|
||||
throw new \InvalidArgumentException('$response of doAuthenticate() method only accepts object.');
|
||||
}
|
||||
|
||||
if( property_exists( $response, 'errorCode') && $response->errorCode !== 0 ) {
|
||||
throw new Error('User-agent returned error. Error code: ' . $response->errorCode, ERR_BAD_UA_RETURNING );
|
||||
}
|
||||
|
||||
/** @var object|null $req */
|
||||
$req = null;
|
||||
|
||||
/** @var object|null $reg */
|
||||
$reg = null;
|
||||
|
||||
$clientData = $this->base64u_decode($response->clientData);
|
||||
$decodedClient = json_decode($clientData);
|
||||
foreach ($requests as $req) {
|
||||
if( !is_object( $req ) ) {
|
||||
throw new \InvalidArgumentException('$requests of doAuthenticate() method only accepts array of object.');
|
||||
}
|
||||
|
||||
if($req->keyHandle === $response->keyHandle && $req->challenge === $decodedClient->challenge) {
|
||||
break;
|
||||
}
|
||||
|
||||
$req = null;
|
||||
}
|
||||
if($req === null) {
|
||||
throw new Error('No matching request found', ERR_NO_MATCHING_REQUEST );
|
||||
}
|
||||
foreach ($registrations as $reg) {
|
||||
if( !is_object( $reg ) ) {
|
||||
throw new \InvalidArgumentException('$registrations of doAuthenticate() method only accepts array of object.');
|
||||
}
|
||||
|
||||
if($reg->keyHandle === $response->keyHandle) {
|
||||
break;
|
||||
}
|
||||
$reg = null;
|
||||
}
|
||||
if($reg === null) {
|
||||
throw new Error('No matching registration found', ERR_NO_MATCHING_REGISTRATION );
|
||||
}
|
||||
$pemKey = $this->pubkey_to_pem($this->base64u_decode($reg->publicKey));
|
||||
if($pemKey === null) {
|
||||
throw new Error('Decoding of public key failed', ERR_PUBKEY_DECODE );
|
||||
}
|
||||
|
||||
$signData = $this->base64u_decode($response->signatureData);
|
||||
$dataToVerify = hash('sha256', $req->appId, true);
|
||||
$dataToVerify .= substr($signData, 0, 5);
|
||||
$dataToVerify .= hash('sha256', $clientData, true);
|
||||
$signature = substr($signData, 5);
|
||||
|
||||
if(openssl_verify($dataToVerify, $signature, $pemKey, 'sha256') === 1) {
|
||||
$ctr = unpack("Nctr", substr($signData, 1, 4));
|
||||
$counter = $ctr['ctr'];
|
||||
/* TODO: wrap-around should be handled somehow.. */
|
||||
if($counter > $reg->counter) {
|
||||
$reg->counter = $counter;
|
||||
return $reg;
|
||||
} else {
|
||||
throw new Error('Counter too low.', ERR_COUNTER_TOO_LOW );
|
||||
}
|
||||
} else {
|
||||
throw new Error('Authentication failed', ERR_AUTHENTICATION_FAILURE );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
private function get_certs()
|
||||
{
|
||||
$files = array();
|
||||
$dir = $this->attestDir;
|
||||
if($dir && $handle = opendir($dir)) {
|
||||
while(false !== ($entry = readdir($handle))) {
|
||||
if(is_file("$dir/$entry")) {
|
||||
$files[] = "$dir/$entry";
|
||||
}
|
||||
}
|
||||
closedir($handle);
|
||||
}
|
||||
return $files;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $data
|
||||
* @return string
|
||||
*/
|
||||
private function base64u_encode($data)
|
||||
{
|
||||
return trim(strtr(base64_encode($data), '+/', '-_'), '=');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $data
|
||||
* @return string
|
||||
*/
|
||||
private function base64u_decode($data)
|
||||
{
|
||||
return base64_decode(strtr($data, '-_', '+/'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
* @return null|string
|
||||
*/
|
||||
private function pubkey_to_pem($key)
|
||||
{
|
||||
if(strlen($key) !== PUBKEY_LEN || $key[0] !== "\x04") {
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert the public key to binary DER format first
|
||||
* Using the ECC SubjectPublicKeyInfo OIDs from RFC 5480
|
||||
*
|
||||
* SEQUENCE(2 elem) 30 59
|
||||
* SEQUENCE(2 elem) 30 13
|
||||
* OID1.2.840.10045.2.1 (id-ecPublicKey) 06 07 2a 86 48 ce 3d 02 01
|
||||
* OID1.2.840.10045.3.1.7 (secp256r1) 06 08 2a 86 48 ce 3d 03 01 07
|
||||
* BIT STRING(520 bit) 03 42 ..key..
|
||||
*/
|
||||
$der = "\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01";
|
||||
$der .= "\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42";
|
||||
$der .= "\0".$key;
|
||||
|
||||
$pem = "-----BEGIN PUBLIC KEY-----\r\n";
|
||||
$pem .= chunk_split(base64_encode($der), 64);
|
||||
$pem .= "-----END PUBLIC KEY-----";
|
||||
|
||||
return $pem;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
* @throws Error
|
||||
*/
|
||||
private function createChallenge()
|
||||
{
|
||||
$challenge = openssl_random_pseudo_bytes(32, $crypto_strong );
|
||||
if( $crypto_strong !== true ) {
|
||||
throw new Error('Unable to obtain a good source of randomness', ERR_BAD_RANDOM);
|
||||
}
|
||||
|
||||
$challenge = $this->base64u_encode( $challenge );
|
||||
|
||||
return $challenge;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fixes a certificate where the signature contains unused bits.
|
||||
*
|
||||
* @param string $cert
|
||||
* @return mixed
|
||||
*/
|
||||
private function fixSignatureUnusedBits($cert)
|
||||
{
|
||||
if(in_array(hash('sha256', $cert), $this->FIXCERTS)) {
|
||||
$cert[strlen($cert) - 257] = "\0";
|
||||
}
|
||||
return $cert;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class for building a registration request
|
||||
*
|
||||
* @package u2flib_server
|
||||
*/
|
||||
class RegisterRequest
|
||||
{
|
||||
/** Protocol version */
|
||||
public $version = U2F_VERSION;
|
||||
|
||||
/** Registration challenge */
|
||||
public $challenge;
|
||||
|
||||
/** Application id */
|
||||
public $appId;
|
||||
|
||||
/**
|
||||
* @param string $challenge
|
||||
* @param string $appId
|
||||
* @internal
|
||||
*/
|
||||
public function __construct($challenge, $appId)
|
||||
{
|
||||
$this->challenge = $challenge;
|
||||
$this->appId = $appId;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class for building up an authentication request
|
||||
*
|
||||
* @package u2flib_server
|
||||
*/
|
||||
class SignRequest
|
||||
{
|
||||
/** Protocol version */
|
||||
public $version = U2F_VERSION;
|
||||
|
||||
/** Authentication challenge */
|
||||
public $challenge;
|
||||
|
||||
/** Key handle of a registered authenticator */
|
||||
public $keyHandle;
|
||||
|
||||
/** Application id */
|
||||
public $appId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Class returned for successful registrations
|
||||
*
|
||||
* @package u2flib_server
|
||||
*/
|
||||
class Registration
|
||||
{
|
||||
/** The key handle of the registered authenticator */
|
||||
public $keyHandle;
|
||||
|
||||
/** The public key of the registered authenticator */
|
||||
public $publicKey;
|
||||
|
||||
/** The attestation certificate of the registered authenticator */
|
||||
public $certificate;
|
||||
|
||||
/** The counter associated with this registration */
|
||||
public $counter = -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Error class, returned on errors
|
||||
*
|
||||
* @package u2flib_server
|
||||
*/
|
||||
class Error extends \Exception
|
||||
{
|
||||
/**
|
||||
* Override constructor and make message and code mandatory
|
||||
* @param string $message
|
||||
* @param int $code
|
||||
* @param \Exception|null $previous
|
||||
*/
|
||||
public function __construct($message, $code, \Exception $previous = null) {
|
||||
parent::__construct($message, $code, $previous);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,475 @@
|
|||
<?php
|
||||
/**
|
||||
* Class for verifying Yubico One-Time-Passcodes
|
||||
*
|
||||
* @category Auth
|
||||
* @package Auth_Yubico
|
||||
* @author Simon Josefsson <simon@yubico.com>, Olov Danielson <olov@yubico.com>
|
||||
* @copyright 2007-2015 Yubico AB
|
||||
* @license http://opensource.org/licenses/bsd-license.php New BSD License
|
||||
* @version 2.0
|
||||
* @link http://www.yubico.com/
|
||||
*/
|
||||
|
||||
require_once 'PEAR.php';
|
||||
|
||||
/**
|
||||
* Class for verifying Yubico One-Time-Passcodes
|
||||
*
|
||||
* Simple example:
|
||||
* <code>
|
||||
* require_once 'Auth/Yubico.php';
|
||||
* $otp = "ccbbddeertkrctjkkcglfndnlihhnvekchkcctif";
|
||||
*
|
||||
* # Generate a new id+key from https://api.yubico.com/get-api-key/
|
||||
* $yubi = new Auth_Yubico('42', 'FOOBAR=');
|
||||
* $auth = $yubi->verify($otp);
|
||||
* if (PEAR::isError($auth)) {
|
||||
* print "<p>Authentication failed: " . $auth->getMessage();
|
||||
* print "<p>Debug output from server: " . $yubi->getLastResponse();
|
||||
* } else {
|
||||
* print "<p>You are authenticated!";
|
||||
* }
|
||||
* </code>
|
||||
*/
|
||||
class Auth_Yubico
|
||||
{
|
||||
/**#@+
|
||||
* @access private
|
||||
*/
|
||||
|
||||
/**
|
||||
* Yubico client ID
|
||||
* @var string
|
||||
*/
|
||||
var $_id;
|
||||
|
||||
/**
|
||||
* Yubico client key
|
||||
* @var string
|
||||
*/
|
||||
var $_key;
|
||||
|
||||
/**
|
||||
* URL part of validation server
|
||||
* @var string
|
||||
*/
|
||||
var $_url;
|
||||
|
||||
/**
|
||||
* List with URL part of validation servers
|
||||
* @var array
|
||||
*/
|
||||
var $_url_list;
|
||||
|
||||
/**
|
||||
* index to _url_list
|
||||
* @var int
|
||||
*/
|
||||
var $_url_index;
|
||||
|
||||
/**
|
||||
* Last query to server
|
||||
* @var string
|
||||
*/
|
||||
var $_lastquery;
|
||||
|
||||
/**
|
||||
* Response from server
|
||||
* @var string
|
||||
*/
|
||||
var $_response;
|
||||
|
||||
/**
|
||||
* Flag whether to use https or not.
|
||||
* @var boolean
|
||||
*/
|
||||
var $_https;
|
||||
|
||||
/**
|
||||
* Flag whether to verify HTTPS server certificates or not.
|
||||
* @var boolean
|
||||
*/
|
||||
var $_httpsverify;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* Sets up the object
|
||||
* @param string $id The client identity
|
||||
* @param string $key The client MAC key (optional)
|
||||
* @param boolean $https Flag whether to use https (optional)
|
||||
* @param boolean $httpsverify Flag whether to use verify HTTPS
|
||||
* server certificates (optional,
|
||||
* default true)
|
||||
* @access public
|
||||
*/
|
||||
function __construct($id, $key = '', $https = 0, $httpsverify = 1)
|
||||
{
|
||||
$this->_id = $id;
|
||||
$this->_key = base64_decode($key);
|
||||
$this->_https = $https;
|
||||
$this->_httpsverify = $httpsverify;
|
||||
}
|
||||
|
||||
function Auth_Yubico($id, $key = '', $https = 0, $httpsverify = 1)
|
||||
{
|
||||
self::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify to use a different URL part for verification.
|
||||
* The default is "api.yubico.com/wsapi/verify".
|
||||
*
|
||||
* @param string $url New server URL part to use
|
||||
* @access public
|
||||
*/
|
||||
function setURLpart($url)
|
||||
{
|
||||
$this->_url = $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get URL part to use for validation.
|
||||
*
|
||||
* @return string Server URL part
|
||||
* @access public
|
||||
*/
|
||||
function getURLpart()
|
||||
{
|
||||
if ($this->_url) {
|
||||
return $this->_url;
|
||||
} else {
|
||||
return "api.yubico.com/wsapi/verify";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get next URL part from list to use for validation.
|
||||
*
|
||||
* @return mixed string with URL part of false if no more URLs in list
|
||||
* @access public
|
||||
*/
|
||||
function getNextURLpart()
|
||||
{
|
||||
if ($this->_url_list) $url_list=$this->_url_list;
|
||||
else $url_list=array('api.yubico.com/wsapi/2.0/verify',
|
||||
'api2.yubico.com/wsapi/2.0/verify',
|
||||
'api3.yubico.com/wsapi/2.0/verify',
|
||||
'api4.yubico.com/wsapi/2.0/verify',
|
||||
'api5.yubico.com/wsapi/2.0/verify');
|
||||
|
||||
if ($this->_url_index>=count($url_list)) return false;
|
||||
else return $url_list[$this->_url_index++];
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets index to URL list
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
function URLreset()
|
||||
{
|
||||
$this->_url_index=0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add another URLpart.
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
function addURLpart($URLpart)
|
||||
{
|
||||
$this->_url_list[]=$URLpart;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the last query sent to the server, if any.
|
||||
*
|
||||
* @return string Request to server
|
||||
* @access public
|
||||
*/
|
||||
function getLastQuery()
|
||||
{
|
||||
return $this->_lastquery;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the last data received from the server, if any.
|
||||
*
|
||||
* @return string Output from server
|
||||
* @access public
|
||||
*/
|
||||
function getLastResponse()
|
||||
{
|
||||
return $this->_response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse input string into password, yubikey prefix,
|
||||
* ciphertext, and OTP.
|
||||
*
|
||||
* @param string Input string to parse
|
||||
* @param string Optional delimiter re-class, default is '[:]'
|
||||
* @return array Keyed array with fields
|
||||
* @access public
|
||||
*/
|
||||
function parsePasswordOTP($str, $delim = '[:]')
|
||||
{
|
||||
if (!preg_match("/^((.*)" . $delim . ")?" .
|
||||
"(([cbdefghijklnrtuv]{0,16})" .
|
||||
"([cbdefghijklnrtuv]{32}))$/i",
|
||||
$str, $matches)) {
|
||||
/* Dvorak? */
|
||||
if (!preg_match("/^((.*)" . $delim . ")?" .
|
||||
"(([jxe\.uidchtnbpygk]{0,16})" .
|
||||
"([jxe\.uidchtnbpygk]{32}))$/i",
|
||||
$str, $matches)) {
|
||||
return false;
|
||||
} else {
|
||||
$ret['otp'] = strtr($matches[3], "jxe.uidchtnbpygk", "cbdefghijklnrtuv");
|
||||
}
|
||||
} else {
|
||||
$ret['otp'] = $matches[3];
|
||||
}
|
||||
$ret['password'] = $matches[2];
|
||||
$ret['prefix'] = $matches[4];
|
||||
$ret['ciphertext'] = $matches[5];
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/* TODO? Add functions to get parsed parts of server response? */
|
||||
|
||||
/**
|
||||
* Parse parameters from last response
|
||||
*
|
||||
* example: getParameters("timestamp", "sessioncounter", "sessionuse");
|
||||
*
|
||||
* @param array @parameters Array with strings representing
|
||||
* parameters to parse
|
||||
* @return array parameter array from last response
|
||||
* @access public
|
||||
*/
|
||||
function getParameters($parameters)
|
||||
{
|
||||
if ($parameters == null) {
|
||||
$parameters = array('timestamp', 'sessioncounter', 'sessionuse');
|
||||
}
|
||||
$param_array = array();
|
||||
foreach ($parameters as $param) {
|
||||
if(!preg_match("/" . $param . "=([0-9]+)/", $this->_response, $out)) {
|
||||
return PEAR::raiseError('Could not parse parameter ' . $param . ' from response');
|
||||
}
|
||||
$param_array[$param]=$out[1];
|
||||
}
|
||||
return $param_array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify Yubico OTP against multiple URLs
|
||||
* Protocol specification 2.0 is used to construct validation requests
|
||||
*
|
||||
* @param string $token Yubico OTP
|
||||
* @param int $use_timestamp 1=>send request with ×tamp=1 to
|
||||
* get timestamp and session information
|
||||
* in the response
|
||||
* @param boolean $wait_for_all If true, wait until all
|
||||
* servers responds (for debugging)
|
||||
* @param string $sl Sync level in percentage between 0
|
||||
* and 100 or "fast" or "secure".
|
||||
* @param int $timeout Max number of seconds to wait
|
||||
* for responses
|
||||
* @return mixed PEAR error on error, true otherwise
|
||||
* @access public
|
||||
*/
|
||||
function verify($token, $use_timestamp=null, $wait_for_all=False,
|
||||
$sl=null, $timeout=null)
|
||||
{
|
||||
/* Construct parameters string */
|
||||
$ret = $this->parsePasswordOTP($token);
|
||||
if (!$ret) {
|
||||
return PEAR::raiseError('Could not parse Yubikey OTP');
|
||||
}
|
||||
$params = array('id'=>$this->_id,
|
||||
'otp'=>$ret['otp'],
|
||||
'nonce'=>md5(uniqid(rand())));
|
||||
/* Take care of protocol version 2 parameters */
|
||||
if ($use_timestamp) $params['timestamp'] = 1;
|
||||
if ($sl) $params['sl'] = $sl;
|
||||
if ($timeout) $params['timeout'] = $timeout;
|
||||
ksort($params);
|
||||
$parameters = '';
|
||||
foreach($params as $p=>$v) $parameters .= "&" . $p . "=" . $v;
|
||||
$parameters = ltrim($parameters, "&");
|
||||
|
||||
/* Generate signature. */
|
||||
if($this->_key <> "") {
|
||||
$signature = base64_encode(hash_hmac('sha1', $parameters,
|
||||
$this->_key, true));
|
||||
$signature = preg_replace('/\+/', '%2B', $signature);
|
||||
$parameters .= '&h=' . $signature;
|
||||
}
|
||||
|
||||
/* Generate and prepare request. */
|
||||
$this->_lastquery=null;
|
||||
$this->URLreset();
|
||||
$mh = curl_multi_init();
|
||||
$ch = array();
|
||||
while($URLpart=$this->getNextURLpart())
|
||||
{
|
||||
/* Support https. */
|
||||
if ($this->_https) {
|
||||
$query = "https://";
|
||||
} else {
|
||||
$query = "http://";
|
||||
}
|
||||
$query .= $URLpart . "?" . $parameters;
|
||||
|
||||
if ($this->_lastquery) { $this->_lastquery .= " "; }
|
||||
$this->_lastquery .= $query;
|
||||
|
||||
$handle = curl_init($query);
|
||||
curl_setopt($handle, CURLOPT_USERAGENT, "PEAR Auth_Yubico");
|
||||
curl_setopt($handle, CURLOPT_RETURNTRANSFER, 1);
|
||||
if (!$this->_httpsverify) {
|
||||
curl_setopt($handle, CURLOPT_SSL_VERIFYPEER, 0);
|
||||
curl_setopt($handle, CURLOPT_SSL_VERIFYHOST, 0);
|
||||
}
|
||||
curl_setopt($handle, CURLOPT_FAILONERROR, true);
|
||||
/* If timeout is set, we better apply it here as well
|
||||
in case the validation server fails to follow it.
|
||||
*/
|
||||
if ($timeout) curl_setopt($handle, CURLOPT_TIMEOUT, $timeout);
|
||||
curl_multi_add_handle($mh, $handle);
|
||||
|
||||
$ch[(int)$handle] = $handle;
|
||||
}
|
||||
|
||||
/* Execute and read request. */
|
||||
$this->_response=null;
|
||||
$replay=False;
|
||||
$valid=False;
|
||||
do {
|
||||
/* Let curl do its work. */
|
||||
while (($mrc = curl_multi_exec($mh, $active))
|
||||
== CURLM_CALL_MULTI_PERFORM)
|
||||
;
|
||||
|
||||
while ($info = curl_multi_info_read($mh)) {
|
||||
if ($info['result'] == CURLE_OK) {
|
||||
|
||||
/* We have a complete response from one server. */
|
||||
|
||||
$str = curl_multi_getcontent($info['handle']);
|
||||
$cinfo = curl_getinfo ($info['handle']);
|
||||
|
||||
if ($wait_for_all) { # Better debug info
|
||||
$this->_response .= 'URL=' . $cinfo['url'] ."\n"
|
||||
. $str . "\n";
|
||||
}
|
||||
|
||||
if (preg_match("/status=([a-zA-Z0-9_]+)/", $str, $out)) {
|
||||
$status = $out[1];
|
||||
|
||||
/*
|
||||
* There are 3 cases.
|
||||
*
|
||||
* 1. OTP or Nonce values doesn't match - ignore
|
||||
* response.
|
||||
*
|
||||
* 2. We have a HMAC key. If signature is invalid -
|
||||
* ignore response. Return if status=OK or
|
||||
* status=REPLAYED_OTP.
|
||||
*
|
||||
* 3. Return if status=OK or status=REPLAYED_OTP.
|
||||
*/
|
||||
if (!preg_match("/otp=".$params['otp']."/", $str) ||
|
||||
!preg_match("/nonce=".$params['nonce']."/", $str)) {
|
||||
/* Case 1. Ignore response. */
|
||||
}
|
||||
elseif ($this->_key <> "") {
|
||||
/* Case 2. Verify signature first */
|
||||
$rows = explode("\r\n", trim($str));
|
||||
$response=array();
|
||||
while (list($key, $val) = each($rows)) {
|
||||
/* = is also used in BASE64 encoding so we only replace the first = by # which is not used in BASE64 */
|
||||
$val = preg_replace('/=/', '#', $val, 1);
|
||||
$row = explode("#", $val);
|
||||
$response[$row[0]] = $row[1];
|
||||
}
|
||||
|
||||
$parameters=array('nonce','otp', 'sessioncounter', 'sessionuse', 'sl', 'status', 't', 'timeout', 'timestamp');
|
||||
sort($parameters);
|
||||
$check=Null;
|
||||
foreach ($parameters as $param) {
|
||||
if (array_key_exists($param, $response)) {
|
||||
if ($check) $check = $check . '&';
|
||||
$check = $check . $param . '=' . $response[$param];
|
||||
}
|
||||
}
|
||||
|
||||
$checksignature =
|
||||
base64_encode(hash_hmac('sha1', utf8_encode($check),
|
||||
$this->_key, true));
|
||||
|
||||
if($response['h'] == $checksignature) {
|
||||
if ($status == 'REPLAYED_OTP') {
|
||||
if (!$wait_for_all) { $this->_response = $str; }
|
||||
$replay=True;
|
||||
}
|
||||
if ($status == 'OK') {
|
||||
if (!$wait_for_all) { $this->_response = $str; }
|
||||
$valid=True;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* Case 3. We check the status directly */
|
||||
if ($status == 'REPLAYED_OTP') {
|
||||
if (!$wait_for_all) { $this->_response = $str; }
|
||||
$replay=True;
|
||||
}
|
||||
if ($status == 'OK') {
|
||||
if (!$wait_for_all) { $this->_response = $str; }
|
||||
$valid=True;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!$wait_for_all && ($valid || $replay))
|
||||
{
|
||||
/* We have status=OK or status=REPLAYED_OTP, return. */
|
||||
foreach ($ch as $h) {
|
||||
curl_multi_remove_handle($mh, $h);
|
||||
curl_close($h);
|
||||
}
|
||||
curl_multi_close($mh);
|
||||
if ($replay) return PEAR::raiseError('REPLAYED_OTP');
|
||||
if ($valid) return true;
|
||||
return PEAR::raiseError($status);
|
||||
}
|
||||
|
||||
curl_multi_remove_handle($mh, $info['handle']);
|
||||
curl_close($info['handle']);
|
||||
unset ($ch[(int)$info['handle']]);
|
||||
}
|
||||
curl_multi_select($mh);
|
||||
}
|
||||
} while ($active);
|
||||
|
||||
/* Typically this is only reached for wait_for_all=true or
|
||||
* when the timeout is reached and there is no
|
||||
* OK/REPLAYED_REQUEST answer (think firewall).
|
||||
*/
|
||||
|
||||
foreach ($ch as $h) {
|
||||
curl_multi_remove_handle ($mh, $h);
|
||||
curl_close ($h);
|
||||
}
|
||||
curl_multi_close ($mh);
|
||||
|
||||
if ($replay) return PEAR::raiseError('REPLAYED_OTP');
|
||||
if ($valid) return true;
|
||||
return PEAR::raiseError('NO_VALID_ANSWER');
|
||||
}
|
||||
}
|
||||
?>
|
|
@ -3,18 +3,32 @@
|
|||
//ini_set("session.cookie_httponly", 1);
|
||||
session_start();
|
||||
if (isset($_POST["logout"])) {
|
||||
session_unset();
|
||||
session_destroy();
|
||||
session_write_close();
|
||||
setcookie(session_name(),'',0,'/');
|
||||
if (isset($_SESSION["dual-login"])) {
|
||||
$_SESSION["mailcow_cc_username"] = $_SESSION["dual-login"]["username"];
|
||||
$_SESSION["mailcow_cc_role"] = $_SESSION["dual-login"]["role"];
|
||||
unset($_SESSION["dual-login"]);
|
||||
}
|
||||
else {
|
||||
session_unset();
|
||||
session_destroy();
|
||||
session_write_close();
|
||||
setcookie(session_name(),'',0,'/');
|
||||
}
|
||||
}
|
||||
|
||||
require_once 'inc/vars.inc.php';
|
||||
|
||||
if (file_exists('./inc/vars.local.inc.php')) {
|
||||
include_once 'inc/vars.local.inc.php';
|
||||
}
|
||||
|
||||
// Yubi OTP API
|
||||
require_once 'inc/lib/Yubico.php';
|
||||
|
||||
// U2F API
|
||||
require_once 'inc/lib/U2F.php';
|
||||
$u2f = new u2flib_server\U2F('https://' . $_SERVER['SERVER_NAME']);
|
||||
|
||||
// PDO
|
||||
$dsn = "$database_type:host=$database_host;dbname=$database_name";
|
||||
$opt = [
|
||||
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
||||
|
@ -28,7 +42,9 @@ catch (PDOException $e) {
|
|||
?>
|
||||
<center style='font-family: "Lucida Sans Unicode", "Lucida Grande", Verdana, Arial, Helvetica, sans-serif;'>🐮 Connection failed, database may be in warm-up state, please try again later.<br /><br />The following error was reported:<br/> <?=$e->getMessage();?></center>
|
||||
<?php
|
||||
exit;
|
||||
}
|
||||
|
||||
$_SESSION['mailcow_locale'] = strtolower(trim($DEFAULT_LANG));
|
||||
setcookie('language', $DEFAULT_LANG);
|
||||
if (isset($_COOKIE['language'])) {
|
||||
|
@ -41,6 +57,10 @@ if (isset($_COOKIE['language'])) {
|
|||
$_SESSION['mailcow_locale'] = 'en';
|
||||
setcookie('language', 'en');
|
||||
break;
|
||||
case "es":
|
||||
$_SESSION['mailcow_locale'] = 'es';
|
||||
setcookie('language', 'es');
|
||||
break;
|
||||
case "nl":
|
||||
$_SESSION['mailcow_locale'] = 'nl';
|
||||
setcookie('language', 'nl');
|
||||
|
@ -61,6 +81,10 @@ if (isset($_GET['lang'])) {
|
|||
$_SESSION['mailcow_locale'] = 'en';
|
||||
setcookie('language', 'en');
|
||||
break;
|
||||
case "es":
|
||||
$_SESSION['mailcow_locale'] = 'es';
|
||||
setcookie('language', 'es');
|
||||
break;
|
||||
case "nl":
|
||||
$_SESSION['mailcow_locale'] = 'nl';
|
||||
setcookie('language', 'nl');
|
||||
|
@ -75,4 +99,4 @@ require_once 'lang/lang.en.php';
|
|||
include 'lang/lang.'.$_SESSION['mailcow_locale'].'.php';
|
||||
require_once 'inc/functions.inc.php';
|
||||
require_once 'inc/triggers.inc.php';
|
||||
init_db_schema();
|
||||
(!isset($_SESSION['mailcow_cc_username'])) ? init_db_schema() : null;
|
||||
|
|
|
@ -0,0 +1,133 @@
|
|||
<div class="modal fade" id="YubiOTPModal" tabindex="-1" role="dialog" aria-labelledby="YubiOTPModalLabel">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header"><b><?=$lang['tfa']['yubi_otp'];?></b></div>
|
||||
<div class="modal-body">
|
||||
<form role="form" method="post">
|
||||
<div class="form-group">
|
||||
<input type="text" class="form-control" name="key_id" id="key_id" placeholder="<?=$lang['tfa']['key_id'];?>" autocomplete="off" required>
|
||||
</div>
|
||||
<hr>
|
||||
<p class="help-block"><?=$lang['tfa']['api_register'];?></p>
|
||||
<div class="form-group">
|
||||
<input type="text" class="form-control" name="yubico_id" id="yubico_id" placeholder="Yubico API ID" autocomplete="off" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="text" class="form-control" name="yubico_key" id="yubico_key" placeholder="Yubico API Key" autocomplete="off" required>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="form-group">
|
||||
<input type="password" class="form-control" name="confirm_password" id="confirm_password" placeholder="<?=$lang['user']['password_now'];?>" autocomplete="off" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon" id="yubi-addon"><img alt="Yubicon Icon" src="/img/yubi.ico"></span>
|
||||
<input type="text" name="otp_token" class="form-control" placeholder="Touch Yubikey" aria-describedby="yubi-addon">
|
||||
<input type="hidden" name="tfa_method" value="yubi_otp">
|
||||
</div>
|
||||
</div>
|
||||
<button class="btn btn-sm btn-default" type="submit" name="set_tfa"><?=$lang['user']['save_changes'];?></button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="U2FModal" tabindex="-1" role="dialog" aria-labelledby="U2FModalLabel">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header"><b><?=$lang['tfa']['u2f'];?></b></div>
|
||||
<div class="modal-body">
|
||||
<form role="form" method="post" id="u2f_reg_form">
|
||||
<div class="form-group">
|
||||
<input type="text" class="form-control" name="key_id" id="key_id" placeholder="<?=$lang['tfa']['key_id'];?>" autocomplete="off" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="password" class="form-control" name="confirm_password" id="confirm_password" placeholder="<?=$lang['user']['password_now'];?>" autocomplete="off" required>
|
||||
</div>
|
||||
<hr>
|
||||
<p><?=$lang['tfa']['waiting_usb_register'];?></p>
|
||||
<div class="alert alert-danger" style="display:none" id="u2f_return_code"></div>
|
||||
<input type="hidden" name="token" id="u2f_register_data"/>
|
||||
<input type="hidden" name="tfa_method" value="u2f">
|
||||
<input type="hidden" name="set_tfa"/><br/>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="DisableTFAModal" tabindex="-1" role="dialog" aria-labelledby="DisableTFAModalLabel">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header"><b><?=$lang['tfa']['delete_tfa'];?></b></div>
|
||||
<div class="modal-body">
|
||||
<form role="form" method="post">
|
||||
<div class="input-group">
|
||||
<input type="password" class="form-control" name="confirm_password" id="confirm_password" placeholder="<?=$lang['user']['password_now'];?>" autocomplete="off" required>
|
||||
<span class="input-group-btn">
|
||||
<input type="hidden" name="tfa_method" value="none">
|
||||
<button class="btn btn-danger" type="submit" name="set_tfa"><?=$lang['tfa']['delete_tfa'];?></button>
|
||||
</span>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
if (isset($_SESSION['pending_tfa_method'])):
|
||||
$tfa_method = $_SESSION['pending_tfa_method'];
|
||||
?>
|
||||
<div class="modal fade" id="ConfirmTFAModal" tabindex="-1" role="dialog" aria-labelledby="ConfirmTFAModalLabel">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header"><button type="button" class="close" data-dismiss="modal">×</button><b><?=$lang['tfa'][$tfa_method];?></b></div>
|
||||
<div class="modal-body">
|
||||
<?php
|
||||
switch ($tfa_method) {
|
||||
case "yubi_otp":
|
||||
?>
|
||||
<form role="form" method="post">
|
||||
<div class="form-group">
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon" id="yubi-addon"><img alt="Yubicon Icon" src="/img/yubi.ico"></span>
|
||||
<input type="text" name="token" id="token" class="form-control" placeholder="Touch Yubikey" aria-describedby="yubi-addon">
|
||||
<input type="hidden" name="tfa_method" value="yubi_otp">
|
||||
</div>
|
||||
</div>
|
||||
<button class="btn btn-sm btn-default" type="submit" name="verify_tfa_login"><?=$lang['login']['login'];?></button>
|
||||
</form>
|
||||
<?php
|
||||
break;
|
||||
case "u2f":
|
||||
?>
|
||||
<form role="form" method="post" id="u2f_auth_form">
|
||||
<p><?=$lang['tfa']['waiting_usb_auth'];?></p>
|
||||
<div class="alert alert-danger" style="display:none" id="u2f_return_code"></div>
|
||||
<input type="hidden" name="token" id="u2f_auth_data"/>
|
||||
<input type="hidden" name="tfa_method" value="u2f">
|
||||
<input type="hidden" name="verify_tfa_login"/><br/>
|
||||
</form>
|
||||
<?php
|
||||
break;
|
||||
case "totp":
|
||||
?>
|
||||
<div class="empty"></div>
|
||||
<?php
|
||||
break;
|
||||
case "hotp":
|
||||
?>
|
||||
<div class="empty"></div>
|
||||
<?php
|
||||
break;
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
endif;
|
||||
?>
|
|
@ -1,4 +1,15 @@
|
|||
<?php
|
||||
if (isset($_POST["verify_tfa_login"])) {
|
||||
if (verify_tfa_login($_SESSION['pending_mailcow_cc_username'], $_POST["token"])) {
|
||||
$_SESSION['mailcow_cc_username'] = $_SESSION['pending_mailcow_cc_username'];
|
||||
$_SESSION['mailcow_cc_role'] = $_SESSION['pending_mailcow_cc_role'];
|
||||
unset($_SESSION['pending_mailcow_cc_username']);
|
||||
unset($_SESSION['pending_mailcow_cc_role']);
|
||||
unset($_SESSION['pending_tfa_method']);
|
||||
header("Location: /user.php");
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($_POST["login_user"]) && isset($_POST["pass_user"])) {
|
||||
$login_user = strtolower(trim($_POST["login_user"]));
|
||||
$as = check_login($login_user, $_POST["pass_user"]);
|
||||
|
@ -17,100 +28,146 @@ if (isset($_POST["login_user"]) && isset($_POST["pass_user"])) {
|
|||
$_SESSION['mailcow_cc_role'] = "user";
|
||||
header("Location: /user.php");
|
||||
}
|
||||
else {
|
||||
elseif ($as != "pending") {
|
||||
unset($_SESSION['pending_mailcow_cc_username']);
|
||||
unset($_SESSION['pending_mailcow_cc_role']);
|
||||
unset($_SESSION['pending_tfa_method']);
|
||||
unset($_SESSION['mailcow_cc_username']);
|
||||
unset($_SESSION['mailcow_cc_role']);
|
||||
$_SESSION['return'] = array(
|
||||
'type' => 'danger',
|
||||
'msg' => $lang['danger']['login_failed']
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == "admin") {
|
||||
if (isset($_POST["trigger_set_admin"])) {
|
||||
set_admin_account($_POST);
|
||||
if (isset($_GET["duallogin"])) {
|
||||
if (filter_var($_GET["duallogin"], FILTER_VALIDATE_EMAIL)) {
|
||||
$stmt = $pdo->prepare("SELECT `username` FROM `mailbox` WHERE `username` = :duallogin");
|
||||
$stmt->execute(array(':duallogin' => $_GET["duallogin"]));
|
||||
$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
|
||||
if ($num_results != 0) {
|
||||
$_SESSION["dual-login"]["username"] = $_SESSION['mailcow_cc_username'];
|
||||
$_SESSION["dual-login"]["role"] = $_SESSION['mailcow_cc_role'];
|
||||
$_SESSION['mailcow_cc_username'] = $_GET["duallogin"];
|
||||
$_SESSION['mailcow_cc_role'] = "user";
|
||||
header("Location: /user.php");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($_POST["edit_admin_account"])) {
|
||||
edit_admin_account($_POST);
|
||||
}
|
||||
if (isset($_POST["delete_dkim_record"])) {
|
||||
dkim_table("delete", $_POST);
|
||||
if (isset($_POST["dkim_delete_key"])) {
|
||||
dkim_delete_key($_POST);
|
||||
}
|
||||
if (isset($_POST["add_dkim_record"])) {
|
||||
dkim_table("add", $_POST);
|
||||
if (isset($_POST["dkim_add_key"])) {
|
||||
dkim_add_key($_POST);
|
||||
}
|
||||
if (isset($_POST["trigger_add_domain_admin"])) {
|
||||
if (isset($_POST["add_domain_admin"])) {
|
||||
add_domain_admin($_POST);
|
||||
}
|
||||
if (isset($_POST["trigger_delete_domain_admin"])) {
|
||||
if (isset($_POST["delete_domain_admin"])) {
|
||||
delete_domain_admin($_POST);
|
||||
}
|
||||
if (isset($_POST["trigger_edit_domain_admin"])) {
|
||||
edit_domain_admin($_POST);
|
||||
}
|
||||
}
|
||||
if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == "user") {
|
||||
if (isset($_POST["trigger_set_user_account"])) {
|
||||
set_user_account($_POST);
|
||||
if (isset($_POST["edit_user_account"])) {
|
||||
edit_user_account($_POST);
|
||||
}
|
||||
if (isset($_POST["trigger_set_spam_score"])) {
|
||||
set_spam_score($_POST);
|
||||
if (isset($_POST["mailbox_reset_eas"])) {
|
||||
mailbox_reset_eas($_POST);
|
||||
}
|
||||
if (isset($_POST["trigger_set_tagging_options"])) {
|
||||
tagging_options('set', $_POST);
|
||||
if (isset($_POST["edit_spam_score"])) {
|
||||
edit_spam_score($_POST);
|
||||
}
|
||||
if (isset($_POST["trigger_set_policy_list"])) {
|
||||
set_policy_list($_POST);
|
||||
if (isset($_POST["edit_delimiter_action"])) {
|
||||
edit_delimiter_action($_POST);
|
||||
}
|
||||
if (isset($_POST["trigger_set_tls_policy"])) {
|
||||
set_tls_policy($_POST);
|
||||
if (isset($_POST["add_policy_list_item"])) {
|
||||
add_policy_list_item($_POST);
|
||||
}
|
||||
if (isset($_POST["trigger_set_time_limited_aliases"])) {
|
||||
if (isset($_POST["delete_policy_list_item"])) {
|
||||
delete_policy_list_item($_POST);
|
||||
}
|
||||
if (isset($_POST["edit_tls_policy"])) {
|
||||
edit_tls_policy($_POST);
|
||||
}
|
||||
if (isset($_POST["add_syncjob"])) {
|
||||
add_syncjob($_POST);
|
||||
}
|
||||
if (isset($_POST["edit_syncjob"])) {
|
||||
edit_syncjob($_POST);
|
||||
}
|
||||
if (isset($_POST["delete_syncjob"])) {
|
||||
delete_syncjob($_POST);
|
||||
}
|
||||
if (isset($_POST["set_time_limited_aliases"])) {
|
||||
set_time_limited_aliases($_POST);
|
||||
}
|
||||
}
|
||||
if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "admin" || $_SESSION['mailcow_cc_role'] == "domainadmin")) {
|
||||
if (isset($_GET["js"])) {
|
||||
switch ($_GET["js"]) {
|
||||
case "remaining_specs":
|
||||
remaining_specs($_GET['domain'], $_GET['object'], "y");
|
||||
break;
|
||||
}
|
||||
if (isset($_POST["edit_domain_admin"])) {
|
||||
edit_domain_admin($_POST);
|
||||
}
|
||||
if (isset($_POST["trigger_mailbox_action"])) {
|
||||
switch ($_POST["trigger_mailbox_action"]) {
|
||||
case "adddomain":
|
||||
mailbox_add_domain($_POST);
|
||||
break;
|
||||
case "addalias":
|
||||
mailbox_add_alias($_POST);
|
||||
break;
|
||||
case "editalias":
|
||||
mailbox_edit_alias($_POST);
|
||||
break;
|
||||
case "addaliasdomain":
|
||||
mailbox_add_alias_domain($_POST);
|
||||
break;
|
||||
case "addmailbox":
|
||||
mailbox_add_mailbox($_POST);
|
||||
break;
|
||||
case "editdomain":
|
||||
mailbox_edit_domain($_POST);
|
||||
break;
|
||||
case "editmailbox":
|
||||
mailbox_edit_mailbox($_POST);
|
||||
break;
|
||||
case "deletedomain":
|
||||
mailbox_delete_domain($_POST);
|
||||
break;
|
||||
case "deletealias":
|
||||
mailbox_delete_alias($_POST);
|
||||
break;
|
||||
case "deletealiasdomain":
|
||||
mailbox_delete_alias_domain($_POST);
|
||||
break;
|
||||
case "editaliasdomain":
|
||||
mailbox_edit_alias_domain($_POST);
|
||||
break;
|
||||
case "deletemailbox":
|
||||
mailbox_delete_mailbox($_POST);
|
||||
break;
|
||||
}
|
||||
if (isset($_POST["set_tfa"])) {
|
||||
set_tfa($_POST);
|
||||
}
|
||||
if (isset($_POST["unset_tfa_key"])) {
|
||||
unset_tfa_key($_POST);
|
||||
}
|
||||
if (isset($_POST["add_policy_list_item"])) {
|
||||
add_policy_list_item($_POST);
|
||||
}
|
||||
if (isset($_POST["delete_policy_list_item"])) {
|
||||
delete_policy_list_item($_POST);
|
||||
}
|
||||
if (isset($_POST["mailbox_add_domain"])) {
|
||||
mailbox_add_domain($_POST);
|
||||
}
|
||||
if (isset($_POST["mailbox_add_alias"])) {
|
||||
mailbox_add_alias($_POST);
|
||||
}
|
||||
if (isset($_POST["mailbox_add_alias_domain"])) {
|
||||
mailbox_add_alias_domain($_POST);
|
||||
}
|
||||
if (isset($_POST["mailbox_add_mailbox"])) {
|
||||
mailbox_add_mailbox($_POST);
|
||||
}
|
||||
if (isset($_POST["mailbox_add_resource"])) {
|
||||
mailbox_add_resource($_POST);
|
||||
}
|
||||
if (isset($_POST["mailbox_edit_alias"])) {
|
||||
mailbox_edit_alias($_POST);
|
||||
}
|
||||
if (isset($_POST["mailbox_edit_domain"])) {
|
||||
mailbox_edit_domain($_POST);
|
||||
}
|
||||
if (isset($_POST["mailbox_edit_mailbox"])) {
|
||||
mailbox_edit_mailbox($_POST);
|
||||
}
|
||||
if (isset($_POST["mailbox_edit_alias_domain"])) {
|
||||
mailbox_edit_alias_domain($_POST);
|
||||
}
|
||||
if (isset($_POST["mailbox_edit_resource"])) {
|
||||
mailbox_edit_resource($_POST);
|
||||
}
|
||||
if (isset($_POST["mailbox_delete_domain"])) {
|
||||
mailbox_delete_domain($_POST);
|
||||
}
|
||||
if (isset($_POST["mailbox_delete_alias"])) {
|
||||
mailbox_delete_alias($_POST);
|
||||
}
|
||||
if (isset($_POST["mailbox_delete_alias_domain"])) {
|
||||
mailbox_delete_alias_domain($_POST);
|
||||
}
|
||||
if (isset($_POST["mailbox_delete_mailbox"])) {
|
||||
mailbox_delete_mailbox($_POST);
|
||||
}
|
||||
if (isset($_POST["mailbox_delete_resource"])) {
|
||||
mailbox_delete_resource($_POST);
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
<?php
|
||||
error_reporting(0);
|
||||
error_reporting(E_ERROR | E_WARNING);
|
||||
//error_reporting(E_ALL);
|
||||
|
||||
/*
|
||||
PLEASE USE THE FILE "vars.local.inc.php" TO OVERWRITE SETTINGS AND MAKE THEM PERSISTENT!
|
||||
|
@ -33,4 +34,7 @@ $DEFAULT_LANG = "en";
|
|||
// simplex, slate, spacelab, superhero, united, yeti
|
||||
// See https://bootswatch.com/
|
||||
$DEFAULT_THEME = "lumen";
|
||||
|
||||
// Password complexity as regular expression
|
||||
$PASSWD_REGEP = '.{4,}';
|
||||
?>
|
||||
|
|
|
@ -48,6 +48,7 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
|
|||
<ul class="dropdown-menu">
|
||||
<li <?=($_SESSION['mailcow_locale'] == 'de') ? 'class="active"' : ''?>><a href="?<?= http_build_query(array_merge($_GET, array("lang" => "de"))) ?>"><span class="lang-xs lang-lbl-full" lang="de"></span></a></li>
|
||||
<li <?=($_SESSION['mailcow_locale'] == 'en') ? 'class="active"' : ''?>><a href="?<?= http_build_query(array_merge($_GET, array("lang" => "en"))) ?>"><span class="lang-xs lang-lbl-full" lang="en"></span></a></li>
|
||||
<li <?=($_SESSION['mailcow_locale'] == 'es') ? 'class="active"' : ''?>><a href="?<?= http_build_query(array_merge($_GET, array("lang" => "es"))) ?>"><span class="lang-xs lang-lbl-full" lang="es"></span></a></li>
|
||||
<li <?=($_SESSION['mailcow_locale'] == 'nl') ? 'class="active"' : ''?>><a href="?<?= http_build_query(array_merge($_GET, array("lang" => "nl"))) ?>"><span class="lang-xs lang-lbl-full" lang="nl"></span></a></li>
|
||||
<li <?=($_SESSION['mailcow_locale'] == 'pt') ? 'class="active"' : ''?>><a href="?<?= http_build_query(array_merge($_GET, array("lang" => "pt"))) ?>"><span class="lang-xs lang-lbl-full" lang="pt"></span></a></li>
|
||||
</ul>
|
||||
|
|
|
@ -2,13 +2,15 @@ $(document).ready(function() {
|
|||
// add.php
|
||||
// Get max. possible quota for a domain when domain field changes
|
||||
$('#addSelectDomain').on('change', function() {
|
||||
$.get("add.php", { js:"remaining_specs", domain:this.value, object:"new" }, function(data){
|
||||
if (data != '0') {
|
||||
$("#quotaBadge").html('max. ' + data + ' MiB');
|
||||
$('#addInputQuota').attr({"disabled": false, "value": "", "type": "number", "max": data});
|
||||
$.get("json_api.php", { action:"get_domain_details", object:this.value }, function(data){
|
||||
var result = jQuery.parseJSON( data );
|
||||
max_new_mailbox_quota = ( result.max_new_mailbox_quota / 1048576);
|
||||
if (max_new_mailbox_quota != '0') {
|
||||
$("#quotaBadge").html('max. ' + max_new_mailbox_quota + ' MiB');
|
||||
$('#addInputQuota').attr({"disabled": false, "value": "", "type": "number", "max": max_new_mailbox_quota});
|
||||
}
|
||||
else {
|
||||
$("#quotaBadge").html('max. ' + data + ' MiB');
|
||||
$("#quotaBadge").html('max. ' + max_new_mailbox_quota + ' MiB');
|
||||
$('#addInputQuota').attr({"disabled": true, "value": "", "type": "text", "value": "n/a"});
|
||||
}
|
||||
});
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -5,10 +5,12 @@ $(document).ready(function() {
|
|||
var rowCountDomain = $('#domaintable >tbody >#data').length;
|
||||
var rowCountMailbox = $('#mailboxtable >tbody >#data').length;
|
||||
var rowCountAlias = $('#aliastable >tbody >#data').length;
|
||||
var rowCountResource = $('#resourcetable >tbody >#data').length;
|
||||
$("#numRowsDomainAlias").text(rowCountDomainAlias);
|
||||
$("#numRowsDomain").text(rowCountDomain);
|
||||
$("#numRowsMailbox").text(rowCountMailbox);
|
||||
$("#numRowsAlias").text(rowCountAlias);
|
||||
$("#numRowsResource").text(rowCountResource);
|
||||
|
||||
// Filter table function
|
||||
$.fn.extend({
|
||||
|
@ -16,10 +18,10 @@ $(document).ready(function() {
|
|||
return this.each(function(){
|
||||
$(this).on('keyup', function(e){
|
||||
var $this = $(this),
|
||||
search = $this.val().toLowerCase(),
|
||||
target = $this.attr('data-filters'),
|
||||
$target = $(target),
|
||||
$rows = $target.find('tbody #data');
|
||||
search = $this.val().toLowerCase(),
|
||||
target = $this.attr('data-filters'),
|
||||
$target = $(target),
|
||||
$rows = $target.find('tbody #data');
|
||||
$target.find('tbody .filterTable_no_results').remove();
|
||||
if(search == '') {
|
||||
$target.find('tbody #no-data').show();
|
||||
|
|
|
@ -0,0 +1,651 @@
|
|||
// Copyright 2014-2015 Google Inc. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file or at
|
||||
// https://developers.google.com/open-source/licenses/bsd
|
||||
|
||||
/**
|
||||
* @fileoverview The U2F api.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/** Namespace for the U2F api.
|
||||
* @type {Object}
|
||||
*/
|
||||
var u2f = u2f || {};
|
||||
|
||||
/**
|
||||
* The U2F extension id
|
||||
* @type {string}
|
||||
* @const
|
||||
*/
|
||||
u2f.EXTENSION_ID = 'kmendfapggjehodndflmmgagdbamhnfd';
|
||||
|
||||
/**
|
||||
* Message types for messsages to/from the extension
|
||||
* @const
|
||||
* @enum {string}
|
||||
*/
|
||||
u2f.MessageTypes = {
|
||||
'U2F_REGISTER_REQUEST': 'u2f_register_request',
|
||||
'U2F_SIGN_REQUEST': 'u2f_sign_request',
|
||||
'U2F_REGISTER_RESPONSE': 'u2f_register_response',
|
||||
'U2F_SIGN_RESPONSE': 'u2f_sign_response'
|
||||
};
|
||||
|
||||
/**
|
||||
* Response status codes
|
||||
* @const
|
||||
* @enum {number}
|
||||
*/
|
||||
u2f.ErrorCodes = {
|
||||
'OK': 0,
|
||||
'OTHER_ERROR': 1,
|
||||
'BAD_REQUEST': 2,
|
||||
'CONFIGURATION_UNSUPPORTED': 3,
|
||||
'DEVICE_INELIGIBLE': 4,
|
||||
'TIMEOUT': 5
|
||||
};
|
||||
|
||||
/**
|
||||
* A message type for registration requests
|
||||
* @typedef {{
|
||||
* type: u2f.MessageTypes,
|
||||
* signRequests: Array<u2f.SignRequest>,
|
||||
* registerRequests: ?Array<u2f.RegisterRequest>,
|
||||
* timeoutSeconds: ?number,
|
||||
* requestId: ?number
|
||||
* }}
|
||||
*/
|
||||
u2f.Request;
|
||||
|
||||
/**
|
||||
* A message for registration responses
|
||||
* @typedef {{
|
||||
* type: u2f.MessageTypes,
|
||||
* responseData: (u2f.Error | u2f.RegisterResponse | u2f.SignResponse),
|
||||
* requestId: ?number
|
||||
* }}
|
||||
*/
|
||||
u2f.Response;
|
||||
|
||||
/**
|
||||
* An error object for responses
|
||||
* @typedef {{
|
||||
* errorCode: u2f.ErrorCodes,
|
||||
* errorMessage: ?string
|
||||
* }}
|
||||
*/
|
||||
u2f.Error;
|
||||
|
||||
/**
|
||||
* Data object for a single sign request.
|
||||
* @typedef {{
|
||||
* version: string,
|
||||
* challenge: string,
|
||||
* keyHandle: string,
|
||||
* appId: string
|
||||
* }}
|
||||
*/
|
||||
u2f.SignRequest;
|
||||
|
||||
/**
|
||||
* Data object for a sign response.
|
||||
* @typedef {{
|
||||
* keyHandle: string,
|
||||
* signatureData: string,
|
||||
* clientData: string
|
||||
* }}
|
||||
*/
|
||||
u2f.SignResponse;
|
||||
|
||||
/**
|
||||
* Data object for a registration request.
|
||||
* @typedef {{
|
||||
* version: string,
|
||||
* challenge: string,
|
||||
* appId: string
|
||||
* }}
|
||||
*/
|
||||
u2f.RegisterRequest;
|
||||
|
||||
/**
|
||||
* Data object for a registration response.
|
||||
* @typedef {{
|
||||
* registrationData: string,
|
||||
* clientData: string
|
||||
* }}
|
||||
*/
|
||||
u2f.RegisterResponse;
|
||||
|
||||
|
||||
// Low level MessagePort API support
|
||||
|
||||
/**
|
||||
* Sets up a MessagePort to the U2F extension using the
|
||||
* available mechanisms.
|
||||
* @param {function((MessagePort|u2f.WrappedChromeRuntimePort_))} callback
|
||||
*/
|
||||
u2f.getMessagePort = function(callback) {
|
||||
if (typeof chrome != 'undefined' && chrome.runtime) {
|
||||
// The actual message here does not matter, but we need to get a reply
|
||||
// for the callback to run. Thus, send an empty signature request
|
||||
// in order to get a failure response.
|
||||
var msg = {
|
||||
type: u2f.MessageTypes.U2F_SIGN_REQUEST,
|
||||
signRequests: []
|
||||
};
|
||||
chrome.runtime.sendMessage(u2f.EXTENSION_ID, msg, function() {
|
||||
if (!chrome.runtime.lastError) {
|
||||
// We are on a whitelisted origin and can talk directly
|
||||
// with the extension.
|
||||
u2f.getChromeRuntimePort_(callback);
|
||||
} else {
|
||||
// chrome.runtime was available, but we couldn't message
|
||||
// the extension directly, use iframe
|
||||
u2f.getIframePort_(callback);
|
||||
}
|
||||
});
|
||||
} else if (u2f.isAndroidChrome_()) {
|
||||
u2f.getAuthenticatorPort_(callback);
|
||||
} else {
|
||||
// chrome.runtime was not available at all, which is normal
|
||||
// when this origin doesn't have access to any extensions.
|
||||
u2f.getIframePort_(callback);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Detect chrome running on android based on the browser's useragent.
|
||||
* @private
|
||||
*/
|
||||
u2f.isAndroidChrome_ = function() {
|
||||
var userAgent = navigator.userAgent;
|
||||
return userAgent.indexOf('Chrome') != -1 &&
|
||||
userAgent.indexOf('Android') != -1;
|
||||
};
|
||||
|
||||
/**
|
||||
* Connects directly to the extension via chrome.runtime.connect
|
||||
* @param {function(u2f.WrappedChromeRuntimePort_)} callback
|
||||
* @private
|
||||
*/
|
||||
u2f.getChromeRuntimePort_ = function(callback) {
|
||||
var port = chrome.runtime.connect(u2f.EXTENSION_ID,
|
||||
{'includeTlsChannelId': true});
|
||||
setTimeout(function() {
|
||||
callback(new u2f.WrappedChromeRuntimePort_(port));
|
||||
}, 0);
|
||||
};
|
||||
|
||||
/**
|
||||
* Return a 'port' abstraction to the Authenticator app.
|
||||
* @param {function(u2f.WrappedAuthenticatorPort_)} callback
|
||||
* @private
|
||||
*/
|
||||
u2f.getAuthenticatorPort_ = function(callback) {
|
||||
setTimeout(function() {
|
||||
callback(new u2f.WrappedAuthenticatorPort_());
|
||||
}, 0);
|
||||
};
|
||||
|
||||
/**
|
||||
* A wrapper for chrome.runtime.Port that is compatible with MessagePort.
|
||||
* @param {Port} port
|
||||
* @constructor
|
||||
* @private
|
||||
*/
|
||||
u2f.WrappedChromeRuntimePort_ = function(port) {
|
||||
this.port_ = port;
|
||||
};
|
||||
|
||||
/**
|
||||
* Format a return a sign request.
|
||||
* @param {Array<u2f.SignRequest>} signRequests
|
||||
* @param {number} timeoutSeconds
|
||||
* @param {number} reqId
|
||||
* @return {Object}
|
||||
*/
|
||||
u2f.WrappedChromeRuntimePort_.prototype.formatSignRequest_ =
|
||||
function(signRequests, timeoutSeconds, reqId) {
|
||||
return {
|
||||
type: u2f.MessageTypes.U2F_SIGN_REQUEST,
|
||||
signRequests: signRequests,
|
||||
timeoutSeconds: timeoutSeconds,
|
||||
requestId: reqId
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Format a return a register request.
|
||||
* @param {Array<u2f.SignRequest>} signRequests
|
||||
* @param {Array<u2f.RegisterRequest>} signRequests
|
||||
* @param {number} timeoutSeconds
|
||||
* @param {number} reqId
|
||||
* @return {Object}
|
||||
*/
|
||||
u2f.WrappedChromeRuntimePort_.prototype.formatRegisterRequest_ =
|
||||
function(signRequests, registerRequests, timeoutSeconds, reqId) {
|
||||
return {
|
||||
type: u2f.MessageTypes.U2F_REGISTER_REQUEST,
|
||||
signRequests: signRequests,
|
||||
registerRequests: registerRequests,
|
||||
timeoutSeconds: timeoutSeconds,
|
||||
requestId: reqId
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Posts a message on the underlying channel.
|
||||
* @param {Object} message
|
||||
*/
|
||||
u2f.WrappedChromeRuntimePort_.prototype.postMessage = function(message) {
|
||||
this.port_.postMessage(message);
|
||||
};
|
||||
|
||||
/**
|
||||
* Emulates the HTML 5 addEventListener interface. Works only for the
|
||||
* onmessage event, which is hooked up to the chrome.runtime.Port.onMessage.
|
||||
* @param {string} eventName
|
||||
* @param {function({data: Object})} handler
|
||||
*/
|
||||
u2f.WrappedChromeRuntimePort_.prototype.addEventListener =
|
||||
function(eventName, handler) {
|
||||
var name = eventName.toLowerCase();
|
||||
if (name == 'message' || name == 'onmessage') {
|
||||
this.port_.onMessage.addListener(function(message) {
|
||||
// Emulate a minimal MessageEvent object
|
||||
handler({'data': message});
|
||||
});
|
||||
} else {
|
||||
console.error('WrappedChromeRuntimePort only supports onMessage');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Wrap the Authenticator app with a MessagePort interface.
|
||||
* @constructor
|
||||
* @private
|
||||
*/
|
||||
u2f.WrappedAuthenticatorPort_ = function() {
|
||||
this.requestId_ = -1;
|
||||
this.requestObject_ = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Launch the Authenticator intent.
|
||||
* @param {Object} message
|
||||
*/
|
||||
u2f.WrappedAuthenticatorPort_.prototype.postMessage = function(message) {
|
||||
var intentLocation = /** @type {string} */ (message);
|
||||
document.location = intentLocation;
|
||||
};
|
||||
|
||||
/**
|
||||
* Emulates the HTML 5 addEventListener interface.
|
||||
* @param {string} eventName
|
||||
* @param {function({data: Object})} handler
|
||||
*/
|
||||
u2f.WrappedAuthenticatorPort_.prototype.addEventListener =
|
||||
function(eventName, handler) {
|
||||
var name = eventName.toLowerCase();
|
||||
if (name == 'message') {
|
||||
var self = this;
|
||||
/* Register a callback to that executes when
|
||||
* chrome injects the response. */
|
||||
window.addEventListener(
|
||||
'message', self.onRequestUpdate_.bind(self, handler), false);
|
||||
} else {
|
||||
console.error('WrappedAuthenticatorPort only supports message');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Callback invoked when a response is received from the Authenticator.
|
||||
* @param function({data: Object}) callback
|
||||
* @param {Object} message message Object
|
||||
*/
|
||||
u2f.WrappedAuthenticatorPort_.prototype.onRequestUpdate_ =
|
||||
function(callback, message) {
|
||||
var messageObject = JSON.parse(message.data);
|
||||
var intentUrl = messageObject['intentURL'];
|
||||
|
||||
var errorCode = messageObject['errorCode'];
|
||||
var responseObject = null;
|
||||
if (messageObject.hasOwnProperty('data')) {
|
||||
responseObject = /** @type {Object} */ (
|
||||
JSON.parse(messageObject['data']));
|
||||
responseObject['requestId'] = this.requestId_;
|
||||
}
|
||||
|
||||
/* Sign responses from the authenticator do not conform to U2F,
|
||||
* convert to U2F here. */
|
||||
responseObject = this.doResponseFixups_(responseObject);
|
||||
callback({'data': responseObject});
|
||||
};
|
||||
|
||||
/**
|
||||
* Fixup the response provided by the Authenticator to conform with
|
||||
* the U2F spec.
|
||||
* @param {Object} responseData
|
||||
* @return {Object} the U2F compliant response object
|
||||
*/
|
||||
u2f.WrappedAuthenticatorPort_.prototype.doResponseFixups_ =
|
||||
function(responseObject) {
|
||||
if (responseObject.hasOwnProperty('responseData')) {
|
||||
return responseObject;
|
||||
} else if (this.requestObject_['type'] != u2f.MessageTypes.U2F_SIGN_REQUEST) {
|
||||
// Only sign responses require fixups. If this is not a response
|
||||
// to a sign request, then an internal error has occurred.
|
||||
return {
|
||||
'type': u2f.MessageTypes.U2F_REGISTER_RESPONSE,
|
||||
'responseData': {
|
||||
'errorCode': u2f.ErrorCodes.OTHER_ERROR,
|
||||
'errorMessage': 'Internal error: invalid response from Authenticator'
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/* Non-conformant sign response, do fixups. */
|
||||
var encodedChallengeObject = responseObject['challenge'];
|
||||
if (typeof encodedChallengeObject !== 'undefined') {
|
||||
var challengeObject = JSON.parse(atob(encodedChallengeObject));
|
||||
var serverChallenge = challengeObject['challenge'];
|
||||
var challengesList = this.requestObject_['signData'];
|
||||
var requestChallengeObject = null;
|
||||
for (var i = 0; i < challengesList.length; i++) {
|
||||
var challengeObject = challengesList[i];
|
||||
if (challengeObject['keyHandle'] == responseObject['keyHandle']) {
|
||||
requestChallengeObject = challengeObject;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
var responseData = {
|
||||
'errorCode': responseObject['resultCode'],
|
||||
'keyHandle': responseObject['keyHandle'],
|
||||
'signatureData': responseObject['signature'],
|
||||
'clientData': encodedChallengeObject
|
||||
};
|
||||
return {
|
||||
'type': u2f.MessageTypes.U2F_SIGN_RESPONSE,
|
||||
'responseData': responseData,
|
||||
'requestId': responseObject['requestId']
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Base URL for intents to Authenticator.
|
||||
* @const
|
||||
* @private
|
||||
*/
|
||||
u2f.WrappedAuthenticatorPort_.INTENT_URL_BASE_ =
|
||||
'intent:#Intent;action=com.google.android.apps.authenticator.AUTHENTICATE';
|
||||
|
||||
/**
|
||||
* Format a return a sign request.
|
||||
* @param {Array<u2f.SignRequest>} signRequests
|
||||
* @param {number} timeoutSeconds (ignored for now)
|
||||
* @param {number} reqId
|
||||
* @return {string}
|
||||
*/
|
||||
u2f.WrappedAuthenticatorPort_.prototype.formatSignRequest_ =
|
||||
function(signRequests, timeoutSeconds, reqId) {
|
||||
if (!signRequests || signRequests.length == 0) {
|
||||
return null;
|
||||
}
|
||||
/* TODO(fixme): stash away requestId, as the authenticator app does
|
||||
* not return it for sign responses. */
|
||||
this.requestId_ = reqId;
|
||||
/* TODO(fixme): stash away the signRequests, to deal with the legacy
|
||||
* response format returned by the Authenticator app. */
|
||||
this.requestObject_ = {
|
||||
'type': u2f.MessageTypes.U2F_SIGN_REQUEST,
|
||||
'signData': signRequests,
|
||||
'requestId': reqId,
|
||||
'timeout': timeoutSeconds
|
||||
};
|
||||
|
||||
var appId = signRequests[0]['appId'];
|
||||
var intentUrl =
|
||||
u2f.WrappedAuthenticatorPort_.INTENT_URL_BASE_ +
|
||||
';S.appId=' + encodeURIComponent(appId) +
|
||||
';S.eventId=' + reqId +
|
||||
';S.challenges=' +
|
||||
encodeURIComponent(
|
||||
JSON.stringify(this.getBrowserDataList_(signRequests))) + ';end';
|
||||
return intentUrl;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the browser data objects from the challenge list
|
||||
* @param {Array} challenges list of challenges
|
||||
* @return {Array} list of browser data objects
|
||||
* @private
|
||||
*/
|
||||
u2f.WrappedAuthenticatorPort_
|
||||
.prototype.getBrowserDataList_ = function(challenges) {
|
||||
return challenges
|
||||
.map(function(challenge) {
|
||||
var browserData = {
|
||||
'typ': 'navigator.id.getAssertion',
|
||||
'challenge': challenge['challenge']
|
||||
};
|
||||
var challengeObject = {
|
||||
'challenge' : browserData,
|
||||
'keyHandle' : challenge['keyHandle']
|
||||
};
|
||||
return challengeObject;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Format a return a register request.
|
||||
* @param {Array<u2f.SignRequest>} signRequests
|
||||
* @param {Array<u2f.RegisterRequest>} enrollChallenges
|
||||
* @param {number} timeoutSeconds (ignored for now)
|
||||
* @param {number} reqId
|
||||
* @return {Object}
|
||||
*/
|
||||
u2f.WrappedAuthenticatorPort_.prototype.formatRegisterRequest_ =
|
||||
function(signRequests, enrollChallenges, timeoutSeconds, reqId) {
|
||||
if (!enrollChallenges || enrollChallenges.length == 0) {
|
||||
return null;
|
||||
}
|
||||
// Assume the appId is the same for all enroll challenges.
|
||||
var appId = enrollChallenges[0]['appId'];
|
||||
var registerRequests = [];
|
||||
for (var i = 0; i < enrollChallenges.length; i++) {
|
||||
var registerRequest = {
|
||||
'challenge': enrollChallenges[i]['challenge'],
|
||||
'version': enrollChallenges[i]['version']
|
||||
};
|
||||
if (enrollChallenges[i]['appId'] != appId) {
|
||||
// Only include the appId when it differs from the first appId.
|
||||
registerRequest['appId'] = enrollChallenges[i]['appId'];
|
||||
}
|
||||
registerRequests.push(registerRequest);
|
||||
}
|
||||
var registeredKeys = [];
|
||||
if (signRequests) {
|
||||
for (i = 0; i < signRequests.length; i++) {
|
||||
var key = {
|
||||
'keyHandle': signRequests[i]['keyHandle'],
|
||||
'version': signRequests[i]['version']
|
||||
};
|
||||
// Only include the appId when it differs from the appId that's
|
||||
// being registered now.
|
||||
if (signRequests[i]['appId'] != appId) {
|
||||
key['appId'] = signRequests[i]['appId'];
|
||||
}
|
||||
registeredKeys.push(key);
|
||||
}
|
||||
}
|
||||
var request = {
|
||||
'type': u2f.MessageTypes.U2F_REGISTER_REQUEST,
|
||||
'appId': appId,
|
||||
'registerRequests': registerRequests,
|
||||
'registeredKeys': registeredKeys,
|
||||
'requestId': reqId,
|
||||
'timeoutSeconds': timeoutSeconds
|
||||
};
|
||||
var intentUrl =
|
||||
u2f.WrappedAuthenticatorPort_.INTENT_URL_BASE_ +
|
||||
';S.request=' + encodeURIComponent(JSON.stringify(request)) +
|
||||
';end';
|
||||
/* TODO(fixme): stash away requestId, this is is not necessary for
|
||||
* register requests, but here to keep parity with sign.
|
||||
*/
|
||||
this.requestId_ = reqId;
|
||||
return intentUrl;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets up an embedded trampoline iframe, sourced from the extension.
|
||||
* @param {function(MessagePort)} callback
|
||||
* @private
|
||||
*/
|
||||
u2f.getIframePort_ = function(callback) {
|
||||
// Create the iframe
|
||||
var iframeOrigin = 'chrome-extension://' + u2f.EXTENSION_ID;
|
||||
var iframe = document.createElement('iframe');
|
||||
iframe.src = iframeOrigin + '/u2f-comms.html';
|
||||
iframe.setAttribute('style', 'display:none');
|
||||
document.body.appendChild(iframe);
|
||||
|
||||
var channel = new MessageChannel();
|
||||
var ready = function(message) {
|
||||
if (message.data == 'ready') {
|
||||
channel.port1.removeEventListener('message', ready);
|
||||
callback(channel.port1);
|
||||
} else {
|
||||
console.error('First event on iframe port was not "ready"');
|
||||
}
|
||||
};
|
||||
channel.port1.addEventListener('message', ready);
|
||||
channel.port1.start();
|
||||
|
||||
iframe.addEventListener('load', function() {
|
||||
// Deliver the port to the iframe and initialize
|
||||
iframe.contentWindow.postMessage('init', iframeOrigin, [channel.port2]);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
// High-level JS API
|
||||
|
||||
/**
|
||||
* Default extension response timeout in seconds.
|
||||
* @const
|
||||
*/
|
||||
u2f.EXTENSION_TIMEOUT_SEC = 30;
|
||||
|
||||
/**
|
||||
* A singleton instance for a MessagePort to the extension.
|
||||
* @type {MessagePort|u2f.WrappedChromeRuntimePort_}
|
||||
* @private
|
||||
*/
|
||||
u2f.port_ = null;
|
||||
|
||||
/**
|
||||
* Callbacks waiting for a port
|
||||
* @type {Array<function((MessagePort|u2f.WrappedChromeRuntimePort_))>}
|
||||
* @private
|
||||
*/
|
||||
u2f.waitingForPort_ = [];
|
||||
|
||||
/**
|
||||
* A counter for requestIds.
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
u2f.reqCounter_ = 0;
|
||||
|
||||
/**
|
||||
* A map from requestIds to client callbacks
|
||||
* @type {Object.<number,(function((u2f.Error|u2f.RegisterResponse))
|
||||
* |function((u2f.Error|u2f.SignResponse)))>}
|
||||
* @private
|
||||
*/
|
||||
u2f.callbackMap_ = {};
|
||||
|
||||
/**
|
||||
* Creates or retrieves the MessagePort singleton to use.
|
||||
* @param {function((MessagePort|u2f.WrappedChromeRuntimePort_))} callback
|
||||
* @private
|
||||
*/
|
||||
u2f.getPortSingleton_ = function(callback) {
|
||||
if (u2f.port_) {
|
||||
callback(u2f.port_);
|
||||
} else {
|
||||
if (u2f.waitingForPort_.length == 0) {
|
||||
u2f.getMessagePort(function(port) {
|
||||
u2f.port_ = port;
|
||||
u2f.port_.addEventListener('message',
|
||||
/** @type {function(Event)} */ (u2f.responseHandler_));
|
||||
|
||||
// Careful, here be async callbacks. Maybe.
|
||||
while (u2f.waitingForPort_.length)
|
||||
u2f.waitingForPort_.shift()(u2f.port_);
|
||||
});
|
||||
}
|
||||
u2f.waitingForPort_.push(callback);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles response messages from the extension.
|
||||
* @param {MessageEvent.<u2f.Response>} message
|
||||
* @private
|
||||
*/
|
||||
u2f.responseHandler_ = function(message) {
|
||||
var response = message.data;
|
||||
var reqId = response['requestId'];
|
||||
if (!reqId || !u2f.callbackMap_[reqId]) {
|
||||
console.error('Unknown or missing requestId in response.');
|
||||
return;
|
||||
}
|
||||
var cb = u2f.callbackMap_[reqId];
|
||||
delete u2f.callbackMap_[reqId];
|
||||
cb(response['responseData']);
|
||||
};
|
||||
|
||||
/**
|
||||
* Dispatches an array of sign requests to available U2F tokens.
|
||||
* @param {Array<u2f.SignRequest>} signRequests
|
||||
* @param {function((u2f.Error|u2f.SignResponse))} callback
|
||||
* @param {number=} opt_timeoutSeconds
|
||||
*/
|
||||
u2f.sign = function(signRequests, callback, opt_timeoutSeconds) {
|
||||
u2f.getPortSingleton_(function(port) {
|
||||
var reqId = ++u2f.reqCounter_;
|
||||
u2f.callbackMap_[reqId] = callback;
|
||||
var timeoutSeconds = (typeof opt_timeoutSeconds !== 'undefined' ?
|
||||
opt_timeoutSeconds : u2f.EXTENSION_TIMEOUT_SEC);
|
||||
var req = port.formatSignRequest_(signRequests, timeoutSeconds, reqId);
|
||||
port.postMessage(req);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Dispatches register requests to available U2F tokens. An array of sign
|
||||
* requests identifies already registered tokens.
|
||||
* @param {Array<u2f.RegisterRequest>} registerRequests
|
||||
* @param {Array<u2f.SignRequest>} signRequests
|
||||
* @param {function((u2f.Error|u2f.RegisterResponse))} callback
|
||||
* @param {number=} opt_timeoutSeconds
|
||||
*/
|
||||
u2f.register = function(registerRequests, signRequests,
|
||||
callback, opt_timeoutSeconds) {
|
||||
u2f.getPortSingleton_(function(port) {
|
||||
var reqId = ++u2f.reqCounter_;
|
||||
u2f.callbackMap_[reqId] = callback;
|
||||
var timeoutSeconds = (typeof opt_timeoutSeconds !== 'undefined' ?
|
||||
opt_timeoutSeconds : u2f.EXTENSION_TIMEOUT_SEC);
|
||||
var req = port.formatRegisterRequest_(
|
||||
signRequests, registerRequests, timeoutSeconds, reqId);
|
||||
port.postMessage(req);
|
||||
});
|
||||
};
|
|
@ -14,15 +14,20 @@ $(document).ready(function() {
|
|||
$(".passFields").slideUp();
|
||||
}
|
||||
});
|
||||
|
||||
// Show generate button after time selection
|
||||
$('#trigger_set_time_limited_aliases').hide();
|
||||
$('#generate_tla').hide();
|
||||
$('#validity').change(function(){
|
||||
$('#trigger_set_time_limited_aliases').show();
|
||||
$('#generate_tla').show();
|
||||
});
|
||||
|
||||
// Init Bootstrap Switch
|
||||
$.fn.bootstrapSwitch.defaults.onColor = 'success';
|
||||
$("[name='tls_out']").bootstrapSwitch();
|
||||
$("[name='tls_in']").bootstrapSwitch();
|
||||
|
||||
// Log modal
|
||||
$('#logModal').on('show.bs.modal', function(e) {
|
||||
var logText = $(e.relatedTarget).data('log-text');
|
||||
$(e.currentTarget).find('#logText').html('<pre style="background:none;font-size:11px;line-height:1.1;border:0px">' + logText + '</pre>');
|
||||
});
|
||||
});
|
|
@ -0,0 +1,57 @@
|
|||
<?php
|
||||
require_once 'inc/prerequisites.inc.php';
|
||||
error_reporting(E_ALL);
|
||||
if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_username'])) {
|
||||
if ($_GET['action'] && $_GET['object']) {
|
||||
$action = $_GET['action'];
|
||||
$object = $_GET['object'];
|
||||
switch ($action) {
|
||||
case "get_mailbox_details":
|
||||
$data = mailbox_get_mailbox_details($object);
|
||||
if (!$data || empty($data)) {
|
||||
echo '{}';
|
||||
}
|
||||
else {
|
||||
echo json_encode(mailbox_get_mailbox_details($object), JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
|
||||
}
|
||||
break;
|
||||
case "get_domain_details":
|
||||
$data = mailbox_get_domain_details($object);
|
||||
if (!$data || empty($data)) {
|
||||
echo '{}';
|
||||
}
|
||||
else {
|
||||
echo json_encode(mailbox_get_domain_details($object), JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
|
||||
}
|
||||
break;
|
||||
case "get_u2f_reg_challenge":
|
||||
if (
|
||||
($_SESSION["mailcow_cc_role"] == "admin" || $_SESSION["mailcow_cc_role"] == "domainadmin")
|
||||
&&
|
||||
($_SESSION["mailcow_cc_username"] == $object)
|
||||
) {
|
||||
$data = $u2f->getRegisterData(get_u2f_registrations($object));
|
||||
list($req, $sigs) = $data;
|
||||
$_SESSION['regReq'] = json_encode($req);
|
||||
echo 'var req = ' . json_encode($req) . '; var sigs = ' . json_encode($sigs) . ';';
|
||||
}
|
||||
else {
|
||||
echo '{}';
|
||||
}
|
||||
break;
|
||||
case "get_u2f_auth_challenge":
|
||||
if (isset($_SESSION['pending_mailcow_cc_username']) && $_SESSION['pending_mailcow_cc_username'] == $object) {
|
||||
$reqs = json_encode($u2f->getAuthenticateData(get_u2f_registrations($object)));
|
||||
$_SESSION['authReq'] = $reqs;
|
||||
echo 'var req = ' . $reqs . ';';
|
||||
}
|
||||
else {
|
||||
echo '{}';
|
||||
}
|
||||
break;
|
||||
default:
|
||||
echo '{}';
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -10,13 +10,13 @@ $lang['footer']['restart_sogo'] = 'SOGo neustarten';
|
|||
$lang['footer']['restart_now'] = 'Jetzt neustarten';
|
||||
$lang['footer']['restart_sogo_info'] = 'Einige Änderungen an Domains benötigen einen Neustart SOGos. Hier können Sie SOGo neustarten.<br /><br /><b>Wichtig:</b> Ein korrekter Neustart SOGos kann eine Weile in Anspruch nehmen, bitte warten Sie, bis der Prozess vollständig beendet wurde.';
|
||||
$lang['dkim']['confirm'] = 'Sind Sie sicher?';
|
||||
$lang['danger']['dkim_not_found'] = 'DKIM-Record nicht gefunden';
|
||||
$lang['danger']['dkim_remove_failed'] = 'Kann DKIM-Record nicht entfernen';
|
||||
$lang['danger']['dkim_add_failed'] = 'Kann DKIM-Record nicht hinzufügen';
|
||||
$lang['danger']['dkim_not_found'] = 'DKIM-Key nicht gefunden';
|
||||
$lang['danger']['dkim_remove_failed'] = 'Kann DKIM-Key nicht entfernen';
|
||||
$lang['danger']['dkim_add_failed'] = 'Kann DKIM-Key nicht hinzufügen';
|
||||
$lang['danger']['dkim_domain_or_sel_invalid'] = 'DKIM-Domain oder -Selector nicht korrekt';
|
||||
$lang['danger']['dkim_key_length_invalid'] = 'DKIM Schlüssellänge ungültig';
|
||||
$lang['success']['dkim_removed'] = 'DKIM-Record wurde entfernt';
|
||||
$lang['success']['dkim_added'] = 'DKIM-Record wurde hinzugefügt';
|
||||
$lang['success']['dkim_removed'] = 'DKIM-Key wurde entfernt';
|
||||
$lang['success']['dkim_added'] = 'DKIM-Key wurde hinzugefügt';
|
||||
$lang['danger']['access_denied'] = 'Zugriff verweigert oder unvollständige/ungültige Daten';
|
||||
$lang['danger']['whitelist_from_invalid'] = 'Whitelist-Eintrag ist ungültig';
|
||||
$lang['danger']['domain_invalid'] = 'Domainname ist ungültig';
|
||||
|
@ -29,6 +29,7 @@ $lang['danger']['policy_list_from_exists'] = 'Ein Eintrag mit diesem Wert existi
|
|||
$lang['danger']['policy_list_from_invalid'] = 'Eintrag hat ungültiges Format';
|
||||
$lang['danger']['alias_invalid'] = 'Alias-Adrese ist ungültig';
|
||||
$lang['danger']['goto_invalid'] = 'Ziel-Adrese ist ungültig';
|
||||
$lang['danger']['last_key'] = 'Letzter Key kann nicht gelöscht werden';
|
||||
$lang['danger']['alias_domain_invalid'] = 'Alias-Domain ist ungültig';
|
||||
$lang['danger']['target_domain_invalid'] = 'Ziel-Domain ist ungültig';
|
||||
$lang['danger']['object_exists'] = 'Objekt %s existiert bereits';
|
||||
|
@ -39,6 +40,8 @@ $lang['success']['alias_added'] = 'Alias-Adresse(n) wurden angelegt';
|
|||
$lang['success']['alias_modified'] = 'Änderungen an Alias %s wurden gespeichert';
|
||||
$lang['success']['aliasd_modified'] = 'Änderungen an Alias-Domain %s wurden gespeichert';
|
||||
$lang['success']['mailbox_modified'] = 'Änderungen an Mailbox %s wurden gespeichert';
|
||||
$lang['success']['resource_modified'] = "Änderungen an Ressource %s wurden gespeichert";
|
||||
$lang['success']['object_modified'] = "Änderungen an Objekt %s wurden gespeichert";
|
||||
$lang['success']['msg_size_saved'] = 'Limit wurde gesetzt';
|
||||
$lang['danger']['aliasd_not_found'] = 'Alias-Domain nicht gefunden';
|
||||
$lang['danger']['targetd_not_found'] = 'Ziel-Domain nicht gefunden';
|
||||
|
@ -54,28 +57,30 @@ $lang['danger']['exit_code_not_null'] = 'Fehler: Exit-Code ist %d';
|
|||
$lang['danger']['mailbox_not_available'] = 'Mailbox nicht verfügbar';
|
||||
$lang['danger']['username_invalid'] = 'Benutzername kann nicht verwendet werden';
|
||||
$lang['danger']['password_mismatch'] = 'Passwort-Wiederholung stimmt nicht überein';
|
||||
$lang['danger']['password_complexity'] = 'Passwort entspricht nicht den Vorgaben';
|
||||
$lang['danger']['password_complexity'] = 'Passwort entspricht nicht den Richtlinien';
|
||||
$lang['danger']['password_empty'] = 'Passwort darf nicht leer sein';
|
||||
$lang['danger']['login_failed'] = 'Anmeldung fehlgeschlagen';
|
||||
$lang['danger']['mailbox_invalid'] = 'Mailboxname ist ungültig';
|
||||
$lang['danger']['resource_invalid'] = 'Ressourcenname ist ungültig';
|
||||
$lang['danger']['description_invalid'] = 'Ressourcenbeschreibung ist ungültig';
|
||||
$lang['danger']['mailbox_invalid_suggest'] = 'Mailboxname ist ungültig, meinten Sie vielleicht "%s"?';
|
||||
$lang['info']['fetchmail_planned'] = 'Aufgabe zur Mailabholung wurde geplant. Bitte prüfen Sie den Vorgangsstatus zu einem späteren Zeitpunkt noch einmal.';
|
||||
$lang['danger']['fetchmail_source_empty'] = 'Bitte geben Sie einen Quell-Ordner an';
|
||||
$lang['danger']['fetchmail_dest_empty'] = 'Bitte geben Sie einen Ziel-Ordner an';
|
||||
$lang['danger']['is_alias'] = '%s lautet bereits eine Alias-Adresse';
|
||||
$lang['danger']['is_alias_or_mailbox'] = "Eine Mailbox oder ein Alias mit der Adresse %s ist bereits vorhanden";
|
||||
$lang['danger']['is_spam_alias'] = '%s lautet bereits eine Spam-Alias-Adresse';
|
||||
$lang['danger']['quota_not_0_not_numeric'] = 'Speicherplatz muss numerisch und >= 0 sein';
|
||||
$lang['danger']['domain_not_found'] = 'Domain nicht gefunden.';
|
||||
$lang['danger']['domain_not_found'] = 'Domain "%s" nicht gefunden.';
|
||||
$lang['danger']['max_mailbox_exceeded'] = 'Anzahl an Mailboxen überschritten (%d von %d)';
|
||||
$lang['danger']['mailbox_quota_exceeded'] = 'Speicherplatz überschreitet das Limit (max. %d MiB)';
|
||||
$lang['danger']['mailbox_quota_left_exceeded'] = 'Nicht genügend Speicherplatz vorhanden (Speicherplatz anwendbar: %d MiB)';
|
||||
$lang['success']['mailbox_added'] = 'Mailbox %s wurde angelegt';
|
||||
$lang['success']['resource_added'] = 'Ressource %s wurde angelegt';
|
||||
$lang['success']['domain_removed'] = 'Domain %s wurde entfernt';
|
||||
$lang['success']['alias_removed'] = 'Alias-Adresse %s wurde entfernt';
|
||||
$lang['success']['alias_domain_removed'] = 'Alias-Domain %s wurde entfernt';
|
||||
$lang['success']['domain_admin_removed'] = 'Domain-Administrator %s wurde entfernt';
|
||||
$lang['success']['mailbox_removed'] = 'Mailbox %s wurde entfernt';
|
||||
$lang['success']['eas_reset'] = "ActiveSync Gerät des Benutzers %s wurden zurückgesetzt";
|
||||
$lang['success']['resource_removed'] = 'Ressource %s wurde entfernt';
|
||||
$lang['danger']['max_quota_in_use'] = 'Mailbox Speicherplatzlimit muss größer oder gleich %d MiB sein';
|
||||
$lang['danger']['domain_quota_m_in_use'] = 'Domain Speicherplatzlimit muss größer oder gleich %d MiB sein';
|
||||
$lang['danger']['mailboxes_in_use'] = 'Maximale Anzahl an Mailboxen muss größer oder gleich %d sein';
|
||||
|
@ -84,34 +89,38 @@ $lang['danger']['sender_acl_invalid'] = 'Sender ACL Wert muss eine Adresse oder
|
|||
$lang['danger']['domain_not_empty'] = 'Kann nur leere Domains entfernen';
|
||||
$lang['warning']['spam_alias_temp_error'] = 'Kann zur Zeit keinen Spam-Alias erstellen, bitte versuchen Sie es später noch einmal.';
|
||||
$lang['danger']['spam_alias_max_exceeded'] = 'Maximale Anzahl an Spam-Alias-Adressen erreicht';
|
||||
$lang['danger']['fetchmail_active'] = 'Ein Vorgang zur Mailabholung ist bereits aktiv, bitte haben Sie etwas Geduld.';
|
||||
$lang['danger']['validity_missing'] = 'Bitte geben Sie eine Gültigkeitsdauer an';
|
||||
$lang['user']['on'] = 'Ein';
|
||||
$lang['user']['off'] = 'Aus';
|
||||
$lang['user']['messages'] = "Nachrichten";
|
||||
$lang['user']['in_use'] = "Verwendet";
|
||||
$lang['user']['user_change_fn'] = '';
|
||||
$lang['user']['user_settings'] = 'Benutzereinstellungen';
|
||||
$lang['user']['mailbox_settings'] = 'Mailbox-Einstellungen';
|
||||
$lang['user']['mailbox_details'] = 'Mailbox-Details';
|
||||
$lang['user']['change_password'] = 'Passwort ändern';
|
||||
$lang['user']['new_password'] = 'Neues Passwort:';
|
||||
$lang['user']['new_password'] = 'Neues Passwort';
|
||||
$lang['user']['save_changes'] = 'Änderungen speichern';
|
||||
$lang['user']['password_now'] = 'Aktuelles Passwort (Änderungen bestätigen):';
|
||||
$lang['user']['new_password_repeat'] = 'Neues Passwort (Wiederholung):';
|
||||
$lang['user']['password_now'] = 'Aktuelles Passwort (Änderungen bestätigen)';
|
||||
$lang['user']['new_password_repeat'] = 'Neues Passwort (Wiederholung)';
|
||||
$lang['user']['new_password_description'] = 'Mindestanforderung: 6 Zeichen lang, Buchstaben und Zahlen.';
|
||||
$lang['user']['did_you_know'] = '<b>Wussten Sie schon?</b> Sie können Ihre E-Mail-Adresse mit Tags versehen, etwa "ich+<b>Privat</b>@example.com", um Nachrichten automatisch in einem Unterordner (Beispiel: "Privat") abzulegen.';
|
||||
$lang['user']['spam_aliases'] = 'Temporäre E-Mail Aliasse';
|
||||
$lang['user']['alias'] = 'Alias';
|
||||
$lang['user']['aliases'] = 'Aliasse';
|
||||
$lang['user']['domain_aliases'] = 'Domain-Alias Adressen';
|
||||
$lang['user']['is_catch_all'] = 'Ist Catch-All Adresse für Domain(s)';
|
||||
$lang['user']['aliases_also_send_as'] = 'Darf außerdem versenden als';
|
||||
$lang['user']['aliases_send_as_all'] = 'Absender für folgende Domains nicht prüfen';
|
||||
$lang['user']['aliases_also_send_as'] = 'Darf außerdem versenden als Benutzer';
|
||||
$lang['user']['aliases_send_as_all'] = 'Absender für folgende Domains und zugehörige Alias-Domains nicht prüfen';
|
||||
$lang['user']['alias_create_random'] = 'Zufälligen Alias generieren';
|
||||
$lang['user']['alias_extend_all'] = 'Gültigkeit +1h';
|
||||
$lang['user']['alias_valid_until'] = 'Gültig bis';
|
||||
$lang['user']['alias_remove_all'] = 'Alle entfernen';
|
||||
$lang['user']['alias_time_left'] = 'Zeit verbleibend';
|
||||
$lang['user']['alias_full_date'] = 'd.m.Y, H:i:s T';
|
||||
$lang['user']['syncjob_full_date'] = 'd.m.Y, H:i:s T';
|
||||
$lang['user']['alias_select_validity'] = 'Bitte Gültigkeit auswählen';
|
||||
$lang['user']['sync_jobs'] = 'Sync Jobs';
|
||||
$lang['user']['hour'] = 'Stunde';
|
||||
$lang['user']['hours'] = 'Stunden';
|
||||
$lang['user']['day'] = 'Tag';
|
||||
|
@ -134,6 +143,7 @@ $lang['user']['spamfilter_yellow'] = 'Gelb: Die Nachricht ist vielleicht Spam, w
|
|||
$lang['user']['spamfilter_red'] = 'Rot: Die Nachricht ist eindeutig Spam und wird vom Server abgelehnt';
|
||||
$lang['user']['spamfilter_default_score'] = 'Standardwert:';
|
||||
$lang['user']['spamfilter_hint'] = 'Der erste Wert beschreibt den "low spam score", der zweite Wert den "high spam score".';
|
||||
$lang['user']['spamfilter_table_domain_policy'] = "n.v. (Domainrichtlinie)";
|
||||
|
||||
$lang['user']['tls_policy_warning'] = '<strong>Vorsicht:</strong> Entscheiden Sie sich unverschlüsselte Verbindungen abzulehnen, kann dies dazu führen, dass Kontakte Sie nicht mehr erreichen.<br />Nachrichten, die die Richtlinie nicht erfüllen, werden durch einen Hard-Fail im Mailsystem abgewiesen.';
|
||||
$lang['user']['tls_policy'] = 'Verschlüsselungsrichtlinie';
|
||||
|
@ -150,6 +160,22 @@ $lang['user']['tag_in_subject'] = 'In Betreff';
|
|||
$lang['user']['tag_help_explain'] = 'Als Unterordner: Es wird ein Ordner mit dem Namen des Tags unterhalb der Inbox erstellt ("INBOX/Facebook").<br />
|
||||
In Betreff: Der Name des Tags wird dem Betreff angefügt, etwa "[Facebook] Meine Neuigkeiten".';
|
||||
$lang['user']['tag_help_example'] = 'Beispiel für eine getaggte E-Mail-Adresse: ich<b>+Facebook</b>@example.org';
|
||||
$lang['user']['eas_reset'] = 'ActiveSync Geräte-Cache zurücksetzen';
|
||||
$lang['user']['eas_reset_now'] = 'Jetzt zurücksetzen';
|
||||
$lang['user']['eas_reset_help'] = 'In vielen Fällen kann ein ActiveSync Profil durch das Zurücksetzen des Caches repariert werden.<br /><b>Vorsicht:</b> Alle Elemente werden erneut heruntergeladen!';
|
||||
|
||||
$lang['user']['encryption'] = 'Verschlüsselung';
|
||||
$lang['user']['username'] = 'Benutzername';
|
||||
$lang['user']['password'] = 'Password';
|
||||
$lang['user']['last_run'] = 'Letzte Ausführung';
|
||||
$lang['user']['excludes'] = 'Ausschlüsse';
|
||||
$lang['user']['interval'] = 'Intervall';
|
||||
$lang['user']['active'] = 'Aktiv';
|
||||
$lang['user']['action'] = 'Aktion';
|
||||
$lang['user']['edit'] = 'Bearbeiten';
|
||||
$lang['user']['remove'] = 'Entfernen';
|
||||
$lang['user']['delete_now'] = 'Sofort löschen';
|
||||
$lang['user']['create_syncjob'] = 'Neuen Sync-Job erstellen';
|
||||
|
||||
$lang['start']['dashboard'] = '%s - Dashboard';
|
||||
$lang['start']['start_rc'] = 'Roundcube öffnen';
|
||||
|
@ -180,10 +206,17 @@ $lang['header']['mailboxes'] = 'Mailboxen';
|
|||
$lang['header']['user_settings'] = 'Benutzereinstellungen';
|
||||
$lang['header']['login'] = 'Anmeldung';
|
||||
$lang['header']['logged_in_as_logout'] = 'Eingeloggt als <b>%s</b> (abmelden)';
|
||||
$lang['header']['logged_in_as_logout_dual'] = 'Eingeloggt als <b>%s <span class="text-info">[%s]</span></b>';
|
||||
$lang['header']['locale'] = 'Sprache';
|
||||
$lang['mailbox']['domain'] = 'Domain';
|
||||
$lang['mailbox']['spam_aliases'] = 'Temp. Alias';
|
||||
$lang['mailbox']['alias'] = 'Alias';
|
||||
$lang['mailbox']['aliases'] = 'Aliasse';
|
||||
$lang['mailbox']['multiple_bookings'] = 'Mehrfachbuchen';
|
||||
$lang['mailbox']['kind'] = 'Art';
|
||||
$lang['mailbox']['description'] = 'Beschreibung';
|
||||
$lang['mailbox']['resources'] = 'Ressourcen';
|
||||
$lang['mailbox']['resource_name'] = 'Ressourcenname';
|
||||
$lang['mailbox']['domains'] = 'Domains';
|
||||
$lang['mailbox']['mailboxes'] = 'Mailboxen';
|
||||
$lang['mailbox']['mailbox_quota'] = 'Max. Größe einer Mailbox';
|
||||
|
@ -206,10 +239,12 @@ $lang['mailbox']['msg_num'] = 'Anzahl Nachrichten';
|
|||
$lang['mailbox']['remove'] = 'Entfernen';
|
||||
$lang['mailbox']['edit'] = 'Bearbeiten';
|
||||
$lang['mailbox']['archive'] = 'Archiv-Zugriff';
|
||||
$lang['mailbox']['no_record'] = 'Kein Eintrag';
|
||||
$lang['mailbox']['no_record'] = 'Kein Eintrag für Objekt %s';
|
||||
$lang['mailbox']['no_record_single'] = 'Kein Eintrag';
|
||||
$lang['mailbox']['add_domain'] = 'Domain hinzufügen';
|
||||
$lang['mailbox']['add_domain_alias'] = 'Domain-Alias hinzufügen';
|
||||
$lang['mailbox']['add_mailbox'] = 'Mailbox hinzufügen';
|
||||
$lang['mailbox']['add_resource'] = 'Ressource hinzufügen';
|
||||
$lang['mailbox']['add_alias'] = 'Alias hinzufügen';
|
||||
|
||||
$lang['info']['no_action'] = 'Keine Aktion anwendbar';
|
||||
|
@ -219,14 +254,26 @@ $lang['delete']['remove_domain_warning'] = '<b>Warnung:</b> Sie entfernen die Do
|
|||
$lang['delete']['remove_domainalias_warning'] = '<b>Warnung:</b> Sie entfernen die Alias-Domain <b>%s</b>!';
|
||||
$lang['delete']['remove_domainadmin_warning'] = '<b>Warnung:</b> Sie entfernen den Domain-Administrator <b>%s</b>!';
|
||||
$lang['delete']['remove_alias_warning'] = '<b>Warnung:</b> Sie entfernen die Alias-Adresse <b>%s</b>!';
|
||||
$lang['delete']['remove_syncjob_warning'] = '<b>Warnung:</b> Sie entfernen einen Sync-Job des Benutzers <b>%s</b>!';
|
||||
$lang['delete']['remove_mailbox_warning'] = '<b>Warnung:</b> Sie entfernen die Mailbox <b>%s</b>!';
|
||||
$lang['delete']['remove_mailbox_details'] = 'Die Mailbox wird <b>vollständig und permanent</b> entfernt!';
|
||||
$lang['delete']['remove_resource_warning'] = '<b>Warnung:</b> Sie entfernen die Ressource <b>%s</b>!';
|
||||
$lang['delete']['remove_resource_details'] = 'Die Ressource wird <b>vollständig und permanent</b> entfernt!';
|
||||
$lang['delete']['remove_domain_details'] = 'Diese Aktion entfernt ebenfalls Domain-Aliasse.<br /><br /><b>Eine Domain muss leer sein, um entfernt zu werden.</b>';
|
||||
$lang['delete']['remove_syncjob_details'] = 'Objekte dieses Sync-Jobs werden nicht mehr vom entfernten Server abgeholt.';
|
||||
$lang['delete']['remove_alias_details'] = 'Benutzer werden keine Nachrichten mehr von dieser Adresse erhalten und versenden koennen!</b>';
|
||||
$lang['delete']['remove_button'] = 'Entfernen';
|
||||
$lang['delete']['previous'] = 'Vorherige Seite';
|
||||
|
||||
$lang['edit']['syncjob'] = 'Sync-Job bearbeiten';
|
||||
$lang['edit']['save'] = 'Änderungen speichern';
|
||||
$lang['edit']['username'] = 'Benutzername';
|
||||
$lang['edit']['hostname'] = 'Servername';
|
||||
$lang['edit']['encryption'] = 'Verschlüsselungsmethode';
|
||||
$lang['edit']['maxage'] = 'Maximales Alter in Tagen einer Nachricht, die kopiert werden soll</br ><small>(0 = alle Nachrichten kopieren)</small>';
|
||||
$lang['edit']['subfolder2'] = 'Ziel-Ordner<br /><small>(leer = kein Unterordner)</small>';
|
||||
$lang['edit']['mins_interval'] = 'Intervall (min)';
|
||||
$lang['edit']['exclude'] = 'Elemente ausschließen (Regex)';
|
||||
$lang['edit']['archive'] = 'Archiv-Zugriff';
|
||||
$lang['edit']['max_mailboxes'] = 'Max. Mailboxanzahl:';
|
||||
$lang['edit']['title'] = 'Objekt bearbeiten';
|
||||
|
@ -263,11 +310,26 @@ $lang['edit']['dkim_txt_name'] = 'TXT-Record Name:';
|
|||
$lang['edit']['dkim_txt_value'] = 'TXT-Record Wert:';
|
||||
$lang['edit']['previous'] = 'Vorherige Seite';
|
||||
$lang['edit']['unchanged_if_empty'] = 'Unverändert, wenn leer';
|
||||
$lang['edit']['dont_check_sender_acl'] = 'Absender für Domain %s nicht prüfen';
|
||||
$lang['edit']['dont_check_sender_acl'] = 'Absender für Domain %s u. Alias-Dom. nicht prüfen';
|
||||
$lang['edit']['multiple_bookings'] = 'Mehrfaches Buchen';
|
||||
$lang['edit']['kind'] = 'Art';
|
||||
$lang['edit']['resource'] = 'Ressource';
|
||||
|
||||
$lang['add']['syncjob'] = 'Sync-Job erstellen';
|
||||
$lang['add']['syncjob_hint'] = 'Passwörter werden unverschlüsselt abgelegt!';
|
||||
$lang['add']['hostname'] = 'Servername';
|
||||
$lang['add']['username'] = 'Benutzername';
|
||||
$lang['add']['enc_method'] = 'Verschlüsselungsmethode';
|
||||
$lang['add']['maxage'] = 'Maximum age of messages that will be polled from remote (0 = ignore age)';
|
||||
$lang['add']['subfolder2'] = 'Sync into subfolder on destination';
|
||||
$lang['add']['mins_interval'] = 'Abrufintervall (Minuten)';
|
||||
$lang['add']['exclude'] = 'Elemente ausschließen (Regex)';
|
||||
$lang['add']['delete2duplicates'] = 'Lösche Duplikate im Ziel';
|
||||
|
||||
$lang['add']['title'] = 'Objekt anlegen';
|
||||
$lang['add']['domain'] = 'Domain';
|
||||
$lang['add']['active'] = 'Aktiv';
|
||||
$lang['add']['multiple_bookings'] = 'Mehrfaches Buchen möglich';
|
||||
$lang['add']['save'] = 'Änderungen speichern';
|
||||
$lang['add']['description'] = 'Beschreibung:';
|
||||
$lang['add']['max_aliases'] = 'Max. mögliche Aliasse:';
|
||||
|
@ -289,7 +351,10 @@ $lang['add']['alias_domain'] = 'Alias-Domain';
|
|||
$lang['add']['select'] = 'Bitte auswählen';
|
||||
$lang['add']['target_domain'] = 'Ziel-Domain:';
|
||||
$lang['add']['mailbox'] = 'Mailbox';
|
||||
$lang['add']['resource'] = 'Ressource';
|
||||
$lang['add']['kind'] = 'Art';
|
||||
$lang['add']['mailbox_username'] = 'Benutzername (linker Teil der E-Mail-Adresse):';
|
||||
$lang['add']['resource_name'] = 'Ressourcenname:';
|
||||
$lang['add']['full_name'] = 'Vor- und Zuname:';
|
||||
$lang['add']['quota_mb'] = 'Speicherplatz (MiB):';
|
||||
$lang['add']['select_domain'] = 'Bitte zuerst eine Domain auswählen';
|
||||
|
@ -310,11 +375,24 @@ $lang['login']['login'] = 'Anmelden';
|
|||
$lang['login']['previous'] = 'Vorherige Seite';
|
||||
$lang['login']['delayed'] = 'Login wurde zur Sicherheit um %s Sekunde/n verzögert.';
|
||||
|
||||
$lang['login']['tfa'] = 'Zwei-Faktor-Authentifizierung';
|
||||
$lang['login']['tfa_details'] = 'Bitte bestätigen Sie Ihr Einmalpasswort im folgenden Feld';
|
||||
$lang['login']['confirm'] = 'Bestätigen';
|
||||
$lang['login']['otp'] = 'Einmalpasswort';
|
||||
$lang['login']['trash_login'] = 'Login verwerfen';
|
||||
$lang['tfa']['tfa'] = "Two-Factor Authentication";
|
||||
$lang['tfa']['set_tfa'] = "Konfiguriere Two-Factor Authentication Methode";
|
||||
$lang['tfa']['yubi_otp'] = "Yubico OTP Authentifizierung";
|
||||
$lang['tfa']['key_id'] = "Ein Name für diesen YubiKey";
|
||||
$lang['tfa']['api_register'] = 'mailcow verwendet die Yubico Cloud API. Ein API-Key für den Yubico Stick kann <a href="https://upgrade.yubico.com/getapikey/" target="_blank">hier</a> bezogen werden.';
|
||||
$lang['tfa']['u2f'] = "U2F Authentifizierung";
|
||||
$lang['tfa']['hotp'] = "HOTP Authentifizierung";
|
||||
$lang['tfa']['totp'] = "TOTP Authentifizierung";
|
||||
$lang['tfa']['none'] = "Deaktiviert";
|
||||
$lang['tfa']['delete_tfa'] = "Deaktiviere TFA";
|
||||
$lang['tfa']['disable_tfa'] = "Deaktiviere TFA bis zur nächsten erfolgreichen Anmeldung";
|
||||
$lang['tfa']['confirm_tfa'] = "Please confirm your one-time password in the below field";
|
||||
$lang['tfa']['confirm'] = "Bestätigen";
|
||||
$lang['tfa']['otp'] = "Einmalpasswort";
|
||||
$lang['tfa']['trash_login'] = "Login verwerfen";
|
||||
$lang['tfa']['select'] = "Bitte auswählen";
|
||||
$lang['tfa']['waiting_usb_auth'] = "<i>Warte auf USB-Gerät...</i><br /><br />Bitte jetzt den vorgesehenen Taster des U2F USB-Gerätes berühren.";
|
||||
$lang['tfa']['waiting_usb_register'] = "<i>Warte auf USB-Gerät...</i><br /><br />Bitte zuerst das obere Passwortfeld ausfüllen und erst dann den vorgesehenen Taster des U2F USB-Gerätes berühren.";
|
||||
|
||||
$lang['admin']['search_domain_da'] = 'Domains durchsuchen';
|
||||
$lang['admin']['restrictions'] = 'Postifx Restriktionen';
|
||||
|
@ -340,9 +418,12 @@ $lang['admin']['msg_size_limit_details'] = 'Diese Einstellung wird Postfix und d
|
|||
$lang['admin']['save'] = 'Änderungen speichern';
|
||||
$lang['admin']['maintenance'] = 'Wartung und Information';
|
||||
$lang['admin']['sys_info'] = 'Systeminformation';
|
||||
$lang['admin']['dkim_add_key'] = 'DKIM-Record hinzufügen';
|
||||
$lang['admin']['dkim_keys'] = 'DKIM-Records';
|
||||
$lang['admin']['dkim_key_length'] = 'DKIM Schlüssellänge (Bits)';
|
||||
$lang['admin']['dkim_add_key'] = 'DKIM-Key hinzufügen';
|
||||
$lang['admin']['dkim_keys'] = 'DKIM-Keys';
|
||||
$lang['admin']['dkim_key_valid'] = 'Key gültig';
|
||||
$lang['admin']['dkim_key_unused'] = 'Key ohne Zuweisung';
|
||||
$lang['admin']['dkim_key_missing'] = 'Key fehlt';
|
||||
$lang['admin']['dkim_key_hint'] = 'Der Selector für DKIM-Keys lautet immer <code>dkim</code>.';
|
||||
$lang['admin']['add'] = 'Hinzufügen';
|
||||
$lang['admin']['configuration'] = 'Konfiguration';
|
||||
$lang['admin']['password'] = 'Passwort';
|
||||
|
|
|
@ -10,13 +10,13 @@ $lang['footer']['restart_sogo'] = 'Restart SOGo';
|
|||
$lang['footer']['restart_now'] = 'Restart now';
|
||||
$lang['footer']['restart_sogo_info'] = 'Some tasks, e.g. adding a domain, require you to restart SOGo to catch changes made in the mailcow UI.<br /><br /><b>Important:</b> A graceful restart may take a while to complete, please wait for it to finish.';
|
||||
$lang['dkim']['confirm'] = "Are you sure?";
|
||||
$lang['danger']['dkim_not_found'] = "DKIM record not found";
|
||||
$lang['danger']['dkim_remove_failed'] = "Cannot remove selected DKIM record";
|
||||
$lang['danger']['dkim_add_failed'] = "Cannot add given DKIM record";
|
||||
$lang['danger']['dkim_not_found'] = "DKIM key not found";
|
||||
$lang['danger']['dkim_remove_failed'] = "Cannot remove selected DKIM key";
|
||||
$lang['danger']['dkim_add_failed'] = "Cannot add given DKIM key";
|
||||
$lang['danger']['dkim_domain_or_sel_invalid'] = "DKIM domain or selector invalid";
|
||||
$lang['danger']['dkim_key_length_invalid'] = "DKIM key length invalid";
|
||||
$lang['success']['dkim_removed'] = "DKIM record has been removed";
|
||||
$lang['success']['dkim_added'] = "DKIM record has been saved";
|
||||
$lang['success']['dkim_removed'] = "DKIM key has been removed";
|
||||
$lang['success']['dkim_added'] = "DKIM key has been saved";
|
||||
$lang['danger']['access_denied'] = "Access denied or invalid form data";
|
||||
$lang['danger']['whitelist_from_invalid'] = "Whitelist entry invalid";
|
||||
$lang['danger']['domain_invalid'] = "Domain name is invalid";
|
||||
|
@ -24,6 +24,7 @@ $lang['danger']['mailbox_quota_exceeds_domain_quota'] = "Max. quota exceeds doma
|
|||
$lang['danger']['object_is_not_numeric'] = "Value %s is not numeric";
|
||||
$lang['success']['domain_added'] = "Added domain %s";
|
||||
$lang['danger']['alias_empty'] = "Alias address must not be empty";
|
||||
$lang['danger']['last_key'] = 'Last key cannot be deleted';
|
||||
$lang['danger']['goto_empty'] = "Goto address must not be empty";
|
||||
$lang['danger']['policy_list_from_exists'] = "A record with given name exists";
|
||||
$lang['danger']['policy_list_from_invalid'] = "Record has invalid format";
|
||||
|
@ -41,6 +42,8 @@ $lang['success']['alias_added'] = "Alias address/es has/have been added";
|
|||
$lang['success']['alias_modified'] = "Changes to alias have been saved";
|
||||
$lang['success']['aliasd_modified'] = "Changes to alias domain have been saved";
|
||||
$lang['success']['mailbox_modified'] = "Changes to mailbox %s have been saved";
|
||||
$lang['success']['resource_modified'] = "Changes to mailbox %s have been saved";
|
||||
$lang['success']['object_modified'] = "Changes to object %s have been saved";
|
||||
$lang['success']['msg_size_saved'] = "Message size limit has been set";
|
||||
$lang['danger']['aliasd_not_found'] = "Alias domain not found";
|
||||
$lang['danger']['targetd_not_found'] = "Target domain not found";
|
||||
|
@ -56,14 +59,13 @@ $lang['danger']['exit_code_not_null'] = "Error: Exit code was %d";
|
|||
$lang['danger']['mailbox_not_available'] = "Mailbox not available";
|
||||
$lang['danger']['username_invalid'] = "Username cannot be used";
|
||||
$lang['danger']['password_mismatch'] = "Confirmation password is not identical";
|
||||
$lang['danger']['password_complexity'] = "Password does not meet requirements";
|
||||
$lang['danger']['password_complexity'] = "Password does not meet the policy";
|
||||
$lang['danger']['password_empty'] = "Password must not be empty";
|
||||
$lang['danger']['login_failed'] = "Login failed";
|
||||
$lang['danger']['mailbox_invalid'] = "Mailbox name is invalid";
|
||||
$lang['danger']['description_invalid'] = 'Resource description is invalid';
|
||||
$lang['danger']['resource_invalid'] = "Resource name is invalid";
|
||||
$lang['danger']['mailbox_invalid_suggest'] = 'Mailbox name is invalid, did you mean to type "%s"?';
|
||||
$lang['info']['fetchmail_planned'] = "Task to fetch emails has been planned. Please check the process at a later time.";
|
||||
$lang['danger']['fetchmail_source_empty'] = "Please define a source folder";
|
||||
$lang['danger']['fetchmail_dest_empty'] = "Please define a target folder";
|
||||
$lang['danger']['is_alias'] = "%s is already known as an alias address";
|
||||
$lang['danger']['is_alias_or_mailbox'] = "%s is already known as an alias or a mailbox";
|
||||
$lang['danger']['is_spam_alias'] = "%s is already known as a spam alias address";
|
||||
|
@ -73,11 +75,14 @@ $lang['danger']['max_mailbox_exceeded'] = "Max. mailboxes exceeded (%d of %d)";
|
|||
$lang['danger']['mailbox_quota_exceeded'] = "Quota exceeds the domain limit (max. %d MiB)";
|
||||
$lang['danger']['mailbox_quota_left_exceeded'] = "Not enough space left (space left: %d MiB)";
|
||||
$lang['success']['mailbox_added'] = "Mailbox %s has been added";
|
||||
$lang['success']['resource_added'] = "Resource %s has been added";
|
||||
$lang['success']['domain_removed'] = "Domain %s has been removed";
|
||||
$lang['success']['alias_removed'] = "Alias-Adresse %s has been removed";
|
||||
$lang['success']['alias_domain_removed'] = "Alias domain %s has been removed";
|
||||
$lang['success']['domain_admin_removed'] = "Domain administrator %s has been removed";
|
||||
$lang['success']['mailbox_removed'] = "Mailbox %s has been removed";
|
||||
$lang['success']['eas_reset'] = "ActiveSync devices for user %s were reset";
|
||||
$lang['success']['resource_removed'] = "Resource %s has been removed";
|
||||
$lang['danger']['max_quota_in_use'] = "Mailbox quota must be greater or equal to %d MiB";
|
||||
$lang['danger']['domain_quota_m_in_use'] = "Domain quota must be greater or equal to %s MiB";
|
||||
$lang['danger']['mailboxes_in_use'] = "Max. mailboxes must be greater or equal to %d";
|
||||
|
@ -86,34 +91,38 @@ $lang['danger']['sender_acl_invalid'] = "Sender ACL value is invalid";
|
|||
$lang['danger']['domain_not_empty'] = "Cannot remove non-empty domain";
|
||||
$lang['warning']['spam_alias_temp_error'] = "Temporary error: Cannot add spam alias, please try again later.";
|
||||
$lang['danger']['spam_alias_max_exceeded'] = "Max. allowed spam alias addresses exceeded";
|
||||
$lang['danger']['fetchmail_active'] = "A process is already running, please wait for it to finish.";
|
||||
$lang['danger']['validity_missing'] = 'Please assign a period of validity';
|
||||
$lang['user']['on'] = "On";
|
||||
$lang['user']['off'] = "Off";
|
||||
$lang['user']['messages'] = "messages"; // "123 messages"
|
||||
$lang['user']['in_use'] = "Used";
|
||||
$lang['user']['user_change_fn'] = "";
|
||||
$lang['user']['user_settings'] = 'User settings';
|
||||
$lang['user']['mailbox_settings'] = 'Mailbox settings';
|
||||
$lang['user']['mailbox_details'] = 'Mailbox details';
|
||||
$lang['user']['change_password'] = 'Change password';
|
||||
$lang['user']['new_password'] = 'New password:';
|
||||
$lang['user']['new_password'] = 'New password';
|
||||
$lang['user']['save_changes'] = 'Save changes';
|
||||
$lang['user']['password_now'] = 'Current password (confirm changes):';
|
||||
$lang['user']['new_password_repeat'] = 'Confirmation password (repeat):';
|
||||
$lang['user']['password_now'] = 'Current password (confirm changes)';
|
||||
$lang['user']['new_password_repeat'] = 'Confirmation password (repeat)';
|
||||
$lang['user']['new_password_description'] = 'Requirement: 6 characters long, letters and numbers.';
|
||||
$lang['user']['did_you_know'] = '<b>Did you know?</b> You can use tags in your email address ("me+<b>privat</b>@example.com") to move messages to a folder automatically (example: "privat").';
|
||||
$lang['user']['spam_aliases'] = 'Temporary email aliases';
|
||||
$lang['user']['alias'] = 'Alias';
|
||||
$lang['user']['aliases'] = 'Aliases';
|
||||
$lang['user']['domain_aliases'] = 'Domain alias addresses';
|
||||
$lang['user']['is_catch_all'] = 'Catch-all for domain/s';
|
||||
$lang['user']['aliases_also_send_as'] = 'Also allowed to send as';
|
||||
$lang['user']['aliases_send_as_all'] = 'Do not check sender access for following domains';
|
||||
$lang['user']['aliases_also_send_as'] = 'Also allowed to send as user';
|
||||
$lang['user']['aliases_send_as_all'] = 'Do not check sender access for the following domain(s) and its alias domains';
|
||||
$lang['user']['alias_create_random'] = 'Generate random alias';
|
||||
$lang['user']['alias_extend_all'] = 'Extend aliases by 1 hour';
|
||||
$lang['user']['alias_valid_until'] = 'Valid until';
|
||||
$lang['user']['alias_remove_all'] = 'Remove all aliases';
|
||||
$lang['user']['alias_time_left'] = 'Time left';
|
||||
$lang['user']['alias_full_date'] = 'd.m.Y, H:i:s T';
|
||||
$lang['user']['syncjob_full_date'] = 'd.m.Y, H:i:s T';
|
||||
$lang['user']['alias_select_validity'] = 'Period of validity';
|
||||
$lang['user']['sync_jobs'] = 'Sync jobs';
|
||||
$lang['user']['hour'] = 'Hour';
|
||||
$lang['user']['hours'] = 'Hours';
|
||||
$lang['user']['day'] = 'Day';
|
||||
|
@ -136,12 +145,13 @@ $lang['user']['spamfilter_yellow'] = 'Yellow: this message may be spam, will be
|
|||
$lang['user']['spamfilter_red'] = 'Red: This message is spam and will be rejected by the server';
|
||||
$lang['user']['spamfilter_default_score'] = 'Default values:';
|
||||
$lang['user']['spamfilter_hint'] = 'The first value describes the "low spam score", the second represents the "high spam score".';
|
||||
$lang['user']['spamfilter_table_domain_policy'] = "n/a (domain policy)";
|
||||
|
||||
$lang['user']['tls_policy_warning'] = '<strong>Warning:</strong> If you decide to enforce encrypted mail transfer, you may lose emails.<br />Messages to not satisfy the policy will be bounced with a hard fail by the mail system.';
|
||||
$lang['user']['tls_policy'] = 'Encryption policy';
|
||||
$lang['user']['tls_enforce_in'] = 'Enforce TLS incoming';
|
||||
$lang['user']['tls_enforce_out'] = 'Enforce TLS outgoing';
|
||||
$lang['user']['no_record'] = 'No Record';
|
||||
$lang['user']['no_record'] = 'No record';
|
||||
|
||||
$lang['user']['misc_settings'] = 'Other profile settings';
|
||||
$lang['user']['misc_delete_profile'] = 'Other profile settings';
|
||||
|
@ -152,6 +162,22 @@ $lang['user']['tag_in_subject'] = 'In subject';
|
|||
$lang['user']['tag_help_explain'] = 'In subfolder: a new subfolder named after the tag will be created below INBOX ("INBOX/Facebook").<br />
|
||||
In subject: the tags name will be prepended to the mails subject, example: "[Facebook] Meine Neuigkeiten".';
|
||||
$lang['user']['tag_help_example'] = 'Example for a tagged email address: ich<b>+Facebook</b>@example.org';
|
||||
$lang['user']['eas_reset'] = 'Reset ActiveSync device cache';
|
||||
$lang['user']['eas_reset_now'] = 'Reset now';
|
||||
$lang['user']['eas_reset_help'] = 'In many cases a device cache reset will help to recover a broken ActiveSync profile.<br /><b>Attention:</b> All elements will be redownloaded!';
|
||||
|
||||
$lang['user']['encryption'] = 'Encyrption';
|
||||
$lang['user']['username'] = 'Username';
|
||||
$lang['user']['password'] = 'Password';
|
||||
$lang['user']['last_run'] = 'Last run';
|
||||
$lang['user']['excludes'] = 'Excludes';
|
||||
$lang['user']['interval'] = 'Interval';
|
||||
$lang['user']['active'] = 'Active';
|
||||
$lang['user']['action'] = 'Action';
|
||||
$lang['user']['edit'] = 'Edit';
|
||||
$lang['user']['remove'] = 'Remove';
|
||||
$lang['user']['delete_now'] = 'Remove now';
|
||||
$lang['user']['create_syncjob'] = 'Create new sync job';
|
||||
|
||||
$lang['start']['dashboard'] = '%s - dashboard';
|
||||
$lang['start']['start_rc'] = 'Open Roundcube';
|
||||
|
@ -182,12 +208,19 @@ $lang['header']['mailboxes'] = 'Mailboxes';
|
|||
$lang['header']['user_settings'] = 'User settings';
|
||||
$lang['header']['login'] = 'Login';
|
||||
$lang['header']['logged_in_as_logout'] = 'Logged in as <b>%s</b> (logout)';
|
||||
$lang['header']['logged_in_as_logout_dual'] = 'Logged in as <b>%s <span class="text-info">[%s]</span></b>';
|
||||
$lang['header']['locale'] = 'Language';
|
||||
$lang['mailbox']['domain'] = 'Domain';
|
||||
$lang['mailbox']['spam_aliases'] = 'Temp. alias';
|
||||
$lang['mailbox']['multiple_bookings'] = 'Multiple bookings';
|
||||
$lang['mailbox']['kind'] = 'Kind';
|
||||
$lang['mailbox']['description'] = 'Description';
|
||||
$lang['mailbox']['alias'] = 'Alias';
|
||||
$lang['mailbox']['resource_name'] = 'Resource name';
|
||||
$lang['mailbox']['aliases'] = 'Aliases';
|
||||
$lang['mailbox']['domains'] = 'Domains';
|
||||
$lang['mailbox']['mailboxes'] = 'Mailboxes';
|
||||
$lang['mailbox']['resources'] = 'Resources';
|
||||
$lang['mailbox']['mailbox_quota'] = 'Max. size of a mailbox';
|
||||
$lang['mailbox']['domain_quota'] = 'Quota';
|
||||
$lang['mailbox']['active'] = 'Active';
|
||||
|
@ -208,37 +241,53 @@ $lang['mailbox']['msg_num'] = 'Message #';
|
|||
$lang['mailbox']['remove'] = 'Remove';
|
||||
$lang['mailbox']['edit'] = 'Edit';
|
||||
$lang['mailbox']['archive'] = 'Archive';
|
||||
$lang['mailbox']['no_record'] = 'No Record';
|
||||
$lang['mailbox']['no_record'] = 'No record for object %s';
|
||||
$lang['mailbox']['no_record_single'] = 'No record';
|
||||
$lang['mailbox']['add_domain'] = 'Add domain';
|
||||
$lang['mailbox']['add_domain_alias'] = 'Add domain alias';
|
||||
$lang['mailbox']['add_mailbox'] = 'Add mailbox';
|
||||
$lang['mailbox']['add_resource'] = 'Add resource';
|
||||
$lang['mailbox']['add_alias'] = 'Add alias';
|
||||
$lang['mailbox']['add_domain_record_first'] = 'Please add a domain first';
|
||||
|
||||
$lang['info']['no_action'] = 'No action applicable';
|
||||
|
||||
$lang['delete']['title'] = 'Remove object';
|
||||
$lang['delete']['remove_domain_warning'] = '<b>Warning:</b> You are about to remove the domain <b>%s</b>!';
|
||||
$lang['delete']['remove_syncjob_warning'] = '<b>Warning:</b> You are about to remove a sync job for user <b>%s</b>!';
|
||||
$lang['delete']['remove_domainalias_warning'] = '<b>Warning:</b> You are about to remove the domain alias <b>%s</b>!';
|
||||
$lang['delete']['remove_domainadmin_warning'] = '<b>Warning:</b> You are about to remove the domain administrator <b>%s</b>!';
|
||||
$lang['delete']['remove_alias_warning'] = '<b>Warning:</b> You are about to remove the alias address <b>%s</b>!';
|
||||
$lang['delete']['remove_mailbox_warning'] = '<b>Warning:</b> You are about to remove the mailbox <b>%s</b>!';
|
||||
$lang['delete']['remove_mailbox_details'] = 'The mailbox will be <b>purged permanently</b>!';
|
||||
$lang['delete']['remove_resource_warning'] = '<b>Warning:</b> You are about to remove the resource <b>%s</b>!';
|
||||
$lang['delete']['remove_resource_details'] = 'The resource will be <b>purged permanently</b>!';
|
||||
$lang['delete']['remove_domain_details'] = 'This also removes domain aliases.<br /><br /><b>A domain must be empty to be removed.</b>';
|
||||
$lang['delete']['remove_syncjob_details'] = 'Objects from this sync job will not be pulled from the remote server anymore.';
|
||||
$lang['delete']['remove_alias_details'] = 'Users will no longer be able to receive mail for or send mail from this address.</b>';
|
||||
$lang['delete']['remove_button'] = 'Remove';
|
||||
$lang['delete']['previous'] = 'Previous page';
|
||||
|
||||
$lang['edit']['syncjob'] = 'Edit sync job';
|
||||
$lang['edit']['save'] = 'Save changes';
|
||||
$lang['edit']['username'] = 'Username';
|
||||
$lang['edit']['hostname'] = 'Hostname';
|
||||
$lang['edit']['encryption'] = 'Encryption';
|
||||
$lang['edit']['maxage'] = 'Maximum age of messages in days that will be polled from remote<br /><small>(0 = ignore age)</small>';
|
||||
$lang['edit']['subfolder2'] = 'Sync into subfolder on destination<br /><small>(empty = do not use subfolder)</small>';
|
||||
$lang['edit']['mins_interval'] = 'Interval (min)';
|
||||
$lang['edit']['exclude'] = 'Exclude objects (regex)';
|
||||
$lang['edit']['save'] = 'Save changes';
|
||||
$lang['edit']['archive'] = 'Archive access';
|
||||
$lang['edit']['max_mailboxes'] = 'Max. possible mailboxes:';
|
||||
$lang['edit']['max_mailboxes'] = 'Max. possible mailboxes';
|
||||
$lang['edit']['title'] = 'Edit object';
|
||||
$lang['edit']['target_address'] = 'Goto address/es <small>(comma-separated)</small>:';
|
||||
$lang['edit']['target_address'] = 'Goto address/es <small>(comma-separated)</small>';
|
||||
$lang['edit']['active'] = 'Active';
|
||||
$lang['edit']['target_domain'] = 'Target domain:';
|
||||
$lang['edit']['password'] = 'Password:';
|
||||
$lang['edit']['ratelimit'] = 'Outgoing rate limit/h:';
|
||||
$lang['edit']['target_domain'] = 'Target domain';
|
||||
$lang['edit']['password'] = 'Password';
|
||||
$lang['edit']['ratelimit'] = 'Outgoing rate limit/h';
|
||||
$lang['danger']['ratelimt_less_one'] = 'Outgoing rate limit/h must not be less than 1';
|
||||
$lang['edit']['password_repeat'] = 'Confirmation password (repeat):';
|
||||
$lang['edit']['password_repeat'] = 'Confirmation password (repeat)';
|
||||
$lang['edit']['domain_admin'] = 'Edit domain administrator';
|
||||
$lang['edit']['domain'] = 'Edit domain';
|
||||
$lang['edit']['alias_domain'] = 'Alias domain';
|
||||
|
@ -247,14 +296,14 @@ $lang['edit']['domains'] = 'Domains';
|
|||
$lang['edit']['destroy'] = 'Manual data input';
|
||||
$lang['edit']['alias'] = 'Edit alias';
|
||||
$lang['edit']['mailbox'] = 'Edit mailbox';
|
||||
$lang['edit']['description'] = 'Description:';
|
||||
$lang['edit']['max_aliases'] = 'Max. aliases:';
|
||||
$lang['edit']['max_quota'] = 'Max. quota per mailbox (MiB):';
|
||||
$lang['edit']['domain_quota'] = 'Domain quota:';
|
||||
$lang['edit']['backup_mx_options'] = 'Backup MX options:';
|
||||
$lang['edit']['description'] = 'Description';
|
||||
$lang['edit']['max_aliases'] = 'Max. aliases';
|
||||
$lang['edit']['max_quota'] = 'Max. quota per mailbox (MiB)';
|
||||
$lang['edit']['domain_quota'] = 'Domain quota';
|
||||
$lang['edit']['backup_mx_options'] = 'Backup MX options';
|
||||
$lang['edit']['relay_domain'] = 'Relay domain';
|
||||
$lang['edit']['relay_all'] = 'Relay all recipients';
|
||||
$lang['edit']['dkim_signature'] = 'DKIM signature:';
|
||||
$lang['edit']['dkim_signature'] = 'DKIM signature';
|
||||
$lang['edit']['dkim_record_info'] = '<small>Please add a TXT record with the given value to your DNS settings.</small>';
|
||||
$lang['edit']['relay_all_info'] = '<small>If you choose <b>not</b> to relay all recipients, you will need to add a ("blind") mailbox for every single recipient that should be relayed.</small>';
|
||||
$lang['edit']['full_name'] = 'Full name';
|
||||
|
@ -265,14 +314,30 @@ $lang['edit']['dkim_txt_name'] = 'TXT record name:';
|
|||
$lang['edit']['dkim_txt_value'] = 'TXT record value:';
|
||||
$lang['edit']['previous'] = 'Previous page';
|
||||
$lang['edit']['unchanged_if_empty'] = 'If unchanged leave blank';
|
||||
$lang['edit']['dont_check_sender_acl'] = 'Do not check sender for domain %s';
|
||||
$lang['edit']['dont_check_sender_acl'] = "Disable sender check for domain %s + alias domains";
|
||||
$lang['edit']['multiple_bookings'] = 'Multiple bookings';
|
||||
$lang['edit']['kind'] = 'Kind';
|
||||
$lang['edit']['resource'] = 'Resource';
|
||||
|
||||
$lang['add']['syncjob'] = 'Add sync job';
|
||||
$lang['add']['syncjob_hint'] = 'Be aware that passwords need to be saved plain-text!';
|
||||
$lang['add']['hostname'] = 'Hostname';
|
||||
$lang['add']['username'] = 'Username';
|
||||
$lang['add']['enc_method'] = 'Encryption method';
|
||||
$lang['add']['mins_interval'] = 'Polling interval (minutes)';
|
||||
$lang['add']['maxage'] = 'Maximum age of messages that will be polled from remote (0 = ignore age)';
|
||||
$lang['add']['subfolder2'] = 'Sync into subfolder on destination';
|
||||
$lang['add']['exclude'] = 'Exclude objects (regex)';
|
||||
$lang['add']['delete2duplicates'] = 'Delete duplicates on destination';
|
||||
|
||||
$lang['add']['title'] = 'Add object';
|
||||
$lang['add']['domain'] = 'Domain';
|
||||
$lang['add']['active'] = 'Active';
|
||||
$lang['add']['multiple_bookings'] = 'Multiple bookings';
|
||||
$lang['add']['save'] = 'Save changes';
|
||||
$lang['add']['description'] = 'Description:';
|
||||
$lang['add']['max_aliases'] = 'Max. possible aliases:';
|
||||
$lang['add']['resource_name'] = 'Resource name';
|
||||
$lang['add']['max_mailboxes'] = 'Max. possible mailboxes:';
|
||||
$lang['add']['mailbox_quota_m'] = 'Max. quota per mailbox (MiB):';
|
||||
$lang['add']['domain_quota_m'] = 'Total domain quota (MiB):';
|
||||
|
@ -291,6 +356,8 @@ $lang['add']['alias_domain'] = 'Alias domain';
|
|||
$lang['add']['select'] = 'Please select...';
|
||||
$lang['add']['target_domain'] = 'Target domain:';
|
||||
$lang['add']['mailbox'] = 'Mailbox';
|
||||
$lang['add']['resource'] = 'Resource';
|
||||
$lang['add']['kind'] = 'Kind';
|
||||
$lang['add']['mailbox_username'] = 'Username (left part of an email address):';
|
||||
$lang['add']['full_name'] = 'Full name:';
|
||||
$lang['add']['quota_mb'] = 'Quota (MiB):';
|
||||
|
@ -312,11 +379,24 @@ $lang['login']['login'] = 'Login';
|
|||
$lang['login']['previous'] = "Previous page";
|
||||
$lang['login']['delayed'] = 'Login was delayed by %s seconds.';
|
||||
|
||||
$lang['login']['tfa'] = "Two-factor authentication";
|
||||
$lang['login']['tfa_details'] = "Please confirm your one-time password in the below field";
|
||||
$lang['login']['confirm'] = "Confirm";
|
||||
$lang['login']['otp'] = "One-time password";
|
||||
$lang['login']['trash_login'] = "Trash login";
|
||||
$lang['tfa']['tfa'] = "Two-factor authentication";
|
||||
$lang['tfa']['set_tfa'] = "Set two-factor authentication method";
|
||||
$lang['tfa']['yubi_otp'] = "Yubico OTP authentication";
|
||||
$lang['tfa']['key_id'] = "An identifier for your YubiKey";
|
||||
$lang['tfa']['api_register'] = 'mailcow uses the Yubico Cloud API. Please get an API key for your key <a href="https://upgrade.yubico.com/getapikey/" target="_blank">here</a>';
|
||||
$lang['tfa']['u2f'] = "U2F authentication";
|
||||
$lang['tfa']['hotp'] = "HOTP authentication";
|
||||
$lang['tfa']['totp'] = "TOTP authentication";
|
||||
$lang['tfa']['none'] = "Deaktiviert";
|
||||
$lang['tfa']['delete_tfa'] = "Disable TFA";
|
||||
$lang['tfa']['disable_tfa'] = "Disable TFA until next successful login";
|
||||
$lang['tfa']['confirm_tfa'] = "Please confirm your one-time password in the below field";
|
||||
$lang['tfa']['confirm'] = "Confirm";
|
||||
$lang['tfa']['otp'] = "One-time password";
|
||||
$lang['tfa']['trash_login'] = "Trash login";
|
||||
$lang['tfa']['select'] = "Please select";
|
||||
$lang['tfa']['waiting_usb_auth'] = "<i>Waiting for USB device...</i><br /><br />Please tap the button on your U2F USB device now.";
|
||||
$lang['tfa']['waiting_usb_register'] = "<i>Waiting for USB device...</i><br /><br />Please enter your password above and confirm your U2F registration by tapping the button on your U2F USB device.";
|
||||
|
||||
$lang['admin']['search_domain_da'] = 'Search domains';
|
||||
$lang['admin']['restrictions'] = 'Postifx Restrictions';
|
||||
|
@ -340,6 +420,10 @@ $lang['admin']['privacy_anon_mail'] = 'Anonymize outgoing mail';
|
|||
$lang['admin']['dkim_txt_name'] = 'TXT record name:';
|
||||
$lang['admin']['dkim_txt_value'] = 'TXT record value:';
|
||||
$lang['admin']['dkim_key_length'] = 'DKIM key length (bits)';
|
||||
$lang['admin']['dkim_key_valid'] = 'Key valid';
|
||||
$lang['admin']['dkim_key_unused'] = 'Key unused';
|
||||
$lang['admin']['dkim_key_missing'] = 'Key missing';
|
||||
$lang['admin']['dkim_key_hint'] = 'Selector for DKIM keys is always <code>dkim</code>.';
|
||||
$lang['admin']['previous'] = 'Previous page';
|
||||
$lang['admin']['quota_mb'] = 'Quota (MiB):';
|
||||
$lang['admin']['sender_acl'] = 'Allow to send as:';
|
||||
|
@ -349,8 +433,8 @@ $lang['admin']['msg_size_limit_details'] = 'Applying a new limit will reload Pos
|
|||
$lang['admin']['save'] = 'Save changes';
|
||||
$lang['admin']['maintenance'] = 'Maintenance and Information';
|
||||
$lang['admin']['sys_info'] = 'System information';
|
||||
$lang['admin']['dkim_add_key'] = 'Add DKIM record';
|
||||
$lang['admin']['dkim_keys'] = 'DKIM records';
|
||||
$lang['admin']['dkim_add_key'] = 'Add DKIM key';
|
||||
$lang['admin']['dkim_keys'] = 'DKIM keys';
|
||||
$lang['admin']['add'] = 'Add';
|
||||
$lang['admin']['configuration'] = 'Configuration';
|
||||
$lang['admin']['password'] = 'Password';
|
||||
|
@ -374,5 +458,5 @@ $lang['admin']['invalid_max_msg_size'] = 'Invalid max. message size';
|
|||
$lang['admin']['site_not_found'] = 'Cannot locate mailcow site configuration';
|
||||
$lang['admin']['public_folder_empty'] = 'Public folder name must not be empty';
|
||||
$lang['admin']['set_rr_failed'] = 'Cannot set Postfix restrictions';
|
||||
$lang['admin']['no_record'] = 'No Record';
|
||||
$lang['admin']['no_record'] = 'No record';
|
||||
?>
|
||||
|
|
|
@ -0,0 +1,378 @@
|
|||
<?php
|
||||
/*
|
||||
//
|
||||
// Spanish language file
|
||||
//
|
||||
*/
|
||||
$lang['footer']['loading'] = "Espere Por Favor...";
|
||||
$lang['header']['restart_sogo'] = 'Reiniciar SOGo';
|
||||
$lang['footer']['restart_sogo'] = 'Reiniciar SOGo';
|
||||
$lang['footer']['restart_now'] = 'Reiniciar ahora';
|
||||
$lang['footer']['restart_sogo_info'] = 'Algunas tareas, por ejemplo agregar un dominio, requieren que reinicies SOGo para detectar cambios hechos en la UI de mailcow.<br /><br /><b>Importante:</b> Un reinicio sencillo puede tardar un poco en completarse, por favor espere a que termine.';
|
||||
$lang['dkim']['confirm'] = "¿Estás Seguro?";
|
||||
$lang['danger']['dkim_not_found'] = "Registro DKIM no encontrado";
|
||||
$lang['danger']['dkim_remove_failed'] = "No se puede eliminar el registro DKIM seleccionado";
|
||||
$lang['danger']['dkim_add_failed'] = "No se puede agregar el registro DKIM dado";
|
||||
$lang['danger']['dkim_domain_or_sel_invalid'] = "Dominio DKIM ó selector inválido";
|
||||
$lang['danger']['dkim_key_length_invalid'] = "Longitud de la llave DKIM inválida";
|
||||
$lang['success']['dkim_removed'] = "Registro DKIM removido";
|
||||
$lang['success']['dkim_added'] = "Registro DKIM guardado";
|
||||
$lang['danger']['access_denied'] = "Acceso denegado o datos del formulario inválidos";
|
||||
$lang['danger']['whitelist_from_invalid'] = "Entrada de la lista blanca inválida";
|
||||
$lang['danger']['domain_invalid'] = "Nombre de dominio inválido";
|
||||
$lang['danger']['mailbox_quota_exceeds_domain_quota'] = "Cuota máx. excede el limite de cuota del dominio";
|
||||
$lang['danger']['object_is_not_numeric'] = "El valor %s no es numérico";
|
||||
$lang['success']['domain_added'] = "Dominio agregado %s";
|
||||
$lang['danger']['alias_empty'] = "Dirección alias no debe estar vacía";
|
||||
$lang['danger']['goto_empty'] = "Dirección \"goto\" no debe estar vacía";
|
||||
$lang['danger']['policy_list_from_exists'] = "Un registro con ese nombre ya existe";
|
||||
$lang['danger']['policy_list_from_invalid'] = "El registro tiene formato inválido";
|
||||
$lang['danger']['whitelist_exists'] = "Ya existe un registro con ese nombre en la lista blanca";
|
||||
$lang['danger']['whitelist_from_invalid'] = "Formato inválido para el registro de lista blanca";
|
||||
$lang['danger']['alias_invalid'] = "Dirección alias inválida";
|
||||
$lang['danger']['goto_invalid'] = "Dirección \"goto\" inválida";
|
||||
$lang['danger']['alias_domain_invalid'] = "El dominio alias es inválido";
|
||||
$lang['danger']['target_domain_invalid'] = "El dominio \"goto\" es inválido";
|
||||
$lang['danger']['object_exists'] = "El objeto %s ya existe";
|
||||
$lang['danger']['domain_exists'] = "El dominio %s ya existe";
|
||||
$lang['danger']['alias_goto_identical'] = "Las direcciones alias y \"goto\" no deben ser idénticas";
|
||||
$lang['danger']['aliasd_targetd_identical'] = "El dominio alias no debe ser igual al dominio destino";
|
||||
$lang['success']['alias_added'] = "Dirección/es alias ha/han sidgo agregada";
|
||||
$lang['success']['alias_modified'] = "Cambios al alias guardados";
|
||||
$lang['success']['aliasd_modified'] = "Cambios al dominio alias guardados";
|
||||
$lang['success']['mailbox_modified'] = "Cambios al buzón %s guardados";
|
||||
$lang['success']['msg_size_saved'] = "Limite del mensaje establecido";
|
||||
$lang['danger']['aliasd_not_found'] = "Dominio alias no encontrado";
|
||||
$lang['danger']['targetd_not_found'] = "Dominio destino no encontrado";
|
||||
$lang['danger']['aliasd_exists'] = "Dominio alias ya existe";
|
||||
$lang['success']['aliasd_added'] = "Agregado dominio alias %s";
|
||||
$lang['success']['aliasd_modified'] = "Cambios al dominio alias %s guardados";
|
||||
$lang['success']['domain_modified'] = "Cambios al dominio %s guardados";
|
||||
$lang['success']['domain_admin_modified'] = "Cambios al administrador del dominio %s guardados";
|
||||
$lang['success']['domain_admin_added'] = "Administrador del dominio %s agregado";
|
||||
$lang['success']['changes_general'] = 'Cambios guardados';
|
||||
$lang['success']['admin_modified'] = "Cambios al administrador guardados";
|
||||
$lang['danger']['exit_code_not_null'] = "Error: Código de salida es %d";
|
||||
$lang['danger']['mailbox_not_available'] = "Buzón no disponible";
|
||||
$lang['danger']['username_invalid'] = "Nombre de usuario no se puede utilizar";
|
||||
$lang['danger']['password_mismatch'] = "Confirmación de contraseña no es identica";
|
||||
$lang['danger']['password_complexity'] = "La contraseña no cumple con los requisitos";
|
||||
$lang['danger']['password_empty'] = "El campo de la contraseña no debe estar vacío";
|
||||
$lang['danger']['login_failed'] = "Inicio de sesión fallido";
|
||||
$lang['danger']['mailbox_invalid'] = "Nombre de buzón inválido";
|
||||
$lang['danger']['mailbox_invalid_suggest'] = 'El nombre del buzón es inválido, ¿pretendías escribir "%s"?';
|
||||
$lang['info']['fetchmail_planned'] = "La tarea para buscar correos se ha planeado. Por favor verifica el proceso más tarde.";
|
||||
$lang['danger']['fetchmail_source_empty'] = "Por favor define una carpeta fuente";
|
||||
$lang['danger']['fetchmail_dest_empty'] = "Por favor define una carpeta destino";
|
||||
$lang['danger']['is_alias'] = "%s ya está definida como una dirección alias";
|
||||
$lang['danger']['is_alias_or_mailbox'] = "%s ya está definido como un alias ó como un buzón";
|
||||
$lang['danger']['is_spam_alias'] = "%s ya está definida como una dirección alias de correo no deseado";
|
||||
$lang['danger']['quota_not_0_not_numeric'] = "Cuota debe ser numérica y >= 0";
|
||||
$lang['danger']['domain_not_found'] = "Dominio no encontrado.";
|
||||
$lang['danger']['max_mailbox_exceeded'] = "Máx. de buzones superado (%d de %d)";
|
||||
$lang['danger']['mailbox_quota_exceeded'] = "Cuota excede el límite de dominio (máx. %d MiB)";
|
||||
$lang['danger']['mailbox_quota_left_exceeded'] = "No queda espacio suficiente (espacio libre: %d MiB)";
|
||||
$lang['success']['mailbox_added'] = "Buzón %s agregado";
|
||||
$lang['success']['domain_removed'] = "Dominio %s removido";
|
||||
$lang['success']['alias_removed'] = "Dirección alias %s removida";
|
||||
$lang['success']['alias_domain_removed'] = "Dominio alias %s removido";
|
||||
$lang['success']['domain_admin_removed'] = "Administrador del dominio %s removido";
|
||||
$lang['success']['mailbox_removed'] = "Buzón %s removido";
|
||||
$lang['danger']['max_quota_in_use'] = "Cuota del buzón debe ser mayor o igual a %d MiB";
|
||||
$lang['danger']['domain_quota_m_in_use'] = "Cuota del dominio debe ser mayor o igual a %d MiB";
|
||||
$lang['danger']['mailboxes_in_use'] = "Máx. de buzones debe ser mayor o igual a %d";
|
||||
$lang['danger']['aliases_in_use'] = "Máx. de alias debe ser mayor o igual a %d";
|
||||
$lang['danger']['sender_acl_invalid'] = "Valor del remitente ACL inválido";
|
||||
$lang['danger']['domain_not_empty'] = "No se puede eliminar un dominio que no esté vacío";
|
||||
$lang['warning']['spam_alias_temp_error'] = "Error temporal: No se puede agregar ese \"spam alias\", inténtelo más tarde.";
|
||||
$lang['danger']['spam_alias_max_exceeded'] = "Máx. direcciones \"spam alias\" permitidas excedido";
|
||||
$lang['danger']['fetchmail_active'] = "Un proceso ya se está ejecutando, por favor espera a que termine.";
|
||||
$lang['danger']['validity_missing'] = 'Por favor asigna un periodo de validez';
|
||||
$lang['user']['off'] = "Apagado";
|
||||
$lang['user']['user_change_fn'] = "";
|
||||
$lang['user']['user_settings'] = 'Configuración del usuario';
|
||||
$lang['user']['mailbox_settings'] = 'Configuración del buzón';
|
||||
$lang['user']['mailbox_details'] = 'Detalles del buzón';
|
||||
$lang['user']['change_password'] = 'Cambiar contraseña';
|
||||
$lang['user']['new_password'] = 'Nueva contraseña:';
|
||||
$lang['user']['save_changes'] = 'Guardar cambios';
|
||||
$lang['user']['password_now'] = 'Contraseña actual (confirmar cambios):';
|
||||
$lang['user']['new_password_repeat'] = 'Confirmación de contraseña (repetir):';
|
||||
$lang['user']['new_password_description'] = 'Requisitos: longitud de 6 caracteres, letras y números.';
|
||||
$lang['user']['did_you_know'] = '<b>¿Sabías qué?</b> Puedes utilizar etiquetas en tu dirección email ("me+<b>privat</b>@example.com") para mover mensajes a una carpeta automáticamente (ejemplo: "privat").';
|
||||
$lang['user']['spam_aliases'] = 'Alias de email temporales';
|
||||
$lang['user']['alias'] = 'Alias';
|
||||
$lang['user']['aliases'] = 'Alias';
|
||||
$lang['user']['is_catch_all'] = 'Atrapa-Todo para el/los dominio/s';
|
||||
$lang['user']['aliases_also_send_as'] = 'También permitido para mandarse como';
|
||||
$lang['user']['aliases_send_as_all'] = 'No verifiques el acceso del remitente para los siguientes dominios';
|
||||
$lang['user']['alias_create_random'] = 'Generar alias aleatorio';
|
||||
$lang['user']['alias_extend_all'] = 'Extender alias por 1 hora';
|
||||
$lang['user']['alias_valid_until'] = 'Válido hasta';
|
||||
$lang['user']['alias_remove_all'] = 'Eliminar todos los alias';
|
||||
$lang['user']['alias_time_left'] = 'Tiempo restante';
|
||||
$lang['user']['alias_full_date'] = 'd.m.Y, H:i:s T';
|
||||
$lang['user']['alias_select_validity'] = 'Periodo de validez';
|
||||
$lang['user']['hour'] = 'Hora';
|
||||
$lang['user']['hours'] = 'Horas';
|
||||
$lang['user']['day'] = 'Día';
|
||||
$lang['user']['week'] = 'Semana';
|
||||
$lang['user']['weeks'] = 'Semanas';
|
||||
$lang['user']['spamfilter'] = 'Filtro de spam';
|
||||
$lang['user']['spamfilter_wl'] = 'Lista blanca';
|
||||
$lang['user']['spamfilter_wl_desc'] = 'Direcciones en la lista blanca <b>nunca</b> clasificarán como spam. Probablemente se usará un comodín.';
|
||||
$lang['user']['spamfilter_bl'] = 'Lista negra';
|
||||
$lang['user']['spamfilter_bl_desc'] = 'Direcciones en la lista negra <b>siempre</b> clasificarán como spam. Probablemente se usará un comodín.';
|
||||
$lang['user']['spamfilter_behavior'] = 'Clasificación';
|
||||
$lang['user']['spamfilter_table_rule'] = 'Regla';
|
||||
$lang['user']['spamfilter_table_action'] = 'Acción';
|
||||
$lang['user']['spamfilter_table_empty'] = 'No hay datos para mostrar';
|
||||
$lang['user']['spamfilter_table_remove'] = 'eliminar';
|
||||
$lang['user']['spamfilter_table_add'] = 'Agregar elemento';
|
||||
$lang['user']['spamfilter_default_score'] = 'Calificación de spam:';
|
||||
$lang['user']['spamfilter_green'] = 'Verde: éste mensaje no es spam';
|
||||
$lang['user']['spamfilter_yellow'] = 'Amarillo: éste mensaje puede ser spam, será etiquetado como spam y trasladado a tu carpeta basura';
|
||||
$lang['user']['spamfilter_red'] = 'Rojo: Este mensaje es spam y sera rechazado por el servidor';
|
||||
$lang['user']['spamfilter_default_score'] = 'Valores por defecto:';
|
||||
$lang['user']['spamfilter_hint'] = 'El primer valor representa la "calificación baja de spam", el segundo representa la "calificación alta de spam".';
|
||||
|
||||
$lang['user']['tls_policy_warning'] = '<strong>Advertencia:</strong> Si decides forzar la transmisión de correo encriptado, puedes perder correos.<br />Mensajes que no satisfagan la política serán rebotados con una falla grave en el sistema de correos .';
|
||||
$lang['user']['tls_policy'] = 'Política de encriptación';
|
||||
$lang['user']['tls_enforce_in'] = 'Aplicar TLS entrante';
|
||||
$lang['user']['tls_enforce_out'] = 'Aplicar TLS saliente';
|
||||
$lang['user']['no_record'] = 'Sin registro';
|
||||
|
||||
$lang['user']['misc_settings'] = 'Otras configuraciones de usuario';
|
||||
$lang['user']['misc_delete_profile'] = 'Otras configuraciones de usuario';
|
||||
|
||||
$lang['user']['tag_handling'] = 'Establecer manejo para el correo etiquetado';
|
||||
$lang['user']['tag_in_subfolder'] = 'En subcarpeta';
|
||||
$lang['user']['tag_in_subject'] = 'En asunto';
|
||||
$lang['user']['tag_help_explain'] = 'En subcarpeta: una nueva subcarpeta llamada como la etiqueta será creada debajo de INBOX ("INBOX/Facebook").<br />
|
||||
En asunto: los nombres de las etiquetas serán añadidos al asunto de los correos, ejemplo: "[Facebook] Mis Noticias".';
|
||||
$lang['user']['tag_help_example'] = 'Ejemplo de una dirección email etiquetada: mi<b>+Facebook</b>@ejemplo.org';
|
||||
|
||||
$lang['start']['dashboard'] = '%s - panel';
|
||||
$lang['start']['start_rc'] = 'Abrir Roundcube';
|
||||
$lang['start']['start_sogo'] = 'Abrir SOGo';
|
||||
$lang['start']['mailcow_apps_detail'] = 'Utiliza una aplicación de mailcow para acceder a tus correos, calendario, contactos y más.';
|
||||
$lang['start']['mailcow_panel'] = 'Iniciar mailcow UI';
|
||||
$lang['start']['mailcow_panel_description'] = 'Mailcow UI está disponible para administradores y usuarios de buzón.';
|
||||
$lang['start']['mailcow_panel_detail'] = '<b>Administradores del dominio</b> crean, modifican o eliminan buzones y alias, cambia dominios y lee información más detallada sobre sus dominios asignados<br />
|
||||
<b>Usuarios de buzón</b> son capaces de crear alias de tiempo limitado (spam alias), cambiar su contraseña y la configuración del filtro de spam.';
|
||||
$lang['start']['recommended_config'] = 'Configuración recomendada (sin ActiveSync)';
|
||||
$lang['start']['imap_smtp_server'] = 'IMAP- y SMTP datos del servidor';
|
||||
$lang['start']['imap_smtp_server_description'] = 'Para la mejor experiencia recomendamos utilizar <a href="%s" target="_blank"><b>Mozilla Thunderbird</b></a>.';
|
||||
$lang['start']['imap_smtp_server_badge'] = 'Leer/Escribir correos';
|
||||
$lang['start']['imap_smtp_server_auth_info'] = 'Por favor utiliza tu dirección de correo completa y el mecanismo de autenticación PLANO.<br />
|
||||
Tus datos para iniciar sesión serán encriptados por la encriptación obligatoria del servidor';
|
||||
$lang['start']['managesieve'] = 'ManageSieve';
|
||||
$lang['start']['managesieve_badge'] = 'Filtro de correos';
|
||||
$lang['start']['managesieve_description'] = 'Por favor utiliza <b>Mozilla Thunderbird</b> con la <a style="text-decoration:none" target="_blank" href="%s"><b>extensión nightly sieve</b></a>.<br />Inicia Thunderbird, abre la configuración de complementos y suelta el archivo xpi descargado en la ventana abierta.<br />El servidor es <b>%s</b>, utiliza el puerto <b>4190</b> si se te pregunta. Los datos para iniciar sesión coinciden con los datos de tu correo.';
|
||||
$lang['start']['service'] = 'Servicio';
|
||||
$lang['start']['encryption'] = 'Método de encriptación';
|
||||
$lang['start']['help'] = 'Mostrar/Ocultar panel de ayuda';
|
||||
$lang['start']['hostname'] = 'Hostname';
|
||||
$lang['start']['port'] = 'Port';
|
||||
$lang['start']['footer'] = '';
|
||||
$lang['header']['mailcow_settings'] = 'Configuracion';
|
||||
$lang['header']['administration'] = 'Administración';
|
||||
$lang['header']['mailboxes'] = 'Buzones';
|
||||
$lang['header']['user_settings'] = 'Configuraciones de usuario';
|
||||
$lang['header']['login'] = 'Inicio de sesión';
|
||||
$lang['header']['logged_in_as_logout'] = 'Sesión iniciada como <b>%s</b> (cerrar sesión)';
|
||||
$lang['header']['locale'] = 'Idioma';
|
||||
$lang['mailbox']['domain'] = 'Dominio';
|
||||
$lang['mailbox']['alias'] = 'Alias';
|
||||
$lang['mailbox']['aliases'] = 'Alias';
|
||||
$lang['mailbox']['domains'] = 'Dominios';
|
||||
$lang['mailbox']['mailboxes'] = 'Buzones';
|
||||
$lang['mailbox']['mailbox_quota'] = 'Tamaño máx. de cuota';
|
||||
$lang['mailbox']['domain_quota'] = 'Cuota';
|
||||
$lang['mailbox']['active'] = 'Activo';
|
||||
$lang['mailbox']['action'] = 'Acción';
|
||||
$lang['mailbox']['ratelimit'] = 'Límite de la tarifa saliente/h';
|
||||
$lang['mailbox']['backup_mx'] = 'Respaldar MX';
|
||||
$lang['mailbox']['domain_aliases'] = 'Alias de dominio';
|
||||
$lang['mailbox']['target_domain'] = 'Dominio destino';
|
||||
$lang['mailbox']['target_address'] = 'Dirección Goto';
|
||||
$lang['mailbox']['username'] = 'Nombre de usuario';
|
||||
$lang['mailbox']['fname'] = 'Nombre completo';
|
||||
$lang['mailbox']['filter_table'] = 'Filtrar tabla';
|
||||
$lang['mailbox']['yes'] = '✔';
|
||||
$lang['mailbox']['no'] = '✘';
|
||||
$lang['mailbox']['quota'] = 'Cuota';
|
||||
$lang['mailbox']['in_use'] = 'En uso (%)';
|
||||
$lang['mailbox']['msg_num'] = 'Mensaje #';
|
||||
$lang['mailbox']['remove'] = 'Eliminar';
|
||||
$lang['mailbox']['edit'] = 'Editar';
|
||||
$lang['mailbox']['archive'] = 'Archivar';
|
||||
$lang['mailbox']['no_record'] = 'Sin registro';
|
||||
$lang['mailbox']['add_domain'] = 'Agregar dominio';
|
||||
$lang['mailbox']['add_domain_alias'] = 'Agregar alias de dominio';
|
||||
$lang['mailbox']['add_mailbox'] = 'Agregar buzón';
|
||||
$lang['mailbox']['add_alias'] = 'Agregar alias';
|
||||
|
||||
$lang['info']['no_action'] = 'No hay acción aplicable';
|
||||
|
||||
$lang['delete']['title'] = 'Eliminar objeto';
|
||||
$lang['delete']['remove_domain_warning'] = '<b>Advertencia:</b> ¡Estás a punto de eliminar el dominio <b>%s</b>!';
|
||||
$lang['delete']['remove_domainalias_warning'] = '<b>Advertencia:</b> ¡Estás a punto de eliminar el alias de dominio <b>%s</b>!';
|
||||
$lang['delete']['remove_domainadmin_warning'] = '<b>Advertencia:</b> ¡Estás a punto de eliminar el administrador de dominio <b>%s</b>!';
|
||||
$lang['delete']['remove_alias_warning'] = '<b>Advertencia:</b> ¡Estás a punto de eliminar la dirección alias <b>%s</b>!';
|
||||
$lang['delete']['remove_mailbox_warning'] = '<b>Advertencia:</b> ¡Estás a punto de eliminar el buzón <b>%s</b>!';
|
||||
$lang['delete']['remove_mailbox_details'] = 'El buzón será <b>purgado permanentemente</b>!';
|
||||
$lang['delete']['remove_domain_details'] = 'Esto también eliminará alias de dominio.<br /><br /><b>Un dominio debe estar vacío para poder ser eliminado.</b>';
|
||||
$lang['delete']['remove_alias_details'] = 'Los usuarios ya no serán capaces de recibir correos o enviar correos desde esta dirección.</b>';
|
||||
$lang['delete']['remove_button'] = 'Eliminar';
|
||||
$lang['delete']['previous'] = 'Página anterior';
|
||||
|
||||
$lang['edit']['save'] = 'Guardar cambios';
|
||||
$lang['edit']['archive'] = 'Acceso a archivos';
|
||||
$lang['edit']['max_mailboxes'] = 'Máx. buzones posibles:';
|
||||
$lang['edit']['title'] = 'Editas objeto';
|
||||
$lang['edit']['target_address'] = 'Dirección/es goto <small>(separadas por coma)</small>:';
|
||||
$lang['edit']['active'] = 'Activo';
|
||||
$lang['edit']['target_domain'] = 'Dominio destino:';
|
||||
$lang['edit']['password'] = 'Contraseña:';
|
||||
$lang['edit']['ratelimit'] = 'Límite de la tarifa saliente/h:';
|
||||
$lang['danger']['ratelimt_less_one'] = 'El límite de la tarifa saliente/h no puede ser menos que 1';
|
||||
$lang['edit']['password_repeat'] = 'Confirmación de contraseña (repetir):';
|
||||
$lang['edit']['domain_admin'] = 'Editar administrador del dominio';
|
||||
$lang['edit']['domain'] = 'Editar dominio';
|
||||
$lang['edit']['alias_domain'] = 'Alias de dominio';
|
||||
$lang['edit']['edit_alias_domain'] = 'Editar alias de dominio';
|
||||
$lang['edit']['domains'] = 'Dominios';
|
||||
$lang['edit']['destroy'] = 'Entrada manual de datos';
|
||||
$lang['edit']['alias'] = 'Editar alias';
|
||||
$lang['edit']['mailbox'] = 'Editar buzón';
|
||||
$lang['edit']['description'] = 'Descripción:';
|
||||
$lang['edit']['max_aliases'] = 'Máx. alias:';
|
||||
$lang['edit']['max_quota'] = 'Máx. cuota por buzón (MiB):';
|
||||
$lang['edit']['domain_quota'] = 'Cuota de dominio:';
|
||||
$lang['edit']['backup_mx_options'] = 'Opciones del respaldo MX:';
|
||||
$lang['edit']['relay_domain'] = 'Dominio de retransmisión';
|
||||
$lang['edit']['relay_all'] = 'Retransmitir todos los recipientes';
|
||||
$lang['edit']['dkim_signature'] = 'Firma DKIM:';
|
||||
$lang['edit']['dkim_record_info'] = '<small>Por favor agrega un registro TXT con el siguiente valor a tu configuración DNS.</small>';
|
||||
$lang['edit']['relay_all_info'] = '<small>Si eliges <b>no</b> retransmitir a todos los recipientes, necesitas agregar un buzón "blind"("ciego") por cada recipiente que debe ser retransmitido.</small>';
|
||||
$lang['edit']['full_name'] = 'Nombre completo';
|
||||
$lang['edit']['full_name'] = 'Nombre completo';
|
||||
$lang['edit']['quota_mb'] = 'Cuota (MiB)';
|
||||
$lang['edit']['sender_acl'] = 'Permitir envío como:';
|
||||
$lang['edit']['sender_acl_info'] = 'Los alias no pueden deseleccionarse.';
|
||||
$lang['edit']['dkim_txt_name'] = 'Nombre del registro TXT:';
|
||||
$lang['edit']['dkim_txt_value'] = 'Valor del registro TXT:';
|
||||
$lang['edit']['previous'] = 'Página anterior';
|
||||
$lang['edit']['unchanged_if_empty'] = 'Si no hay cambios dejalo en blanco';
|
||||
$lang['edit']['dont_check_sender_acl'] = 'No verifiques remitente para el dominio %s';
|
||||
|
||||
$lang['add']['title'] = 'Agregar objeto';
|
||||
$lang['add']['domain'] = 'Dominio';
|
||||
$lang['add']['active'] = 'Activo';
|
||||
$lang['add']['save'] = 'Guardar cambios';
|
||||
$lang['add']['description'] = 'Descripción:';
|
||||
$lang['add']['max_aliases'] = 'Máx. alias posibles:';
|
||||
$lang['add']['max_mailboxes'] = 'Máx. buzones posibles:';
|
||||
$lang['add']['mailbox_quota_m'] = 'Máx. cuota por buzón (MiB):';
|
||||
$lang['add']['domain_quota_m'] = 'Cuota total del dominio (MiB):';
|
||||
$lang['add']['backup_mx_options'] = 'Opciones del respaldo MX:';
|
||||
$lang['add']['relay_all'] = 'Retransmitir todos los recipientes';
|
||||
$lang['add']['relay_domain'] = 'Retransmitir este dominio';
|
||||
$lang['add']['relay_all_info'] = '<small>Si eliges <b>no</b> retransmitir a todos los recipientes, necesitas agregar un buzón "blind"("ciego") por cada recipiente que debe ser retransmitido.</small>';
|
||||
$lang['add']['alias'] = 'Alias';
|
||||
$lang['add']['alias_spf_fail'] = '<b>Nota:</b> Si tu dirección destino está en un buzón externo, el <b>servidor de correo que recibe</b> puede rechazar tu mensaje por una falla SPF.</a>';
|
||||
$lang['add']['alias_address'] = 'Dirección/es alias:';
|
||||
$lang['add']['alias_address_info'] = '<small>Dirección/es de correo completa/s ó @ejemplo.com, para atrapar todos los mensajes para un dominio (separado por coma). <b>Dominios mailcow solamente</b>.</small>';
|
||||
$lang['add']['alias_domain_info'] = '<small>Nombres de dominio válidos solamente (separado por coma).</small>';
|
||||
$lang['add']['target_address'] = 'Direcciones goto:';
|
||||
$lang['add']['target_address_info'] = '<small>Dirección/es de correo completa/s (separado por coma).</small>';
|
||||
$lang['add']['alias_domain'] = 'Dominio alias';
|
||||
$lang['add']['select'] = 'Por favor selecciona...';
|
||||
$lang['add']['target_domain'] = 'Dominio destino:';
|
||||
$lang['add']['mailbox'] = 'Buzón';
|
||||
$lang['add']['mailbox_username'] = 'Nombre de usuario (parte izquierda de una dirección de correo):';
|
||||
$lang['add']['full_name'] = 'Nombre completo:';
|
||||
$lang['add']['quota_mb'] = 'Cuota (MiB):';
|
||||
$lang['add']['select_domain'] = 'Por favor elige un dominio primero';
|
||||
$lang['add']['password'] = 'Constraseña:';
|
||||
$lang['add']['password_repeat'] = 'Confirmación de contraseña (repetir):';
|
||||
$lang['add']['previous'] = 'Página anterior';
|
||||
$lang['add']['restart_sogo_hint'] = '¡Necesitas reiniciar el contenedor del servicio SOGo antes de agregar un nuevo dominio!';
|
||||
|
||||
$lang['login']['title'] = 'Inicio de sesión';
|
||||
$lang['login']['administration'] = 'Administración';
|
||||
$lang['login']['administration_details'] = 'Por favor utiliza tu inicio de sesión de Administrador para realizar tareas administrativas.';
|
||||
$lang['login']['user_settings'] = 'Configuración de usuario';
|
||||
$lang['login']['user_settings_details'] = 'Usuarios de buzón pueden utilizar mailcow UI para cambiar sus contraseñas, crear alias temporales (alias de spam), ajustar el comportamiento del filtro de spam ó importar mensajes desde un servidor IMAP remoto.';
|
||||
$lang['login']['username'] = 'Nombre de usuario';
|
||||
$lang['login']['password'] = 'Contraseña';
|
||||
$lang['login']['reset_password'] = 'Reiniciar mi contraseña';
|
||||
$lang['login']['login'] = 'Inicio de sesión';
|
||||
$lang['login']['previous'] = "Página anterior";
|
||||
$lang['login']['delayed'] = 'El inicio de sesión ha sido retrasado %s segundos.';
|
||||
|
||||
$lang['login']['tfa'] = "Autenticación de dos factores";
|
||||
$lang['login']['tfa_details'] = "Por favor confirma tu contraseña de un solo uso en el campo de abajo";
|
||||
$lang['login']['confirm'] = "Confirmar";
|
||||
$lang['login']['otp'] = "Contraseña de un solo uso";
|
||||
$lang['login']['trash_login'] = "Inicio de sesión basura";
|
||||
|
||||
$lang['admin']['search_domain_da'] = 'Buscar dominios';
|
||||
$lang['admin']['restrictions'] = 'Restricciones Postfix';
|
||||
$lang['admin']['rr'] = 'Restricciones Postfix para recipientes';
|
||||
$lang['admin']['sr'] = 'Restricciones Postfix para remitentes';
|
||||
$lang['admin']['reset_defaults'] = 'Restablecer los valores predeterminados';
|
||||
$lang['admin']['sr'] = 'Restricciones Postfix para remitentes';
|
||||
$lang['admin']['r_inactive'] = 'Restricciones inactivas';
|
||||
$lang['admin']['r_active'] = 'Restricciones activas';
|
||||
$lang['admin']['r_info'] = 'Elementos en gris/deshabilitados en la lista de restricciones activas no son reconocidas como restricciones válidas para mailcow y no pueden ser movidas. Restricciones desconocidas serán establecidas en el orden de aparicion de todas maneras. <br />Puedes agregar nuevos elementos en <code>inc/vars.local.inc.php</code> para ser capaz de habilitarlas.';
|
||||
$lang['admin']['public_folders'] = 'Carpetas Públicas';
|
||||
$lang['admin']['public_folders_text'] = 'Un espacio de nombres "Public" (Público) será creado. Debajo del nombre de la carpeta pública se indica el nombre del primer buzón creado automáticamente dentro de este espacio de nombres';
|
||||
$lang['admin']['public_folder_name'] = 'Nombre de la carpeta <small>(alfanumérico)</small>';
|
||||
$lang['admin']['public_folder_enable'] = 'Habilitar carpeta pública';
|
||||
$lang['admin']['public_folder_enable_text'] = 'Activar ésta opción no elimina correos en cualquier otra carpeta pública.';
|
||||
$lang['admin']['public_folder_pusf'] = 'Habilitar el indicador visto por usuario';
|
||||
$lang['admin']['public_folder_pusf_text'] = 'Un sistema habilitado por indicador "por usuario visto" no marcará un correo como leído para el Usuario B, cuando el Usuario A lo haya visto, pero el Usuario B no.';
|
||||
$lang['admin']['privacy'] = 'Privacidad';
|
||||
$lang['admin']['privacy_text'] = 'Ésta opción activa una tabla PCRE para remover "User-Agent", "X-Enigmail", "X-Mailer", "X-Originating-IP" y remplaza las cabezeras "Received: from" con localhost/127.0.0.1.';
|
||||
$lang['admin']['privacy_anon_mail'] = 'Anonimizar correo saliente';
|
||||
$lang['admin']['dkim_txt_name'] = 'Nombre del registro TXT:';
|
||||
$lang['admin']['dkim_txt_value'] = 'Valor del registro TXT:';
|
||||
$lang['admin']['dkim_key_length'] = 'Longitud de la llave DKIM (bits)';
|
||||
$lang['admin']['previous'] = 'Página anterior';
|
||||
$lang['admin']['quota_mb'] = 'Cuota (MiB):';
|
||||
$lang['admin']['sender_acl'] = 'Permitir envío como:';
|
||||
$lang['admin']['msg_size'] = 'Tamaño del mensaje';
|
||||
$lang['admin']['msg_size_limit'] = 'Límite del tamaño del mensaje ahora';
|
||||
$lang['admin']['msg_size_limit_details'] = 'Aplicando un nuebo límite reiniciará Postfix y el servidor web.';
|
||||
$lang['admin']['save'] = 'Guardar cambios';
|
||||
$lang['admin']['maintenance'] = 'Mantenimiento e Información';
|
||||
$lang['admin']['sys_info'] = 'información del sistema';
|
||||
$lang['admin']['dkim_add_key'] = 'Agregar registro DKIM';
|
||||
$lang['admin']['dkim_keys'] = 'Registros DKIM';
|
||||
$lang['admin']['add'] = 'Agregar';
|
||||
$lang['admin']['configuration'] = 'Configuración';
|
||||
$lang['admin']['password'] = 'Contraseña';
|
||||
$lang['admin']['password_repeat'] = 'Confirmación de contraseña (repetir)';
|
||||
$lang['admin']['active'] = 'Activo';
|
||||
$lang['admin']['action'] = 'Acción';
|
||||
$lang['admin']['add_domain_admin'] = 'Agregar Administrador del dominio';
|
||||
$lang['admin']['admin_domains'] = 'Asignaciones de dominio';
|
||||
$lang['admin']['domain_admins'] = 'Administradores de dominio';
|
||||
$lang['admin']['username'] = 'Nombre de usuario';
|
||||
$lang['admin']['edit'] = 'Editar';
|
||||
$lang['admin']['remove'] = 'Eliminar';
|
||||
$lang['admin']['save'] = 'Guardar cambios';
|
||||
$lang['admin']['admin'] = 'Administrador';
|
||||
$lang['admin']['admin_details'] = 'Editar detalles del administrador';
|
||||
$lang['admin']['unchanged_if_empty'] = 'Si no hay cambios dejalo en blanco';
|
||||
$lang['admin']['yes'] = '✔';
|
||||
$lang['admin']['no'] = '✘';
|
||||
$lang['admin']['access'] = 'Acceso';
|
||||
$lang['admin']['invalid_max_msg_size'] = 'Tamaño máx. del mensaje no válido';
|
||||
$lang['admin']['site_not_found'] = 'No se puede localizar la configuración del sitio de mailcow';
|
||||
$lang['admin']['public_folder_empty'] = 'El nombre de la carpeta pública no debe estar vacío';
|
||||
$lang['admin']['set_rr_failed'] = 'No se pueden establecer las restricciones de Postfix';
|
||||
$lang['admin']['no_record'] = 'Sin registro';
|
||||
?>
|
|
@ -4,7 +4,10 @@
|
|||
// Dutch language file
|
||||
*/
|
||||
$lang['footer']['loading'] = "Even geduld a.u.b. ...";
|
||||
$lang['getmail']['no_status'] = "Geen vorige status gevonden.";
|
||||
$lang['header']['restart_sogo'] = 'SOGo herstarten';
|
||||
$lang['footer']['restart_sogo'] = 'SOGo herstarten';
|
||||
$lang['footer']['restart_now'] = 'Nu opnieuw starten';
|
||||
$lang['footer']['restart_sogo_info'] = 'Sommige taken, zoals het toevoegen van een domein, vereisen een herstart van SOGo om de veranderingen door te voeren.<br /><br /><b>Belangrijk:</b> Het opnieuw opstarten kan een poos duren, wacht a.u.b. totdat dit volledig voltooid is.';
|
||||
$lang['dkim']['confirm'] = "Weet u het zeker?";
|
||||
$lang['danger']['dkim_not_found'] = "DKIM record niet gevonden.";
|
||||
$lang['danger']['dkim_remove_failed'] = "Kan geselecteerde DKIM record niet verwijderen.";
|
||||
|
@ -21,8 +24,8 @@ $lang['danger']['object_is_not_numeric'] = "%s is niet numeriek.";
|
|||
$lang['success']['domain_added'] = "Domein toegevoegd: %s.";
|
||||
$lang['danger']['alias_empty'] = "Aliasadres mag niet leeg blijven.";
|
||||
$lang['danger']['goto_empty'] = "Doeladres mag niet leeg blijven.";
|
||||
$lang['danger']['blacklist_exists'] = "Deze invoer staat op de zwarte lijst.";
|
||||
$lang['danger']['blacklist_from_invalid'] = "Zwarte lijst invoer heeft een ongeldig format.";
|
||||
$lang['danger']['policy_list_from_exists'] = "Deze invoer bestaat al.";
|
||||
$lang['danger']['policy_list_from_invalid'] = "Deze invoer heeft een ongeldig format.";
|
||||
$lang['danger']['whitelist_exists'] = "Deze invoer staat op de witte lijst.";
|
||||
$lang['danger']['whitelist_from_invalid'] = "Witte lijst invoer heeft een ongeldig format.";
|
||||
$lang['danger']['alias_invalid'] = "Aliasadres is ongeldig.";
|
||||
|
@ -90,10 +93,10 @@ $lang['user']['user_settings'] = 'Gebruikersinstellingen';
|
|||
$lang['user']['mailbox_settings'] = 'Postvakinstellingen';
|
||||
$lang['user']['mailbox_details'] = 'Postvakdetails';
|
||||
$lang['user']['change_password'] = 'Verander wachtwoord';
|
||||
$lang['user']['new_password'] = 'Nieuw wachtwoord:';
|
||||
$lang['user']['new_password'] = 'Nieuw wachtwoord';
|
||||
$lang['user']['save_changes'] = 'Wijzigingen opslaan';
|
||||
$lang['user']['password_now'] = 'Huidig wachtwoord (bevestig wijzigingen):';
|
||||
$lang['user']['new_password_repeat'] = 'Bevestig wachtwoord (herhalen):';
|
||||
$lang['user']['password_now'] = 'Huidig wachtwoord (bevestig wijzigingen)';
|
||||
$lang['user']['new_password_repeat'] = 'Bevestig wachtwoord (herhalen)';
|
||||
$lang['user']['new_password_description'] = 'Vereisten: 6 karakters lang, letters en nummers.';
|
||||
$lang['user']['did_you_know'] = '<b>Wist u dat?</b> U kunt tags in het e-mailadres gebruiken ("me+<b>prive</b>@voorbeeld.nl") om berichten automatisch naar een bijbehorende map te sturen (voorbeeld: "prive").';
|
||||
$lang['user']['spam_aliases'] = 'Tijdelijk e-mailadres';
|
||||
|
@ -120,6 +123,7 @@ $lang['user']['spamfilter_table_rule'] = 'Regel';
|
|||
$lang['user']['spamfilter_table_action'] = 'Handeling';
|
||||
$lang['user']['spamfilter_table_empty'] = 'Geen gegevens om weer te geven.';
|
||||
$lang['user']['spamfilter_table_remove'] = 'verwijder';
|
||||
$lang['user']['spamfilter_table_add'] = 'Voeg item toe';
|
||||
$lang['user']['spamfilter_default_score'] = 'Spamscore:';
|
||||
$lang['user']['spamfilter_green'] = 'Groen: Dit bericht is geen spam.';
|
||||
$lang['user']['spamfilter_yellow'] = 'Geel: Dit bericht is mogelijk spam, zal worden gelabeled en verplaatst worden naar de Junk-map.';
|
||||
|
@ -135,7 +139,13 @@ $lang['user']['no_record'] = 'Geen vermelding.';
|
|||
|
||||
$lang['user']['misc_settings'] = 'Andere profielinstellingen';
|
||||
$lang['user']['misc_delete_profile'] = 'Andere profielinstellingen';
|
||||
$lang['start']['dashboard'] = '%s - dashboard';
|
||||
$lang['user']['tag_handling'] = 'Omgaan met e-mail tags';
|
||||
$lang['user']['tag_in_subfolder'] = 'In onderliggende map';
|
||||
$lang['user']['tag_in_subject'] = 'In onderwerp';
|
||||
$lang['user']['tag_help_explain'] = 'In onderliggende map: maakt onder INBOX een nieuwe map aan met de naam van de tag (bijv.: "INBOX/Facebook").<br />
|
||||
In onderwerp: de tag wordt vóór het oorspronkelijke e-mail onderwerp geplaatst (bijv.: "[Facebook] Mijn nieuws").';
|
||||
$lang['user']['tag_help_example'] = 'Voorbeeld van een e-mailadres met tag: ik<b>+Facebook</b>@voorbeeld.org';
|
||||
$lang['start']['dashboard'] = '%s - startpagina';
|
||||
$lang['start']['start_rc'] = 'Open Roundcube';
|
||||
$lang['start']['start_sogo'] = 'Open SOGo';
|
||||
$lang['start']['mailcow_apps_detail'] = 'Gebruik een mailcow app om toegang te hebben tot uw e-mails, kalender, contactpersonen en meer.';
|
||||
|
@ -280,6 +290,7 @@ $lang['add']['select_domain'] = 'Selecteer eerst een domein';
|
|||
$lang['add']['password'] = 'Wachtwoord:';
|
||||
$lang['add']['password_repeat'] = 'Bevestig wachtwoord (herhalen):';
|
||||
$lang['add']['previous'] = 'Vorige pagina';
|
||||
$lang['add']['restart_sogo_hint'] = 'SOGo dient opnieuw te worden gestart nadat een domein is toegevoegd!';
|
||||
|
||||
$lang['login']['title'] = 'Aanmelden';
|
||||
$lang['login']['administration'] = 'Beheer';
|
||||
|
|
|
@ -91,10 +91,10 @@ $lang['user']['user_settings'] = 'Configurações do usuário';
|
|||
$lang['user']['mailbox_settings'] = 'Configrações da conta';
|
||||
$lang['user']['mailbox_details'] = 'Detalhes da conta';
|
||||
$lang['user']['change_password'] = 'Alterar senha';
|
||||
$lang['user']['new_password'] = 'Nova senha:';
|
||||
$lang['user']['new_password'] = 'Nova senha';
|
||||
$lang['user']['save_changes'] = 'Salvar';
|
||||
$lang['user']['password_now'] = 'Senha atual (confirme a alteração):';
|
||||
$lang['user']['new_password_repeat'] = 'Confirmar senha (repetir):';
|
||||
$lang['user']['password_now'] = 'Senha atual (confirme a alteração)';
|
||||
$lang['user']['new_password_repeat'] = 'Confirmar senha (repetir)';
|
||||
$lang['user']['new_password_description'] = 'Requerido: mínimo de 6 characteres com letras e números.';
|
||||
$lang['user']['did_you_know'] = '<b>Você sabia?</b> Você pode usar tags no endereço de email ("conta+<b>privado</b>@example.com") para classificar as mensagens automaticamente para uma determinada pasta (exemplo: "privado").';
|
||||
$lang['user']['spam_aliases'] = 'Apelidos temporários';
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?php
|
||||
require_once "inc/prerequisites.inc.php";
|
||||
|
||||
if ($_SESSION['mailcow_cc_role'] == "admin" || $_SESSION['mailcow_cc_role'] == "domainadmin") {
|
||||
if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "admin" || $_SESSION['mailcow_cc_role'] == "domainadmin")) {
|
||||
require_once "inc/header.inc.php";
|
||||
$_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
|
||||
?>
|
||||
|
@ -49,80 +49,32 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
|
|||
</thead>
|
||||
<tbody>
|
||||
<?php
|
||||
try {
|
||||
$stmt = $pdo->prepare("SELECT
|
||||
`domain`,
|
||||
`aliases`,
|
||||
`mailboxes`,
|
||||
`maxquota` * 1048576 AS `maxquota`,
|
||||
`quota` * 1048576 AS `quota`,
|
||||
CASE `backupmx` WHEN 1 THEN '".$lang['mailbox']['yes']."' ELSE '".$lang['mailbox']['no']."' END AS `backupmx`,
|
||||
CASE `active` WHEN 1 THEN '".$lang['mailbox']['yes']."' ELSE '".$lang['mailbox']['no']."' END AS `active`
|
||||
FROM `domain` WHERE
|
||||
`domain` IN (
|
||||
SELECT `domain` FROM `domain_admins` WHERE `username`= :username AND `active`='1'
|
||||
)
|
||||
OR 'admin'= :admin");
|
||||
$stmt->execute(array(
|
||||
':username' => $_SESSION['mailcow_cc_username'],
|
||||
':admin' => $_SESSION['mailcow_cc_role'],
|
||||
));
|
||||
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
}
|
||||
catch (PDOException $e) {
|
||||
$_SESSION['return'] = array(
|
||||
'type' => 'danger',
|
||||
'msg' => 'MySQL: '.$e
|
||||
);
|
||||
return false;
|
||||
}
|
||||
if(!empty($rows)):
|
||||
while($row = array_shift($rows)):
|
||||
try {
|
||||
$stmt = $pdo->prepare("SELECT COUNT(*) AS `count` FROM `alias`
|
||||
WHERE `domain`= :domain
|
||||
AND `address` NOT IN (
|
||||
SELECT `username` FROM `mailbox`)");
|
||||
$stmt->execute(array(':domain' => $row['domain']));
|
||||
$AliasData = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
$stmt = $pdo->prepare("SELECT
|
||||
COUNT(*) AS `count`,
|
||||
COALESCE(SUM(`quota`), '0') AS `quota`
|
||||
FROM `mailbox`
|
||||
WHERE `domain` = :domain");
|
||||
$stmt->execute(array(':domain' => $row['domain']));
|
||||
$MailboxData = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
}
|
||||
catch (PDOException $e) {
|
||||
$_SESSION['return'] = array(
|
||||
'type' => 'danger',
|
||||
'msg' => 'MySQL: '.$e
|
||||
);
|
||||
return false;
|
||||
}
|
||||
$domains = mailbox_get_domains();
|
||||
if (!empty($domains)):
|
||||
foreach ($domains as $domain):
|
||||
$domaindata = mailbox_get_domain_details($domain);
|
||||
?>
|
||||
<tr id="data">
|
||||
<td><?=htmlspecialchars($row['domain']);?></td>
|
||||
<td><?=intval($AliasData['count']);?> / <?=intval($row['aliases']);?></td>
|
||||
<td><?=$MailboxData['count'];?> / <?=$row['mailboxes'];?></td>
|
||||
<td><?=formatBytes(intval($row['maxquota']), 2);?></td>
|
||||
<td><?=formatBytes(intval($MailboxData['quota']), 2);?> / <?=formatBytes(intval($row['quota']));?></td>
|
||||
<td><?=htmlspecialchars($domaindata['domain_name']);?></td>
|
||||
<td><?=$domaindata['aliases_in_domain'];?> / <?=$domaindata['max_num_aliases_for_domain'];?></td>
|
||||
<td><?=$domaindata['mboxes_in_domain'];?> / <?=$domaindata['max_num_mboxes_for_domain'];?></td>
|
||||
<td><?=formatBytes($domaindata['max_quota_for_mbox']);?></td>
|
||||
<td><?=formatBytes($domaindata['quota_used_in_domain'], 2);?> / <?=formatBytes($domaindata['max_quota_for_domain'], 2);?></td>
|
||||
<?php
|
||||
if ($_SESSION['mailcow_cc_role'] == "admin"):
|
||||
?>
|
||||
<td><?=$row['backupmx'];?></td>
|
||||
<td><?=$domaindata['backupmx'];?></td>
|
||||
<?php
|
||||
endif;
|
||||
?>
|
||||
<td><?=$row['active'];?></td>
|
||||
<td><?=$domaindata['active'];?></td>
|
||||
<?php
|
||||
if ($_SESSION['mailcow_cc_role'] == "admin"):
|
||||
?>
|
||||
<td style="text-align: right;">
|
||||
<div class="btn-group">
|
||||
<a href="/edit.php?domain=<?=urlencode($row['domain']);?>" class="btn btn-xs btn-default"><span class="glyphicon glyphicon-pencil"></span> <?=$lang['mailbox']['edit'];?></a>
|
||||
<a href="/delete.php?domain=<?=urlencode($row['domain']);?>" class="btn btn-xs btn-danger"><span class="glyphicon glyphicon-trash"></span> <?=$lang['mailbox']['remove'];?></a>
|
||||
<a href="/edit.php?domain=<?=urlencode($domaindata['domain_name']);?>" class="btn btn-xs btn-default"><span class="glyphicon glyphicon-pencil"></span> <?=$lang['mailbox']['edit'];?></a>
|
||||
<a href="/delete.php?domain=<?=urlencode($domaindata['domain_name']);?>" class="btn btn-xs btn-danger"><span class="glyphicon glyphicon-trash"></span> <?=$lang['mailbox']['remove'];?></a>
|
||||
</div>
|
||||
</td>
|
||||
<?php
|
||||
|
@ -130,26 +82,26 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
|
|||
?>
|
||||
<td style="text-align: right;">
|
||||
<div class="btn-group">
|
||||
<a href="/edit.php?domain=<?=urlencode($row['domain']);?>" class="btn btn-xs btn-default"><span class="glyphicon glyphicon-pencil"></span> <?=$lang['mailbox']['edit'];?></a>
|
||||
<a href="/edit.php?domain=<?=urlencode($domaindata['domain_name']);?>" class="btn btn-xs btn-default"><span class="glyphicon glyphicon-pencil"></span> <?=$lang['mailbox']['edit'];?></a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<?php
|
||||
endif;
|
||||
endwhile;
|
||||
else:
|
||||
?>
|
||||
<tr id="no-data"><td colspan="8" style="text-align: center; font-style: italic;"><?=$lang['mailbox']['no_record'];?></td></tr>
|
||||
<?php
|
||||
endif;
|
||||
<?php
|
||||
endif;
|
||||
endforeach;
|
||||
else:
|
||||
?>
|
||||
<tr id="no-data"><td colspan="999" style="text-align: center; font-style: italic;"><?=$lang['mailbox']['no_record_single'];?></td></tr>
|
||||
<?php
|
||||
endif;
|
||||
?>
|
||||
</tbody>
|
||||
<?php
|
||||
if ($_SESSION['mailcow_cc_role'] == "admin"):
|
||||
?>
|
||||
<tfoot>
|
||||
<tr id="no-data">
|
||||
<td colspan="8" style="text-align: center; font-style: normal; border-top: 1px solid #e7e7e7;">
|
||||
<td colspan="999" style="text-align: center; font-style: normal; border-top: 1px solid #e7e7e7;">
|
||||
<a href="/add.php?domain"><?=$lang['mailbox']['add_domain'];?></a>
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -162,6 +114,175 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title"><?=$lang['mailbox']['mailboxes'];?> <span class="badge" id="numRowsMailbox"></span></h3>
|
||||
<div class="pull-right">
|
||||
<span class="clickable filter" data-toggle="tooltip" title="<?=$lang['mailbox']['filter_table'];?>" data-container="body">
|
||||
<i class="glyphicon glyphicon-filter"></i>
|
||||
</span>
|
||||
<a href="/add.php?mailbox"><span class="glyphicon glyphicon-plus"></span></a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<input type="text" class="form-control" id="mailboxtable-filter" data-action="filter" data-filters="#mailboxtable" placeholder="Filter" />
|
||||
</div>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped sortable-theme-bootstrap" data-sortable id="mailboxtable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="sort-table" style="min-width: 100px;"><?=$lang['mailbox']['username'];?></th>
|
||||
<th class="sort-table" style="min-width: 98px;"><?=$lang['mailbox']['fname'];?></th>
|
||||
<th class="sort-table" style="min-width: 86px;"><?=$lang['mailbox']['domain'];?></th>
|
||||
<th class="sort-table" style="min-width: 75px;"><?=$lang['mailbox']['quota'];?></th>
|
||||
<th class="sort-table" style="min-width: 75px;"><?=$lang['mailbox']['spam_aliases'];?></th>
|
||||
<th class="sort-table" style="min-width: 99px;"><?=$lang['mailbox']['in_use'];?></th>
|
||||
<th class="sort-table" style="min-width: 100px;"><?=$lang['mailbox']['msg_num'];?></th>
|
||||
<th class="sort-table" style="min-width: 76px;"><?=$lang['mailbox']['active'];?></th>
|
||||
<th style="text-align: right; min-width: 200px;" data-sortable="false"><?=$lang['mailbox']['action'];?></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php
|
||||
if (!empty($domains)) {
|
||||
foreach (mailbox_get_domains() as $domain) {
|
||||
$mailboxes = mailbox_get_mailboxes($domain);
|
||||
if (!empty($mailboxes)) {
|
||||
foreach ($mailboxes as $mailbox) {
|
||||
$mailboxdata = mailbox_get_mailbox_details($mailbox);
|
||||
?>
|
||||
<tr id="data">
|
||||
<td><?=($mailboxdata['is_relayed'] == "0") ? htmlspecialchars($mailboxdata['username']) : '<span data-toggle="tooltip" title="Relayed"><i class="glyphicon glyphicon-forward"></i>' . htmlspecialchars($mailboxdata['username']) . '</span>';?></td>
|
||||
<td><?=htmlspecialchars($mailboxdata['name'], ENT_QUOTES, 'UTF-8');?></td>
|
||||
<td><?=htmlspecialchars($mailboxdata['domain']);?></td>
|
||||
<td><?=formatBytes($mailboxdata['quota_used'], 2);?> / <?=formatBytes($mailboxdata['quota'], 2);?></td>
|
||||
<td><?=$mailboxdata['spam_aliases'];?></td>
|
||||
<td style="min-width:120px;">
|
||||
<div class="progress">
|
||||
<div class="progress-bar progress-bar-<?=$mailboxdata['percent_class'];?>" role="progressbar" aria-valuenow="<?=$mailboxdata['percent_in_use'];?>" aria-valuemin="0" aria-valuemax="100" style="min-width:2em;width: <?=$mailboxdata['percent_in_use'];?>%;">
|
||||
<?=$mailboxdata['percent_in_use'];?>%
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td><?=$mailboxdata['messages'];?></td>
|
||||
<td><?=$mailboxdata['active'];?></td>
|
||||
<td style="text-align: right;">
|
||||
<div class="btn-group">
|
||||
<a href="/edit.php?mailbox=<?=urlencode($mailboxdata['username']);?>" class="btn btn-xs btn-default"><span class="glyphicon glyphicon-pencil"></span> <?=$lang['mailbox']['edit'];?></a>
|
||||
<a href="/delete.php?mailbox=<?=urlencode($mailboxdata['username']);?>" class="btn btn-xs btn-danger"><span class="glyphicon glyphicon-trash"></span> <?=$lang['mailbox']['remove'];?></a>
|
||||
<?php if ($_SESSION['mailcow_cc_role'] == "admin"): ?>
|
||||
<a href="/index.php?duallogin=<?=urlencode($mailboxdata['username']);?>" class="btn btn-xs btn-success"><span class="glyphicon glyphicon-user"></span> Login</a>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
else {
|
||||
?>
|
||||
<tr id="no-data"><td colspan="999" style="text-align: center; font-style: italic;"><?=sprintf($lang['mailbox']['no_record'], $domain);?></td></tr>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
} else {
|
||||
?>
|
||||
<tr id="no-data"><td colspan="999" style="text-align: center; font-style: italic;"><?=$lang['mailbox']['add_domain_record_first'];?></td></tr>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr id="no-data">
|
||||
<td colspan="999" style="text-align: center; border-top: 1px solid #e7e7e7;">
|
||||
<a href="/add.php?mailbox"><?=$lang['mailbox']['add_mailbox'];?></a>
|
||||
</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title"><?=$lang['mailbox']['resources'];?> <span class="badge" id="numRowsResource"></span></h3>
|
||||
<div class="pull-right">
|
||||
<span class="clickable filter" data-toggle="tooltip" title="<?=$lang['mailbox']['filter_table'];?>" data-container="body">
|
||||
<i class="glyphicon glyphicon-filter"></i>
|
||||
</span>
|
||||
<a href="/add.php?resource"><span class="glyphicon glyphicon-plus"></span></a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<input type="text" class="form-control" id="resourcetable-filter" data-action="filter" data-filters="#resourcetable" placeholder="Filter" />
|
||||
</div>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped sortable-theme-bootstrap" data-sortable id="resourcetable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="sort-table" style="min-width: 98px;"><?=$lang['mailbox']['description'];?></th>
|
||||
<th class="sort-table" style="min-width: 98px;"><?=$lang['mailbox']['kind'];?></th>
|
||||
<th class="sort-table" style="min-width: 86px;"><?=$lang['mailbox']['domain'];?></th>
|
||||
<th class="sort-table" style="min-width: 98px;"><?=$lang['mailbox']['multiple_bookings'];?></th>
|
||||
<th class="sort-table" style="min-width: 76px;"><?=$lang['mailbox']['active'];?></th>
|
||||
<th style="text-align: right; min-width: 200px;" data-sortable="false"><?=$lang['mailbox']['action'];?></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php
|
||||
if (!empty($domains)) {
|
||||
foreach (mailbox_get_domains() as $domain) {
|
||||
$resources = mailbox_get_resources($domain);
|
||||
if (!empty($resources)) {
|
||||
foreach ($resources as $resource) {
|
||||
$resourcedata = mailbox_get_resource_details($resource);
|
||||
?>
|
||||
<tr id="data">
|
||||
<td><?=htmlspecialchars($resourcedata['description'], ENT_QUOTES, 'UTF-8');?></td>
|
||||
<td><?=$resourcedata['kind'];?></td>
|
||||
<td><?=htmlspecialchars($resourcedata['domain']);?></td>
|
||||
<td><?=$resourcedata['multiple_bookings'];?></td>
|
||||
<td><?=$resourcedata['active'];?></td>
|
||||
<td style="text-align: right;">
|
||||
<div class="btn-group">
|
||||
<a href="/edit.php?resource=<?=urlencode($resourcedata['name']);?>" class="btn btn-xs btn-default"><span class="glyphicon glyphicon-pencil"></span> <?=$lang['mailbox']['edit'];?></a>
|
||||
<a href="/delete.php?resource=<?=urlencode($resourcedata['name']);?>" class="btn btn-xs btn-danger"><span class="glyphicon glyphicon-trash"></span> <?=$lang['mailbox']['remove'];?></a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
else {
|
||||
?>
|
||||
<tr id="no-data"><td colspan="999" style="text-align: center; font-style: italic;"><?=sprintf($lang['mailbox']['no_record'], $domain);?></td></tr>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
} else {
|
||||
?>
|
||||
<tr id="no-data"><td colspan="999" style="text-align: center; font-style: italic;"><?=$lang['mailbox']['add_domain_record_first'];?></td></tr>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr id="no-data">
|
||||
<td colspan="999" style="text-align: center; border-top: 1px solid #e7e7e7;">
|
||||
<a href="/add.php?resource"><?=$lang['mailbox']['add_resource'];?></a>
|
||||
</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="panel panel-default">
|
||||
|
@ -189,55 +310,43 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
|
|||
</thead>
|
||||
<tbody>
|
||||
<?php
|
||||
try {
|
||||
$stmt = $pdo->prepare("SELECT
|
||||
`alias_domain`,
|
||||
`target_domain`,
|
||||
CASE `active` WHEN 1 THEN '".$lang['mailbox']['yes']."' ELSE '".$lang['mailbox']['no']."' END AS `active`
|
||||
FROM `alias_domain`
|
||||
WHERE `target_domain` IN (
|
||||
SELECT `domain` FROM `domain_admins`
|
||||
WHERE `username`= :username
|
||||
AND `active`='1'
|
||||
)
|
||||
OR 'admin' = :admin");
|
||||
$stmt->execute(array(
|
||||
':username' => $_SESSION['mailcow_cc_username'],
|
||||
':admin' => $_SESSION['mailcow_cc_role'],
|
||||
));
|
||||
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
} catch(PDOException $e) {
|
||||
$_SESSION['return'] = array(
|
||||
'type' => 'danger',
|
||||
'msg' => 'MySQL: '.$e
|
||||
);
|
||||
}
|
||||
if(!empty($rows)):
|
||||
while($row = array_shift($rows)):
|
||||
?>
|
||||
<tr id="data">
|
||||
<td><?=htmlspecialchars($row['alias_domain']);?></td>
|
||||
<td><?=htmlspecialchars($row['target_domain']);?></td>
|
||||
<td><?=$row['active'];?></td>
|
||||
<td style="text-align: right;">
|
||||
<div class="btn-group">
|
||||
<a href="/edit.php?aliasdomain=<?=urlencode($row['alias_domain']);?>" class="btn btn-xs btn-default"><span class="glyphicon glyphicon-pencil"></span> <?=$lang['mailbox']['edit'];?></a>
|
||||
<a href="/delete.php?aliasdomain=<?=urlencode($row['alias_domain']);?>" class="btn btn-xs btn-danger"><span class="glyphicon glyphicon-trash"></span> <?=$lang['mailbox']['remove'];?></a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<?php
|
||||
endwhile;
|
||||
else:
|
||||
if (!empty($domains)) {
|
||||
foreach (mailbox_get_domains() as $domain) {
|
||||
$alias_domains = mailbox_get_alias_domains($domain);
|
||||
if (!empty($alias_domains)) {
|
||||
foreach ($alias_domains as $alias_domain) {
|
||||
$aliasdomaindata = mailbox_get_alias_domain_details($alias_domain);
|
||||
?>
|
||||
<tr id="data">
|
||||
<td><?=htmlspecialchars($aliasdomaindata['alias_domain']);?></td>
|
||||
<td><?=htmlspecialchars($aliasdomaindata['target_domain']);?></td>
|
||||
<td><?=$aliasdomaindata['active'];?></td>
|
||||
<td style="text-align: right;">
|
||||
<div class="btn-group">
|
||||
<a href="/edit.php?aliasdomain=<?=urlencode($aliasdomaindata['alias_domain']);?>" class="btn btn-xs btn-default"><span class="glyphicon glyphicon-pencil"></span> <?=$lang['mailbox']['edit'];?></a>
|
||||
<a href="/delete.php?aliasdomain=<?=urlencode($aliasdomaindata['alias_domain']);?>" class="btn btn-xs btn-danger"><span class="glyphicon glyphicon-trash"></span> <?=$lang['mailbox']['remove'];?></a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
else {
|
||||
?>
|
||||
<tr id="no-data"><td colspan="4" style="text-align: center; font-style: italic;"><?=$lang['mailbox']['no_record'];?></td></tr>
|
||||
<tr id="no-data"><td colspan="999" style="text-align: center; font-style: italic;"><?=sprintf($lang['mailbox']['no_record'], $domain);?></td></tr>
|
||||
<?php
|
||||
endif;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
?>
|
||||
<tr id="no-data"><td colspan="999" style="text-align: center; font-style: italic;"><?=$lang['mailbox']['add_domain_record_first'];?></td></tr>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr id="no-data">
|
||||
<td colspan="8" style="text-align: center; border-top: 1px solid #e7e7e7;">
|
||||
<td colspan="999" style="text-align: center; border-top: 1px solid #e7e7e7;">
|
||||
<a href="/add.php?aliasdomain"><?=$lang['mailbox']['add_domain_alias'];?></a>
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -247,136 +356,7 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title"><?=$lang['mailbox']['mailboxes'];?> <span class="badge" id="numRowsMailbox"></span></h3>
|
||||
<div class="pull-right">
|
||||
<span class="clickable filter" data-toggle="tooltip" title="<?=$lang['mailbox']['filter_table'];?>" data-container="body">
|
||||
<i class="glyphicon glyphicon-filter"></i>
|
||||
</span>
|
||||
<a href="/add.php?mailbox"><span class="glyphicon glyphicon-plus"></span></a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<input type="text" class="form-control" id="mailboxtable-filter" data-action="filter" data-filters="#mailboxtable" placeholder="Filter" />
|
||||
</div>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped sortable-theme-bootstrap" data-sortable id="mailboxtable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="sort-table" style="min-width: 100px;"><?=$lang['mailbox']['username'];?></th>
|
||||
<th class="sort-table" style="min-width: 98px;"><?=$lang['mailbox']['fname'];?></th>
|
||||
<th class="sort-table" style="min-width: 86px;"><?=$lang['mailbox']['domain'];?></th>
|
||||
<th class="sort-table" style="min-width: 75px;"><?=$lang['mailbox']['quota'];?></th>
|
||||
<th class="sort-table" style="min-width: 99px;"><?=$lang['mailbox']['in_use'];?></th>
|
||||
<th class="sort-table" style="min-width: 100px;"><?=$lang['mailbox']['msg_num'];?></th>
|
||||
<th class="sort-table" style="min-width: 76px;"><?=$lang['mailbox']['active'];?></th>
|
||||
<th style="text-align: right; min-width: 200px;" data-sortable="false"><?=$lang['mailbox']['action'];?></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php
|
||||
try {
|
||||
$stmt = $pdo->prepare("SELECT
|
||||
`domain`.`backupmx`,
|
||||
`mailbox`.`username`,
|
||||
`mailbox`.`name`,
|
||||
CASE `mailbox`.`active` WHEN 1 THEN '".$lang['mailbox']['yes']."' ELSE '".$lang['mailbox']['no']."' END AS `active`,
|
||||
`mailbox`.`domain`,
|
||||
`mailbox`.`quota`,
|
||||
`quota2`.`bytes`,
|
||||
`quota2`.`messages`
|
||||
FROM `mailbox`, `quota2`, `domain`
|
||||
WHERE (`mailbox`.`username` = `quota2`.`username`)
|
||||
AND (`domain`.`domain` = `mailbox`.`domain`)
|
||||
AND (`mailbox`.`domain` IN (
|
||||
SELECT `domain` FROM `domain_admins`
|
||||
WHERE `username`= :username
|
||||
AND `active`='1'
|
||||
)
|
||||
OR 'admin' = :admin)");
|
||||
$stmt->execute(array(
|
||||
':username' => $_SESSION['mailcow_cc_username'],
|
||||
':admin' => $_SESSION['mailcow_cc_role'],
|
||||
));
|
||||
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
}
|
||||
catch (PDOException $e) {
|
||||
$_SESSION['return'] = array(
|
||||
'type' => 'danger',
|
||||
'msg' => 'MySQL: '.$e
|
||||
);
|
||||
return false;
|
||||
}
|
||||
if(!empty($rows)):
|
||||
while($row = array_shift($rows)):
|
||||
?>
|
||||
<tr id="data">
|
||||
<?php
|
||||
if ($row['backupmx'] == "0"):
|
||||
?>
|
||||
<td><?=htmlspecialchars($row['username']);?></td>
|
||||
<?php
|
||||
else:
|
||||
?>
|
||||
<td><span data-toggle="tooltip" title="Relayed"><i class="glyphicon glyphicon-forward"></i> <?=htmlspecialchars($row['username']);?></span></td>
|
||||
<?php
|
||||
endif;
|
||||
?>
|
||||
<td><?=htmlspecialchars($row['name'], ENT_QUOTES, 'UTF-8');?></td>
|
||||
<td><?=htmlspecialchars($row['domain']);?></td>
|
||||
<td><?=formatBytes(intval($row['bytes']), 2);?> / <?=formatBytes(intval($row['quota']), 2);?></td>
|
||||
<td style="min-width:120px;">
|
||||
<?php
|
||||
$percentInUse = round((intval($row['bytes']) / intval($row['quota'])) * 100);
|
||||
if ($percentInUse >= 90) {
|
||||
$pbar = "progress-bar-danger";
|
||||
}
|
||||
elseif ($percentInUse >= 75) {
|
||||
$pbar = "progress-bar-warning";
|
||||
}
|
||||
else {
|
||||
$pbar = "progress-bar-success";
|
||||
}
|
||||
?>
|
||||
<div class="progress">
|
||||
<div class="progress-bar <?=$pbar;?>" role="progressbar" aria-valuenow="<?=$percentInUse;?>" aria-valuemin="0" aria-valuemax="100" style="min-width:2em;width: <?=$percentInUse;?>%;">
|
||||
<?=$percentInUse;?>%
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td><?=$row['messages'];?></td>
|
||||
<td><?=$row['active'];?></td>
|
||||
<td style="text-align: right;">
|
||||
<div class="btn-group">
|
||||
<a href="/edit.php?mailbox=<?=urlencode($row['username']);?>" class="btn btn-xs btn-default"><span class="glyphicon glyphicon-pencil"></span> <?=$lang['mailbox']['edit'];?></a>
|
||||
<a href="/delete.php?mailbox=<?=urlencode($row['username']);?>" class="btn btn-xs btn-danger"><span class="glyphicon glyphicon-trash"></span> <?=$lang['mailbox']['remove'];?></a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<?php
|
||||
endwhile;
|
||||
else:
|
||||
?>
|
||||
<tr id="no-data"><td colspan="8" style="text-align: center; font-style: italic;"><?=$lang['mailbox']['no_record'];?></td></tr>
|
||||
<?php
|
||||
endif;
|
||||
?>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr id="no-data">
|
||||
<td colspan="8" style="text-align: center; border-top: 1px solid #e7e7e7;">
|
||||
<a href="/add.php?mailbox"><?=$lang['mailbox']['add_mailbox'];?></a>
|
||||
</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="panel panel-default">
|
||||
|
@ -405,80 +385,52 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
|
|||
</thead>
|
||||
<tbody>
|
||||
<?php
|
||||
try {
|
||||
$stmt = $pdo->prepare("SELECT
|
||||
`address`,
|
||||
`goto`,
|
||||
`domain`,
|
||||
CASE `active` WHEN 1 THEN '".$lang['mailbox']['yes']."' ELSE '".$lang['mailbox']['no']."' END AS `active`
|
||||
FROM alias
|
||||
WHERE (
|
||||
`address` NOT IN (
|
||||
SELECT `username` FROM `mailbox`
|
||||
)
|
||||
AND `address` != `goto`
|
||||
) AND (`domain` IN (
|
||||
SELECT `domain` FROM `domain_admins`
|
||||
WHERE `username` = :username
|
||||
AND active='1'
|
||||
)
|
||||
OR 'admin' = :admin)");
|
||||
$stmt->execute(array(
|
||||
':username' => $_SESSION['mailcow_cc_username'],
|
||||
':admin' => $_SESSION['mailcow_cc_role'],
|
||||
));
|
||||
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
}
|
||||
catch (PDOException $e) {
|
||||
$_SESSION['return'] = array(
|
||||
'type' => 'danger',
|
||||
'msg' => 'MySQL: '.$e
|
||||
);
|
||||
return false;
|
||||
}
|
||||
if(!empty($rows)):
|
||||
while($row = array_shift($rows)):
|
||||
if (!empty($domains)) {
|
||||
foreach (array_merge(mailbox_get_domains(), mailbox_get_alias_domains()) as $domain) {
|
||||
$aliases = mailbox_get_aliases($domain);
|
||||
if (!empty($aliases)) {
|
||||
foreach ($aliases as $alias) {
|
||||
$aliasdata = mailbox_get_alias_details($alias);
|
||||
?>
|
||||
<tr id="data">
|
||||
<td>
|
||||
<?php
|
||||
if(!filter_var($row['address'], FILTER_VALIDATE_EMAIL)):
|
||||
?>
|
||||
<span class="glyphicon glyphicon-pushpin" aria-hidden="true"></span> Catch-all @<?=htmlspecialchars($row['domain']);?>
|
||||
<?php
|
||||
else:
|
||||
echo htmlspecialchars($row['address']);
|
||||
endif;
|
||||
?>
|
||||
<?= ($aliasdata['is_catch_all'] == "1") ? '<span class="glyphicon glyphicon-pushpin" aria-hidden="true"></span> Catch-all ' . htmlspecialchars($aliasdata['address']) : htmlspecialchars($aliasdata['address']); ?>
|
||||
</td>
|
||||
<td>
|
||||
<?php
|
||||
foreach(explode(",", $row['goto']) as $goto) {
|
||||
foreach(explode(",", $aliasdata['goto']) as $goto) {
|
||||
echo nl2br(htmlspecialchars($goto.PHP_EOL));
|
||||
}
|
||||
?>
|
||||
</td>
|
||||
<td><?=htmlspecialchars($row['domain']);?></td>
|
||||
<td><?=$row['active'];?></td>
|
||||
<td><?=htmlspecialchars($aliasdata['domain']);?></td>
|
||||
<td><?=$aliasdata['active'];?></td>
|
||||
<td style="text-align: right;">
|
||||
<div class="btn-group">
|
||||
<a href="/edit.php?alias=<?=urlencode($row['address']);?>" class="btn btn-xs btn-default"><span class="glyphicon glyphicon-pencil"></span> <?=$lang['mailbox']['edit'];?></a>
|
||||
<a href="/delete.php?alias=<?=urlencode($row['address']);?>" class="btn btn-xs btn-danger"><span class="glyphicon glyphicon-trash"></span> <?=$lang['mailbox']['remove'];?></a>
|
||||
<a href="/edit.php?alias=<?=urlencode($aliasdata['address']);?>" class="btn btn-xs btn-default"><span class="glyphicon glyphicon-pencil"></span> <?=$lang['mailbox']['edit'];?></a>
|
||||
<a href="/delete.php?alias=<?=urlencode($aliasdata['address']);?>" class="btn btn-xs btn-danger"><span class="glyphicon glyphicon-trash"></span> <?=$lang['mailbox']['remove'];?></a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<?php
|
||||
endwhile;
|
||||
else:
|
||||
?>
|
||||
<tr id="no-data"><td colspan="5" style="text-align: center; font-style: italic;"><?=$lang['mailbox']['no_record'];?></td></tr>
|
||||
<?php
|
||||
endif;
|
||||
?>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
else {
|
||||
?>
|
||||
<tr id="no-data"><td colspan="999" style="text-align: center; font-style: italic;"><?=sprintf($lang['mailbox']['no_record'], $domain);?></td></tr>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
} else {
|
||||
?>
|
||||
<tr id="no-data"><td colspan="999" style="text-align: center; font-style: italic;"><?=$lang['mailbox']['add_domain_record_first'];?></td></tr>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr id="no-data">
|
||||
<td colspan="8" style="text-align: center; border-top: 1px solid #e7e7e7;">
|
||||
<td colspan="999" style="text-align: center; border-top: 1px solid #e7e7e7;">
|
||||
<a href="/add.php?alias"><?=$lang['mailbox']['add_alias'];?></a>
|
||||
</td>
|
||||
</tr>
|
||||
|
|
|
@ -0,0 +1,156 @@
|
|||
<?php
|
||||
require_once('inc/prerequisites.inc.php');
|
||||
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_OBJ);
|
||||
|
||||
$u2f = new u2flib_server\U2F('https://' . $_SERVER['SERVER_NAME']);
|
||||
|
||||
function getRegs($username) {
|
||||
global $pdo;
|
||||
$sel = $pdo->prepare("select * from tfa where username = ?");
|
||||
$sel->execute(array($username));
|
||||
return $sel->fetchAll();
|
||||
}
|
||||
function addReg($username, $reg) {
|
||||
global $pdo;
|
||||
$ins = $pdo->prepare("INSERT INTO `tfa` (`username`, `keyHandle`, `publicKey`, `certificate`, `counter`) values (?, ?, ?, ?, ?)");
|
||||
$ins->execute(array($username, $reg->keyHandle, $reg->publicKey, $reg->certificate, $reg->counter));
|
||||
}
|
||||
function updateReg($reg) {
|
||||
global $pdo;
|
||||
$upd = $pdo->prepare("update tfa set counter = ? where id = ?");
|
||||
$upd->execute(array($reg->counter, $reg->id));
|
||||
}
|
||||
?>
|
||||
<html>
|
||||
<head>
|
||||
<script src="js/u2f-api.js"></script>
|
||||
<?php
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
if ((empty($_POST['u2f_username'])) || (!isset($_POST['action']) && !isset($_POST['u2f_register_data']) && !isset($_POST['u2f_auth_data']))) {
|
||||
print_r($_POST);
|
||||
exit();
|
||||
}
|
||||
else {
|
||||
$username = $_POST['u2f_username'];
|
||||
if (isset($_POST['action'])) {
|
||||
switch($_POST['action']) {
|
||||
case 'register':
|
||||
try {
|
||||
$data = $u2f->getRegisterData(getRegs($username));
|
||||
list($req, $sigs) = $data;
|
||||
$_SESSION['regReq'] = json_encode($req);
|
||||
?>
|
||||
<script>
|
||||
var req = <?=json_encode($req);?>;
|
||||
var sigs = <?=json_encode($sigs);?>;
|
||||
var username = "<?=$username;?>";
|
||||
setTimeout(function() {
|
||||
console.log("Register: ", req);
|
||||
u2f.register([req], sigs, function(data) {
|
||||
var form = document.getElementById('u2f_form');
|
||||
var reg = document.getElementById('u2f_register_data');
|
||||
var user = document.getElementById('u2f_username');
|
||||
var status = document.getElementById('u2f_status');
|
||||
console.log("Register callback", data);
|
||||
if (data.errorCode && data.errorCode != 0) {
|
||||
var div = document.getElementById('u2f_return_code');
|
||||
div.innerHTML = 'Error code: ' + data.errorCode;
|
||||
return;
|
||||
}
|
||||
reg.value = JSON.stringify(data);
|
||||
user.value = username;
|
||||
status.value = "1";
|
||||
form.submit();
|
||||
});
|
||||
}, 1000);
|
||||
</script>
|
||||
<?php
|
||||
}
|
||||
catch( Exception $e ) {
|
||||
echo "U2F error: " . $e->getMessage();
|
||||
}
|
||||
break;
|
||||
|
||||
case 'authenticate':
|
||||
try {
|
||||
$reqs = json_encode($u2f->getAuthenticateData(getRegs($username)));
|
||||
$_SESSION['authReq'] = $reqs;
|
||||
?>
|
||||
<script>
|
||||
var req = <?=$reqs;?>;
|
||||
var username = "<?=$username;?>";
|
||||
setTimeout(function() {
|
||||
console.log("sign: ", req);
|
||||
u2f.sign(req, function(data) {
|
||||
var form = document.getElementById('u2f_form');
|
||||
var auth = document.getElementById('u2f_auth_data');
|
||||
var user = document.getElementById('u2f_username');
|
||||
console.log("Authenticate callback", data);
|
||||
auth.value = JSON.stringify(data);
|
||||
user.value = username;
|
||||
form.submit();
|
||||
});
|
||||
}, 1000);
|
||||
</script>
|
||||
<?php
|
||||
}
|
||||
catch (Exception $e) {
|
||||
echo "U2F error: " . $e->getMessage();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!empty($_POST['u2f_register_data'])) {
|
||||
try {
|
||||
$reg = $u2f->doRegister(json_decode($_SESSION['regReq']), json_decode($_POST['u2f_register_data']));
|
||||
addReg($username, $reg);
|
||||
}
|
||||
catch (Exception $e) {
|
||||
echo "U2F error: " . $e->getMessage();
|
||||
}
|
||||
finally {
|
||||
echo "Success";
|
||||
$_SESSION['regReq'] = null;
|
||||
}
|
||||
}
|
||||
if (!empty($_POST['u2f_auth_data'])) {
|
||||
try {
|
||||
$reg = $u2f->doAuthenticate(json_decode($_SESSION['authReq']), getRegs($username), json_decode($_POST['u2f_auth_data']));
|
||||
updateReg($reg);
|
||||
}
|
||||
catch (Exception $e) {
|
||||
echo "U2F error: " . $e->getMessage();
|
||||
}
|
||||
finally {
|
||||
echo "Success";
|
||||
$_SESSION['authReq'] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
</head>
|
||||
<body>
|
||||
<div id="u2f_return_code"></div>
|
||||
<form method="POST" id="u2f_form">
|
||||
<input type="hidden" name="u2f_register_data" id="u2f_register_data"/>
|
||||
<input type="hidden" name="u2f_auth_data" id="u2f_auth_data"/>
|
||||
<input type="hidden" name="u2f_username" id="u2f_username"/><br/>
|
||||
<input type="hidden" name="u2f_status" id="u2f_status"/><br/>
|
||||
</form>
|
||||
<?php
|
||||
}
|
||||
else {
|
||||
?>
|
||||
<form method="POST" id="post_form">
|
||||
Username: <input name="u2f_username" id="u2f_username"/><br/><hr>
|
||||
Action: <br />
|
||||
<input value="register" name="action" type="radio"/> Register<br/>
|
||||
<input value="authenticate" name="action" type="radio"/> Authenticate<br/>
|
||||
<button type="submit">Submit!</button>
|
||||
</form>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
</body>
|
||||
</html>
|
|
@ -1,90 +1,134 @@
|
|||
<?php
|
||||
require_once("inc/prerequisites.inc.php");
|
||||
if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == 'domainadmin') {
|
||||
|
||||
/*
|
||||
/ DOMAIN ADMIN
|
||||
*/
|
||||
|
||||
require_once("inc/header.inc.php");
|
||||
$_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
|
||||
$tfa_data = get_tfa();
|
||||
$username = $_SESSION['mailcow_cc_username'];
|
||||
?>
|
||||
<div class="container">
|
||||
<h3><?=$lang['user']['user_settings'];?></h3>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading"><?=$lang['user']['user_settings'];?></div>
|
||||
<div class="panel-body">
|
||||
<div class="row">
|
||||
<div class="col-sm-offset-3 col-sm-9">
|
||||
<p><a href="#pwChangeModal" data-toggle="modal">[<?=$lang['user']['change_password'];?>]</a></p>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="row">
|
||||
<div class="col-md-3 col-xs-5 text-right"><?=$lang['tfa']['tfa'];?></div>
|
||||
<div class="col-sm-9 col-xs-7">
|
||||
<p id="tfa_pretty"><?=$tfa_data['pretty'];?></p>
|
||||
<div id="tfa_additional">
|
||||
<?php if($tfa_data['additional']):
|
||||
foreach ($tfa_data['additional'] as $key_info): ?>
|
||||
<form style="display:inline;" method="post">
|
||||
<input type="hidden" name="unset_tfa_key" value="<?=$key_info['id'];?>" />
|
||||
<div class="label label-default">?? <?=$key_info['key_id'];?> <a href="#" style="font-weight:bold;color:white" onClick="$(this).closest('form').submit()">[<?=strtolower($lang['admin']['remove']);?>]</a></div>
|
||||
</form>
|
||||
<?php endforeach;
|
||||
endif;?>
|
||||
</div>
|
||||
<br />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-3 col-xs-5 text-right"><?=$lang['tfa']['set_tfa'];?></div>
|
||||
<div class="col-md-9 col-xs-7">
|
||||
<select id="selectTFA" class="selectpicker" title="<?=$lang['tfa']['select'];?>">
|
||||
<option value="yubi_otp"><?=$lang['tfa']['yubi_otp'];?></option>
|
||||
<option value="u2f"><?=$lang['tfa']['u2f'];?></option>
|
||||
<option value="none"><?=$lang['tfa']['none'];?></option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == 'user') {
|
||||
|
||||
/*
|
||||
/ USER
|
||||
*/
|
||||
|
||||
if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == 'user') {
|
||||
require_once("inc/header.inc.php");
|
||||
$_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
|
||||
$username = $_SESSION['mailcow_cc_username'];
|
||||
$get_tls_policy = get_tls_policy($_SESSION['mailcow_cc_username']);
|
||||
$mailboxdata = mailbox_get_mailbox_details($username);
|
||||
?>
|
||||
<div class="container">
|
||||
<h3><?=$lang['user']['mailbox_settings'];?></h3>
|
||||
<p class="help-block"><?=$lang['user']['did_you_know'];?></p>
|
||||
<h3><?=$lang['user']['user_settings'];?></h3>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading"><?=$lang['user']['mailbox_details'];?></div>
|
||||
<div class="panel-body">
|
||||
<form class="form-horizontal" role="form" method="post" autocomplete="off">
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-3 col-sm-10">
|
||||
<div class="checkbox">
|
||||
<label><input type="checkbox" name="togglePwNew" id="togglePwNew"> <?=$lang['user']['change_password'];?></label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-offset-3 col-sm-9">
|
||||
<p><a href="#pwChangeModal" data-toggle="modal">[<?=$lang['user']['change_password'];?>]</a></p>
|
||||
</div>
|
||||
<div class="passFields">
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-3" for="user_new_pass"><?=$lang['user']['new_password'];?></label>
|
||||
<div class="col-sm-5">
|
||||
<input type="password" class="form-control" pattern=".{6,}" name="user_new_pass" id="user_new_pass" autocomplete="off" disabled="disabled" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-3" for="user_new_pass2"><?=$lang['user']['new_password_repeat'];?></label>
|
||||
<div class="col-sm-5">
|
||||
<input type="password" class="form-control" pattern=".{6,}" name="user_new_pass2" id="user_new_pass2" disabled="disabled" autocomplete="off" required>
|
||||
<p class="help-block"><?=$lang['user']['new_password_description'];?></p>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-3" for="user_old_pass"><?=$lang['user']['password_now'];?></label>
|
||||
<div class="col-sm-5">
|
||||
<input type="password" class="form-control" name="user_old_pass" id="user_old_pass" autocomplete="off" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-3 col-sm-9">
|
||||
<button type="submit" name="trigger_set_user_account" class="btn btn-success btn-default"><?=$lang['user']['save_changes'];?></button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<hr>
|
||||
<?php // Get user information about aliases
|
||||
$get_user_object_info = user_object_info('get');?>
|
||||
$user_get_alias_details = user_get_alias_details($username);?>
|
||||
<div class="row">
|
||||
<div class="col-md-3 col-xs-5 text-right"><?=$lang['user']['aliases'];?>:</div>
|
||||
<div class="col-md-9 col-xs-7">
|
||||
<p><?=$get_user_object_info['aliases'];?></p>
|
||||
<p><?=$user_get_alias_details['aliases'];?></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-3 col-xs-5 text-right"><?=$lang['user']['domain_aliases'];?>:</div>
|
||||
<div class="col-md-9 col-xs-7">
|
||||
<p><?=$user_get_alias_details['ad_alias'];?></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-3 col-xs-5 text-right"><?=$lang['user']['aliases_also_send_as'];?>:</div>
|
||||
<div class="col-md-9 col-xs-7">
|
||||
<p><?=$get_user_object_info['aliases_also_send_as'];?></p>
|
||||
<p><?=$user_get_alias_details['aliases_also_send_as'];?></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-3 col-xs-5 text-right"><?=$lang['user']['aliases_send_as_all'];?>:</div>
|
||||
<div class="col-md-9 col-xs-7">
|
||||
<p><?=$get_user_object_info['aliases_send_as_all'];?></p>
|
||||
<p><?=$user_get_alias_details['aliases_send_as_all'];?></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-3 col-xs-5 text-right"><?=$lang['user']['is_catch_all'];?>:</div>
|
||||
<div class="col-md-9 col-xs-7">
|
||||
<p><?=$get_user_object_info['is_catch_all'];?></p>
|
||||
<p><?=$user_get_alias_details['is_catch_all'];?></p>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="row">
|
||||
<div class="col-md-3 col-xs-5 text-right"><?=$lang['user']['in_use'];?>:</div>
|
||||
<div class="col-md-5 col-xs-7">
|
||||
<div class="progress">
|
||||
<div class="progress-bar progress-bar-<?=$mailboxdata['percent_class'];?>" role="progressbar" aria-valuenow="<?=$mailboxdata['percent_in_use'];?>" aria-valuemin="0" aria-valuemax="100" style="min-width:2em;width: <?=$mailboxdata['percent_in_use'];?>%;">
|
||||
<?=$mailboxdata['percent_in_use'];?>%
|
||||
</div>
|
||||
</div>
|
||||
<p><?=formatBytes($mailboxdata['quota_used'], 2);?> / <?=formatBytes($mailboxdata['quota'], 2);?>, <?=$mailboxdata['messages'];?> <?=$lang['user']['messages'];?></p>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<?php // Show tagging options ?>
|
||||
<form class="form-horizontal" role="form" method="post">
|
||||
<?php $get_tagging_options = tagging_options('get');?>
|
||||
<?php $get_tagging_options = get_delimiter_action()['wants_tagged_subject'];?>
|
||||
<div class="row">
|
||||
<div class="col-md-3 col-xs-5 text-right"><?=$lang['user']['tag_handling'];?>:</div>
|
||||
<div class="col-md-9 col-xs-7">
|
||||
<input type="hidden" name="trigger_set_tagging_options" value="1">
|
||||
<input type="hidden" name="edit_delimiter_action" value="1">
|
||||
<select name="tagged_mail_handler" class="selectpicker" onchange="this.form.submit()">
|
||||
<option value="subfolder" <?=($get_tagging_options == "0") ? 'selected' : null; ?>><?=$lang['user']['tag_in_subfolder'];?></option>
|
||||
<option value="subject" <?=($get_tagging_options == "1") ? 'selected' : null; ?>><?=$lang['user']['tag_in_subject'];?></option>
|
||||
|
@ -94,6 +138,16 @@ if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == 'user
|
|||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<?php // Rest EAS devices ?>
|
||||
<form class="form-horizontal" role="form" method="post">
|
||||
<div class="row">
|
||||
<div class="col-md-3 col-xs-5 text-right"><?=$lang['user']['eas_reset'];?>:</div>
|
||||
<div class="col-md-9 col-xs-7">
|
||||
<button type="submit" name="mailbox_reset_eas" id="mailbox_reset_eas" value="1" class="btn btn-xs btn-default"><?=$lang['user']['eas_reset_now'];?></button>
|
||||
<p class="help-block"><?=$lang['user']['eas_reset_help'];?></p>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -102,56 +156,61 @@ if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == 'user
|
|||
<li role="presentation" class="active"><a href="#SpamAliases" aria-controls="SpamAliases" role="tab" data-toggle="tab"><?=$lang['user']['spam_aliases'];?></a></li>
|
||||
<li role="presentation"><a href="#Spamfilter" aria-controls="Spamfilter" role="tab" data-toggle="tab"><?=$lang['user']['spamfilter'];?></a></li>
|
||||
<li role="presentation"><a href="#TLSPolicy" aria-controls="TLSPolicy" role="tab" data-toggle="tab"><?=$lang['user']['tls_policy'];?></a></li>
|
||||
<li role="presentation"><a href="#Syncjobs" aria-controls="Syncjobs" role="tab" data-toggle="tab"><?=$lang['user']['sync_jobs'];?></a></li>
|
||||
</ul>
|
||||
<hr>
|
||||
|
||||
<div class="tab-content">
|
||||
<div role="tabpanel" class="tab-pane active" id="SpamAliases">
|
||||
<form class="form-horizontal" role="form" method="post">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped sortable-theme-bootstrap" data-sortable id="timelimitedaliases">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="sort-table" style="min-width: 96px;"><?=$lang['user']['alias'];?></th>
|
||||
<th class="sort-table" style="min-width: 135px;"><?=$lang['user']['alias_valid_until'];?></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<div class="row">
|
||||
<div class="col-xs-6">
|
||||
<p><b><?=$lang['user']['alias'];?></b></p>
|
||||
</div>
|
||||
<div class="col-xs-2">
|
||||
<p><b><?=$lang['user']['alias_valid_until'];?></b></p>
|
||||
</div>
|
||||
<div class="col-xs-2">
|
||||
<p><b><?=$lang['user']['action'];?></b></p>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
try {
|
||||
$stmt = $pdo->prepare("SELECT `address`,
|
||||
`goto`,
|
||||
`validity`
|
||||
FROM `spamalias`
|
||||
WHERE `goto` = :username
|
||||
AND `validity` >= :unixnow");
|
||||
$stmt->execute(array(':username' => $username, ':unixnow' => time()));
|
||||
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
}
|
||||
catch(PDOException $e) {
|
||||
$_SESSION['return'] = array(
|
||||
'type' => 'danger',
|
||||
'msg' => 'MySQL: '.$e
|
||||
);
|
||||
}
|
||||
if(!empty($rows)):
|
||||
while ($row = array_shift($rows)):
|
||||
?>
|
||||
<tr id="data">
|
||||
<td><?=htmlspecialchars($row['address']);?></td>
|
||||
<td><?=htmlspecialchars(date($lang['user']['alias_full_date'], $row['validity']));?></td>
|
||||
</tr>
|
||||
<?php
|
||||
endwhile;
|
||||
$get_time_limited_aliases = get_time_limited_aliases($username);
|
||||
if (!empty($get_time_limited_aliases)):
|
||||
foreach ($get_time_limited_aliases as $row):
|
||||
?>
|
||||
<div class="row">
|
||||
<div class="col-xs-6">
|
||||
<p><?=htmlspecialchars($row['address']);?></p>
|
||||
</div>
|
||||
<div class="col-xs-2">
|
||||
<p><?=htmlspecialchars(date($lang['user']['alias_full_date'], $row['validity']));?></p>
|
||||
</div>
|
||||
<div class="col-xs-1">
|
||||
<form class="form-inline" role="form" method="post">
|
||||
<a class="text-danger" href="#" onclick="$(this).closest('form').submit()"><span class="glyphicon glyphicon-remove"></span></a>
|
||||
<input type="hidden" name="set_time_limited_aliases" value="delete">
|
||||
<input type="hidden" name="item" value="<?=htmlspecialchars($row['address']);?>">
|
||||
</form>
|
||||
</div>
|
||||
<div class="col-xs-1">
|
||||
<form class="form-inline" role="form" method="post">
|
||||
<a href="#" onclick="$(this).closest('form').submit()"><span class="glyphicon glyphicon-time"></span> + 1h</a>
|
||||
<input type="hidden" name="set_time_limited_aliases" value="extend">
|
||||
<input type="hidden" name="item" value="<?=htmlspecialchars($row['address']);?>">
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
endforeach;
|
||||
else:
|
||||
?>
|
||||
<tr id="no-data"><td colspan="2" style="text-align: center; font-style: italic;"><?=$lang['user']['no_record'];?></td></tr>
|
||||
<div class="col-xs-12">
|
||||
<center><i><?=$lang['user']['no_record'];?></i></center>
|
||||
</div>
|
||||
<?php
|
||||
endif;
|
||||
?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<form class="form-horizontal" role="form" method="post">
|
||||
<div class="form-group">
|
||||
<div class="col-sm-9">
|
||||
<select id="validity" name="validity" title="<?=$lang['user']['alias_select_validity'];?>">
|
||||
|
@ -161,16 +220,16 @@ if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == 'user
|
|||
<option value="168">1 <?=$lang['user']['week'];?></option>
|
||||
<option value="672">4 <?=$lang['user']['weeks'];?></option>
|
||||
</select>
|
||||
<button type="submit" id="trigger_set_time_limited_aliases" name="trigger_set_time_limited_aliases" value="generate" class="btn btn-success"><?=$lang['user']['alias_create_random'];?></button>
|
||||
<button type="submit" name="set_time_limited_aliases" id="generate_tla" value="generate" class="btn btn-success"><?=$lang['user']['alias_create_random'];?></button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12">
|
||||
<button style="border-color:#f5f5f5;background:none;color:red" type="submit" name="trigger_set_time_limited_aliases" value="delete" class="btn btn-sm">
|
||||
<button type="submit" name="set_time_limited_aliases" value="deleteall" class="btn-danger btn btn-sm">
|
||||
<span class="glyphicon glyphicon-remove" aria-hidden="true"></span> <?=$lang['user']['alias_remove_all'];?>
|
||||
</button>
|
||||
<button style="border-color:#f5f5f5;background:none;color:grey" type="submit" name="trigger_set_time_limited_aliases" value="extend" class="btn btn-sm">
|
||||
<span class="glyphicon glyphicon-hourglass" aria-hidden="true"></span> <?=$lang['user']['alias_extend_all'];?>
|
||||
<button type="submit" name="set_time_limited_aliases" value="extendall" class="btn-default btn btn-sm">
|
||||
<span class="glyphicon glyphicon-time" aria-hidden="true"></span> <?=$lang['user']['alias_extend_all'];?>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -189,7 +248,7 @@ if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == 'user
|
|||
data-slider-range="true"
|
||||
data-slider-tooltip='always'
|
||||
data-slider-id="slider1"
|
||||
data-slider-value="[<?=get_spam_score($_SESSION['mailcow_cc_username']);?>]"
|
||||
data-slider-value="[<?=get_spam_score($username);?>]"
|
||||
data-slider-step="1" />
|
||||
<br /><br />
|
||||
<ul>
|
||||
|
@ -203,7 +262,7 @@ if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == 'user
|
|||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-2 col-sm-10">
|
||||
<button type="submit" id="trigger_set_spam_score" name="trigger_set_spam_score" class="btn btn-success"><?=$lang['user']['save_changes'];?></button>
|
||||
<button type="submit" id="edit_spam_score" name="edit_spam_score" class="btn btn-success"><?=$lang['user']['save_changes'];?></button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
@ -217,49 +276,39 @@ if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == 'user
|
|||
<div class="col-sm-6"><b><?=$lang['user']['spamfilter_table_action'];?></b></div>
|
||||
</div>
|
||||
<?php
|
||||
try {
|
||||
$stmt = $pdo->prepare("SELECT `value`, `prefid` FROM `filterconf` WHERE `option`='whitelist_from' AND `object`= :username");
|
||||
$stmt->execute(array(':username' => $username));
|
||||
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
}
|
||||
catch(PDOException $e) {
|
||||
$_SESSION['return'] = array(
|
||||
'type' => 'danger',
|
||||
'msg' => 'MySQL: '.$e
|
||||
);
|
||||
}
|
||||
if (count($rows) == 0):
|
||||
$get_policy_list = get_policy_list($username);
|
||||
if (empty($get_policy_list['whitelist'])):
|
||||
?>
|
||||
<div class="row">
|
||||
<div class="col-sm-12"><i><?=$lang['user']['spamfilter_table_empty'];?></i></div>
|
||||
</div>
|
||||
<?php
|
||||
endif;
|
||||
while ($whitelistRow = array_shift($rows)):
|
||||
?>
|
||||
<div class="row striped">
|
||||
<form class="form-inline" method="post">
|
||||
<div class="col-xs-6"><code><?=$whitelistRow['value'];?></code></div>
|
||||
<div class="col-xs-6">
|
||||
<input type="hidden" name="prefid" value="<?=$whitelistRow['prefid'];?>">
|
||||
<?php
|
||||
if ($whitelistRow['username'] != array_pop(explode('@', $username))):
|
||||
?>
|
||||
<input type="hidden" name="trigger_set_policy_list">
|
||||
<a href="#n" onclick="$(this).closest('form').submit()"><?=$lang['user']['spamfilter_table_remove'];?></a>
|
||||
<?php
|
||||
else:
|
||||
?>
|
||||
<span style="cursor:not-allowed"><?=$lang['user']['spamfilter_table_domain_policy'];?></span>
|
||||
<?php
|
||||
endif;
|
||||
?>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
endwhile;
|
||||
else:
|
||||
foreach($get_policy_list['whitelist'] as $wl):
|
||||
?>
|
||||
<div class="row striped">
|
||||
<form class="form-inline" method="post">
|
||||
<div class="col-xs-6"><code><?=$wl['value'];?></code></div>
|
||||
<div class="col-xs-6">
|
||||
<input type="hidden" name="delete_prefid" value="<?=$wl['prefid'];?>">
|
||||
<?php
|
||||
if (filter_var($wl['object'], FILTER_VALIDATE_EMAIL)):
|
||||
?>
|
||||
<input type="hidden" name="delete_policy_list_item">
|
||||
<a href="#" onclick="$(this).closest('form').submit()" data-toggle="tooltip" data-placement="left" title="<?=$lang['user']['delete_now'];?>"><span class="glyphicon glyphicon-remove"></span></a>
|
||||
<?php
|
||||
else:
|
||||
?>
|
||||
<span style="cursor:not-allowed"><?=$lang['user']['spamfilter_table_domain_policy'];?></span>
|
||||
<?php
|
||||
endif;
|
||||
?>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<?php
|
||||
endforeach;
|
||||
endif;
|
||||
?>
|
||||
<hr style="margin:5px 0px 7px 0px">
|
||||
<div class="row">
|
||||
|
@ -269,7 +318,7 @@ if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == 'user
|
|||
<input type="hidden" name="object_list" value="wl">
|
||||
</div>
|
||||
<div class="col-xs-6">
|
||||
<button type="submit" id="trigger_set_policy_list" name="trigger_set_policy_list" class="btn btn-xs btn-default"><?=$lang['user']['spamfilter_table_add'];?></button>
|
||||
<button type="submit" id="add_policy_list_item" name="add_policy_list_item" class="btn btn-xs btn-default"><?=$lang['user']['spamfilter_table_add'];?></button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
@ -282,48 +331,38 @@ if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == 'user
|
|||
<div class="col-sm-6"><b><?=$lang['user']['spamfilter_table_action'];?></b></div>
|
||||
</div>
|
||||
<?php
|
||||
try {
|
||||
$stmt = $pdo->prepare("SELECT `value`, `prefid` FROM `filterconf` WHERE `option`='blacklist_from' AND `object`= :username");
|
||||
$stmt->execute(array(':username' => $username));
|
||||
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
}
|
||||
catch(PDOException $e) {
|
||||
$_SESSION['return'] = array(
|
||||
'type' => 'danger',
|
||||
'msg' => 'MySQL: '.$e
|
||||
);
|
||||
}
|
||||
if (count($rows) == 0):
|
||||
if (empty($get_policy_list['blacklist'])):
|
||||
?>
|
||||
<div class="row">
|
||||
<div class="col-sm-12"><i><?=$lang['user']['spamfilter_table_empty'];?></i></div>
|
||||
</div>
|
||||
<?php
|
||||
endif;
|
||||
while ($blacklistRow = array_shift($rows)):
|
||||
?>
|
||||
<div class="row striped">
|
||||
<form class="form-inline" method="post">
|
||||
<div class="col-xs-6"><code><?=$blacklistRow['value'];?></code></div>
|
||||
<div class="col-xs-6">
|
||||
<input type="hidden" name="prefid" value="<?=$blacklistRow['prefid'];?>">
|
||||
<?php
|
||||
if ($blacklistRow['username'] != array_pop(explode('@', $username))):
|
||||
?>
|
||||
<input type="hidden" name="trigger_set_policy_list">
|
||||
<a href="#n" onclick="$(this).closest('form').submit()"><?=$lang['user']['spamfilter_table_remove'];?></a>
|
||||
<?php
|
||||
else:
|
||||
?>
|
||||
<span style="cursor:not-allowed"><?=$lang['user']['spamfilter_table_domain_policy'];?></span>
|
||||
<?php
|
||||
endif;
|
||||
?>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<?php
|
||||
endwhile;
|
||||
else:
|
||||
foreach($get_policy_list['blacklist'] as $bl):
|
||||
?>
|
||||
<div class="row striped">
|
||||
<form class="form-inline" method="post">
|
||||
<div class="col-xs-6"><code><?=$bl['value'];?></code></div>
|
||||
<div class="col-xs-6">
|
||||
<?php
|
||||
if (filter_var($bl['object'], FILTER_VALIDATE_EMAIL)):
|
||||
?>
|
||||
<input type="hidden" name="delete_prefid" value="<?=$bl['prefid'];?>">
|
||||
<input type="hidden" name="delete_policy_list_item">
|
||||
<a href="#" onclick="$(this).closest('form').submit()" data-toggle="tooltip" data-placement="left" title="<?=$lang['user']['delete_now'];?>"><span class="glyphicon glyphicon-remove"></span></a>
|
||||
<?php
|
||||
else:
|
||||
?>
|
||||
<span style="cursor:not-allowed"><?=$lang['user']['spamfilter_table_domain_policy'];?></span>
|
||||
<?php
|
||||
endif;
|
||||
?>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<?php
|
||||
endforeach;
|
||||
endif;
|
||||
?>
|
||||
<hr style="margin:5px 0px 7px 0px">
|
||||
<div class="row">
|
||||
|
@ -333,7 +372,7 @@ if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == 'user
|
|||
<input type="hidden" name="object_list" value="bl">
|
||||
</div>
|
||||
<div class="col-xs-6">
|
||||
<button type="submit" id="trigger_set_policy_list" name="trigger_set_policy_list" class="btn btn-xs btn-default"><?=$lang['user']['spamfilter_table_add'];?></button>
|
||||
<button type="submit" id="add_policy_list_item" name="add_policy_list_item" class="btn btn-xs btn-default"><?=$lang['user']['spamfilter_table_add'];?></button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
@ -359,19 +398,144 @@ if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == 'user
|
|||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12">
|
||||
<button type="submit" id="trigger_set_tls_policy" name="trigger_set_tls_policy" class="btn btn-default"><?=$lang['user']['save_changes'];?></button>
|
||||
<button type="submit" id="edit_tls_policy" name="edit_tls_policy" class="btn btn-default"><?=$lang['user']['save_changes'];?></button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div role="tabpanel" class="tab-pane" id="Syncjobs">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped sortable-theme-bootstrap" data-sortable id="timelimitedaliases">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="sort-table" style="min-width: 96px;">Server:Port</th>
|
||||
<th class="sort-table" style="min-width: 96px;"><?=$lang['user']['encryption'];?></th>
|
||||
<th class="sort-table" style="min-width: 96px;"><?=$lang['user']['username'];?></th>
|
||||
<th class="sort-table" style="min-width: 96px;"><?=$lang['user']['excludes'];?></th>
|
||||
<th class="sort-table" style="min-width: 35px;"><?=$lang['user']['interval'];?></th>
|
||||
<th class="sort-table" style="min-width: 35px;"><?=$lang['user']['last_run'];?></th>
|
||||
<th class="sort-table" style="min-width: 35px;">Log</th>
|
||||
<th class="sort-table" style="max-width: 95px;"><?=$lang['user']['active'];?></th>
|
||||
<th style="text-align: right; min-width: 200px;" data-sortable="false"><?=$lang['user']['action'];?></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php
|
||||
$get_syncjobs = get_syncjobs($username);
|
||||
if (!empty($get_syncjobs)):
|
||||
foreach ($get_syncjobs as $row):
|
||||
?>
|
||||
<tr id="data">
|
||||
<td><?=htmlspecialchars($row['host1'] . ':' . $row['port1']);?></td>
|
||||
<td><?=htmlspecialchars($row['enc1']);?></td>
|
||||
<td><?=htmlspecialchars($row['user1']);?></td>
|
||||
<td><?=($row['exclude'] == '') ? '✘' : '<code>' . $row['exclude'] . '</code>';?></td>
|
||||
<td><?=htmlspecialchars($row['mins_interval']);?> min</td>
|
||||
<td><?=(empty($row['last_run'])) ? '✘' : htmlspecialchars(date($lang['user']['syncjob_full_date'], strtotime($row['last_run'] . ' UTC')));?></td>
|
||||
<td>
|
||||
<?php
|
||||
if (empty($row['returned_text'])) {
|
||||
echo '✘';
|
||||
}
|
||||
else {
|
||||
?>
|
||||
<a href="#logModal" data-toggle="modal" data-log-text="<?=htmlspecialchars($row['returned_text']);?>">Open logs</a>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
</td>
|
||||
<td><?=($row['active'] == '1') ? '✔' : '✘';?></td>
|
||||
<td style="text-align: right;">
|
||||
<div class="btn-group">
|
||||
<a href="/edit.php?syncjob=<?=urlencode($row['id']);?>" class="btn btn-xs btn-default"><span class="glyphicon glyphicon-pencil"></span> <?=$lang['user']['edit'];?></a>
|
||||
<a href="/delete.php?syncjob=<?=urlencode($row['id']);?>" class="btn btn-xs btn-danger"><span class="glyphicon glyphicon-trash"></span> <?=$lang['user']['remove'];?></a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<?php
|
||||
endforeach;
|
||||
else:
|
||||
?>
|
||||
<tr id="no-data"><td colspan="9" style="text-align: center; font-style: italic;"><?=$lang['user']['no_record'];?></td></tr>
|
||||
<?php
|
||||
endif;
|
||||
?>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr id="no-data">
|
||||
<td colspan="9" style="text-align: center; font-style: normal; border-top: 1px solid #e7e7e7;">
|
||||
<a href="/add.php?syncjob"><?=$lang['user']['create_syncjob'];?></a>
|
||||
</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
}
|
||||
if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "user" || $_SESSION['mailcow_cc_role'] == "domainadmin")) {
|
||||
|
||||
/*
|
||||
/ USER OR DOMAIN ADMIN
|
||||
*/
|
||||
|
||||
?>
|
||||
<div class="modal fade" id="logModal" tabindex="-1" role="dialog" aria-labelledby="logTextLabel">
|
||||
<div class="modal-dialog" style="width:90%" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-body">
|
||||
<span id="logText"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="margin-bottom:200px;"></div>
|
||||
<div class="modal fade" id="pwChangeModal" tabindex="-1" role="dialog" aria-labelledby="pwChangeModalLabel">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-body">
|
||||
<form class="form-horizontal" role="form" method="post" autocomplete="off">
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-3" for="user_new_pass"><?=$lang['user']['new_password'];?></label>
|
||||
<div class="col-sm-5">
|
||||
<input type="password" class="form-control" name="user_new_pass" id="user_new_pass" autocomplete="off" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-3" for="user_new_pass2"><?=$lang['user']['new_password_repeat'];?></label>
|
||||
<div class="col-sm-5">
|
||||
<input type="password" class="form-control" name="user_new_pass2" id="user_new_pass2" autocomplete="off" required>
|
||||
<p class="help-block"><?=$lang['user']['new_password_description'];?></p>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-3" for="user_old_pass"><?=$lang['user']['password_now'];?></label>
|
||||
<div class="col-sm-5">
|
||||
<input type="password" class="form-control" name="user_old_pass" id="user_old_pass" autocomplete="off" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-3 col-sm-9">
|
||||
<button type="submit" name="edit_<?=($_SESSION['mailcow_cc_role'] == "domainadmin") ? "domain_admin" : "user_account";?>" class="btn btn-sm btn-success"><?=$lang['user']['change_password'];?></button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
</div> <!-- /container -->
|
||||
<script src="js/sorttable.js"></script>
|
||||
<script src="js/user.js"></script>
|
||||
<?php
|
||||
require_once("inc/footer.inc.php");
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
header('Location: /');
|
||||
exit();
|
||||
}
|
||||
|
|
|
@ -1,22 +1,28 @@
|
|||
version: '2'
|
||||
version: '2.1'
|
||||
|
||||
services:
|
||||
pdns-mailcow:
|
||||
image: andryyy/mailcow-dockerized:pdns
|
||||
bind9-mailcow:
|
||||
image: resystit/bind9
|
||||
command: "named -c /etc/bind/named.conf -g -u named -4"
|
||||
depends_on:
|
||||
mysql-mailcow:
|
||||
condition: service_healthy
|
||||
volumes:
|
||||
- ./data/conf/pdns/:/etc/powerdns/
|
||||
- ./data/conf/bind9/named.conf:/etc/bind/named.conf
|
||||
restart: always
|
||||
networks:
|
||||
mailcow-network:
|
||||
ipv4_address: 172.22.1.254
|
||||
aliases:
|
||||
- pdns
|
||||
- bind9
|
||||
|
||||
mysql-mailcow:
|
||||
image: mariadb:10.1
|
||||
depends_on:
|
||||
- pdns-mailcow
|
||||
command: mysqld
|
||||
healthcheck:
|
||||
test: ["CMD", "mysqladmin", "ping", "--host", "localhost", "--silent"]
|
||||
interval: 10s
|
||||
timeout: 30s
|
||||
retries: 5
|
||||
volumes:
|
||||
- mysql-vol-1:/var/lib/mysql/
|
||||
- ./data/conf/mysql/:/etc/mysql/conf.d/:ro
|
||||
|
@ -37,7 +43,7 @@ services:
|
|||
redis-mailcow:
|
||||
image: redis
|
||||
depends_on:
|
||||
- pdns-mailcow
|
||||
- bind9-mailcow
|
||||
volumes:
|
||||
- redis-vol-1:/data/
|
||||
restart: always
|
||||
|
@ -50,9 +56,16 @@ services:
|
|||
- redis
|
||||
|
||||
rspamd-mailcow:
|
||||
image: andryyy/mailcow-dockerized:rspamd
|
||||
image: mailcow/rspamd
|
||||
build: ./data/Dockerfiles/rspamd
|
||||
command: >
|
||||
/bin/bash -c "
|
||||
sleep 5;
|
||||
/usr/bin/rspamd -f -u _rspamd -g _rspamd
|
||||
"
|
||||
depends_on:
|
||||
- pdns-mailcow
|
||||
nginx-mailcow:
|
||||
condition: service_healthy
|
||||
volumes:
|
||||
- ./data/conf/rspamd/override.d/:/etc/rspamd/override.d:ro
|
||||
- ./data/conf/rspamd/local.d/:/etc/rspamd/local.d:ro
|
||||
|
@ -60,19 +73,21 @@ services:
|
|||
- dkim-vol-1:/data/dkim
|
||||
- rspamd-vol-1:/var/lib/rspamd
|
||||
restart: always
|
||||
dns:
|
||||
dns:
|
||||
- 172.22.1.254
|
||||
dns_search: mailcow-network
|
||||
networks:
|
||||
mailcow-network:
|
||||
ipv4_address: 172.22.1.253
|
||||
aliases:
|
||||
- rspamd
|
||||
|
||||
php-fpm-mailcow:
|
||||
image: andryyy/mailcow-dockerized:phpfpm
|
||||
image: mailcow/phpfpm
|
||||
build: ./data/Dockerfiles/php-fpm
|
||||
command: "php-fpm -d date.timezone=${TZ}"
|
||||
depends_on:
|
||||
- pdns-mailcow
|
||||
- bind9-mailcow
|
||||
volumes:
|
||||
- ./data/web:/web:ro
|
||||
- ./data/conf/rspamd/dynmaps:/dynmaps:ro
|
||||
|
@ -93,10 +108,10 @@ services:
|
|||
- phpfpm
|
||||
|
||||
sogo-mailcow:
|
||||
image: andryyy/mailcow-dockerized:sogo
|
||||
image: mailcow/sogo
|
||||
build: ./data/Dockerfiles/sogo
|
||||
depends_on:
|
||||
- pdns-mailcow
|
||||
- mysql-mailcow
|
||||
- bind9-mailcow
|
||||
environment:
|
||||
- DBNAME=${DBNAME}
|
||||
- DBUSER=${DBUSER}
|
||||
|
@ -111,13 +126,15 @@ services:
|
|||
restart: always
|
||||
networks:
|
||||
mailcow-network:
|
||||
ipv4_address: 172.22.1.252
|
||||
aliases:
|
||||
- sogo
|
||||
|
||||
rmilter-mailcow:
|
||||
image: andryyy/mailcow-dockerized:rmilter
|
||||
image: mailcow/rmilter
|
||||
build: ./data/Dockerfiles/rmilter
|
||||
depends_on:
|
||||
- pdns-mailcow
|
||||
- bind9-mailcow
|
||||
volumes:
|
||||
- ./data/conf/rmilter/:/etc/rmilter.conf.d/:ro
|
||||
restart: always
|
||||
|
@ -130,25 +147,25 @@ services:
|
|||
- rmilter
|
||||
|
||||
dovecot-mailcow:
|
||||
image: andryyy/mailcow-dockerized:dovecot
|
||||
image: mailcow/dovecot
|
||||
build: ./data/Dockerfiles/dovecot
|
||||
depends_on:
|
||||
- pdns-mailcow
|
||||
- bind9-mailcow
|
||||
volumes:
|
||||
- ./data/conf/dovecot:/etc/dovecot
|
||||
- ./data/assets/ssl:/etc/ssl/mail/:ro
|
||||
- ./data/conf/sogo/:/etc/sogo/
|
||||
- vmail-vol-1:/var/vmail
|
||||
volumes_from:
|
||||
- sogo-mailcow
|
||||
environment:
|
||||
- DBNAME=${DBNAME}
|
||||
- DBUSER=${DBUSER}
|
||||
- DBPASS=${DBPASS}
|
||||
ports:
|
||||
- "${IMAP_PORT}:143"
|
||||
- "${IMAPS_PORT}:993"
|
||||
- "${POP_PORT}:110"
|
||||
- "${POPS_PORT}:995"
|
||||
- "${SIEVE_PORT}:4190"
|
||||
- "${IMAP_PORT:-143}:143"
|
||||
- "${IMAPS_PORT:-993}:993"
|
||||
- "${POP_PORT-110}:110"
|
||||
- "${POPS_PORT:-995}:995"
|
||||
- "${SIEVE_PORT:-4190}:4190"
|
||||
dns:
|
||||
- 172.22.1.254
|
||||
dns_search: mailcow-network
|
||||
|
@ -160,24 +177,26 @@ services:
|
|||
- dovecot
|
||||
|
||||
postfix-mailcow:
|
||||
image: andryyy/mailcow-dockerized:postfix
|
||||
image: mailcow/postfix
|
||||
build: ./data/Dockerfiles/postfix
|
||||
depends_on:
|
||||
- pdns-mailcow
|
||||
- bind9-mailcow
|
||||
volumes:
|
||||
- ./data/conf/postfix:/opt/postfix/conf
|
||||
- ./data/assets/ssl:/etc/ssl/mail/:ro
|
||||
- postfix-vol-1:/var/spool/postfix
|
||||
- var-vol-1:/var/log/
|
||||
environment:
|
||||
- DBNAME=${DBNAME}
|
||||
- DBUSER=${DBUSER}
|
||||
- DBPASS=${DBPASS}
|
||||
ports:
|
||||
- "${SMTP_PORT}:25"
|
||||
- "${SMTPS_PORT}:465"
|
||||
- "${SUBMISSION_PORT}:587"
|
||||
- "${SMTP_PORT:-25}:25"
|
||||
- "${SMTPS_PORT:-465}:465"
|
||||
- "${SUBMISSION_PORT:-587}:587"
|
||||
restart: always
|
||||
hostname: ${MAILCOW_HOSTNAME}
|
||||
dns:
|
||||
dns:
|
||||
- 172.22.1.254
|
||||
dns_search: mailcow-network
|
||||
networks:
|
||||
|
@ -188,7 +207,7 @@ services:
|
|||
memcached-mailcow:
|
||||
image: memcached
|
||||
depends_on:
|
||||
- pdns-mailcow
|
||||
- bind9-mailcow
|
||||
restart: always
|
||||
dns:
|
||||
- 172.22.1.254
|
||||
|
@ -200,26 +219,37 @@ services:
|
|||
|
||||
nginx-mailcow:
|
||||
depends_on:
|
||||
- mysql-mailcow
|
||||
- sogo-mailcow
|
||||
- php-fpm-mailcow
|
||||
- rspamd-mailcow
|
||||
image: nginx:mainline
|
||||
volumes_from:
|
||||
- sogo-mailcow
|
||||
healthcheck:
|
||||
test: ["CMD", "ping", "php-fpm-mailcow", "-c", "10"]
|
||||
interval: 10s
|
||||
timeout: 30s
|
||||
retries: 5
|
||||
command: /bin/bash -c "envsubst < /etc/nginx/conf.d/templates/listen_plain.template > /etc/nginx/conf.d/listen_plain.active &&
|
||||
envsubst < /etc/nginx/conf.d/templates/listen_ssl.template > /etc/nginx/conf.d/listen_ssl.active &&
|
||||
envsubst < /etc/nginx/conf.d/templates/server_name.template > /etc/nginx/conf.d/server_name.active &&
|
||||
nginx -g 'daemon off;'"
|
||||
environment:
|
||||
- HTTPS_PORT=${HTTPS_PORT:-443}
|
||||
- HTTP_PORT=${HTTP_PORT:-80}
|
||||
- MAILCOW_HOSTNAME=${MAILCOW_HOSTNAME}
|
||||
volumes:
|
||||
- ./data/web:/web:ro
|
||||
- ./data/conf/rspamd/dynmaps:/dynmaps:ro
|
||||
- ./data/assets/ssl/:/etc/ssl/mail/:ro
|
||||
- ./data/conf/nginx/:/etc/nginx/conf.d/:ro
|
||||
- ./data/conf/nginx/:/etc/nginx/conf.d/:rw
|
||||
dns:
|
||||
- 172.22.1.254
|
||||
dns_search: mailcow-network
|
||||
ports:
|
||||
- "443:443"
|
||||
- "${HTTPS_BIND:-0.0.0.0}:${HTTPS_PORT:-443}:${HTTPS_PORT:-443}"
|
||||
- "${HTTP_BIND:-127.0.0.1}:${HTTP_PORT:-80}:${HTTP_PORT:-80}"
|
||||
restart: always
|
||||
networks:
|
||||
mailcow-network:
|
||||
ipv4_address: 172.22.1.251
|
||||
aliases:
|
||||
- nginx
|
||||
|
||||
|
@ -230,7 +260,6 @@ networks:
|
|||
driver: default
|
||||
config:
|
||||
- subnet: 172.22.1.0/24
|
||||
gateway: 172.22.1.1
|
||||
|
||||
volumes:
|
||||
vmail-vol-1:
|
||||
|
@ -238,4 +267,5 @@ volumes:
|
|||
dkim-vol-1:
|
||||
redis-vol-1:
|
||||
rspamd-vol-1:
|
||||
postfix-vol-1:
|
||||
var-vol-1:
|
||||
|
|
|
@ -0,0 +1,171 @@
|
|||
## SSL (and: How to use Let's Encrypt)
|
||||
|
||||
mailcow dockerized comes with a snakeoil CA "mailcow" and a server certificate in `data/assets/ssl`. Please use your own trusted certificates.
|
||||
|
||||
mailcow uses 3 domain names that should be covered by your new certificate:
|
||||
|
||||
- ${MAILCOW_HOSTNAME}
|
||||
- autodiscover.**example.org**
|
||||
- autoconfig.**example.org**
|
||||
|
||||
### Obtain multi-SAN certificate by Let's Encrypt
|
||||
|
||||
This is just an example of how to obtain certificates with certbot. There are several methods!
|
||||
|
||||
1\. Get the certbot client:
|
||||
``` bash
|
||||
wget https://dl.eff.org/certbot-auto -O /usr/local/sbin/certbot && chmod +x /usr/local/sbin/certbot
|
||||
```
|
||||
|
||||
2\. Make sure you set `HTTP_BIND=0.0.0.0` and `HTTP_PORT=80` in `mailcow.conf` or setup a reverse proxy to enable connections to port 80. If you changed HTTP_BIND, then restart Nginx:
|
||||
``` bash
|
||||
docker-compose restart nginx-mailcow
|
||||
```
|
||||
|
||||
3\. Request the certificate with the webroot method:
|
||||
``` bash
|
||||
cd /path/to/git/clone/mailcow-dockerized
|
||||
source mailcow.conf
|
||||
certbot certonly \
|
||||
--webroot \
|
||||
-w ${PWD}/data/web \
|
||||
-d ${MAILCOW_HOSTNAME} \
|
||||
-d autodiscover.example.org \
|
||||
-d autoconfig.example.org \
|
||||
--email you@example.org \
|
||||
--agree-tos
|
||||
```
|
||||
|
||||
4\. Create hard links to the full path of the new certificates. Assuming you are still in the mailcow root folder:
|
||||
``` bash
|
||||
mv data/assets/ssl/cert.{pem,pem.backup}
|
||||
mv data/assets/ssl/key.{pem,pem.backup}
|
||||
ln $(readlink -f /etc/letsencrypt/live/${MAILCOW_HOSTNAME}/fullchain.pem) data/assets/ssl/cert.pem
|
||||
ln $(readlink -f /etc/letsencrypt/live/${MAILCOW_HOSTNAME}/privkey.pem) data/assets/ssl/key.pem
|
||||
```
|
||||
|
||||
5\. Restart affected containers:
|
||||
```
|
||||
docker-compose restart postfix-mailcow dovecot-mailcow nginx-mailcow
|
||||
```
|
||||
|
||||
When renewing certificates, run the last two steps (link + restart) as post-hook in a script.
|
||||
|
||||
## Rspamd Web UI
|
||||
At first you may want to setup Rspamds web interface which provides some useful features and information.
|
||||
|
||||
1\. Generate a Rspamd controller password hash:
|
||||
```
|
||||
docker-compose exec rspamd-mailcow rspamadm pw
|
||||
```
|
||||
|
||||
2\. Replace the default hash in `data/conf/rspamd/override.d/worker-controller.inc` by your newly generated:
|
||||
```
|
||||
enable_password = "myhash";
|
||||
```
|
||||
|
||||
3\. Restart rspamd:
|
||||
```
|
||||
docker-compose restart rspamd-mailcow
|
||||
```
|
||||
|
||||
Open https://${MAILCOW_HOSTNAME}/rspamd in a browser and login!
|
||||
|
||||
## Optional: Reverse proxy
|
||||
|
||||
You don't need to change the Nginx site that comes with mailcow: dockerized.
|
||||
mailcow: dockerized trusts the default gateway IP 172.22.1.1 as proxy. This is very important to control access to Rspamd's web UI.
|
||||
|
||||
1\. Make sure you change HTTP_BIND and HTTPS_BIND in `mailcow.conf` to a local address and set the ports accordingly, for example:
|
||||
``` bash
|
||||
HTTP_BIND=127.0.0.1
|
||||
HTTP_PORT=8080
|
||||
HTTPS_PORT=127.0.0.1
|
||||
HTTPS_PORT=8443
|
||||
```
|
||||
** IMPORTANT: Do not use port 8081 **
|
||||
|
||||
Recreate affected containers by running `docker-compose up -d`.
|
||||
|
||||
2\. Configure your local webserver as reverse proxy:
|
||||
|
||||
### Apache 2.4
|
||||
``` apache
|
||||
<VirtualHost *:443>
|
||||
ServerName mail.example.org
|
||||
ServerAlias autodiscover.example.org
|
||||
ServerAlias autoconfig.example.org
|
||||
|
||||
[...]
|
||||
# You should proxy to a plain HTTP session to offload SSL processing
|
||||
ProxyPass / http://127.0.0.1:8080/
|
||||
ProxyPassReverse / http://127.0.0.1:8080/
|
||||
ProxyPreserveHost Off
|
||||
your-ssl-configuration-here
|
||||
[...]
|
||||
|
||||
# If you plan to proxy to a HTTPS host:
|
||||
#SSLProxyEngine On
|
||||
|
||||
# If you plan to proxy to an untrusted HTTPS host:
|
||||
#SSLProxyVerify none
|
||||
#SSLProxyCheckPeerCN off
|
||||
#SSLProxyCheckPeerName off
|
||||
#SSLProxyCheckPeerExpire off
|
||||
</VirtualHost>
|
||||
```
|
||||
|
||||
### Nginx
|
||||
```
|
||||
server {
|
||||
listen 443;
|
||||
server_name mail.example.org autodiscover.example.org autoconfig.example.org;
|
||||
|
||||
[...]
|
||||
your-ssl-configuration-here
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:8080/;
|
||||
proxy_redirect http://127.0.0.1:8080/ $scheme://$host:$server_port/;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
[...]
|
||||
}
|
||||
```
|
||||
|
||||
## Sender and receiver model
|
||||
|
||||
When a mailbox is created, a user is allowed to send mail from and receive mail for his own mailbox address.
|
||||
|
||||
Mailbox me@example.org is created. example.org is a primary domain.
|
||||
Note: a mailbox cannot be created in an alias domain.
|
||||
|
||||
me@example.org is only known as me@example.org.
|
||||
me@example.org is allowed to send as me@example.org.
|
||||
|
||||
We can add an alias domain for example.org:
|
||||
|
||||
Alias domain alias.com is added and assigned to primary domain example.org.
|
||||
me@example.org is now known as me@example.org and me@alias.com.
|
||||
me@example.org is now allowed to send as me@example.org and me@alias.com.
|
||||
|
||||
We can add aliases for a mailbox to receive mail for and to send from this new address.
|
||||
|
||||
It is important to know, that you are not able to receive mail for `my-alias@my-alias-domain.tld`. You would need to create this particular alias.
|
||||
|
||||
me@example.org is assigned the alias alias@example.org
|
||||
me@example.org is now known as alias@example.org, me@alias.com, alias@example.org
|
||||
|
||||
me@example.org is NOT known as alias@alias.com.
|
||||
|
||||
Administrators and domain administrators can edit mailboxes to allow specific users to send as other mailbox users ("delegate" them).
|
||||
|
||||
You can choose between mailbox users or completely disable the sender check for domains.
|
||||
|
||||
### SOGo "mail from" addresses
|
||||
|
||||
Mailbox users can, obviously, select their own mailbox address, as well as all alias addresses and aliases that exist through alias domains.
|
||||
|
||||
If you want to select another _existing_ mailbox user as your "mail from" address, this user has to delegate you access through SOGo (see SOGo documentation). Moreover a mailcow (domain) administrator
|
||||
needs to grant you access as described above.
|
|
@ -0,0 +1,179 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Generator: Adobe Illustrator 17.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
version="1.1"
|
||||
id="Layer_1"
|
||||
x="0px"
|
||||
y="0px"
|
||||
width="271.27399"
|
||||
height="298.871"
|
||||
viewBox="0 0 271.27398 298.871"
|
||||
enable-background="new 0 0 1600 1200"
|
||||
xml:space="preserve"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="logo.svg"><metadata
|
||||
id="metadata144"><rdf:RDF><cc:Work
|
||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||
id="defs142" /><sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1203"
|
||||
inkscape:window-height="1168"
|
||||
id="namedview140"
|
||||
showgrid="false"
|
||||
inkscape:zoom="1.1125147"
|
||||
inkscape:cx="151.70799"
|
||||
inkscape:cy="136.82484"
|
||||
inkscape:window-x="561"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:current-layer="g5"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0" /><g
|
||||
id="g3"
|
||||
transform="translate(-648.292,-401.988)"><g
|
||||
id="g5"><g
|
||||
id="email"
|
||||
transform="translate(0,-58)"><path
|
||||
style="fill:#5a3620"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path10"
|
||||
d="m 890.306,557.81 29.26,11.373 0,172.027 c 0,9.753 -7.895,17.649 -17.638,17.649 l -235.998,0 c -9.743,0 -17.638,-7.896 -17.638,-17.649 l 0,-172.026 29.259,-8.937" /><path
|
||||
style="fill:#fee70f;fill-opacity:0.89499996"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path12"
|
||||
d="M 758.871,656.221 649.49,747.45 c 2.507,6.648 8.901,11.409 16.44,11.409 l 235.998,0 c 7.536,0 13.933,-4.761 16.444,-11.409 l -107.402,-91.229 -52.099,0 z" /><g
|
||||
id="g14"><path
|
||||
style="fill:#f9e82d;fill-opacity:1"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path16"
|
||||
d="m 810.391,656.686 107.981,90.764 c -0.331,0.881 -0.744,1.726 -1.205,2.536 l 0.028,0.035 c 1.501,-2.596 2.371,-5.594 2.371,-8.81 l 0,-172.004 -109.175,87.479 z" /><path
|
||||
style="fill:#f9e82d;fill-opacity:1"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path18"
|
||||
d="m 649.49,747.45 108.864,-90.764 -110.061,-87.479 0,172.003 c 0,3.216 0.876,6.214 2.367,8.81 l 0.039,-0.035 c -0.466,-0.809 -0.877,-1.654 -1.209,-2.535 z" /></g></g><path
|
||||
style="opacity:0.1;fill:#3d5263"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path26"
|
||||
d="m 783.931,446.247 c -66.396,0 -120.223,53.827 -120.223,120.223 0,66.396 53.827,120.221 120.223,120.221 66.397,0 120.222,-53.825 120.222,-120.221 0,-66.395 -53.825,-120.223 -120.222,-120.223 z m -11.96,215.702 c -53.009,0 -95.982,-43.855 -95.982,-97.953 0,-54.098 42.973,-97.952 95.982,-97.952 53.007,0 95.98,43.855 95.98,97.952 -10e-4,54.098 -42.973,97.953 -95.98,97.953 z" /><g
|
||||
id="g28"><g
|
||||
id="g30"><polyline
|
||||
style="fill:#3d5263"
|
||||
id="polyline32"
|
||||
points="691.144,492.5 673.257,540.276 686.55,605.582 712.496,631.852 " /><g
|
||||
id="g34"><g
|
||||
id="g36"><polyline
|
||||
style="fill:#fef3df"
|
||||
id="polyline38"
|
||||
points="658.248,450.81 673.501,487.076 693.836,496.903 724.04,458.731 " /><g
|
||||
id="g40"><path
|
||||
style="fill:#b58765"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path42"
|
||||
d="m 710.634,473.205 c 0,0 -22.482,-25.556 -49.793,-18.975 0,0 4.667,34.118 46.349,44.019 l 2.61,8.533 c 0,0 -65.612,-9.689 -59.339,-67.593 0,0 49.008,-19.884 72.598,15.106" /><polyline
|
||||
style="fill:#fef3df"
|
||||
id="polyline44"
|
||||
points="909.697,450.81 894.447,487.076 874.114,496.903 843.907,458.731 " /><path
|
||||
style="fill:#b58765"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path46"
|
||||
d="m 857.314,473.205 c 0,0 22.48,-25.556 49.79,-18.975 0,0 -4.664,34.118 -46.347,44.019 l -2.613,8.533 c 0,0 65.611,-9.689 59.339,-67.593 0,0 -49.006,-19.884 -72.6,15.106" /></g></g><path
|
||||
sodipodi:nodetypes="cccscccccc"
|
||||
style="fill:#b58765"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path48"
|
||||
d="m 726.619,647.067 55.945,0 16.40428,-204.81407 c -55.814,0 -112.41728,30.01707 -112.41728,77.85207 0,1.454 0.085,2.787 0.121,4.175 0.127,3.934 0.448,7.585 0.856,11.135 1.689,14.816 5.451,27.177 8.461,43.383 1.452,7.831 5.002,23.374 5.002,23.374 0.056,0.408 0.165,0.804 0.224,1.211 2.535,16.546 11.832,32.027 25.404,43.684 z" /><path
|
||||
style="fill:#b58765"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path50"
|
||||
d="m 781.992,433.489 0,213.577 55.944,0 c 13.572,-11.657 22.867,-27.138 25.406,-43.684 0.058,-0.407 0.163,-0.803 0.221,-1.211 0,0 3.549,-15.543 5.002,-23.374 3.011,-16.206 6.774,-28.567 8.464,-43.381 0.405,-3.552 0.724,-7.203 0.846,-11.137 0.042,-1.388 0.126,-2.721 0.126,-4.175 0,-47.834 -40.191,-86.615 -96.009,-86.615 z" /><g
|
||||
id="g52"><g
|
||||
id="g54"><path
|
||||
style="fill:#fef3df"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path56"
|
||||
d="m 860.944,613.502 c 0,28.321 -35.091,51.289 -78.383,51.289 -43.299,0 -78.388,-22.968 -78.388,-51.289 0,-28.325 35.089,-51.289 78.388,-51.289 43.292,0 78.383,22.964 78.383,51.289 z" /></g></g><g
|
||||
id="g58"><g
|
||||
id="g60"><g
|
||||
id="g62"><path
|
||||
style="fill:#5a3620"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path64"
|
||||
d="m 747.044,605.582 c 0,6.215 -5.04,11.256 -11.261,11.256 -6.21,0 -11.253,-5.041 -11.253,-11.256 0,-6.223 5.043,-11.257 11.253,-11.257 6.22,0 11.261,5.034 11.261,11.257 z" /></g></g><g
|
||||
id="g66"><g
|
||||
id="g68"><path
|
||||
style="fill:#5a3620"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path70"
|
||||
d="m 840.856,605.582 c 0,6.215 -5.037,11.256 -11.257,11.256 -6.218,0 -11.259,-5.041 -11.259,-11.256 0,-6.223 5.041,-11.257 11.259,-11.257 6.22,0 11.257,5.034 11.257,11.257 z" /></g></g></g><g
|
||||
id="g72"><path
|
||||
style="fill:#87654a"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path74"
|
||||
d="m 875.228,525.835 c 0.354,-3.113 0.634,-6.311 0.743,-9.754 0.035,-1.218 0.109,-2.384 0.109,-3.661 0,-40.785 -33.369,-74.043 -80.237,-75.775 l -7.335,0.005 c -0.003,0 -0.003,0 -0.006,0 -0.007,0.018 -28.632,88.422 76.583,140.268 0.946,-4.317 2.078,-9.585 2.73,-13.088 2.64,-14.196 5.934,-25.021 7.413,-37.995 z" /></g><g
|
||||
id="g76"><g
|
||||
id="g78"><g
|
||||
id="g80"><g
|
||||
id="g82"><path
|
||||
style="fill:#5a3620"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path84"
|
||||
d="m 843.907,519.681 c 0,6.964 -5.65,12.611 -12.618,12.611 -6.963,0 -12.614,-5.646 -12.614,-12.611 0,-6.97 5.651,-12.614 12.614,-12.614 6.968,0 12.618,5.644 12.618,12.614 z" /></g></g></g><g
|
||||
id="g86"><g
|
||||
id="g88"><g
|
||||
id="g90"><path
|
||||
style="fill:#5a3620"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path92"
|
||||
d="m 752.028,519.681 c 0,6.964 -5.649,12.611 -12.612,12.611 -6.969,0 -12.612,-5.646 -12.612,-12.611 0,-6.97 5.642,-12.614 12.612,-12.614 6.964,0 12.612,5.644 12.612,12.614 z" /></g></g></g><g
|
||||
id="g94"><g
|
||||
id="g96"><path
|
||||
style="fill:#ffffff"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path98"
|
||||
d="m 748.75,515.894 c 0,2.558 -2.071,4.629 -4.63,4.629 -2.558,0 -4.633,-2.072 -4.633,-4.629 0,-2.552 2.076,-4.626 4.633,-4.626 2.559,0 4.63,2.073 4.63,4.626 z" /></g></g><g
|
||||
id="g100"><g
|
||||
id="g102"><path
|
||||
style="fill:#ffffff"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path104"
|
||||
d="m 839.771,515.894 c 0,2.558 -2.073,4.629 -4.629,4.629 -2.558,0 -4.631,-2.072 -4.631,-4.629 0,-2.552 2.072,-4.626 4.631,-4.626 2.555,0 4.629,2.073 4.629,4.626 z" /></g></g></g></g><path
|
||||
style="fill:#fef3df"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path106"
|
||||
d="m 734.557,443.625 c 0,0 -18.236,-25.199 0,-41.637 0,0 13.125,32.012 40.242,31.502" /><path
|
||||
style="fill:#fef3df"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path108"
|
||||
d="m 834.496,443.625 c 0,0 18.236,-25.199 0,-41.637 0,0 -13.126,32.012 -40.242,31.502" /><path
|
||||
style="fill:#f1f2f2"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path110"
|
||||
d="m 786.264,431.965 c -66.396,0 -120.223,53.827 -120.223,120.223 0,66.396 53.827,120.221 120.223,120.221 66.397,0 120.222,-53.825 120.222,-120.221 10e-4,-66.395 -53.825,-120.223 -120.222,-120.223 z m -11.96,215.702 c -53.009,0 -95.982,-43.855 -95.982,-97.953 0,-54.098 42.973,-97.952 95.982,-97.952 53.007,0 95.979,43.855 95.979,97.952 0,54.098 -42.972,97.953 -95.979,97.953 z" /></g><g
|
||||
id="g112"><path
|
||||
style="fill:#ffffff"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path114"
|
||||
d="m 781.737,436.751 c 66.396,0 120.221,53.827 120.221,120.223 0,30.718 -11.526,58.74 -30.482,79.991 21.636,-21.74 35.01,-51.708 35.01,-84.803 0,-66.395 -53.825,-120.222 -120.222,-120.222 -35.678,0 -67.721,15.549 -89.739,40.233 21.772,-21.879 51.91,-35.422 85.212,-35.422 z" /></g></g><path
|
||||
d="m 648.292,644.7595 0,46.15088 c 0,5.49435 7.88,9.94862 17.6,9.94862 l 236.073,0 c 9.72,0 17.6,-4.45427 17.6,-9.94862 l 0,-14.07618 c 10e-4,0 -175.814,20.0804 -271.273,-32.0747 z"
|
||||
id="path124"
|
||||
inkscape:connector-curvature="0"
|
||||
style="opacity:0.1;fill:#3d5263" /></g><g
|
||||
id="g126" /></g></svg>
|
After Width: | Height: | Size: 11 KiB |
|
@ -0,0 +1,48 @@
|
|||
# mailcow: dockerized - 🐮 + 🐋 = 💕
|
||||
|
||||
[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=JWBSYHF4SMC68)
|
||||
|
||||
## Screenshots
|
||||
|
||||
You can find screenshots [on Imgur](http://imgur.com/a/oewYt).
|
||||
|
||||
## Overview
|
||||
|
||||
mailcow dockerized comes with **11 containers** linked in **one bridged network**.
|
||||
|
||||
- Dovecot
|
||||
- Memcached
|
||||
- Redis
|
||||
- MySQL
|
||||
- Bind9 (Resolver) (formerly PDNS Recursor)
|
||||
- PHP-FPM
|
||||
- Postfix
|
||||
- Nginx
|
||||
- Rmilter
|
||||
- Rspamd
|
||||
- SOGo
|
||||
|
||||
**6 volumes** to keep dynamic data - take care of them!
|
||||
|
||||
- vmail-vol-1
|
||||
- dkim-vol-1
|
||||
- redis-vol-1
|
||||
- mysql-vol-1
|
||||
- rspamd-vol-1
|
||||
- postfix-vol-1
|
||||
|
||||
The integrated **mailcow UI** allows administrative work on your mail server instance as well as separated domain administrator and mailbox user access:
|
||||
|
||||
- DKIM key management
|
||||
- Black- and whitelists per domain and per user
|
||||
- Spam score managment per-user (reject spam, mark spam, greylist)
|
||||
- Allow mailbox users to create temporary spam aliases
|
||||
- Prepend mail tags to subject or move mail to subfolder (per-user)
|
||||
- Allow mailbox users to toggle incoming and outgoing TLS enforcement
|
||||
- Allow users to reset SOGo ActiveSync device caches
|
||||
- imapsync to migrate or pull remote mailboxes regularly
|
||||
- TFA: Yubi OTP and U2F USB (Google Chrome and derivates only)
|
||||
- Add domains, mailboxes, aliases, domain aliases and SOGo resources
|
||||
|
||||
|
||||
*[Looking for a farm to host your cow?](https://www.servercow.de)*
|
|
@ -0,0 +1,76 @@
|
|||
## Install mailcow
|
||||
|
||||
You need Docker and Docker Compose.
|
||||
|
||||
1\. Learn how to install [Docker](https://docs.docker.com/engine/installation/linux/) and [Docker Compose](https://docs.docker.com/compose/install/).
|
||||
|
||||
Quick installation for most operation systems:
|
||||
|
||||
- Docker
|
||||
```
|
||||
curl -sSL https://get.docker.com/ | sh
|
||||
```
|
||||
|
||||
- Docker-Compose
|
||||
```
|
||||
curl -L https://github.com/docker/compose/releases/download/$(curl -Ls https://www.servercow.de/docker-compose/latest.php)/docker-compose-$(uname -s)-$(uname -m) > /usr/local/bin/docker-compose
|
||||
chmod +x /usr/local/bin/docker-compose
|
||||
```
|
||||
|
||||
Please use the latest Docker engine available and do not use the engine that ships with your distros repository.
|
||||
|
||||
2\. Clone the master branch of the repository
|
||||
```
|
||||
git clone https://github.com/andryyy/mailcow-dockerized && cd mailcow-dockerized
|
||||
```
|
||||
|
||||
3\. Generate a configuration file. Use a FQDN (`host.domain.tld`) as hostname when asked.
|
||||
```
|
||||
./generate_config.sh
|
||||
```
|
||||
|
||||
4\. Change configuration if you want or need to.
|
||||
```
|
||||
nano mailcow.conf
|
||||
```
|
||||
If you plan to use a reverse proxy, you can, for example, bind HTTPS to 127.0.0.1 on port 8443 and HTTP to 127.0.0.1 on port 8080.
|
||||
|
||||
5\. Pull the images and run the composer file. The paramter `-d` will start mailcow: dockerized detached:
|
||||
```
|
||||
docker-compose pull
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
Done!
|
||||
|
||||
You can now access **https://${MAILCOW_HOSTNAME}** with the default credentials `admin` + password `moohoo`.
|
||||
|
||||
The database will be initialized right after a connection to MySQL can be established.
|
||||
|
||||
## Update mailcow
|
||||
|
||||
There is no update routine. You need to refresh your pulled repository clone and apply your local changes (if any). Actually there are many ways to merge local changes. Here is one to
|
||||
stash all local changes, pull changes from the remote master branch and apply your stash on top of it. You will most likely see warnings about non-commited changes; you can ignore them:
|
||||
|
||||
```
|
||||
# Stash local changes
|
||||
git stash
|
||||
# Re-pull master
|
||||
git pull
|
||||
# Apply stash and remove it
|
||||
git stash pop
|
||||
```
|
||||
|
||||
Pull new images (if any) and recreate changed containers:
|
||||
|
||||
```
|
||||
docker-compose pull
|
||||
docker-compose up -d --remove-orphans
|
||||
```
|
||||
|
||||
Clean-up dangling (unused) images and volumes:
|
||||
|
||||
```
|
||||
docker rmi -f $(docker images -f "dangling=true" -q)
|
||||
docker volume rm $(docker volume ls -qf dangling=true)
|
||||
```
|
|
@ -0,0 +1,413 @@
|
|||
## Anonymize headers
|
||||
|
||||
Save as `data/conf/postfix/mailcow_anonymize_headers.pcre`:
|
||||
|
||||
```
|
||||
/^\s*Received:[^\)]+\)\s+\(Authenticated sender:(.+)/
|
||||
REPLACE Received: from localhost (localhost [127.0.0.1]) (Authenticated sender:$1
|
||||
/^\s*User-Agent/ IGNORE
|
||||
/^\s*X-Enigmail/ IGNORE
|
||||
/^\s*X-Mailer/ IGNORE
|
||||
/^\s*X-Originating-IP/ IGNORE
|
||||
/^\s*X-Forward/ IGNORE
|
||||
```
|
||||
|
||||
Add this to `data/conf/postfix/main.cf`:
|
||||
```
|
||||
smtp_header_checks = pcre:/opt/postfix/conf/mailcow_anonymize_headers.pcre
|
||||
```
|
||||
|
||||
## Backup and restore maildir (simple tar file)
|
||||
|
||||
### Backup
|
||||
|
||||
This line backups the vmail directory to a file backup_vmail.tar.gz in the mailcow root directory:
|
||||
```
|
||||
cd /path/to/mailcow-dockerized
|
||||
source mailcow.conf
|
||||
DATE=$(date +"%Y%m%d_%H%M%S")
|
||||
docker run --rm -it -v $(docker inspect --format '{{ range .Mounts }}{{ if eq .Destination "/var/vmail" }}{{ .Name }}{{ end }}{{ end }}' $(docker-compose ps -q dovecot-mailcow)):/vmail -v ${PWD}:/backup debian:jessie tar cvfz /backup/backup_vmail.tar.gz /vmail
|
||||
```
|
||||
|
||||
You can change the path by adjusting ${PWD} (which equals to the current directory) to any path you have write-access to.
|
||||
Set the filename `backup_vmail.tar.gz` to any custom name, but leave the path as it is. Example: `[...] tar cvfz /backup/my_own_filename_.tar.gz`
|
||||
|
||||
### Restore
|
||||
```
|
||||
cd /path/to/mailcow-dockerized
|
||||
source mailcow.conf
|
||||
DATE=$(date +"%Y%m%d_%H%M%S")
|
||||
docker run --rm -it -v $(docker inspect --format '{{ range .Mounts }}{{ if eq .Destination "/var/vmail" }}{{ .Name }}{{ end }}{{ end }}' $(docker-compose ps -q dovecot-mailcow)):/vmail -v ${PWD}:/backup debian:jessie tar xvfz /backup/backup_vmail.tar.gz
|
||||
```
|
||||
|
||||
## Docker Compose Bash completion
|
||||
|
||||
For the tab-tab... :-)
|
||||
|
||||
```
|
||||
curl -L https://raw.githubusercontent.com/docker/compose/$(docker-compose version --short)/contrib/completion/bash/docker-compose -o /etc/bash_completion.d/docker-compose
|
||||
```
|
||||
## Black and Whitelist
|
||||
|
||||
Edit a domain as (domain) administrator to add an item to the filter table.
|
||||
|
||||
Beware that a mailbox user can login to mailcow and override a domain policy filter item.
|
||||
|
||||
## Change default language
|
||||
|
||||
Change `data/conf/sogo/sogo.conf` and replace "English" by your preferred language.
|
||||
|
||||
Create a file `data/web/inc/vars.local.inc.php` and add "DEFAULT_LANG" with either "en", "pt", "de" or "nl":
|
||||
```
|
||||
<?php
|
||||
$DEFAULT_LANG = "de";
|
||||
```
|
||||
## Change UI theme
|
||||
|
||||
mailcow uses [Bootstrap](http://getbootstrap.com/), a HTML, CSS, and JS framework.
|
||||
|
||||
Open or create the file `data/web/inc/vars.local.inc.php` and change `DEFAULT_THEME` to either cerulean, cosmo, custom, cyborg, darkly, flatly, journal, paper, readable, sandstone, simplex, slate, spacelab, superhero, united or yeti (see https://bootswatch.com/):
|
||||
```
|
||||
<?php
|
||||
$DEFAULT_THEME = "paper";
|
||||
```
|
||||
## Customize Dockerfiles
|
||||
|
||||
Make your changes in `data/Dockerfiles/$service` and build the image locally:
|
||||
|
||||
```
|
||||
docker build data/Dockerfiles/service -t andryyy/mailcow-dockerized:$service
|
||||
```
|
||||
|
||||
Now auto-recreate modified containers:
|
||||
|
||||
```
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
## Disable sender addresses verification
|
||||
|
||||
This option is not best-practice and should only be implemented when there is no other option available to archive whatever you are trying to do.
|
||||
|
||||
Simply create a file `data/conf/postfix/check_sender_access` and enter the following content:
|
||||
```
|
||||
user-to-allow-everything@example.com OK
|
||||
```
|
||||
|
||||
Open `data/conf/postfix/main.cf` and find `smtpd_sender_restrictions`. Prepend `check_sasl_access hash:/opt/postfix/conf/check_sender_access` like this:
|
||||
```
|
||||
smtpd_sender_restrictions = check_sasl_access hash:/opt/postfix/conf/check_sender_access reject_authenticated_sender [...]
|
||||
```
|
||||
|
||||
Run postmap on check_sasl_access:
|
||||
|
||||
```
|
||||
docker-compose exec postfix-mailcow postmap /opt/postfix/conf/check_sasl_access
|
||||
```
|
||||
|
||||
Restart the Postfix container.
|
||||
|
||||
## Install Roundcube
|
||||
|
||||
Download Roundcube 1.3.x (beta at the time of Feb 2017) to the web htdocs directory and extract it (here `rc/`):
|
||||
```
|
||||
cd data/web/rc
|
||||
wget -O - https://github.com/roundcube/roundcubemail/releases/download/1.3-beta/roundcubemail-1.3-beta-complete.tar.gz | tar xfvz -
|
||||
# Change folder name
|
||||
mv roundcubemail-1.3* rc
|
||||
# Change permissions
|
||||
chown -R root: rc/
|
||||
```
|
||||
|
||||
Create a file `data/web/rc/config/config.inc.php` with the following content.
|
||||
|
||||
**Change the `des_key` parameter to a random value.** It is used to temporarily store your IMAP password.
|
||||
|
||||
```
|
||||
<?php
|
||||
error_reporting(0);
|
||||
if (!file_exists('/tmp/mime.types')) {
|
||||
file_put_contents("/tmp/mime.types", fopen("http://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types", 'r'));
|
||||
}
|
||||
$config = array();
|
||||
$config['db_dsnw'] = 'mysql://' . getenv('DBUSER') . ':' . getenv('DBPASS') . '@mysql/' . getenv('DBNAME');
|
||||
$config['default_host'] = 'tls://dovecot';
|
||||
$config['default_port'] = '143';
|
||||
$config['smtp_server'] = 'tls://postfix';
|
||||
$config['smtp_port'] = 587;
|
||||
$config['smtp_user'] = '%u';
|
||||
$config['smtp_pass'] = '%p';
|
||||
$config['support_url'] = '';
|
||||
$config['product_name'] = 'Roundcube Webmail';
|
||||
$config['des_key'] = 'rcmail-!24ByteDESkey*Str';
|
||||
$config['log_dir'] = '/dev/null';
|
||||
$config['temp_dir'] = '/tmp';
|
||||
$config['plugins'] = array(
|
||||
'archive',
|
||||
);
|
||||
$config['skin'] = 'larry';
|
||||
$config['mime_types'] = '/tmp/mime.types';
|
||||
$config['imap_conn_options'] = array(
|
||||
'ssl' => array('verify_peer' => false, 'verify_peer_name' => false, 'allow_self_signed' => true)
|
||||
);
|
||||
$config['enable_installer'] = false;
|
||||
$config['smtp_conn_options'] = array(
|
||||
'ssl' => array('verify_peer' => false, 'verify_peer_name' => false, 'allow_self_signed' => true)
|
||||
);
|
||||
```
|
||||
|
||||
Point your browser to `https://myserver/rc/installer` and follow the instructions.
|
||||
Initialize the database and leave the installer.
|
||||
|
||||
**Delete the directory `data/web/rc/installer` after a successful installation!**
|
||||
|
||||
### Enable change password function in Roundcube
|
||||
|
||||
Open `data/web/rc/config/config.inc.php` and enable the password plugin:
|
||||
|
||||
```
|
||||
...
|
||||
$config['plugins'] = array(
|
||||
'archive',
|
||||
'password',
|
||||
);
|
||||
...
|
||||
```
|
||||
|
||||
Open `data/web/rc/plugins/password/password.php`, search for `case 'ssha':` and add above:
|
||||
|
||||
```
|
||||
case 'ssha256':
|
||||
$salt = rcube_utils::random_bytes(8);
|
||||
$crypted = base64_encode( hash('sha256', $password . $salt, TRUE ) . $salt );
|
||||
$prefix = '{SSHA256}';
|
||||
break;
|
||||
```
|
||||
|
||||
Open `data/web/rc/plugins/password/config.inc.php` and change the following parameters (or add them at the bottom of that file):
|
||||
|
||||
```
|
||||
$config['password_driver'] = 'sql';
|
||||
$config['password_algorithm'] = 'ssha256';
|
||||
$config['password_algorithm_prefix'] = '{SSHA256}';
|
||||
$config['password_query'] = "UPDATE mailbox SET password = %P WHERE username = %u";
|
||||
```
|
||||
|
||||
## MySQL
|
||||
|
||||
### Connect
|
||||
```
|
||||
source mailcow.conf
|
||||
docker-compose exec mysql-mailcow mysql -u${DBUSER} -p${DBPASS} ${DBNAME}
|
||||
```
|
||||
|
||||
### Backup
|
||||
```
|
||||
cd /path/to/mailcow-dockerized
|
||||
source mailcow.conf
|
||||
DATE=$(date +"%Y%m%d_%H%M%S")
|
||||
docker-compose exec mysql-mailcow mysqldump --default-character-set=utf8mb4 -u${DBUSER} -p${DBPASS} ${DBNAME} > backup_${DBNAME}_${DATE}.sql
|
||||
```
|
||||
|
||||
### Restore
|
||||
```
|
||||
cd /path/to/mailcow-dockerized
|
||||
source mailcow.conf
|
||||
docker-compose exec mysql-mailcow mysql -u${DBUSER} -p${DBPASS} ${DBNAME} < backup_file.sql
|
||||
```
|
||||
|
||||
## Debugging
|
||||
|
||||
You can use `docker-compose logs $service-name` for all containers.
|
||||
|
||||
Run `docker-compose logs` for all logs at once.
|
||||
|
||||
Follow the log output by running docker-compose with `logs -f`.
|
||||
|
||||
## Redirect port 80 to 443
|
||||
|
||||
Since February the 28th 2017 mailcow does come with port 80 and 443 enabled.
|
||||
|
||||
Open `mailcow.conf` and set `HTTP_BIND=0.0.0.0`.
|
||||
|
||||
Open `data/conf/nginx/site.conf` and add a new "catch-all" site at the top of that file:
|
||||
|
||||
```
|
||||
server {
|
||||
listen 80 default_server;
|
||||
server_name _;
|
||||
return 301 https://$host$request_uri;
|
||||
}
|
||||
```
|
||||
|
||||
Restart the stack, changed containers will be updated:
|
||||
|
||||
`docker-compose up -d`
|
||||
|
||||
## Redis
|
||||
|
||||
### Client
|
||||
|
||||
```
|
||||
docker-compose exec redis-mailcow redis-cli
|
||||
```
|
||||
|
||||
## Remove persistent data
|
||||
|
||||
- Remove volume `mysql-vol-1` to remove all MySQL data.
|
||||
- Remove volume `redis-vol-1` to remove all Redis data.
|
||||
- Remove volume `vmail-vol-1` to remove all contents of `/var/vmail` mounted to `dovecot-mailcow`.
|
||||
- Remove volume `dkim-vol-1` to remove all DKIM keys.
|
||||
- Remove volume `rspamd-vol-1` to remove all Rspamd data.
|
||||
|
||||
Running `docker-compose down -v` will **destroy all mailcow: dockerized volumes** and delete any related containers.
|
||||
|
||||
## Reset admin password
|
||||
Reset mailcow admin to `admin:moohoo`:
|
||||
|
||||
1\. Drop admin table
|
||||
```
|
||||
source mailcow.conf
|
||||
docker-compose exec mysql-mailcow mysql -u${DBUSER} -p${DBPASS} ${DBNAME} -e "DROP TABLE admin;"
|
||||
```
|
||||
|
||||
2\. Open mailcow UI to auto-init the db
|
||||
|
||||
## Rspamd
|
||||
|
||||
### Learn spam and ham
|
||||
|
||||
Rspamd learns mail as spam or ham when you move a message in or out of the junk folder to any mailbox besides trash.
|
||||
This is archived by using the Dovecot plugin "antispam" and a simple parser script.
|
||||
|
||||
Rspamd also auto-learns mail when a high or low score is detected (see https://rspamd.com/doc/configuration/statistic.html#autolearning)
|
||||
|
||||
The bayes statistics are written to Redis as keys `BAYES_HAM` and `BAYES_SPAM`.
|
||||
|
||||
You can also use Rspamd's web ui to learn ham and/or spam.
|
||||
|
||||
### CLI tools
|
||||
|
||||
```
|
||||
docker-compose exec rspamd-mailcow rspamc --help
|
||||
docker-compose exec rspamd-mailcow rspamadm --help
|
||||
```
|
||||
|
||||
See [Rspamd documentation](https://rspamd.com/doc/index.html)
|
||||
|
||||
## Adjust service configurations
|
||||
|
||||
The most important configuration files are mounted from the host into the related containers:
|
||||
|
||||
```
|
||||
data/conf
|
||||
├── bind9
|
||||
│ └── named.conf
|
||||
├── dovecot
|
||||
│ ├── dovecot.conf
|
||||
│ ├── dovecot-master.passwd
|
||||
│ ├── sieve_after
|
||||
│ └── sql
|
||||
│ ├── dovecot-dict-sql.conf
|
||||
│ └── dovecot-mysql.conf
|
||||
├── mysql
|
||||
│ └── my.cnf
|
||||
├── nginx
|
||||
│ ├── dynmaps.conf
|
||||
│ ├── site.conf
|
||||
│ └── templates
|
||||
│ ├── listen_plain.template
|
||||
│ ├── listen_ssl.template
|
||||
│ └── server_name.template
|
||||
├── pdns
|
||||
│ ├── pdns_custom.lua
|
||||
│ └── recursor.conf
|
||||
├── postfix
|
||||
│ ├── main.cf
|
||||
│ ├── master.cf
|
||||
│ ├── postscreen_access.cidr
|
||||
│ ├── smtp_dsn_filter
|
||||
│ └── sql
|
||||
│ ├── mysql_relay_recipient_maps.cf
|
||||
│ ├── mysql_tls_enforce_in_policy.cf
|
||||
│ ├── mysql_tls_enforce_out_policy.cf
|
||||
│ ├── mysql_virtual_alias_domain_catchall_maps.cf
|
||||
│ ├── mysql_virtual_alias_domain_maps.cf
|
||||
│ ├── mysql_virtual_alias_maps.cf
|
||||
│ ├── mysql_virtual_domains_maps.cf
|
||||
│ ├── mysql_virtual_mailbox_maps.cf
|
||||
│ ├── mysql_virtual_relay_domain_maps.cf
|
||||
│ ├── mysql_virtual_sender_acl.cf
|
||||
│ └── mysql_virtual_spamalias_maps.cf
|
||||
├── rmilter
|
||||
│ └── rmilter.conf
|
||||
├── rspamd
|
||||
│ ├── dynmaps
|
||||
│ │ ├── authoritative.php
|
||||
│ │ ├── settings.php
|
||||
│ │ ├── tags.php
|
||||
│ │ └── vars.inc.php -> ../../../web/inc/vars.inc.php
|
||||
│ ├── local.d
|
||||
│ │ ├── dkim.conf
|
||||
│ │ ├── metrics.conf
|
||||
│ │ ├── options.inc
|
||||
│ │ ├── redis.conf
|
||||
│ │ ├── rspamd.conf.local
|
||||
│ │ └── statistic.conf
|
||||
│ ├── lua
|
||||
│ │ └── rspamd.local.lua
|
||||
│ └── override.d
|
||||
│ ├── logging.inc
|
||||
│ ├── worker-controller.inc
|
||||
│ └── worker-normal.inc
|
||||
└── sogo
|
||||
├── sieve.creds
|
||||
└── sogo.conf
|
||||
|
||||
```
|
||||
|
||||
Just change the according configuration file on the host and restart the related service:
|
||||
```
|
||||
docker-compose restart service-mailcow
|
||||
```
|
||||
|
||||
## Tagging
|
||||
|
||||
Mailbox users can tag their mail address like in `me+facebook@example.org` and choose between to setups to handle this tag:
|
||||
|
||||
1\. Move this message to a subfolder "facebook" (will be created lower case if not existing)
|
||||
|
||||
2\. Prepend the tag to the subject: "[facebook] Subject"
|
||||
|
||||
## Two-factor authentication
|
||||
|
||||
So far two methods for TFA are implemented. Both work with the fantastic [Yubikey](https://www.yubico.com).
|
||||
|
||||
While Yubi OTP needs an active internet connection and an API ID and key, U2F will work with any FIDO U2F USB key out of the box, but can only be used when mailcow is accessed over HTTPS.
|
||||
|
||||
Both methods support multiple YubiKeys.
|
||||
|
||||
As administrator you are able to temporary disable a domain administrators TFA login until they successfully logged in.
|
||||
|
||||
The key used to login will be displayed in green, while other keys remain grey.
|
||||
|
||||
### Yubi OTP
|
||||
|
||||
The Yubi API ID and Key will be checked against the Yubico Cloud API. When setting up TFA you will be asked for your personal API account for this key.
|
||||
The API ID, API key and the first 12 characters (your YubiKeys ID in modhex) are stored in the MySQL table as secret.
|
||||
|
||||
### U2F
|
||||
|
||||
Only Google Chrome (+derivates) and Opera support U2F authentication to this day natively.
|
||||
For Firefox you will need to install the "U2F Support Add-on" as provided on [mozilla.org](https://addons.mozilla.org/en-US/firefox/addon/u2f-support-add-on/).
|
||||
|
||||
U2F works without an internet connection.
|
||||
|
||||
## Why Bind?
|
||||
|
||||
For DNS blacklist lookups and DNSSEC.
|
||||
|
||||
Most systems use either a public or a local caching DNS resolver.
|
||||
That's a very bad idea when it comes to filter spam using DNS-based blackhole lists (DNSBL) or similar technics.
|
||||
Most if not all providers apply a rate limit based on the DNS resolver that is used to query their service.
|
||||
Using a public resolver like Googles 4x8, OpenDNS or any other shared DNS resolver like your ISPs will hit that limit very soon.
|
|
@ -12,8 +12,16 @@ if [[ -f mailcow.conf ]]; then
|
|||
esac
|
||||
fi
|
||||
|
||||
read -p "Hostname (FQDN): " -ei "mx.example.org" MAILCOW_HOSTNAME
|
||||
read -p "Timezone: " -ei "Europe/Berlin" TZ
|
||||
if [ -z "$MAILCOW_HOSTNAME" ]; then
|
||||
read -p "Hostname (FQDN): " -ei "mx.example.org" MAILCOW_HOSTNAME
|
||||
fi
|
||||
|
||||
[[ -a /etc/timezone ]] && TZ=$(cat /etc/timezone)
|
||||
if [ -z "$TZ" ]; then
|
||||
read -p "Timezone: " -ei "Europe/Berlin" TZ
|
||||
else
|
||||
read -p "Timezone: " -ei ${TZ} TZ
|
||||
fi
|
||||
|
||||
cat << EOF > mailcow.conf
|
||||
# ------------------------------
|
||||
|
@ -35,10 +43,23 @@ DBPASS=$(</dev/urandom tr -dc A-Za-z0-9 | head -c 28)
|
|||
DBROOT=$(</dev/urandom tr -dc A-Za-z0-9 | head -c 28)
|
||||
|
||||
# ------------------------------
|
||||
# Misc configuration
|
||||
# HTTP/S Bindings
|
||||
# ------------------------------
|
||||
|
||||
# You should use HTTPS, but in case of SSL offloaded reverse proxies:
|
||||
HTTP_PORT=80
|
||||
HTTP_BIND=0.0.0.0
|
||||
|
||||
HTTPS_PORT=443
|
||||
HTTPS_BIND=0.0.0.0
|
||||
|
||||
# ------------------------------
|
||||
# Other bindings
|
||||
# ------------------------------
|
||||
# You should leave that alone
|
||||
# Can also be 11.22.33.44:25 or 0.0.0.0:465 etc. for specific bindings
|
||||
# Format: 11.22.33.44:25 or 0.0.0.0:465 etc.
|
||||
# Do _not_ use IP:PORT in HTTPS_BIND or HTTPS_PORT
|
||||
|
||||
SMTP_PORT=25
|
||||
SMTPS_PORT=465
|
||||
SUBMISSION_PORT=587
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
site_name: "mailcow: dockerized"
|
||||
repo_url: https://github.com/andryyy/mailcow-dockerized
|
||||
remote_branch: gh-pages
|
||||
theme: material
|
||||
extra:
|
||||
social:
|
||||
- type: 'github'
|
||||
link: 'https://github.com/andryyy/mailcow-dockerized'
|
||||
palette:
|
||||
primary: 'indigo'
|
||||
accent: 'yellow'
|
||||
logo: 'images/logo.svg'
|
||||
|
||||
markdown_extensions:
|
||||
- admonition
|
||||
- codehilite(guess_lang=true)
|
||||
- footnotes
|
||||
- meta
|
||||
- toc(permalink=true)
|
||||
|
||||
pages:
|
||||
- 'This is mailcow': 'index.md'
|
||||
- 'Installation': 'install.md'
|
||||
- 'First Steps': 'first_steps.md'
|
||||
- 'Usage & Examples': 'u_and_e.md'
|
Loading…
Reference in New Issue