1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -4,3 +4,4 @@ data/conf/dovecot/dovecot-master.passwd | |||||||
| mailcow.conf | mailcow.conf | ||||||
| mailcow.conf_backup | mailcow.conf_backup | ||||||
| data/conf/nginx/listen.active | data/conf/nginx/listen.active | ||||||
|  | data/web/inc/vars.local.inc.php | ||||||
|   | |||||||
| @@ -19,13 +19,46 @@ RUN apt-get -y install dovecot-common \ | |||||||
| 	dovecot-mysql \ | 	dovecot-mysql \ | ||||||
| 	dovecot-pop3d \ | 	dovecot-pop3d \ | ||||||
| 	dovecot-dev \ | 	dovecot-dev \ | ||||||
|  | 	syslog-ng \ | ||||||
|  | 	syslog-ng-core \ | ||||||
|  | 	ca-certificates \ | ||||||
|  | 	supervisor \ | ||||||
| 	wget \ | 	wget \ | ||||||
| 	curl \ | 	curl \ | ||||||
| 	build-essential \ | 	build-essential \ | ||||||
| 	autotools-dev \ | 	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 | WORKDIR /tmp | ||||||
|  |  | ||||||
| RUN wget http://hg.dovecot.org/dovecot-antispam-plugin/archive/tip.tar.gz -O - | tar xvz  \ | RUN wget http://hg.dovecot.org/dovecot-antispam-plugin/archive/tip.tar.gz -O - | tar xvz  \ | ||||||
| 	&& cd /tmp/dovecot-antispam* \ | 	&& cd /tmp/dovecot-antispam* \ | ||||||
| 	&& ./autogen.sh \ | 	&& ./autogen.sh \ | ||||||
| @@ -33,10 +66,15 @@ RUN wget http://hg.dovecot.org/dovecot-antispam-plugin/archive/tip.tar.gz -O - | | |||||||
| 	&& make \ | 	&& make \ | ||||||
| 	&& make install | 	&& 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 ./rspamd-pipe /usr/local/bin/rspamd-pipe | ||||||
| COPY ./docker-entrypoint.sh / | 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/rspamd-pipe | ||||||
|  | RUN chmod +x /usr/local/bin/imapsync_cron.pl | ||||||
|  |  | ||||||
| RUN groupadd -g 5000 vmail | RUN groupadd -g 5000 vmail | ||||||
| RUN useradd -g vmail -u 5000 vmail -d /var/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 | EXPOSE 24 10001 | ||||||
|  |  | ||||||
| ENTRYPOINT ["/docker-entrypoint.sh"] | 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/* | RUN apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* | ||||||
|   | |||||||
| @@ -1,11 +1,17 @@ | |||||||
| #!/bin/bash | #!/bin/bash | ||||||
| set -e | 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 | ||||||
|  |  | ||||||
|  | # Set Dovecot config parameters, escape " in db password | ||||||
| DBPASS=$(echo ${DBPASS} | sed 's/"/\\"/g') | DBPASS=$(echo ${DBPASS} | sed 's/"/\\"/g') | ||||||
| sed -i "/^connect/c\connect = \"host=mysql dbname=${DBNAME} user=${DBUSER} password=${DBPASS}\"" /etc/dovecot/sql/* | sed -i "/^connect/c\connect = \"host=mysql dbname=${DBNAME} user=${DBUSER} password=${DBPASS}\"" /etc/dovecot/sql/* | ||||||
|  |  | ||||||
| [[ ! -d /var/vmail/sieve ]] && mkdir -p /var/vmail/sieve | [[ ! -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 | cat /etc/dovecot/sieve_after > /var/vmail/sieve/global.sieve | ||||||
| sievec /var/vmail/sieve/global.sieve | sievec /var/vmail/sieve/global.sieve | ||||||
| chown -R vmail:vmail /var/vmail/sieve | chown -R vmail:vmail /var/vmail/sieve | ||||||
|   | |||||||
							
								
								
									
										9488
									
								
								data/Dockerfiles/dovecot/imapsync
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										9488
									
								
								data/Dockerfiles/dovecot/imapsync
									
									
									
									
									
										Executable file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										72
									
								
								data/Dockerfiles/dovecot/imapsync_cron.pl
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										72
									
								
								data/Dockerfiles/dovecot/imapsync_cron.pl
									
									
									
									
									
										Executable file
									
								
							| @@ -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); | ||||||
							
								
								
									
										4
									
								
								data/Dockerfiles/dovecot/postlogin.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										4
									
								
								data/Dockerfiles/dovecot/postlogin.sh
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,4 @@ | |||||||
|  | #!/bin/sh | ||||||
|  |  | ||||||
|  | export MASTER_USER=$USER | ||||||
|  | exec "$@" | ||||||
							
								
								
									
										21
									
								
								data/Dockerfiles/dovecot/supervisord.conf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								data/Dockerfiles/dovecot/supervisord.conf
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||||
| @@ -6,6 +6,11 @@ sed -i "/^user/c\user = ${DBUSER}" /opt/postfix/conf/sql/* | |||||||
| sed -i "/^password/c\password = ${DBPASS}" /opt/postfix/conf/sql/* | sed -i "/^password/c\password = ${DBPASS}" /opt/postfix/conf/sql/* | ||||||
| sed -i "/^dbname/c\dbname = ${DBNAME}" /opt/postfix/conf/sql/* | sed -i "/^dbname/c\dbname = ${DBNAME}" /opt/postfix/conf/sql/* | ||||||
|  |  | ||||||
| postfix -c /opt/postfix/conf start | postconf -c /opt/postfix/conf | ||||||
|  | if [[ $? != 0 ]]; then | ||||||
| sleep infinity | 	echo "Postfix configuration error, refusing to start." | ||||||
|  | 	exit 1 | ||||||
|  | else | ||||||
|  | 	postfix -c /opt/postfix/conf start | ||||||
|  | 	sleep infinity | ||||||
|  | fi | ||||||
|   | |||||||
| @@ -11,7 +11,14 @@ RUN dpkg-divert --local --rename --add /sbin/initctl \ | |||||||
|     && ln -sf /bin/true /usr/bin/ischroot |     && ln -sf /bin/true /usr/bin/ischroot | ||||||
|  |  | ||||||
| RUN apt-get update \ | 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 }')" \ |     && 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 "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" \ |     && 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 ./reconf-domains.sh / | ||||||
| COPY supervisord.conf /etc/supervisor/supervisord.conf | COPY supervisord.conf /etc/supervisor/supervisord.conf | ||||||
|  |  | ||||||
| EXPOSE 20000 | #EXPOSE 20000 | ||||||
| EXPOSE 9191 | #EXPOSE 9191 | ||||||
|  | #EXPOSE 9192 | ||||||
|  |  | ||||||
| CMD exec /usr/bin/supervisord -c /etc/supervisor/supervisord.conf | CMD exec /usr/bin/supervisord -c /etc/supervisor/supervisord.conf | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,22 +1,21 @@ | |||||||
| #!/bin/bash | #!/bin/bash | ||||||
|  |  | ||||||
|  | # Wait for MySQL to warm-up | ||||||
|  | while mysqladmin ping --host mysql --silent; do | ||||||
|  |  | ||||||
| # Recreate view | # 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} -e "DROP VIEW IF EXISTS sogo_view" | ||||||
|  |  | ||||||
| mysql --host mysql -u ${DBUSER} -p${DBPASS} ${DBNAME} << EOF | 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 | CREATE VIEW sogo_view (c_uid, domain, c_name, c_password, c_cn, mail, aliases, ad_aliases, senderacl, 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, ''), IFNULL(gs.send_as, ''), CONCAT('/var/vmail/', maildir) FROM mailbox | 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), mailbox.kind, mailbox.multiple_bookings FROM mailbox | ||||||
| LEFT OUTER JOIN grouped_mail_aliases ga ON ga.username = mailbox.username | 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_sender_acl gs ON gs.username = mailbox.username | ||||||
| LEFT OUTER JOIN grouped_domain_alias_address gda ON gda.username = mailbox.username | LEFT OUTER JOIN grouped_domain_alias_address gda ON gda.username = mailbox.username | ||||||
| WHERE mailbox.active = '1'; | WHERE mailbox.active = '1'; | ||||||
| EOF | EOF | ||||||
|  |  | ||||||
| # Wait for MySQL to warm-up |  | ||||||
| while ! mysqladmin ping --host mysql --silent; do |  | ||||||
| 	sleep 1 |  | ||||||
| done |  | ||||||
|  |  | ||||||
| mkdir -p /var/lib/sogo/GNUstep/Defaults/ | mkdir -p /var/lib/sogo/GNUstep/Defaults/ | ||||||
|  |  | ||||||
| @@ -69,8 +68,6 @@ while read line | |||||||
|                     <string>kind</string> |                     <string>kind</string> | ||||||
|                     <key>MultipleBookingsFieldName</key> |                     <key>MultipleBookingsFieldName</key> | ||||||
|                     <string>multiple_bookings</string> |                     <string>multiple_bookings</string> | ||||||
|                     <key>IMAPLoginFieldName</key> |  | ||||||
|                     <string>c_uid</string> |  | ||||||
|                     <key>canAuthenticate</key> |                     <key>canAuthenticate</key> | ||||||
|                     <string>YES</string> |                     <string>YES</string> | ||||||
|                     <key>displayName</key> |                     <key>displayName</key> | ||||||
| @@ -99,4 +96,6 @@ echo '    </dict> | |||||||
| chown sogo:sogo -R /var/lib/sogo/ | chown sogo:sogo -R /var/lib/sogo/ | ||||||
| chmod 600 /var/lib/sogo/GNUstep/Defaults/sogod.plist | chmod 600 /var/lib/sogo/GNUstep/Defaults/sogod.plist | ||||||
|  |  | ||||||
| sleep infinite | sleep 99999 | ||||||
|  |  | ||||||
|  | done; | ||||||
|   | |||||||
| @@ -30,6 +30,12 @@ stdout_logfile_maxbytes=0 | |||||||
| command=/usr/sbin/cron -f | command=/usr/sbin/cron -f | ||||||
| autorestart=true | 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] | [inet_http_server] | ||||||
| port=9191 | port=9191 | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| auth_mechanisms = plain login | auth_mechanisms = plain login | ||||||
| #mail_debug = yes | #mail_debug = yes | ||||||
| log_path = /dev/stdout | log_path = /var/log/mail.log | ||||||
| disable_plaintext_auth = yes | disable_plaintext_auth = yes | ||||||
| # Uncomment on NFS share | # Uncomment on NFS share | ||||||
| #mmap_disable = yes | #mmap_disable = yes | ||||||
| @@ -21,6 +21,7 @@ ssl_dh_parameters_length = 2048 | |||||||
| log_timestamp = "%Y-%m-%d %H:%M:%S " | log_timestamp = "%Y-%m-%d %H:%M:%S " | ||||||
| recipient_delimiter = + | recipient_delimiter = + | ||||||
| auth_master_user_separator = * | auth_master_user_separator = * | ||||||
|  | mail_prefetch_count = 30 | ||||||
| passdb { | passdb { | ||||||
|   driver = passwd-file |   driver = passwd-file | ||||||
|   args = /etc/dovecot/dovecot-master.passwd |   args = /etc/dovecot/dovecot-master.passwd | ||||||
| @@ -182,6 +183,9 @@ service managesieve-login { | |||||||
|   process_min_avail = 2 |   process_min_avail = 2 | ||||||
|   vsz_limit = 128M |   vsz_limit = 128M | ||||||
| } | } | ||||||
|  | service imap { | ||||||
|  |   executable = imap imap-postlogin | ||||||
|  | } | ||||||
| service managesieve { | service managesieve { | ||||||
|   process_limit = 256 |   process_limit = 256 | ||||||
| } | } | ||||||
| @@ -236,3 +240,8 @@ remote 127.0.0.1 { | |||||||
| } | } | ||||||
| submission_host = postfix:588 | submission_host = postfix:588 | ||||||
| mail_max_userip_connections = 500 | mail_max_userip_connections = 500 | ||||||
|  | service imap-postlogin { | ||||||
|  |   executable = script-login /usr/local/bin/postlogin.sh | ||||||
|  |   unix_listener imap-postlogin { | ||||||
|  |   } | ||||||
|  | } | ||||||
|   | |||||||
							
								
								
									
										1
									
								
								data/conf/nginx/listen.template
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								data/conf/nginx/listen.template
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | |||||||
|  | listen ${HTTPS_PORT}; | ||||||
| @@ -1,5 +1,8 @@ | |||||||
|  | proxy_cache_path /tmp levels=1:2 keys_zone=sogo:10m inactive=24h  max_size=1g; | ||||||
| server { | server { | ||||||
|   listen 443; |   include /etc/nginx/conf.d/listen.active; | ||||||
|  |   charset utf-8; | ||||||
|  |   override_charset on; | ||||||
|   ssl on; |   ssl on; | ||||||
|   ssl_certificate /etc/ssl/mail/cert.pem; |   ssl_certificate /etc/ssl/mail/cert.pem; | ||||||
|   ssl_certificate_key /etc/ssl/mail/key.pem; |   ssl_certificate_key /etc/ssl/mail/key.pem; | ||||||
| @@ -72,7 +75,7 @@ server { | |||||||
|     proxy_set_header x-webobjects-server-protocol HTTP/1.0; |     proxy_set_header x-webobjects-server-protocol HTTP/1.0; | ||||||
|     proxy_set_header x-webobjects-remote-host $remote_addr; |     proxy_set_header x-webobjects-remote-host $remote_addr; | ||||||
|     proxy_set_header x-webobjects-server-name $server_name; |     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_set_header x-webobjects-server-port $server_port; | ||||||
|     client_body_buffer_size 128k; |     client_body_buffer_size 128k; | ||||||
|     client_max_body_size 100m; |     client_max_body_size 100m; | ||||||
| @@ -86,7 +89,7 @@ server { | |||||||
|     proxy_set_header x-webobjects-server-protocol HTTP/1.0; |     proxy_set_header x-webobjects-server-protocol HTTP/1.0; | ||||||
|     proxy_set_header x-webobjects-remote-host $remote_addr; |     proxy_set_header x-webobjects-remote-host $remote_addr; | ||||||
|     proxy_set_header x-webobjects-server-name $server_name; |     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_set_header x-webobjects-server-port $server_port; | ||||||
|     #proxy_connect_timeout 90; |     #proxy_connect_timeout 90; | ||||||
|     #proxy_send_timeout 90; |     #proxy_send_timeout 90; | ||||||
| @@ -101,16 +104,32 @@ server { | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   location /SOGo.woa/WebServerResources/ { |   location /SOGo.woa/WebServerResources/ { | ||||||
|     alias /usr/lib/GNUstep/SOGo/WebServerResources/; |     proxy_pass http://sogo: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; |     allow all; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   location /SOGo/WebServerResources/ { |   location /SOGo/WebServerResources/ { | ||||||
|     alias /usr/lib/GNUstep/SOGo/WebServerResources/; |     proxy_pass http://sogo: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; |     allow all; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   location (^/SOGo/so/ControlPanel/Products/[^/]*UI/Resources/.*\.(jpg|png|gif|css|js)$ { |   location (^/SOGo/so/ControlPanel/Products/[^/]*UI/Resources/.*\.(jpg|png|gif|css|js)$ { | ||||||
|     alias /usr/lib/GNUstep/SOGo/$1.SOGo/Resources/$2; |     proxy_pass http://sogo: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; | ||||||
|   } |   } | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,12 +1,14 @@ | |||||||
| <?php | <?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, | 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 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. | then any of these will trigger the rule. If a rule is triggered then no more rules are matched. | ||||||
| */ | */ | ||||||
|  | ini_set('error_reporting', '0'); | ||||||
|  |  | ||||||
| header('Content-Type: text/plain'); | header('Content-Type: text/plain'); | ||||||
| require_once "vars.inc.php"; | require_once "vars.inc.php"; | ||||||
|  |  | ||||||
| $dsn = $database_type . ':host=' . $database_host . ';dbname=' . $database_name; | $dsn = $database_type . ':host=' . $database_host . ';dbname=' . $database_name; | ||||||
| $opt = [ | $opt = [ | ||||||
|     PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION, |     PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION, | ||||||
| @@ -33,9 +35,9 @@ while ($row = array_shift($rows)) { | |||||||
| 	$spamscore = $stmt->fetchAll(PDO::FETCH_COLUMN|PDO::FETCH_GROUP); | 	$spamscore = $stmt->fetchAll(PDO::FETCH_COLUMN|PDO::FETCH_GROUP); | ||||||
|  |  | ||||||
| 	$stmt = $pdo->prepare("SELECT GROUP_CONCAT(REPLACE(`value`, '*', '.*') SEPARATOR '|') AS `value` FROM `filterconf` | 	$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')"); | 			AND (`option` = 'blacklist_from' OR `option` = 'whitelist_from')"); | ||||||
| 	$stmt->execute(array(':object' => $row['object'])); | 	$stmt->execute(array(':object' => $row['object'], ':object_domain' => substr(strrchr($row['object'], "@"), 1))); | ||||||
| 	$grouped_lists = $stmt->fetchAll(PDO::FETCH_COLUMN); | 	$grouped_lists = $stmt->fetchAll(PDO::FETCH_COLUMN); | ||||||
| 	$value_sane = preg_replace("/\.\./", ".", (preg_replace("/\*/", ".*", $grouped_lists[0]))); | 	$value_sane = preg_replace("/\.\./", ".", (preg_replace("/\*/", ".*", $grouped_lists[0]))); | ||||||
| ?> | ?> | ||||||
| @@ -55,10 +57,13 @@ while ($row = array_shift($rows)) { | |||||||
| 		WHERE `mailbox`.`username` = :object"); | 		WHERE `mailbox`.`username` = :object"); | ||||||
| 	$stmt->execute(array(':object' => $row['object'])); | 	$stmt->execute(array(':object' => $row['object'])); | ||||||
| 	$rows_aliases_2 = $stmt->fetchAll(PDO::FETCH_ASSOC); | 	$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'])) { | ||||||
| ?> | ?> | ||||||
| 		rcpt = "<?=$row_aliases_2['aliases'];?>"; | 		rcpt = "<?=$row_aliases_2['aliases'];?>"; | ||||||
| <?php | <?php | ||||||
|  |     } | ||||||
| 	} | 	} | ||||||
| ?> | ?> | ||||||
| 		apply "default" { | 		apply "default" { | ||||||
| @@ -68,6 +73,7 @@ while ($row = array_shift($rows)) { | |||||||
| 				"add header" = <?=$spamscore['lowspamlevel'][0];?>; | 				"add header" = <?=$spamscore['lowspamlevel'][0];?>; | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  | 	} | ||||||
| <?php | <?php | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -81,7 +87,6 @@ while ($row = array_shift($rows)) { | |||||||
| 	$username_sane = preg_replace("/[^a-zA-Z0-9]+/", "", $row['object']); | 	$username_sane = preg_replace("/[^a-zA-Z0-9]+/", "", $row['object']); | ||||||
| ?> | ?> | ||||||
| 	whitelist_<?=$username_sane;?> { | 	whitelist_<?=$username_sane;?> { | ||||||
| 		priority = high; |  | ||||||
| <?php | <?php | ||||||
| 	$stmt = $pdo->prepare("SELECT GROUP_CONCAT(REPLACE(`value`, '*', '.*') SEPARATOR '|') AS `value` FROM `filterconf` | 	$stmt = $pdo->prepare("SELECT GROUP_CONCAT(REPLACE(`value`, '*', '.*') SEPARATOR '|') AS `value` FROM `filterconf` | ||||||
| 		WHERE `object`= :object | 		WHERE `object`= :object | ||||||
| @@ -94,26 +99,30 @@ while ($row = array_shift($rows)) { | |||||||
| <?php | <?php | ||||||
| 	if (!filter_var(trim($row['object']), FILTER_VALIDATE_EMAIL)) { | 	if (!filter_var(trim($row['object']), FILTER_VALIDATE_EMAIL)) { | ||||||
| ?> | ?> | ||||||
| 		rcpt = "/.*@<?=$username_userpref['username'];?>/"; | 		priority = medium; | ||||||
|  | 		rcpt = "/.*@<?=$row['object'];?>/"; | ||||||
| <?php | <?php | ||||||
| 		$stmt = $pdo->prepare("SELECT `alias_domain` FROM `alias_domain` | 		$stmt = $pdo->prepare("SELECT `alias_domain` FROM `alias_domain` | ||||||
| 			WHERE `target_domain` = :object"); | 			WHERE `target_domain` = :object"); | ||||||
| 		$stmt->execute(array(':object' => $row['object'])); | 		$stmt->execute(array(':object' => $row['object'])); | ||||||
| 		$rows_domain_aliases = $stmt->fetchAll(PDO::FETCH_ASSOC); | 		$rows_domain_aliases = $stmt->fetchAll(PDO::FETCH_ASSOC); | ||||||
|  |     array_filter($rows_domain_aliases); | ||||||
| 		while ($row_domain_aliases = array_shift($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 | <?php | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	else { | 	else { | ||||||
| ?> | ?> | ||||||
|  | 		priority = high; | ||||||
| 		rcpt = "<?=$row['object'];?>"; | 		rcpt = "<?=$row['object'];?>"; | ||||||
| <?php | <?php | ||||||
| 	} | 	} | ||||||
| 	$stmt = $pdo->prepare("SELECT `address` FROM `alias` WHERE `goto` = :object_goto AND `address` NOT LIKE '@%' AND `address` != :object_address"); | 	$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->execute(array(':object_goto' => $row['object'], ':object_address' => $row['object'])); | ||||||
| 	$rows_aliases_wl_1 = $stmt->fetchAll(PDO::FETCH_ASSOC); | 	$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)) { | 	while ($row_aliases_wl_1 = array_shift($rows_aliases_wl_1)) { | ||||||
| ?> | ?> | ||||||
| 		rcpt = "<?=$row_aliases_wl_1['address'];?>"; | 		rcpt = "<?=$row_aliases_wl_1['address'];?>"; | ||||||
| @@ -124,10 +133,13 @@ while ($row = array_shift($rows)) { | |||||||
| 		WHERE `mailbox`.`username` = :object"); | 		WHERE `mailbox`.`username` = :object"); | ||||||
| 	$stmt->execute(array(':object' => $row['object'])); | 	$stmt->execute(array(':object' => $row['object'])); | ||||||
| 	$rows_aliases_wl_2 = $stmt->fetchAll(PDO::FETCH_ASSOC); | 	$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'])) { | ||||||
| ?> | ?> | ||||||
| 		rcpt = "<?=$row_aliases_wl_2['aliases'];?>"; | 		rcpt = "<?=$row_aliases_wl_2['aliases'];?>"; | ||||||
| <?php | <?php | ||||||
|  |     } | ||||||
| 	} | 	} | ||||||
| ?> | ?> | ||||||
| 		apply "default" { | 		apply "default" { | ||||||
| @@ -147,7 +159,6 @@ while ($row = array_shift($rows)) { | |||||||
| 	$username_sane = preg_replace("/[^a-zA-Z0-9]+/", "", $row['object']); | 	$username_sane = preg_replace("/[^a-zA-Z0-9]+/", "", $row['object']); | ||||||
| ?> | ?> | ||||||
| 	blacklist_<?=$username_sane;?> { | 	blacklist_<?=$username_sane;?> { | ||||||
| 		priority = medium; |  | ||||||
| <?php | <?php | ||||||
| 	$stmt = $pdo->prepare("SELECT GROUP_CONCAT(REPLACE(`value`, '*', '.*') SEPARATOR '|') AS `value` FROM `filterconf` | 	$stmt = $pdo->prepare("SELECT GROUP_CONCAT(REPLACE(`value`, '*', '.*') SEPARATOR '|') AS `value` FROM `filterconf` | ||||||
| 		WHERE `object`= :object | 		WHERE `object`= :object | ||||||
| @@ -160,40 +171,47 @@ while ($row = array_shift($rows)) { | |||||||
| <?php | <?php | ||||||
| 	if (!filter_var(trim($row['object']), FILTER_VALIDATE_EMAIL)) { | 	if (!filter_var(trim($row['object']), FILTER_VALIDATE_EMAIL)) { | ||||||
| ?> | ?> | ||||||
| 		rcpt = "/.*@<?=$username_userpref['username'];?>/"; | 		priority = medium; | ||||||
|  | 		rcpt = "/.*@<?=$row['object'];?>/"; | ||||||
| <?php | <?php | ||||||
| 		$stmt = $pdo->prepare("SELECT `alias_domain` FROM `alias_domain` | 		$stmt = $pdo->prepare("SELECT `alias_domain` FROM `alias_domain` | ||||||
| 			WHERE `target_domain` = :object"); | 			WHERE `target_domain` = :object"); | ||||||
| 		$stmt->execute(array(':object' => $row['object'])); | 		$stmt->execute(array(':object' => $row['object'])); | ||||||
| 		$rows_domain_aliases = $stmt->fetchAll(PDO::FETCH_ASSOC); | 		$rows_domain_aliases = $stmt->fetchAll(PDO::FETCH_ASSOC); | ||||||
|  |     array_filter($rows_domain_aliases); | ||||||
| 		while ($row_domain_aliases = array_shift($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 | <?php | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	else { | 	else { | ||||||
| ?> | ?> | ||||||
|  | 		priority = high; | ||||||
| 		rcpt = "<?=$row['object'];?>"; | 		rcpt = "<?=$row['object'];?>"; | ||||||
| <?php | <?php | ||||||
| 	} | 	} | ||||||
| 	$stmt = $pdo->prepare("SELECT `address` FROM `alias` WHERE `goto` = :object_goto AND `address` NOT LIKE '@%' AND `address` != :object_address"); | 	$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->execute(array(':object_goto' => $row['object'], ':object_address' => $row['object'])); | ||||||
| 	$rows_aliases_wl_1 = $stmt->fetchAll(PDO::FETCH_ASSOC); | 	$rows_aliases_bl_1 = $stmt->fetchAll(PDO::FETCH_ASSOC); | ||||||
| 	while ($row_aliases_wl_1 = array_shift($rows_aliases_wl_1)) { |   array_filter($rows_aliases_bl_1); | ||||||
|  | 	while ($row_aliases_bl_1 = array_shift($rows_aliases_bl_1)) { | ||||||
| ?> | ?> | ||||||
| 		rcpt = "<?=$row_aliases_wl_1['address'];?>"; | 		rcpt = "<?=$row_aliases_bl_1['address'];?>"; | ||||||
| <?php | <?php | ||||||
| 	} | 	} | ||||||
| 	$stmt = $pdo->prepare("SELECT CONCAT(`local_part`, '@', `alias_domain`.`alias_domain`) AS `aliases` FROM `mailbox`  | 	$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` | 		LEFT OUTER JOIN `alias_domain` on `mailbox`.`domain` = `alias_domain`.`target_domain` | ||||||
| 		WHERE `mailbox`.`username` = :object"); | 		WHERE `mailbox`.`username` = :object"); | ||||||
| 	$stmt->execute(array(':object' => $row['object'])); | 	$stmt->execute(array(':object' => $row['object'])); | ||||||
| 	$rows_aliases_wl_2 = $stmt->fetchAll(PDO::FETCH_ASSOC); | 	$rows_aliases_bl_2 = $stmt->fetchAll(PDO::FETCH_ASSOC); | ||||||
| 	while ($row_aliases_wl_2 = array_filter(array_shift($rows_aliases_wl_2))) { |   array_filter($rows_aliases_bl_2); | ||||||
|  | 	while ($row_aliases_bl_2 = array_shift($rows_aliases_bl_2)) { | ||||||
|  |     if (!empty($row_aliases_bl_2['aliases'])) { | ||||||
| ?> | ?> | ||||||
| 		rcpt = "<?=$row_aliases_wl_2['aliases'];?>"; | 		rcpt = "<?=$row_aliases_bl_2['aliases'];?>"; | ||||||
| <?php | <?php | ||||||
|  |     } | ||||||
| 	} | 	} | ||||||
| ?> | ?> | ||||||
| 		apply "default" { | 		apply "default" { | ||||||
| @@ -203,4 +221,4 @@ while ($row = array_shift($rows)) { | |||||||
| <?php | <?php | ||||||
| } | } | ||||||
| ?> | ?> | ||||||
| } | } | ||||||
							
								
								
									
										204
									
								
								data/web/add.php
									
									
									
									
									
								
							
							
						
						
									
										204
									
								
								data/web/add.php
									
									
									
									
									
								
							| @@ -1,6 +1,6 @@ | |||||||
| <?php | <?php | ||||||
| require_once("inc/prerequisites.inc.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)) { | if (!isset($_SESSION['mailcow_cc_role']) OR !in_array($_SESSION['mailcow_cc_role'], $AuthUsers)) { | ||||||
| 	header('Location: /'); | 	header('Location: /'); | ||||||
| 	exit(); | 	exit(); | ||||||
| @@ -77,7 +77,7 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm | |||||||
| 					</div> | 					</div> | ||||||
| 					<div class="form-group"> | 					<div class="form-group"> | ||||||
| 						<div class="col-sm-offset-2 col-sm-10"> | 						<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> | ||||||
| 					</div> | 					</div> | ||||||
| 					<p><span class="glyphicon glyphicon-exclamation-sign text-danger"></span> <?=$lang['add']['restart_sogo_hint'];?></p> | 					<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> | ||||||
| 					<div class="form-group"> | 					<div class="form-group"> | ||||||
| 						<div class="col-sm-offset-2 col-sm-10"> | 						<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> | ||||||
| 					</div> | 					</div> | ||||||
| 				</form> | 				</form> | ||||||
| @@ -132,29 +132,12 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm | |||||||
| 					<div class="form-group"> | 					<div class="form-group"> | ||||||
| 						<label class="control-label col-sm-2" for="target_domain"><?=$lang['add']['target_domain'];?></label> | 						<label class="control-label col-sm-2" for="target_domain"><?=$lang['add']['target_domain'];?></label> | ||||||
| 						<div class="col-sm-10"> | 						<div class="col-sm-10"> | ||||||
| 							<select name="target_domain" id="target_domain" title="<?=$lang['add']['select'];?>"> | 							<select name="target_domain" id="target_domain" title="<?=$lang['add']['select'];?>" required> | ||||||
| 								<?php | 							<?php | ||||||
| 								try { |               foreach (mailbox_get_domains() as $domain) { | ||||||
| 									$stmt = $pdo->prepare("SELECT `domain` FROM `domain` | 								echo "<option>".htmlspecialchars($domain)."</option>"; | ||||||
| 											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> | 							</select> | ||||||
| 						</div> | 						</div> | ||||||
| 					</div> | 					</div> | ||||||
| @@ -167,7 +150,7 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm | |||||||
| 					</div> | 					</div> | ||||||
| 					<div class="form-group"> | 					<div class="form-group"> | ||||||
| 						<div class="col-sm-offset-2 col-sm-10"> | 						<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> | ||||||
| 					</div> | 					</div> | ||||||
| 				</form> | 				</form> | ||||||
| @@ -188,30 +171,13 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm | |||||||
| 						<div class="col-sm-10"> | 						<div class="col-sm-10"> | ||||||
| 							<select id="addSelectDomain" name="domain" id="domain" title="<?=$lang['add']['select'];?>" required> | 							<select id="addSelectDomain" name="domain" id="domain" title="<?=$lang['add']['select'];?>" required> | ||||||
| 							<?php | 							<?php | ||||||
| 							try { |               foreach (mailbox_get_domains() as $domain) { | ||||||
| 								$stmt = $pdo->prepare("SELECT `domain` FROM `domain` | 								echo "<option>".htmlspecialchars($domain)."</option>"; | ||||||
| 										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> | 							</select> | ||||||
| 						</div> | 						</div> | ||||||
| 					</div> | 					</div>  | ||||||
| 					<div class="form-group"> | 					<div class="form-group"> | ||||||
| 						<label class="control-label col-sm-2" for="name"><?=$lang['add']['full_name'];?></label> | 						<label class="control-label col-sm-2" for="name"><?=$lang['add']['full_name'];?></label> | ||||||
| 						<div class="col-sm-10"> | 						<div class="col-sm-10"> | ||||||
| @@ -247,7 +213,61 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm | |||||||
| 					</div> | 					</div> | ||||||
| 					<div class="form-group"> | 					<div class="form-group"> | ||||||
| 						<div class="col-sm-offset-2 col-sm-10"> | 						<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> | ||||||
| 					</div> | 					</div> | ||||||
| 				</form> | 				</form> | ||||||
| @@ -259,6 +279,94 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm | |||||||
| 	<?php | 	<?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 { | else { | ||||||
| ?> | ?> | ||||||
| 				<div class="alert alert-danger" role="alert"><?=$lang['danger']['access_denied'];?></div> | 				<div class="alert alert-danger" role="alert"><?=$lang['danger']['access_denied'];?></div> | ||||||
|   | |||||||
| @@ -6,249 +6,284 @@ require_once("inc/header.inc.php"); | |||||||
| $_SESSION['return_to'] = $_SERVER['REQUEST_URI']; | $_SESSION['return_to'] = $_SERVER['REQUEST_URI']; | ||||||
| ?> | ?> | ||||||
| <div class="container"> | <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-group" id="accordion_access"> | ||||||
| 	<div class="panel panel-danger"> |     <div class="panel panel-danger"> | ||||||
| 		<div class="panel-heading"><?=$lang['admin']['admin_details'];?></div> |       <div class="panel-heading"><?=$lang['admin']['admin_details'];?></div> | ||||||
| 		<div class="panel-body"> |       <div class="panel-body"> | ||||||
| 			<form class="form-horizontal" autocapitalize="none" autocorrect="off" role="form" method="post"> |         <form class="form-horizontal" autocapitalize="none" autocorrect="off" role="form" method="post"> | ||||||
| 			<?php |         <?php $admindetails = get_admin_details(); ?> | ||||||
| 			try { |           <div class="form-group"> | ||||||
| 			$stmt = $pdo->prepare("SELECT `username` FROM `admin` |             <label class="control-label col-sm-3" for="admin_user"><?=$lang['admin']['admin'];?>:</label> | ||||||
| 				WHERE `superadmin`='1' and active='1'"); |             <div class="col-sm-9"> | ||||||
| 			$stmt->execute(); |               <input type="text" class="form-control" name="admin_user" id="admin_user" value="<?=htmlspecialchars($admindetails['username']);?>" required> | ||||||
| 			$AdminData = $stmt->fetch(PDO::FETCH_ASSOC); |               ↳ <kbd>a-z A-Z - _ .</kbd> | ||||||
| 			} |             </div> | ||||||
| 			catch(PDOException $e) { |           </div> | ||||||
| 				$_SESSION['return'] = array( |           <div class="form-group"> | ||||||
| 					'type' => 'danger', |             <label class="control-label col-sm-3" for="admin_pass"><?=$lang['admin']['password'];?>:</label> | ||||||
| 					'msg' => 'MySQL: '.$e |             <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> | ||||||
| 				<input type="hidden" name="admin_user_now" value="<?=htmlspecialchars($AdminData['username']);?>"> |           <div class="form-group"> | ||||||
| 				<div class="form-group"> |             <label class="control-label col-sm-3" for="admin_pass2"><?=$lang['admin']['password_repeat'];?>:</label> | ||||||
| 					<label class="control-label col-sm-2" for="admin_user"><?=$lang['admin']['admin'];?>:</label> |             <div class="col-sm-9"> | ||||||
| 					<div class="col-sm-10"> |             <input type="password" class="form-control" name="admin_pass2" id="admin_pass2"> | ||||||
| 						<input type="text" class="form-control" name="admin_user" id="admin_user" value="<?=htmlspecialchars($AdminData['username']);?>" required> |             </div> | ||||||
| 						↳ <kbd>a-z A-Z - _ .</kbd> |           </div> | ||||||
| 					</div> |           <div class="form-group"> | ||||||
| 				</div> |             <div class="col-sm-offset-3 col-sm-9"> | ||||||
| 				<div class="form-group"> |               <button type="submit" name="edit_admin_account" class="btn btn-default"><?=$lang['admin']['save'];?></button> | ||||||
| 					<label class="control-label col-sm-2" for="admin_pass"><?=$lang['admin']['password'];?>:</label> |             </div> | ||||||
| 					<div class="col-sm-10"> |           </div> | ||||||
| 					<input type="password" class="form-control" name="admin_pass" id="admin_pass" placeholder="<?=$lang['admin']['unchanged_if_empty'];?>"> |         </form> | ||||||
| 					</div> |         <hr> | ||||||
| 				</div> |         <div class="row"> | ||||||
| 				<div class="form-group"> |           <div class="col-sm-3 col-xs-5 text-right"><?=$lang['tfa']['tfa'];?>:</div> | ||||||
| 					<label class="control-label col-sm-2" for="admin_pass2"><?=$lang['admin']['password_repeat'];?>:</label> |           <div class="col-sm-9 col-xs-7"> | ||||||
| 					<div class="col-sm-10"> |             <p><?=get_tfa()['pretty'];?></p> | ||||||
| 					<input type="password" class="form-control" name="admin_pass2" id="admin_pass2"> |           </div> | ||||||
| 					</div> |         </div> | ||||||
| 				</div> |         <div class="row"> | ||||||
| 				<div class="form-group"> |           <div class="col-md-3 col-xs-5 text-right"><?=$lang['tfa']['set_tfa'];?>:</div> | ||||||
| 					<div class="col-sm-offset-2 col-sm-10"> |           <div class="col-md-9 col-xs-7"> | ||||||
| 						<button type="submit" name="trigger_set_admin" class="btn btn-default"><?=$lang['admin']['save'];?></button> |             <select data-width="auto" id="selectTFA" class="selectpicker" title="<?=$lang['tfa']['select'];?>"> | ||||||
| 					</div> |               <option value="yubi_otp"><?=$lang['tfa']['yubi_otp'];?></option> | ||||||
| 				</div> |               <option value="u2f"><?=$lang['tfa']['u2f'];?></option> | ||||||
| 			</form> |               <option value="none"><?=$lang['tfa']['none'];?></option> | ||||||
| 		</div> |             </select> | ||||||
| 	</div> |           </div> | ||||||
| 	<div class="panel panel-default"> |         </div> | ||||||
| 	<div style="cursor:pointer;" class="panel-heading" data-toggle="collapse" data-parent="#accordion_access" data-target="#collapseDomAdmins"> |       </div> | ||||||
| 		<span class="accordion-toggle"><?=$lang['admin']['domain_admins'];?></span> |     </div> | ||||||
| 	</div> |     <div class="panel panel-default"> | ||||||
| 		<div id="collapseDomAdmins" class="panel-collapse collapse"> |     <div style="cursor:pointer;" class="panel-heading" data-toggle="collapse" data-parent="#accordion_access" data-target="#collapseDomAdmins"> | ||||||
| 			<div class="panel-body"> |       <span class="accordion-toggle"><?=$lang['admin']['domain_admins'];?></span> | ||||||
| 				<form method="post"> |     </div> | ||||||
| 					<div class="table-responsive"> |       <div id="collapseDomAdmins" class="panel-collapse collapse"> | ||||||
| 					<table class="table table-striped sortable-theme-bootstrap" data-sortable id="domainadminstable"> |         <div class="panel-body"> | ||||||
| 						<thead> |           <form method="post"> | ||||||
| 						<tr> |             <div class="table-responsive"> | ||||||
| 							<th class="sort-table" style="min-width: 100px;"><?=$lang['admin']['username'];?></th> |             <table class="table table-striped sortable-theme-bootstrap" data-sortable id="domainadminstable"> | ||||||
| 							<th class="sort-table" style="min-width: 166px;"><?=$lang['admin']['admin_domains'];?></th> |               <thead> | ||||||
| 							<th class="sort-table" style="min-width: 76px;"><?=$lang['admin']['active'];?></th> |               <tr> | ||||||
| 							<th style="text-align: right; min-width: 200px;" data-sortable="false"><?=$lang['admin']['action'];?></th> |                 <th class="sort-table" style="min-width: 100px;"><?=$lang['admin']['username'];?></th> | ||||||
| 						</tr> |                 <th class="sort-table" style="min-width: 166px;"><?=$lang['admin']['admin_domains'];?></th> | ||||||
| 						</thead> |                 <th class="sort-table" style="min-width: 76px;"><?=$lang['admin']['active'];?></th> | ||||||
| 						<tbody> |                 <th class="sort-table" style="min-width: 76px;"><?=$lang['tfa']['tfa'];?></th> | ||||||
| 							<?php |                 <th style="text-align: right; min-width: 200px;" data-sortable="false"><?=$lang['admin']['action'];?></th> | ||||||
| 							try { |               </tr> | ||||||
| 								$stmt = $pdo->query("SELECT DISTINCT |               </thead> | ||||||
| 									`username`,  |               <tbody> | ||||||
| 									CASE WHEN `active`='1' THEN '".$lang['admin']['yes']."' ELSE '".$lang['admin']['no']."' END AS `active` |                 <?php | ||||||
| 										FROM `domain_admins`  |                 foreach (get_domain_admins() as $domain_admin) { | ||||||
| 											WHERE `username` IN ( |                   $da_data = get_domain_admin_details($domain_admin);  | ||||||
| 												SELECT `username` FROM `admin` |                   if (!empty($da_data)): | ||||||
| 													WHERE `superadmin`!='1' |                 ?> | ||||||
| 											)"); |                 <tr id="data"> | ||||||
| 								$rows_username = $stmt->fetchAll(PDO::FETCH_ASSOC); |                   <td><?=htmlspecialchars(strtolower($domain_admin));?></td> | ||||||
| 							} |                   <td> | ||||||
| 							catch(PDOException $e) { |                   <?php | ||||||
| 								$_SESSION['return'] = array( |                   foreach ($da_data['selected_domains'] as $domain) { | ||||||
| 									'type' => 'danger', |                     echo htmlspecialchars($domain).'<br />'; | ||||||
| 									'msg' => 'MySQL: '.$e |                   } | ||||||
| 								); |                   ?> | ||||||
| 							} |                   </td> | ||||||
| 							if(!empty($rows_username)): |                   <td><?=$da_data['active'];?></td> | ||||||
| 							while ($row_user_state = array_shift($rows_username)): |                   <td><?=empty($da_data['tfa_active_int']) ? "✘" : "✔";?></td> | ||||||
| 							?> |                   <td style="text-align: right;"> | ||||||
| 							<tr id="data"> |                     <div class="btn-group"> | ||||||
| 								<td><?=htmlspecialchars(strtolower($row_user_state['username']));?></td> |                       <a href="edit.php?domainadmin=<?=$domain_admin;?>" class="btn btn-xs btn-default"><span class="glyphicon glyphicon-pencil"></span> <?=$lang['admin']['edit'];?></a> | ||||||
| 								<td> |                       <a href="delete.php?domainadmin=<?=$domain_admin;?>" class="btn btn-xs btn-danger"><span class="glyphicon glyphicon-trash"></span> <?=$lang['admin']['remove'];?></a> | ||||||
| 								<?php |                     </div> | ||||||
| 								try { |                   </td> | ||||||
| 									$stmt = $pdo->prepare("SELECT `domain` FROM `domain_admins` WHERE `username` = :username"); |                   </td> | ||||||
| 									$stmt->execute(array('username' => $row_user_state['username'])); |                 </tr> | ||||||
| 									$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> |  | ||||||
|  |  | ||||||
| 							<?php |                 <?php | ||||||
| 							endwhile; |                 else: | ||||||
| 							else: |                 ?> | ||||||
| 							?> |                   <tr id="no-data"><td colspan="4" style="text-align: center; font-style: italic;"><?=$lang['admin']['no_record'];?></td></tr> | ||||||
| 								<tr id="no-data"><td colspan="4" style="text-align: center; font-style: italic;"><?=$lang['admin']['no_record'];?></td></tr> |                 <?php | ||||||
| 							<?php |                 endif; | ||||||
| 							endif; |                 } | ||||||
| 							?> |                 ?> | ||||||
| 						</tbody> |               </tbody> | ||||||
| 					</table> |             </table> | ||||||
| 					</div> |             </div> | ||||||
| 				</form> |           </form> | ||||||
| 				<small> |           <small> | ||||||
| 				<legend><?=$lang['admin']['add_domain_admin'];?></legend> |           <legend><?=$lang['admin']['add_domain_admin'];?></legend> | ||||||
| 				<form class="form-horizontal" role="form" method="post"> |           <form class="form-horizontal" role="form" method="post"> | ||||||
| 					<div class="form-group"> |             <div class="form-group"> | ||||||
| 						<label class="control-label col-sm-2" for="username"><?=$lang['admin']['username'];?>:</label> |               <label class="control-label col-sm-2" for="username"><?=$lang['admin']['username'];?>:</label> | ||||||
| 						<div class="col-sm-10"> |               <div class="col-sm-10"> | ||||||
| 							<input type="text" class="form-control" name="username" id="username" required> |                 <input type="text" class="form-control" name="username" id="username" required> | ||||||
| 							↳ <kbd>a-z A-Z - _ .</kbd> |                 ↳ <kbd>a-z A-Z - _ .</kbd> | ||||||
| 						</div> |               </div> | ||||||
| 					</div> |             </div> | ||||||
| 					<div class="form-group"> |             <div class="form-group"> | ||||||
| 						<label class="control-label col-sm-2" for="name"><?=$lang['admin']['admin_domains'];?>:</label> |               <label class="control-label col-sm-2" for="name"><?=$lang['admin']['admin_domains'];?>:</label> | ||||||
| 						<div class="col-sm-10"> |               <div class="col-sm-10"> | ||||||
| 							<select title="<?=$lang['admin']['search_domain_da'];?>" style="width:100%" name="domain[]" size="5" multiple> |                 <select title="<?=$lang['admin']['search_domain_da'];?>" style="width:100%" name="domain[]" size="5" multiple> | ||||||
| 							<?php |                 <?php | ||||||
| 							try { |                 foreach (mailbox_get_domains() as $domain) { | ||||||
| 								$stmt = $pdo->query("SELECT domain FROM domain"); |                   echo "<option>".htmlspecialchars($domain)."</option>"; | ||||||
| 								$rows = $stmt->fetchAll(PDO::FETCH_ASSOC); |                 } | ||||||
| 							} |                 ?> | ||||||
| 							catch(PDOException $e) { |                 </select> | ||||||
| 								$_SESSION['return'] = array( |               </div> | ||||||
| 									'type' => 'danger', |             </div> | ||||||
| 									'msg' => 'MySQL: '.$e |             <div class="form-group"> | ||||||
| 								); |               <label class="control-label col-sm-2" for="password"><?=$lang['admin']['password'];?>:</label> | ||||||
| 							} |               <div class="col-sm-10"> | ||||||
| 							while ($row = array_shift($rows)) { |               <input type="password" class="form-control" name="password" id="password" placeholder=""> | ||||||
| 								echo "<option>".htmlspecialchars($row['domain'])."</option>"; |               </div> | ||||||
| 							} |             </div> | ||||||
| 							?> |             <div class="form-group"> | ||||||
| 							</select> |               <label class="control-label col-sm-2" for="password2"><?=$lang['admin']['password_repeat'];?>:</label> | ||||||
| 						</div> |               <div class="col-sm-10"> | ||||||
| 					</div> |               <input type="password" class="form-control" name="password2" id="password2" placeholder=""> | ||||||
| 					<div class="form-group"> |               </div> | ||||||
| 						<label class="control-label col-sm-2" for="password"><?=$lang['admin']['password'];?>:</label> |             </div> | ||||||
| 						<div class="col-sm-10"> |             <div class="form-group"> | ||||||
| 						<input type="password" class="form-control" name="password" id="password" placeholder=""> |               <div class="col-sm-offset-2 col-sm-10"> | ||||||
| 						</div> |                 <div class="checkbox"> | ||||||
| 					</div> |                 <label><input type="checkbox" name="active" checked> <?=$lang['admin']['active'];?></label> | ||||||
| 					<div class="form-group"> |                 </div> | ||||||
| 						<label class="control-label col-sm-2" for="password2"><?=$lang['admin']['password_repeat'];?>:</label> |               </div> | ||||||
| 						<div class="col-sm-10"> |             </div> | ||||||
| 						<input type="password" class="form-control" name="password2" id="password2" placeholder=""> |             <div class="form-group"> | ||||||
| 						</div> |               <div class="col-sm-offset-2 col-sm-10"> | ||||||
| 					</div> |                 <button type="submit" name="add_domain_admin" class="btn btn-default"><?=$lang['admin']['add'];?></button> | ||||||
| 					<div class="form-group"> |               </div> | ||||||
| 						<div class="col-sm-offset-2 col-sm-10"> |             </div> | ||||||
| 							<div class="checkbox"> |           </form> | ||||||
| 							<label><input type="checkbox" name="active" checked> <?=$lang['admin']['active'];?></label> |           </small> | ||||||
| 							</div> |         </div> | ||||||
| 						</div> |       </div> | ||||||
| 					</div> |     </div> | ||||||
| 					<div class="form-group"> |   </div> | ||||||
| 						<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> |  | ||||||
|  |  | ||||||
| <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-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> <!-- /container --> | </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="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/sorttable.js"></script> | ||||||
| <script src="js/admin.js"></script> | <script src="js/admin.js"></script> | ||||||
|   | |||||||
							
								
								
									
										19
									
								
								data/web/css/mailbox.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								data/web/css/mailbox.css
									
									
									
									
									
										Normal file
									
								
							| @@ -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; | ||||||
|  | } | ||||||
							
								
								
									
										46
									
								
								data/web/css/mailcow.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								data/web/css/mailcow.css
									
									
									
									
									
										Normal file
									
								
							| @@ -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;} | ||||||
							
								
								
									
										79
									
								
								data/web/css/tables.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								data/web/css/tables.css
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | <?php | ||||||
| require_once("inc/prerequisites.inc.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)) { | if (!isset($_SESSION['mailcow_cc_role']) OR !in_array($_SESSION['mailcow_cc_role'], $AuthUsers)) { | ||||||
| 	header('Location: /'); | 	header('Location: /'); | ||||||
| 	exit(); | 	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) ?>"> | 				<input type="hidden" name="domain" value="<?php echo htmlspecialchars($domain) ?>"> | ||||||
| 					<div class="form-group"> | 					<div class="form-group"> | ||||||
| 						<div class="col-sm-offset-1 col-sm-10"> | 						<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> | ||||||
| 					</div> | 					</div> | ||||||
| 				</form> | 				</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"]) ?>"> | 					<input type="hidden" name="address" value="<?php echo htmlspecialchars($_GET["alias"]) ?>"> | ||||||
| 						<div class="form-group"> | 						<div class="form-group"> | ||||||
| 							<div class="col-sm-offset-1 col-sm-10"> | 							<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> | ||||||
| 						</div> | 						</div> | ||||||
| 					</form> | 					</form> | ||||||
| @@ -66,27 +66,16 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm | |||||||
| 			isset($_GET["aliasdomain"]) && | 			isset($_GET["aliasdomain"]) && | ||||||
| 			is_valid_domain_name($_GET["aliasdomain"]) &&  | 			is_valid_domain_name($_GET["aliasdomain"]) &&  | ||||||
| 			!empty($_GET["aliasdomain"])) { | 			!empty($_GET["aliasdomain"])) { | ||||||
| 				$alias_domain = strtolower(trim($_GET["aliasdomain"])); |         $alias_domain = $_GET["aliasdomain"]; | ||||||
| 				try { |         $result = mailbox_get_alias_domain_details($alias_domain); | ||||||
| 					$stmt = $pdo->prepare("SELECT `target_domain` FROM `alias_domain` | 				if (!empty($result)) { | ||||||
| 							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'])) { |  | ||||||
| 				?> | 				?> | ||||||
| 					<div class="alert alert-warning" role="alert"><?=sprintf($lang['delete']['remove_domainalias_warning'], htmlspecialchars($_GET["aliasdomain"]));?></div> | 					<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"> | 					<form class="form-horizontal" role="form" method="post" action="/mailbox.php"> | ||||||
| 					<input type="hidden" name="alias_domain" value="<?php echo htmlspecialchars($alias_domain) ?>"> | 					<input type="hidden" name="alias_domain" value="<?php echo htmlspecialchars($alias_domain) ?>"> | ||||||
| 						<div class="form-group"> | 						<div class="form-group"> | ||||||
| 							<div class="col-sm-offset-1 col-sm-10"> | 							<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> | ||||||
| 						</div> | 						</div> | ||||||
| 					</form> | 					</form> | ||||||
| @@ -102,7 +91,7 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm | |||||||
| 		elseif (isset($_GET["domainadmin"]) && | 		elseif (isset($_GET["domainadmin"]) && | ||||||
| 			ctype_alnum(str_replace(array('_', '.', '-'), '', $_GET["domainadmin"])) && | 			ctype_alnum(str_replace(array('_', '.', '-'), '', $_GET["domainadmin"])) && | ||||||
| 			!empty($_GET["domainadmin"]) && | 			!empty($_GET["domainadmin"]) && | ||||||
| 			$_SESSION['mailcow_cc_role'] == "admin") { |         $_SESSION['mailcow_cc_role'] == "admin") { | ||||||
| 				$domain_admin = $_GET["domainadmin"]; | 				$domain_admin = $_GET["domainadmin"]; | ||||||
| 				?> | 				?> | ||||||
| 				<div class="alert alert-warning" role="alert"><?=sprintf($lang['delete']['remove_domainadmin_warning'], htmlspecialchars($_GET["domainadmin"]));?></div> | 				<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);?>"> | 				<input type="hidden" name="username" value="<?=htmlspecialchars($domain_admin);?>"> | ||||||
| 					<div class="form-group"> | 					<div class="form-group"> | ||||||
| 						<div class="col-sm-offset-1 col-sm-10"> | 						<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> | ||||||
| 					</div> | 					</div> | ||||||
| 				</form> | 				</form> | ||||||
| @@ -121,16 +110,74 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm | |||||||
| 			filter_var($_GET["mailbox"], FILTER_VALIDATE_EMAIL) && | 			filter_var($_GET["mailbox"], FILTER_VALIDATE_EMAIL) && | ||||||
| 			!empty($_GET["mailbox"])) { | 			!empty($_GET["mailbox"])) { | ||||||
| 				$mailbox = $_GET["mailbox"]; | 				$mailbox = $_GET["mailbox"]; | ||||||
| 				$domain = substr(strrchr($mailbox, "@"), 1); | 				if (hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $mailbox)) { | ||||||
| 				if (hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $domain)) { |  | ||||||
| 				?> | 				?> | ||||||
| 					<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> | 					<p><?=$lang['delete']['remove_mailbox_details'];?></p> | ||||||
| 					<form class="form-horizontal" role="form" method="post" action="/mailbox.php"> | 					<form class="form-horizontal" role="form" method="post" action="/mailbox.php"> | ||||||
| 					<input type="hidden" name="username" value="<?=htmlspecialchars($mailbox);?>"> | 					<input type="hidden" name="username" value="<?=htmlspecialchars($mailbox);?>"> | ||||||
| 						<div class="form-group"> | 						<div class="form-group"> | ||||||
| 							<div class="col-sm-offset-1 col-sm-10"> | 							<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> | ||||||
| 						</div> | 						</div> | ||||||
| 					</form> | 					</form> | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| <?php | <?php | ||||||
| require_once("inc/prerequisites.inc.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)) { | if (!isset($_SESSION['mailcow_cc_role']) OR !in_array($_SESSION['mailcow_cc_role'], $AuthUsers)) { | ||||||
| 	header('Location: /'); | 	header('Location: /'); | ||||||
| 	exit(); | 	exit(); | ||||||
| @@ -20,34 +20,8 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm | |||||||
| 		if (isset($_GET["alias"]) && | 		if (isset($_GET["alias"]) && | ||||||
| 			!empty($_GET["alias"])) { | 			!empty($_GET["alias"])) { | ||||||
| 				$alias = $_GET["alias"]; | 				$alias = $_GET["alias"]; | ||||||
| 				$domain = substr(strrchr($alias, "@"), 1); |         $result = mailbox_get_alias_details($alias); | ||||||
| 				try { | 				if (!empty($result)) { | ||||||
| 					$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) { |  | ||||||
| 				?> | 				?> | ||||||
| 					<h4><?=$lang['edit']['alias'];?></h4> | 					<h4><?=$lang['edit']['alias'];?></h4> | ||||||
| 					<br /> | 					<br /> | ||||||
| @@ -62,13 +36,13 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm | |||||||
| 						<div class="form-group"> | 						<div class="form-group"> | ||||||
| 							<div class="col-sm-offset-2 col-sm-10"> | 							<div class="col-sm-offset-2 col-sm-10"> | ||||||
| 								<div class="checkbox"> | 								<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> | ||||||
| 						</div> | 						</div> | ||||||
| 						<div class="form-group"> | 						<div class="form-group"> | ||||||
| 							<div class="col-sm-offset-2 col-sm-10"> | 							<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> | ||||||
| 						</div> | 						</div> | ||||||
| 					</form> | 					</form> | ||||||
| @@ -86,68 +60,34 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm | |||||||
| 			$_GET["domainadmin"] != 'admin' && | 			$_GET["domainadmin"] != 'admin' && | ||||||
| 			$_SESSION['mailcow_cc_role'] == "admin") { | 			$_SESSION['mailcow_cc_role'] == "admin") { | ||||||
| 				$domain_admin = $_GET["domainadmin"]; | 				$domain_admin = $_GET["domainadmin"]; | ||||||
| 				try { |         $result = get_domain_admin_details($domain_admin); | ||||||
| 					$stmt = $pdo->prepare("SELECT * FROM `domain_admins` WHERE `username`= :domain_admin"); | 				if (!empty($result)) { | ||||||
| 					$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) { |  | ||||||
| 				?> | 				?> | ||||||
| 				<h4><?=$lang['edit']['domain_admin'];?></h4> | 				<h4><?=$lang['edit']['domain_admin'];?></h4> | ||||||
| 				<br /> | 				<br /> | ||||||
| 				<form class="form-horizontal" role="form" method="post" action="<?=($FORM_ACTION == "previous") ? $_SESSION['return_to'] : null;?>"> | 				<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"> | 					<div class="form-group"> | ||||||
| 						<label class="control-label col-sm-2" for="domain"><?=$lang['edit']['domains'];?></label> | 						<label class="control-label col-sm-2" for="domain"><?=$lang['edit']['domains'];?></label> | ||||||
| 						<div class="col-sm-10"> | 						<div class="col-sm-10"> | ||||||
| 							<select id="domain" name="domain[]" multiple> | 							<select id="domain" name="domain[]" multiple> | ||||||
| 							<?php | 							<?php | ||||||
| 							try { | 							foreach ($result['selected_domains'] as $domain): | ||||||
| 								$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)): |  | ||||||
| 							?> | 							?> | ||||||
| 								<option selected><?=htmlspecialchars($row_selected['domain']);?></option> | 								<option selected><?=htmlspecialchars($domain);?></option> | ||||||
| 							<?php | 							<?php | ||||||
| 							endwhile; | 							endforeach; | ||||||
| 							try { | 							foreach ($result['unselected_domains'] as $domain): | ||||||
| 								$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)): |  | ||||||
| 							?> | 							?> | ||||||
| 								<option><?=htmlspecialchars($row_unselected['domain']);?></option> | 								<option><?=htmlspecialchars($domain);?></option> | ||||||
| 							<?php | 							<?php | ||||||
| 							endwhile; | 							endforeach; | ||||||
| 							?> | 							?> | ||||||
| 							</select> | 							</select> | ||||||
| 						</div> | 						</div> | ||||||
| @@ -167,13 +107,20 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm | |||||||
| 					<div class="form-group"> | 					<div class="form-group"> | ||||||
| 						<div class="col-sm-offset-2 col-sm-10"> | 						<div class="col-sm-offset-2 col-sm-10"> | ||||||
| 							<div class="checkbox"> | 							<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> | ||||||
| 					</div> | 					</div> | ||||||
| 					<div class="form-group"> | 					<div class="form-group"> | ||||||
| 						<div class="col-sm-offset-2 col-sm-10"> | 						<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="delete_tfa"> <?=$lang['tfa']['delete_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> | ||||||
| 					</div> | 					</div> | ||||||
| 				</form> | 				</form> | ||||||
| @@ -189,29 +136,8 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm | |||||||
| 		is_valid_domain_name($_GET["domain"]) && | 		is_valid_domain_name($_GET["domain"]) && | ||||||
| 		!empty($_GET["domain"])) { | 		!empty($_GET["domain"])) { | ||||||
| 			$domain = $_GET["domain"]; | 			$domain = $_GET["domain"]; | ||||||
| 			try { |       $result = mailbox_get_domain_details($domain); | ||||||
| 				$stmt = $pdo->prepare("SELECT * FROM `domain` WHERE `domain`='".$domain."' | 			if (!empty($result)) { | ||||||
| 				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) { |  | ||||||
| 			?> | 			?> | ||||||
| 				<h4><?=$lang['edit']['domain'];?></h4> | 				<h4><?=$lang['edit']['domain'];?></h4> | ||||||
| 				<form class="form-horizontal" role="form" method="post" action="<?=($FORM_ACTION == "previous") ? $_SESSION['return_to'] : null;?>"> | 				<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"> | 					<div class="form-group"> | ||||||
| 						<label class="control-label col-sm-2" for="aliases"><?=$lang['edit']['max_aliases'];?></label> | 						<label class="control-label col-sm-2" for="aliases"><?=$lang['edit']['max_aliases'];?></label> | ||||||
| 						<div class="col-sm-10"> | 						<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> | 					</div> | ||||||
| 					<div class="form-group"> | 					<div class="form-group"> | ||||||
| 						<label class="control-label col-sm-2" for="mailboxes"><?=$lang['edit']['max_mailboxes'];?></label> | 						<label class="control-label col-sm-2" for="mailboxes"><?=$lang['edit']['max_mailboxes'];?></label> | ||||||
| 						<div class="col-sm-10"> | 						<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> | 					</div> | ||||||
| 					<div class="form-group"> | 					<div class="form-group"> | ||||||
| 						<label class="control-label col-sm-2" for="maxquota"><?=$lang['edit']['max_quota'];?></label> | 						<label class="control-label col-sm-2" for="maxquota"><?=$lang['edit']['max_quota'];?></label> | ||||||
| 						<div class="col-sm-10"> | 						<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> | 					</div> | ||||||
| 					<div class="form-group"> | 					<div class="form-group"> | ||||||
| 						<label class="control-label col-sm-2" for="quota"><?=$lang['edit']['domain_quota'];?></label> | 						<label class="control-label col-sm-2" for="quota"><?=$lang['edit']['domain_quota'];?></label> | ||||||
| 						<div class="col-sm-10"> | 						<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> | 					</div> | ||||||
| 					<div class="form-group"> | 					<div class="form-group"> | ||||||
| 						<label class="control-label col-sm-2"><?=$lang['edit']['backup_mx_options'];?></label> | 						<label class="control-label col-sm-2"><?=$lang['edit']['backup_mx_options'];?></label> | ||||||
| 						<div class="col-sm-10"> | 						<div class="col-sm-10"> | ||||||
| 							<div class="checkbox"> | 							<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 /> | 								<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']) && $result['relay_all_recipients']=="1") ? "checked" : null;?>> <?=$lang['edit']['relay_all'];?></label> | ||||||
| 								<p><?=$lang['edit']['relay_all_info'];?></p> | 								<p><?=$lang['edit']['relay_all_info'];?></p> | ||||||
| 							</div> | 							</div> | ||||||
| 						</div> | 						</div> | ||||||
| @@ -266,37 +192,148 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm | |||||||
| 					<div class="form-group"> | 					<div class="form-group"> | ||||||
| 						<div class="col-sm-offset-2 col-sm-10"> | 						<div class="col-sm-offset-2 col-sm-10"> | ||||||
| 							<div class="checkbox"> | 							<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> | ||||||
| 					</div> | 					</div> | ||||||
| 					<div class="form-group"> | 					<div class="form-group"> | ||||||
| 						<div class="col-sm-offset-2 col-sm-10"> | 						<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> | ||||||
| 					</div> | 					</div> | ||||||
| 				</form> | 				</form> | ||||||
| 				<?php | 				<?php | ||||||
| 				if (file_exists($GLOBALS["MC_DKIM_TXTS"]. "/" . $domain . "." . "dkim")) { |         if (!empty($dkim = dkim_get_key_details($domain))) { | ||||||
| 					$pubKey = file_get_contents($GLOBALS["MC_DKIM_TXTS"]. "/" . $domain . "." . "dkim"); |  | ||||||
| 				?> | 				?> | ||||||
| 					<div class="row"> |         <hr> | ||||||
| 						<div class="col-xs-2"> |         <div class="row"> | ||||||
| 							<p>Domain: <strong><?=htmlspecialchars($domain);?></strong> (dkim._domainkey)</p> |           <div class="col-xs-2"> | ||||||
| 						</div> |             <p>Domain: <strong><?=htmlspecialchars($result['domain_name']);?></strong> (dkim._domainkey)</p> | ||||||
| 						<div class="col-xs-9"> |           </div> | ||||||
| 							<pre>v=DKIM1;k=rsa;t=s;s=email;p=<?=$pubKey;?></pre> |           <div class="col-xs-10"> | ||||||
| 						</div> |             <pre><?=$dkim['dkim_txt'];?></pre> | ||||||
| 						<div class="col-xs-1"> |           </div> | ||||||
| 							<form class="form-inline" role="form" method="post"> |         </div> | ||||||
| 							<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 | 				<?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 { | 			else { | ||||||
| 			?> | 			?> | ||||||
| @@ -308,31 +345,8 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm | |||||||
| 		is_valid_domain_name($_GET["aliasdomain"]) && | 		is_valid_domain_name($_GET["aliasdomain"]) && | ||||||
| 		!empty($_GET["aliasdomain"])) { | 		!empty($_GET["aliasdomain"])) { | ||||||
| 			$alias_domain = $_GET["aliasdomain"]; | 			$alias_domain = $_GET["aliasdomain"]; | ||||||
| 			try { |       $result = mailbox_get_alias_domain_details($alias_domain); | ||||||
| 				$stmt = $pdo->prepare("SELECT * FROM `alias_domain` |       if (!empty($result)) { | ||||||
| 					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) { |  | ||||||
| 			?> | 			?> | ||||||
| 				<h4><?=$lang['edit']['edit_alias_domain'];?></h4> | 				<h4><?=$lang['edit']['edit_alias_domain'];?></h4> | ||||||
| 				<form class="form-horizontal" role="form" method="post" action="<?=($FORM_ACTION == "previous") ? $_SESSION['return_to'] : null;?>"> | 				<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="form-group"> | ||||||
| 						<div class="col-sm-offset-2 col-sm-10"> | 						<div class="col-sm-offset-2 col-sm-10"> | ||||||
| 							<div class="checkbox"> | 							<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> | ||||||
| 					</div> | 					</div> | ||||||
| 					<div class="form-group"> | 					<div class="form-group"> | ||||||
| 						<div class="col-sm-offset-2 col-sm-10"> | 						<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> | ||||||
| 					</div> | 					</div> | ||||||
| 				</form> | 				</form> | ||||||
| 				<?php | 				<?php | ||||||
| 				$dnstxt_folder = scandir($GLOBALS["MC_DKIM_TXTS"]); |         if (!empty($dkim = dkim_get_key_details($alias_domain))) { | ||||||
| 				$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]); |  | ||||||
| 						} |  | ||||||
| 				?> | 				?> | ||||||
| 						<div class="row"> |         <hr> | ||||||
| 							<div class="col-xs-2"> |         <div class="row"> | ||||||
| 								<p class="text-right"><?=$lang['edit']['dkim_signature'];?></p> |           <div class="col-xs-2"> | ||||||
| 							</div> |             <p>Domain: <strong><?=htmlspecialchars($result['alias_domain']);?></strong> (dkim._domainkey)</p> | ||||||
| 							<div class="col-xs-10"> |           </div> | ||||||
| 								<div class="col-md-2"><b><?=$lang['edit']['dkim_txt_name'];?></b></div> |           <div class="col-xs-10"> | ||||||
| 								<div class="col-md-10"> |           <pre><?=$dkim['dkim_txt'];?></pre> | ||||||
| 									<pre><?=htmlspecialchars(explode("_", $file)[0]);?>._domainkey</pre> |           </div> | ||||||
| 								</div> |         </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> |  | ||||||
| 				<?php | 				<?php | ||||||
| 					} |  | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| 			else { | 			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"])) { | 	elseif (isset($_GET['mailbox']) && filter_var($_GET["mailbox"], FILTER_VALIDATE_EMAIL) && !empty($_GET["mailbox"])) { | ||||||
| 			$mailbox = $_GET["mailbox"]; |     $mailbox = $_GET["mailbox"]; | ||||||
| 			try { |     $result = mailbox_get_mailbox_details($mailbox); | ||||||
| 				$stmt = $pdo->prepare("SELECT `username`, `domain`, `name`, `quota`, `active` FROM `mailbox` WHERE `username` = :username1"); |     if (!empty($result)) { | ||||||
| 				$stmt->execute(array( |       ?> | ||||||
| 					':username1' => $mailbox, |       <h4><?=$lang['edit']['mailbox'];?></h4> | ||||||
| 				)); |       <form class="form-horizontal" role="form" method="post" action="<?=($FORM_ACTION == "previous") ? $_SESSION['return_to'] : null;?>"> | ||||||
| 				$result = $stmt->fetch(PDO::FETCH_ASSOC); |       <input type="hidden" name="username" value="<?=htmlspecialchars($result['username']);?>"> | ||||||
| 			} |         <div class="form-group"> | ||||||
| 			catch(PDOException $e) { |           <label class="control-label col-sm-2" for="name"><?=$lang['edit']['full_name'];?>:</label> | ||||||
| 				$_SESSION['return'] = array( |           <div class="col-sm-10"> | ||||||
| 					'type' => 'danger', |           <input type="text" class="form-control" name="name" id="name" value="<?=htmlspecialchars($result['name'], ENT_QUOTES, 'UTF-8');?>"> | ||||||
| 					'msg' => 'MySQL: '.$e |           </div> | ||||||
| 				); |         </div> | ||||||
| 			} |         <div class="form-group"> | ||||||
| 			if ($result !== false && hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $result['domain'])) { |           <label class="control-label col-sm-2" for="quota"><?=$lang['edit']['quota_mb'];?>: | ||||||
| 				$left_m = remaining_specs($result['domain'], $_GET['mailbox'])['left_m']; |             <br /><span id="quotaBadge" class="badge">max. <?=intval($result['max_new_quota'] / 1048576)?> MiB</span> | ||||||
| 			?> |           </label> | ||||||
| 				<h4><?=$lang['edit']['mailbox'];?></h4> |           <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="50%" 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;?>"> | 				<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"> | 					<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"> | 						<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> | 					</div> | ||||||
| 					<div class="form-group"> | 					<div class="form-group"> | ||||||
| 						<label class="control-label col-sm-2" for="quota"><?=$lang['edit']['quota_mb'];?>: | 						<label class="control-label col-sm-2" for="domain"><?=$lang['edit']['kind'];?>:</label> | ||||||
| 							<br /><span id="quotaBadge" class="badge">max. <?=intval($left_m)?> MiB</span> |  | ||||||
| 						</label> |  | ||||||
| 						<div class="col-sm-10"> | 						<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"> | 							<select name="kind" id="kind" title="<?=$lang['edit']['select'];?>" required> | ||||||
| 						</div> | 								<option value="location" <?=($result['kind'] == "location") ? "selected" : null;?>>Location</option> | ||||||
| 					</div> | 								<option value="group" <?=($result['kind'] == "group") ? "selected" : null;?>>Group</option> | ||||||
| 					<div class="form-group"> | 								<option value="thing" <?=($result['kind'] == "thing") ? "selected" : null;?>>Thing</option> | ||||||
| 						<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> | 							</select> | ||||||
| 						</div> | 						</div> | ||||||
| 					</div> | 					</div> | ||||||
| 					<div class="form-group"> | 					<div class="form-group"> | ||||||
| 						<label class="control-label col-sm-2" for="password"><?=$lang['edit']['password'];?></label> | 						<div class="col-sm-offset-2 col-sm-10"> | ||||||
| 						<div class="col-sm-10"> | 							<div class="checkbox"> | ||||||
| 						<input type="password" class="form-control" name="password" id="password" placeholder="<?=$lang['edit']['unchanged_if_empty'];?>"> | 							<label><input type="checkbox" name="active" <?=($result['active_int']=="1") ? "checked" : null;?>> <?=$lang['edit']['active'];?></label> | ||||||
|  | 							</div> | ||||||
| 						</div> | 						</div> | ||||||
| 					</div> | 					</div> | ||||||
| 					<div class="form-group"> | 					<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"> | 						<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> | 					</div> | ||||||
| 					<div class="form-group"> | 					<div class="form-group"> | ||||||
| @@ -499,7 +629,7 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm | |||||||
| 					</div> | 					</div> | ||||||
| 					<div class="form-group"> | 					<div class="form-group"> | ||||||
| 						<div class="col-sm-offset-2 col-sm-10"> | 						<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> | ||||||
| 					</div> | 					</div> | ||||||
| 				</form> | 				</form> | ||||||
|   | |||||||
							
								
								
									
										
											BIN
										
									
								
								data/web/img/yubi.ico
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								data/web/img/yubi.ico
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 1.1 KiB | 
| @@ -1,4 +1,5 @@ | |||||||
| <?php | <?php | ||||||
|  | include("inc/tfa_modals.php"); | ||||||
| if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == "admin"): | if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == "admin"): | ||||||
| ?> | ?> | ||||||
| <div id="RestartSOGo" class="modal fade" role="dialog"> | <div id="RestartSOGo" class="modal fade" role="dialog"> | ||||||
| @@ -21,10 +22,12 @@ if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == "admi | |||||||
| <?php | <?php | ||||||
| endif; | 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/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-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-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="//cdnjs.cloudflare.com/ajax/libs/bootstrap-select/1.9.4/js/bootstrap-select.js"></script> | ||||||
|  | <script src="/js/u2f-api.js"></script> | ||||||
| <script> | <script> | ||||||
| // Select language and reopen active URL without POST | // Select language and reopen active URL without POST | ||||||
| function setLang(sel) { | function setLang(sel) { | ||||||
| @@ -33,6 +36,93 @@ function setLang(sel) { | |||||||
| } | } | ||||||
|  |  | ||||||
| $(document).ready(function() { | $(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 | 	// Hide alerts after n seconds | ||||||
| 	$("#alert-fade").fadeTo(7000, 500).slideUp(500, function(){ | 	$("#alert-fade").fadeTo(7000, 500).slideUp(500, function(){ | ||||||
| 		$("#alert-fade").alert('close'); | 		$("#alert-fade").alert('close'); | ||||||
|   | |||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -17,157 +17,11 @@ | |||||||
| <link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/bootstrap-switch/3.3.2/css/bootstrap3/bootstrap-switch.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="//fonts.googleapis.com/css?family=Source+Sans+Pro:400,600,700&subset=latin,latin-ext"> | <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="/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="shortcut icon" href="/favicon.png" type="image/png"> | ||||||
| <link rel="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> | </head> | ||||||
| <body style="padding-top:70px"> | <body style="padding-top:70px"> | ||||||
| <nav class="navbar navbar-default navbar-fixed-top"  role="navigation"> | <nav class="navbar navbar-default navbar-fixed-top"  role="navigation"> | ||||||
| @@ -214,11 +68,11 @@ endif; | |||||||
| 								<li <?=(preg_match("/mailbox/i", $_SERVER['REQUEST_URI'])) ? 'class="active"' : ''?>><a href="/mailbox.php"><?=$lang['header']['mailboxes'];?></a></li> | 								<li <?=(preg_match("/mailbox/i", $_SERVER['REQUEST_URI'])) ? 'class="active"' : ''?>><a href="/mailbox.php"><?=$lang['header']['mailboxes'];?></a></li> | ||||||
| 							<?php | 							<?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> | 								<li <?=(preg_match("/user/i", $_SERVER['REQUEST_URI'])) ? 'class="active"' : ''?>><a href="/user.php"><?=$lang['header']['user_settings'];?></a></li> | ||||||
| 							<?php | 							<?php | ||||||
| 							} |               } | ||||||
| 						} | 						} | ||||||
| 						?> | 						?> | ||||||
| 					</ul> | 					</ul> | ||||||
| @@ -232,10 +86,14 @@ endif; | |||||||
| 				?> | 				?> | ||||||
| 					<?php | 					<?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> | 					<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 | 				<?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; | 				endif; | ||||||
| 				?> | 				?> | ||||||
| 			</ul> | 			</ul> | ||||||
|   | |||||||
| @@ -1,107 +1,145 @@ | |||||||
| CREATE TABLE IF NOT EXISTS `admin` ( | CREATE TABLE IF NOT EXISTS `admin` ( | ||||||
| 	`username` varchar(255) NOT NULL, | 	`username` VARCHAR(255) NOT NULL, | ||||||
| 	`password` varchar(255) NOT NULL, | 	`password` VARCHAR(255) NOT NULL, | ||||||
| 	`superadmin` tinyint(1) NOT NULL DEFAULT '0', | 	`superadmin` TINYINT(1) NOT NULL DEFAULT '0', | ||||||
| 	`created` datetime NOT NULL DEFAULT '2016-01-01 00:00:00', | 	`created` DATETIME NOT NULL DEFAULT '2016-01-01 00:00:00', | ||||||
| 	`modified` 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', | 	`active` TINYINT(1) NOT NULL DEFAULT '1', | ||||||
| 	PRIMARY KEY (`username`) | 	PRIMARY KEY (`username`) | ||||||
| ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC; | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC; | ||||||
|  |  | ||||||
| CREATE TABLE IF NOT EXISTS `alias` ( | CREATE TABLE IF NOT EXISTS `alias` ( | ||||||
| 	`address` varchar(255) NOT NULL, | 	`address` VARCHAR(255) NOT NULL, | ||||||
| 	`goto` text NOT NULL, | 	`goto` TEXT NOT NULL, | ||||||
| 	`domain` varchar(255) NOT NULL, | 	`domain` VARCHAR(255) NOT NULL, | ||||||
| 	`created` datetime NOT NULL DEFAULT '2016-01-01 00:00:00', | 	`created` DATETIME NOT NULL DEFAULT '2016-01-01 00:00:00', | ||||||
| 	`modified` 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', | 	`active` TINYINT(1) NOT NULL DEFAULT '1', | ||||||
| 	PRIMARY KEY (`address`), | 	PRIMARY KEY (`address`), | ||||||
| 	KEY `domain` (`domain`) | 	KEY `domain` (`domain`) | ||||||
| ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC; | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC; | ||||||
|  |  | ||||||
| CREATE TABLE IF NOT EXISTS `sender_acl` ( | CREATE TABLE IF NOT EXISTS `sender_acl` ( | ||||||
| 	`logged_in_as` varchar(255) NOT NULL, | 	`logged_in_as` VARCHAR(255) NOT NULL, | ||||||
| 	`send_as` varchar(255) NOT NULL | 	`send_as` VARCHAR(255) NOT NULL | ||||||
| ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC; | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC; | ||||||
|  |  | ||||||
| CREATE TABLE IF NOT EXISTS `spamalias` ( | CREATE TABLE IF NOT EXISTS `spamalias` ( | ||||||
| 	`address` varchar(255) NOT NULL, | 	`address` VARCHAR(255) NOT NULL, | ||||||
| 	`goto` text NOT NULL, | 	`goto` TEXT NOT NULL, | ||||||
| 	`validity` int(11) NOT NULL, | 	`validity` INT(11) NOT NULL, | ||||||
| 	PRIMARY KEY (`address`) | 	PRIMARY KEY (`address`) | ||||||
| ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC; | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC; | ||||||
|  |  | ||||||
| CREATE TABLE IF NOT EXISTS `alias_domain` ( | CREATE TABLE IF NOT EXISTS `alias_domain` ( | ||||||
| 	`alias_domain` varchar(255) NOT NULL, | 	`alias_domain` VARCHAR(255) NOT NULL, | ||||||
| 	`target_domain` varchar(255) NOT NULL, | 	`target_domain` VARCHAR(255) NOT NULL, | ||||||
| 	`created` datetime NOT NULL DEFAULT '2016-01-01 00:00:00', | 	`created` DATETIME NOT NULL DEFAULT '2016-01-01 00:00:00', | ||||||
| 	`modified` 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', | 	`active` TINYINT(1) NOT NULL DEFAULT '1', | ||||||
| 	PRIMARY KEY (`alias_domain`), | 	PRIMARY KEY (`alias_domain`), | ||||||
| 	KEY `active` (`active`), | 	KEY `active` (`active`), | ||||||
| 	KEY `target_domain` (`target_domain`) | 	KEY `target_domain` (`target_domain`) | ||||||
| ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC; | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC; | ||||||
|  |  | ||||||
| CREATE TABLE IF NOT EXISTS `domain` ( | CREATE TABLE IF NOT EXISTS `domain` ( | ||||||
| 	`domain` varchar(255) NOT NULL, | 	`domain` VARCHAR(255) NOT NULL, | ||||||
| 	`description` varchar(255), | 	`description` VARCHAR(255), | ||||||
| 	`aliases` int(10) NOT NULL DEFAULT '0', | 	`aliases` INT(10) NOT NULL DEFAULT '0', | ||||||
| 	`mailboxes` int(10) NOT NULL DEFAULT '0', | 	`mailboxes` INT(10) NOT NULL DEFAULT '0', | ||||||
| 	`maxquota` bigint(20) NOT NULL DEFAULT '0', | 	`maxquota` BIGINT(20) NOT NULL DEFAULT '0', | ||||||
| 	`quota` bigint(20) NOT NULL DEFAULT '0', | 	`quota` BIGINT(20) NOT NULL DEFAULT '0', | ||||||
| 	`transport` varchar(255) NOT NULL, | 	`transport` VARCHAR(255) NOT NULL, | ||||||
| 	`backupmx` tinyint(1) NOT NULL DEFAULT '0', | 	`backupmx` TINYINT(1) NOT NULL DEFAULT '0', | ||||||
| 	`relay_all_recipients` 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', | 	`created` DATETIME NOT NULL DEFAULT '2016-01-01 00:00:00', | ||||||
| 	`modified` 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', | 	`active` TINYINT(1) NOT NULL DEFAULT '1', | ||||||
| 	PRIMARY KEY (`domain`) | 	PRIMARY KEY (`domain`) | ||||||
| ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC; | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC; | ||||||
|  |  | ||||||
| CREATE TABLE IF NOT EXISTS `domain_admins` ( | CREATE TABLE IF NOT EXISTS `domain_admins` ( | ||||||
| 	`username` varchar(255) NOT NULL, | 	`username` VARCHAR(255) NOT NULL, | ||||||
| 	`domain` varchar(255) NOT NULL, | 	`domain` VARCHAR(255) NOT NULL, | ||||||
| 	`created` datetime NOT NULL DEFAULT '2016-01-01 00:00:00', | 	`created` DATETIME NOT NULL DEFAULT '2016-01-01 00:00:00', | ||||||
| 	`active` tinyint(1) NOT NULL DEFAULT '1', | 	`active` TINYINT(1) NOT NULL DEFAULT '1', | ||||||
| 	KEY `username` (`username`) | 	KEY `username` (`username`) | ||||||
| ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC; | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC; | ||||||
|  |  | ||||||
| CREATE TABLE IF NOT EXISTS `mailbox` ( | CREATE TABLE IF NOT EXISTS `mailbox` ( | ||||||
| 	`username` varchar(255) NOT NULL, | 	`username` VARCHAR(255) NOT NULL, | ||||||
| 	`password` varchar(255) NOT NULL, | 	`password` VARCHAR(255) NOT NULL, | ||||||
| 	`name` varchar(255), | 	`name` VARCHAR(255), | ||||||
| 	`maildir` varchar(255) NOT NULL, | 	`maildir` VARCHAR(255) NOT NULL, | ||||||
| 	`quota` bigint(20) NOT NULL DEFAULT '0', | 	`quota` BIGINT(20) NOT NULL DEFAULT '0', | ||||||
| 	`local_part` varchar(255) NOT NULL, | 	`local_part` VARCHAR(255) NOT NULL, | ||||||
| 	`domain` varchar(255) NOT NULL, | 	`domain` VARCHAR(255) NOT NULL, | ||||||
| 	`created` datetime NOT NULL DEFAULT '2016-01-01 00:00:00', | 	`created` DATETIME NOT NULL DEFAULT '2016-01-01 00:00:00', | ||||||
| 	`modified` 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_in` TINYINT(1) NOT NULL DEFAULT '0', | ||||||
| 	`tls_enforce_out` tinyint(1) NOT NULL DEFAULT '0', | 	`tls_enforce_out` TINYINT(1) NOT NULL DEFAULT '0', | ||||||
| 	`kind` varchar(100) NOT NULL DEFAULT '', | 	`kind` VARCHAR(100) NOT NULL DEFAULT '', | ||||||
| 	`multiple_bookings` tinyint(1) NOT NULL DEFAULT '0', | 	`multiple_bookings` TINYINT(1) NOT NULL DEFAULT '0', | ||||||
| 	`wants_tagged_subject` tinyint(1) NOT NULL DEFAULT '0', | 	`wants_tagged_subject` TINYINT(1) NOT NULL DEFAULT '0', | ||||||
| 	`active` tinyint(1) NOT NULL DEFAULT '1', | 	`active` TINYINT(1) NOT NULL DEFAULT '1', | ||||||
| 	PRIMARY KEY (`username`), | 	PRIMARY KEY (`username`), | ||||||
| 	KEY `domain` (`domain`) | 	KEY `domain` (`domain`) | ||||||
| ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC; | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC; | ||||||
|  |  | ||||||
| CREATE TABLE IF NOT EXISTS `quota2` ( | CREATE TABLE IF NOT EXISTS `quota2` ( | ||||||
| 	`username` varchar(100) NOT NULL, | 	`username` VARCHAR(100) NOT NULL, | ||||||
| 	`bytes` bigint(20) NOT NULL DEFAULT '0', | 	`bytes` BIGINT(20) NOT NULL DEFAULT '0', | ||||||
| 	`messages` int(11) NOT NULL DEFAULT '0', | 	`messages` INT(11) NOT NULL DEFAULT '0', | ||||||
| 	PRIMARY KEY (`username`) | 	PRIMARY KEY (`username`) | ||||||
| ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC; | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC; | ||||||
|  |  | ||||||
| CREATE TABLE IF NOT EXISTS `filterconf` ( | CREATE TABLE IF NOT EXISTS `filterconf` ( | ||||||
| 	`object` varchar(100) NOT NULL DEFAULT '', | 	`object` VARCHAR(100) NOT NULL DEFAULT '', | ||||||
| 	`option` varchar(50) NOT NULL DEFAULT '', | 	`option` VARCHAR(50) NOT NULL DEFAULT '', | ||||||
| 	`value` varchar(100) NOT NULL DEFAULT '', | 	`value` VARCHAR(100) NOT NULL DEFAULT '', | ||||||
| 	`prefid` int(11) NOT NULL AUTO_INCREMENT, | 	`prefid` INT(11) NOT NULL AUTO_INCREMENT, | ||||||
| 	PRIMARY KEY (`prefid`), | 	PRIMARY KEY (`prefid`), | ||||||
| 	KEY `object` (`object`) | 	KEY `object` (`object`) | ||||||
| ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC; | ) 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_mail_aliases; | ||||||
| DROP VIEW IF EXISTS grouped_sender_acl; | DROP VIEW IF EXISTS grouped_sender_acl; | ||||||
| DROP VIEW IF EXISTS grouped_domain_alias_address; | 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; | LEFT OUTER JOIN alias_domain on target_domain=domain GROUP BY username; | ||||||
|  |  | ||||||
| CREATE TABLE IF NOT EXISTS sogo_acl ( | 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_object character varying(255) NOT NULL, | ||||||
| 	c_uid character varying(255) NOT NULL, | 	c_uid character varying(255) NOT NULL, | ||||||
| 	c_role character varying(80) NOT NULL | 	c_role character varying(80) NOT NULL | ||||||
| ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC; | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC; | ||||||
|  |  | ||||||
| CREATE TABLE IF NOT EXISTS sogo_alarms_folder ( | CREATE TABLE IF NOT EXISTS sogo_alarms_folder ( | ||||||
| 	c_path          varchar(255) NOT NULL, | 	c_path          VARCHAR(255) NOT NULL, | ||||||
| 	c_name          varchar(255) NOT NULL, | 	c_name          VARCHAR(255) NOT NULL, | ||||||
| 	c_uid           varchar(255) NOT NULL, | 	c_uid           VARCHAR(255) NOT NULL, | ||||||
| 	c_recurrence_id int(11)      DEFAULT NULL, | 	c_recurrence_id INT(11)      DEFAULT NULL, | ||||||
| 	c_alarm_number  int(11)      NOT NULL, | 	c_alarm_number  INT(11)      NOT NULL, | ||||||
| 	c_alarm_date    int(11)      NOT NULL | 	c_alarm_date    INT(11)      NOT NULL | ||||||
| ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC; | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC; | ||||||
|  |  | ||||||
| CREATE TABLE IF NOT EXISTS sogo_cache_folder ( | CREATE TABLE IF NOT EXISTS sogo_cache_folder ( | ||||||
| 	c_uid          varchar(255) NOT NULL, | 	c_uid          VARCHAR(255) NOT NULL, | ||||||
| 	c_path         varchar(255) NOT NULL, | 	c_path         VARCHAR(255) NOT NULL, | ||||||
| 	c_parent_path  varchar(255) DEFAULT NULL, | 	c_parent_path  VARCHAR(255) DEFAULT NULL, | ||||||
| 	c_type         tinyint(3)   unsigned NOT NULL, | 	c_type         TINYINT(3)   unsigned NOT NULL, | ||||||
| 	c_creationdate int(11)      NOT NULL, | 	c_creationdate INT(11)      NOT NULL, | ||||||
| 	c_lastmodified int(11)      NOT NULL, | 	c_lastmodified INT(11)      NOT NULL, | ||||||
| 	c_version      int(11)      NOT NULL DEFAULT '0', | 	c_version      INT(11)      NOT NULL DEFAULT '0', | ||||||
| 	c_deleted      tinyint(4)   NOT NULL DEFAULT '0', | 	c_deleted      TINYINT(4)   NOT NULL DEFAULT '0', | ||||||
| 	c_content      longtext, | 	c_content      longTEXT, | ||||||
| 	PRIMARY KEY (c_uid,c_path) | 	PRIMARY KEY (c_uid,c_path) | ||||||
| ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC; | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC; | ||||||
|  |  | ||||||
| CREATE TABLE IF NOT EXISTS sogo_folder_info ( | CREATE TABLE IF NOT EXISTS sogo_folder_info ( | ||||||
| 	c_folder_id      bigint(20)    unsigned NOT NULL AUTO_INCREMENT, | 	c_folder_id      BIGINT(20)    unsigned NOT NULL AUTO_INCREMENT, | ||||||
| 	c_path           varchar(255)  NOT NULL, | 	c_path           VARCHAR(255)  NOT NULL, | ||||||
| 	c_path1          varchar(255)  NOT NULL, | 	c_path1          VARCHAR(255)  NOT NULL, | ||||||
| 	c_path2          varchar(255)  DEFAULT NULL, | 	c_path2          VARCHAR(255)  DEFAULT NULL, | ||||||
| 	c_path3          varchar(255)  DEFAULT NULL, | 	c_path3          VARCHAR(255)  DEFAULT NULL, | ||||||
| 	c_path4          varchar(255)  DEFAULT NULL, | 	c_path4          VARCHAR(255)  DEFAULT NULL, | ||||||
| 	c_foldername     varchar(255)  NOT NULL, | 	c_foldername     VARCHAR(255)  NOT NULL, | ||||||
| 	c_location       integer NULL, | 	c_location       INTeger NULL, | ||||||
| 	c_quick_location varchar(2048) DEFAULT NULL, | 	c_quick_location VARCHAR(2048) DEFAULT NULL, | ||||||
| 	c_acl_location   varchar(2048) DEFAULT NULL, | 	c_acl_location   VARCHAR(2048) DEFAULT NULL, | ||||||
| 	c_folder_type    varchar(255)  NOT NULL, | 	c_folder_type    VARCHAR(255)  NOT NULL, | ||||||
| 	PRIMARY KEY (c_path), | 	PRIMARY KEY (c_path), | ||||||
| 	UNIQUE KEY c_folder_id (c_folder_id) | 	UNIQUE KEY c_folder_id (c_folder_id) | ||||||
| ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC; | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC; | ||||||
|  |  | ||||||
| CREATE TABLE IF NOT EXISTS sogo_quick_appointment ( | 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_name character varying(255) NOT NULL, | ||||||
| 	c_uid character varying(255) NOT NULL, | 	c_uid character varying(255) NOT NULL, | ||||||
| 	c_startdate integer, | 	c_startdate INTeger, | ||||||
| 	c_enddate integer, | 	c_enddate INTeger, | ||||||
| 	c_cycleenddate integer, | 	c_cycleenddate INTeger, | ||||||
| 	c_title character varying(1000) NOT NULL, | 	c_title character varying(1000) NOT NULL, | ||||||
| 	c_participants text, | 	c_participants TEXT, | ||||||
| 	c_isallday integer, | 	c_isallday INTeger, | ||||||
| 	c_iscycle integer, | 	c_iscycle INTeger, | ||||||
| 	c_cycleinfo text, | 	c_cycleinfo TEXT, | ||||||
| 	c_classification integer NOT NULL, | 	c_classification INTeger NOT NULL, | ||||||
| 	c_isopaque integer NOT NULL, | 	c_isopaque INTeger NOT NULL, | ||||||
| 	c_status integer NOT NULL, | 	c_status INTeger NOT NULL, | ||||||
| 	c_priority integer, | 	c_priority INTeger, | ||||||
| 	c_location character varying(255), | 	c_location character varying(255), | ||||||
| 	c_orgmail character varying(255), | 	c_orgmail character varying(255), | ||||||
| 	c_partmails text, | 	c_partmails TEXT, | ||||||
| 	c_partstates text, | 	c_partstates TEXT, | ||||||
| 	c_category character varying(255), | 	c_category character varying(255), | ||||||
| 	c_sequence integer, | 	c_sequence INTeger, | ||||||
| 	c_component character varying(10) NOT NULL, | 	c_component character varying(10) NOT NULL, | ||||||
| 	c_nextalarm integer, | 	c_nextalarm INTeger, | ||||||
| 	c_description text, | 	c_description TEXT, | ||||||
| 	CONSTRAINT sogo_quick_appointment_pkey PRIMARY KEY (c_folder_id, c_name) | 	CONSTRAINT sogo_quick_appointment_pkey PRIMARY KEY (c_folder_id, c_name) | ||||||
| ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC; | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC; | ||||||
|  |  | ||||||
| CREATE TABLE IF NOT EXISTS sogo_quick_contact ( | 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_name character varying(255) NOT NULL, | ||||||
| 	c_givenname character varying(255), | 	c_givenname character varying(255), | ||||||
| 	c_cn character varying(255), | 	c_cn character varying(255), | ||||||
| @@ -213,28 +251,28 @@ CREATE TABLE IF NOT EXISTS sogo_quick_contact ( | |||||||
| ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC; | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC; | ||||||
|  |  | ||||||
| CREATE TABLE IF NOT EXISTS sogo_sessions_folder ( | CREATE TABLE IF NOT EXISTS sogo_sessions_folder ( | ||||||
| 	c_id           varchar(255) NOT NULL, | 	c_id           VARCHAR(255) NOT NULL, | ||||||
| 	c_value        varchar(255) NOT NULL, | 	c_value        VARCHAR(255) NOT NULL, | ||||||
| 	c_creationdate int(11)      NOT NULL, | 	c_creationdate INT(11)      NOT NULL, | ||||||
| 	c_lastseen     int(11)      NOT NULL, | 	c_lastseen     INT(11)      NOT NULL, | ||||||
| 	PRIMARY KEY (c_id) | 	PRIMARY KEY (c_id) | ||||||
| ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC; | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC; | ||||||
|  |  | ||||||
| CREATE TABLE IF NOT EXISTS sogo_store ( | 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_name character varying(255) NOT NULL, | ||||||
| 	c_content mediumtext NOT NULL, | 	c_content mediumTEXT NOT NULL, | ||||||
| 	c_creationdate integer NOT NULL, | 	c_creationdate INTeger NOT NULL, | ||||||
| 	c_lastmodified integer NOT NULL, | 	c_lastmodified INTeger NOT NULL, | ||||||
| 	c_version integer NOT NULL, | 	c_version INTeger NOT NULL, | ||||||
| 	c_deleted integer, | 	c_deleted INTeger, | ||||||
| 	CONSTRAINT sogo_store_pkey PRIMARY KEY (c_folder_id, c_name) | 	CONSTRAINT sogo_store_pkey PRIMARY KEY (c_folder_id, c_name) | ||||||
| ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC; | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC; | ||||||
|  |  | ||||||
| CREATE TABLE IF NOT EXISTS sogo_user_profile ( | CREATE TABLE IF NOT EXISTS sogo_user_profile ( | ||||||
| 	c_uid      varchar(255) NOT NULL, | 	c_uid      VARCHAR(255) NOT NULL, | ||||||
| 	c_defaults text, | 	c_defaults TEXT, | ||||||
| 	c_settings text, | 	c_settings TEXT, | ||||||
| 	PRIMARY KEY (c_uid) | 	PRIMARY KEY (c_uid) | ||||||
| ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC; | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC; | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										506
									
								
								data/web/inc/lib/U2F.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										506
									
								
								data/web/inc/lib/U2F.php
									
									
									
									
									
										Normal file
									
								
							| @@ -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); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										475
									
								
								data/web/inc/lib/Yubico.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										475
									
								
								data/web/inc/lib/Yubico.php
									
									
									
									
									
										Normal file
									
								
							| @@ -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,35 @@ | |||||||
| //ini_set("session.cookie_httponly", 1); | //ini_set("session.cookie_httponly", 1); | ||||||
| session_start(); | session_start(); | ||||||
| if (isset($_POST["logout"])) { | if (isset($_POST["logout"])) { | ||||||
| 	session_unset(); |   if (isset($_SESSION["dual-login"])) { | ||||||
| 	session_destroy(); |     $_SESSION["mailcow_cc_username"] = $_SESSION["dual-login"]["username"]; | ||||||
| 	session_write_close(); |     $_SESSION["mailcow_cc_role"] = $_SESSION["dual-login"]["role"]; | ||||||
| 	setcookie(session_name(),'',0,'/'); |     unset($_SESSION["dual-login"]); | ||||||
|  |   } | ||||||
|  |   else { | ||||||
|  |     session_unset(); | ||||||
|  |     session_destroy(); | ||||||
|  |     session_write_close(); | ||||||
|  |     setcookie(session_name(),'',0,'/'); | ||||||
|  |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| require_once 'inc/vars.inc.php'; | require_once 'inc/vars.inc.php'; | ||||||
|  |  | ||||||
| if (file_exists('./inc/vars.local.inc.php')) { | if (file_exists('./inc/vars.local.inc.php')) { | ||||||
| 	include_once 'inc/vars.local.inc.php'; | 	include_once 'inc/vars.local.inc.php'; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // Yubi OTP API | ||||||
|  | if (!empty($YUBI_API['ID']) && !empty($YUBI_API['KEY'])) { | ||||||
|  |   require_once 'inc/lib/Yubico.php'; | ||||||
|  |   $yubi = new Auth_Yubico($YUBI_API['ID'], $YUBI_API['KEY']); | ||||||
|  | } | ||||||
|  | // U2F API | ||||||
|  | require_once 'inc/lib/U2F.php'; | ||||||
|  | $scheme = isset($_SERVER['HTTPS']) ? "https://" : "http://"; | ||||||
|  | $u2f = new u2flib_server\U2F($scheme . $_SERVER['HTTP_HOST']); | ||||||
|  |  | ||||||
|  | // PDO | ||||||
| $dsn = "$database_type:host=$database_host;dbname=$database_name"; | $dsn = "$database_type:host=$database_host;dbname=$database_name"; | ||||||
| $opt = [ | $opt = [ | ||||||
|     PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION, |     PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION, | ||||||
| @@ -29,6 +46,7 @@ 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> | <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 | <?php | ||||||
| } | } | ||||||
|  |  | ||||||
| $_SESSION['mailcow_locale'] = strtolower(trim($DEFAULT_LANG)); | $_SESSION['mailcow_locale'] = strtolower(trim($DEFAULT_LANG)); | ||||||
| setcookie('language', $DEFAULT_LANG); | setcookie('language', $DEFAULT_LANG); | ||||||
| if (isset($_COOKIE['language'])) { | if (isset($_COOKIE['language'])) { | ||||||
| @@ -75,4 +93,4 @@ require_once 'lang/lang.en.php'; | |||||||
| include 'lang/lang.'.$_SESSION['mailcow_locale'].'.php'; | include 'lang/lang.'.$_SESSION['mailcow_locale'].'.php'; | ||||||
| require_once 'inc/functions.inc.php'; | require_once 'inc/functions.inc.php'; | ||||||
| require_once 'inc/triggers.inc.php'; | require_once 'inc/triggers.inc.php'; | ||||||
| init_db_schema(); | (!isset($_SESSION['mailcow_cc_username'])) ? init_db_schema() : null; | ||||||
							
								
								
									
										118
									
								
								data/web/inc/tfa_modals.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										118
									
								
								data/web/inc/tfa_modals.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,118 @@ | |||||||
|  | <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="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="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 | <?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"])) { | if (isset($_POST["login_user"]) && isset($_POST["pass_user"])) { | ||||||
| 	$login_user = strtolower(trim($_POST["login_user"])); | 	$login_user = strtolower(trim($_POST["login_user"])); | ||||||
| 	$as = check_login($login_user, $_POST["pass_user"]); | 	$as = check_login($login_user, $_POST["pass_user"]); | ||||||
| @@ -17,100 +28,143 @@ if (isset($_POST["login_user"]) && isset($_POST["pass_user"])) { | |||||||
| 		$_SESSION['mailcow_cc_role'] = "user"; | 		$_SESSION['mailcow_cc_role'] = "user"; | ||||||
| 		header("Location: /user.php"); | 		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( | 		$_SESSION['return'] = array( | ||||||
| 			'type' => 'danger', | 			'type' => 'danger', | ||||||
| 			'msg' => $lang['danger']['login_failed'] | 			'msg' => $lang['danger']['login_failed'] | ||||||
| 		); | 		); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == "admin") { | if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == "admin") { | ||||||
| 	if (isset($_POST["trigger_set_admin"])) { | 	if (isset($_GET["duallogin"])) { | ||||||
| 		set_admin_account($_POST); |     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"])) { | 	if (isset($_POST["dkim_delete_key"])) { | ||||||
| 		dkim_table("delete", $_POST); | 		dkim_delete_key($_POST); | ||||||
| 	} | 	} | ||||||
| 	if (isset($_POST["add_dkim_record"])) { | 	if (isset($_POST["dkim_add_key"])) { | ||||||
| 		dkim_table("add", $_POST); | 		dkim_add_key($_POST); | ||||||
| 	} | 	} | ||||||
| 	if (isset($_POST["trigger_add_domain_admin"])) { | 	if (isset($_POST["add_domain_admin"])) { | ||||||
| 		add_domain_admin($_POST); | 		add_domain_admin($_POST); | ||||||
| 	} | 	} | ||||||
| 	if (isset($_POST["trigger_delete_domain_admin"])) { | 	if (isset($_POST["delete_domain_admin"])) { | ||||||
| 		delete_domain_admin($_POST); | 		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($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == "user") { | ||||||
| 	if (isset($_POST["trigger_set_user_account"])) { | 	if (isset($_POST["edit_user_account"])) { | ||||||
| 		set_user_account($_POST); | 		edit_user_account($_POST); | ||||||
| 	} | 	} | ||||||
| 	if (isset($_POST["trigger_set_spam_score"])) { | 	if (isset($_POST["mailbox_reset_eas"])) { | ||||||
| 		set_spam_score($_POST); | 		mailbox_reset_eas($_POST); | ||||||
| 	} | 	} | ||||||
| 	if (isset($_POST["trigger_set_tagging_options"])) { | 	if (isset($_POST["edit_spam_score"])) { | ||||||
| 		tagging_options('set', $_POST); | 		edit_spam_score($_POST); | ||||||
| 	} | 	} | ||||||
| 	if (isset($_POST["trigger_set_policy_list"])) { | 	if (isset($_POST["edit_delimiter_action"])) { | ||||||
| 		set_policy_list($_POST); | 		edit_delimiter_action($_POST); | ||||||
| 	} | 	} | ||||||
| 	if (isset($_POST["trigger_set_tls_policy"])) { | 	if (isset($_POST["add_policy_list_item"])) { | ||||||
| 		set_tls_policy($_POST); | 		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); | 		set_time_limited_aliases($_POST); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| if (isset($_SESSION['mailcow_cc_role']) && ($_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")) { | ||||||
| 	if (isset($_GET["js"])) { | 	if (isset($_POST["edit_domain_admin"])) { | ||||||
| 		switch ($_GET["js"]) { | 		edit_domain_admin($_POST); | ||||||
| 			case "remaining_specs": |  | ||||||
| 				remaining_specs($_GET['domain'], $_GET['object'], "y"); |  | ||||||
| 			break; |  | ||||||
| 		} |  | ||||||
| 	} | 	} | ||||||
| 	if (isset($_POST["trigger_mailbox_action"])) { | 	if (isset($_POST["set_tfa"])) { | ||||||
| 		switch ($_POST["trigger_mailbox_action"]) { | 		set_tfa($_POST); | ||||||
| 			case "adddomain": | 	} | ||||||
| 				mailbox_add_domain($_POST); | 	if (isset($_POST["add_policy_list_item"])) { | ||||||
| 			break; | 		add_policy_list_item($_POST); | ||||||
| 			case "addalias": | 	} | ||||||
| 				mailbox_add_alias($_POST); | 	if (isset($_POST["delete_policy_list_item"])) { | ||||||
| 			break; | 		delete_policy_list_item($_POST); | ||||||
| 			case "editalias": | 	} | ||||||
| 				mailbox_edit_alias($_POST); | 	if (isset($_POST["mailbox_add_domain"])) { | ||||||
| 			break; | 		mailbox_add_domain($_POST); | ||||||
| 			case "addaliasdomain": | 	} | ||||||
| 				mailbox_add_alias_domain($_POST); | 	if (isset($_POST["mailbox_add_alias"])) { | ||||||
| 			break; | 		mailbox_add_alias($_POST); | ||||||
| 			case "addmailbox": | 	} | ||||||
| 				mailbox_add_mailbox($_POST); | 	if (isset($_POST["mailbox_add_alias_domain"])) { | ||||||
| 			break; | 		mailbox_add_alias_domain($_POST); | ||||||
| 			case "editdomain": | 	} | ||||||
| 				mailbox_edit_domain($_POST); | 	if (isset($_POST["mailbox_add_mailbox"])) { | ||||||
| 			break; | 		mailbox_add_mailbox($_POST); | ||||||
| 			case "editmailbox": | 	} | ||||||
| 				mailbox_edit_mailbox($_POST); | 	if (isset($_POST["mailbox_add_resource"])) { | ||||||
| 			break; | 		mailbox_add_resource($_POST); | ||||||
| 			case "deletedomain": | 	} | ||||||
| 				mailbox_delete_domain($_POST); | 	if (isset($_POST["mailbox_edit_alias"])) { | ||||||
| 			break; | 		mailbox_edit_alias($_POST); | ||||||
| 			case "deletealias": | 	} | ||||||
| 				mailbox_delete_alias($_POST); | 	if (isset($_POST["mailbox_edit_domain"])) { | ||||||
| 			break; | 		mailbox_edit_domain($_POST); | ||||||
| 			case "deletealiasdomain": | 	} | ||||||
| 				mailbox_delete_alias_domain($_POST); | 	if (isset($_POST["mailbox_edit_mailbox"])) { | ||||||
| 			break; | 		mailbox_edit_mailbox($_POST); | ||||||
| 			case "editaliasdomain": | 	} | ||||||
| 				mailbox_edit_alias_domain($_POST); | 	if (isset($_POST["mailbox_edit_alias_domain"])) { | ||||||
| 			break; | 		mailbox_edit_alias_domain($_POST); | ||||||
| 			case "deletemailbox": | 	} | ||||||
| 				mailbox_delete_mailbox($_POST); | 	if (isset($_POST["mailbox_edit_resource"])) { | ||||||
| 			break; | 		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 | <?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! | PLEASE USE THE FILE "vars.local.inc.php" TO OVERWRITE SETTINGS AND MAKE THEM PERSISTENT! | ||||||
| @@ -33,4 +34,9 @@ $DEFAULT_LANG = "en"; | |||||||
| // simplex, slate, spacelab, superhero, united, yeti | // simplex, slate, spacelab, superhero, united, yeti | ||||||
| // See https://bootswatch.com/ | // See https://bootswatch.com/ | ||||||
| $DEFAULT_THEME = "lumen"; | $DEFAULT_THEME = "lumen"; | ||||||
|  |  | ||||||
|  | // If you want to use Yubico TFA methods, setup an ID and a key here: https://upgrade.yubico.com/getapikey/ | ||||||
|  | // Remember to override this value using vars.local.inc.php, do not change it here. | ||||||
|  | $YUBI_API['ID'] = ""; | ||||||
|  | $YUBI_API['KEY'] = ""; | ||||||
| ?> | ?> | ||||||
|   | |||||||
| @@ -2,13 +2,15 @@ $(document).ready(function() { | |||||||
| 	// add.php | 	// add.php | ||||||
| 	// Get max. possible quota for a domain when domain field changes | 	// Get max. possible quota for a domain when domain field changes | ||||||
| 	$('#addSelectDomain').on('change', function() { | 	$('#addSelectDomain').on('change', function() { | ||||||
| 		$.get("add.php", { js:"remaining_specs", domain:this.value, object:"new" }, function(data){ | 		$.get("json_api.php", { action:"get_domain_details", object:this.value }, function(data){ | ||||||
| 			if (data != '0') { |       var result = jQuery.parseJSON( data ); | ||||||
| 				$("#quotaBadge").html('max. ' + data + ' MiB'); |       max_new_mailbox_quota = ( result.max_new_mailbox_quota / 1048576); | ||||||
| 				$('#addInputQuota').attr({"disabled": false, "value": "", "type": "number", "max": data}); | 			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 { | 			else { | ||||||
| 				$("#quotaBadge").html('max. ' + data + ' MiB'); | 				$("#quotaBadge").html('max. ' + max_new_mailbox_quota + ' MiB'); | ||||||
| 				$('#addInputQuota').attr({"disabled": true, "value": "", "type": "text", "value": "n/a"}); | 				$('#addInputQuota').attr({"disabled": true, "value": "", "type": "text", "value": "n/a"}); | ||||||
| 			} | 			} | ||||||
| 		}); | 		}); | ||||||
|   | |||||||
| @@ -5,10 +5,12 @@ $(document).ready(function() { | |||||||
| 	var rowCountDomain = $('#domaintable >tbody >#data').length; | 	var rowCountDomain = $('#domaintable >tbody >#data').length; | ||||||
| 	var rowCountMailbox = $('#mailboxtable >tbody >#data').length; | 	var rowCountMailbox = $('#mailboxtable >tbody >#data').length; | ||||||
| 	var rowCountAlias = $('#aliastable >tbody >#data').length; | 	var rowCountAlias = $('#aliastable >tbody >#data').length; | ||||||
|  | 	var rowCountResource = $('#resourcetable >tbody >#data').length; | ||||||
| 	$("#numRowsDomainAlias").text(rowCountDomainAlias); | 	$("#numRowsDomainAlias").text(rowCountDomainAlias); | ||||||
| 	$("#numRowsDomain").text(rowCountDomain); | 	$("#numRowsDomain").text(rowCountDomain); | ||||||
| 	$("#numRowsMailbox").text(rowCountMailbox); | 	$("#numRowsMailbox").text(rowCountMailbox); | ||||||
| 	$("#numRowsAlias").text(rowCountAlias); | 	$("#numRowsAlias").text(rowCountAlias); | ||||||
|  | 	$("#numRowsResource").text(rowCountResource); | ||||||
|  |  | ||||||
| 	// Filter table function | 	// Filter table function | ||||||
| 	$.fn.extend({ | 	$.fn.extend({ | ||||||
|   | |||||||
							
								
								
									
										651
									
								
								data/web/js/u2f-api.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										651
									
								
								data/web/js/u2f-api.js
									
									
									
									
									
										Normal file
									
								
							| @@ -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(); | 			$(".passFields").slideUp(); | ||||||
| 		} | 		} | ||||||
| 	}); | 	}); | ||||||
|  |  | ||||||
| 	// Show generate button after time selection | 	// Show generate button after time selection | ||||||
| 	$('#trigger_set_time_limited_aliases').hide();  | 	$('#generate_tla').hide();  | ||||||
| 	$('#validity').change(function(){ | 	$('#validity').change(function(){ | ||||||
| 		$('#trigger_set_time_limited_aliases').show();  | 		$('#generate_tla').show();  | ||||||
| 	}); | 	}); | ||||||
|  |  | ||||||
| 	// Init Bootstrap Switch | 	// Init Bootstrap Switch | ||||||
| 	$.fn.bootstrapSwitch.defaults.onColor = 'success'; | 	$.fn.bootstrapSwitch.defaults.onColor = 'success'; | ||||||
| 	$("[name='tls_out']").bootstrapSwitch(); | 	$("[name='tls_out']").bootstrapSwitch(); | ||||||
| 	$("[name='tls_in']").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>'); | ||||||
|  |   }); | ||||||
| }); | }); | ||||||
							
								
								
									
										57
									
								
								data/web/json_api.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								data/web/json_api.php
									
									
									
									
									
										Normal file
									
								
							| @@ -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_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['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['dkim']['confirm'] = 'Sind Sie sicher?'; | ||||||
| $lang['danger']['dkim_not_found'] = 'DKIM-Record nicht gefunden'; | $lang['danger']['dkim_not_found'] = 'DKIM-Key nicht gefunden'; | ||||||
| $lang['danger']['dkim_remove_failed'] = 'Kann DKIM-Record nicht entfernen'; | $lang['danger']['dkim_remove_failed'] = 'Kann DKIM-Key nicht entfernen'; | ||||||
| $lang['danger']['dkim_add_failed'] = 'Kann DKIM-Record nicht hinzufügen'; | $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_domain_or_sel_invalid'] = 'DKIM-Domain oder -Selector nicht korrekt'; | ||||||
| $lang['danger']['dkim_key_length_invalid'] = 'DKIM Schlüssellänge ungültig'; | $lang['danger']['dkim_key_length_invalid'] = 'DKIM Schlüssellänge ungültig'; | ||||||
| $lang['success']['dkim_removed'] = 'DKIM-Record wurde entfernt'; | $lang['success']['dkim_removed'] = 'DKIM-Key wurde entfernt'; | ||||||
| $lang['success']['dkim_added'] = 'DKIM-Record wurde hinzugefügt'; | $lang['success']['dkim_added'] = 'DKIM-Key wurde hinzugefügt'; | ||||||
| $lang['danger']['access_denied'] = 'Zugriff verweigert oder unvollständige/ungültige Daten'; | $lang['danger']['access_denied'] = 'Zugriff verweigert oder unvollständige/ungültige Daten'; | ||||||
| $lang['danger']['whitelist_from_invalid'] = 'Whitelist-Eintrag ist ungültig'; | $lang['danger']['whitelist_from_invalid'] = 'Whitelist-Eintrag ist ungültig'; | ||||||
| $lang['danger']['domain_invalid'] = 'Domainname ist ungültig'; | $lang['danger']['domain_invalid'] = 'Domainname ist ungültig'; | ||||||
| @@ -39,6 +39,8 @@ $lang['success']['alias_added'] = 'Alias-Adresse(n) wurden angelegt'; | |||||||
| $lang['success']['alias_modified'] = 'Änderungen an Alias %s wurden gespeichert'; | $lang['success']['alias_modified'] = 'Änderungen an Alias %s wurden gespeichert'; | ||||||
| $lang['success']['aliasd_modified'] = 'Änderungen an Alias-Domain %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']['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['success']['msg_size_saved'] = 'Limit wurde gesetzt'; | ||||||
| $lang['danger']['aliasd_not_found'] = 'Alias-Domain nicht gefunden'; | $lang['danger']['aliasd_not_found'] = 'Alias-Domain nicht gefunden'; | ||||||
| $lang['danger']['targetd_not_found'] = 'Ziel-Domain nicht gefunden'; | $lang['danger']['targetd_not_found'] = 'Ziel-Domain nicht gefunden'; | ||||||
| @@ -54,28 +56,30 @@ $lang['danger']['exit_code_not_null'] = 'Fehler: Exit-Code ist %d'; | |||||||
| $lang['danger']['mailbox_not_available'] = 'Mailbox nicht verfügbar'; | $lang['danger']['mailbox_not_available'] = 'Mailbox nicht verfügbar'; | ||||||
| $lang['danger']['username_invalid'] = 'Benutzername kann nicht verwendet werden'; | $lang['danger']['username_invalid'] = 'Benutzername kann nicht verwendet werden'; | ||||||
| $lang['danger']['password_mismatch'] = 'Passwort-Wiederholung stimmt nicht überein'; | $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 Vorgaben (Klein- und Großschreibung und mindestens eine Ziffer, mindestens 6 Zeichen lang)'; | ||||||
| $lang['danger']['password_empty'] = 'Passwort darf nicht leer sein'; | $lang['danger']['password_empty'] = 'Passwort darf nicht leer sein'; | ||||||
| $lang['danger']['login_failed'] = 'Anmeldung fehlgeschlagen'; | $lang['danger']['login_failed'] = 'Anmeldung fehlgeschlagen'; | ||||||
| $lang['danger']['mailbox_invalid'] = 'Mailboxname ist ungültig'; | $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['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'] = '%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_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']['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']['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']['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_exceeded'] = 'Speicherplatz überschreitet das Limit (max. %d MiB)'; | ||||||
| $lang['danger']['mailbox_quota_left_exceeded'] = 'Nicht genügend Speicherplatz vorhanden (Speicherplatz anwendbar: %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']['mailbox_added'] = 'Mailbox %s wurde angelegt'; | ||||||
|  | $lang['success']['resource_added'] = 'Ressource %s wurde angelegt'; | ||||||
| $lang['success']['domain_removed'] = 'Domain %s wurde entfernt'; | $lang['success']['domain_removed'] = 'Domain %s wurde entfernt'; | ||||||
| $lang['success']['alias_removed'] = 'Alias-Adresse %s wurde entfernt'; | $lang['success']['alias_removed'] = 'Alias-Adresse %s wurde entfernt'; | ||||||
| $lang['success']['alias_domain_removed'] = 'Alias-Domain %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']['domain_admin_removed'] = 'Domain-Administrator %s wurde entfernt'; | ||||||
| $lang['success']['mailbox_removed'] = 'Mailbox %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']['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']['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'; | $lang['danger']['mailboxes_in_use'] = 'Maximale Anzahl an Mailboxen muss größer oder gleich %d sein'; | ||||||
| @@ -84,7 +88,6 @@ $lang['danger']['sender_acl_invalid'] = 'Sender ACL Wert muss eine Adresse oder | |||||||
| $lang['danger']['domain_not_empty'] = 'Kann nur leere Domains entfernen'; | $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['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']['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['danger']['validity_missing'] = 'Bitte geben Sie eine Gültigkeitsdauer an'; | ||||||
| $lang['user']['on'] = 'Ein'; | $lang['user']['on'] = 'Ein'; | ||||||
| $lang['user']['off'] = 'Aus'; | $lang['user']['off'] = 'Aus'; | ||||||
| @@ -93,15 +96,16 @@ $lang['user']['user_settings'] = 'Benutzereinstellungen'; | |||||||
| $lang['user']['mailbox_settings'] = 'Mailbox-Einstellungen'; | $lang['user']['mailbox_settings'] = 'Mailbox-Einstellungen'; | ||||||
| $lang['user']['mailbox_details'] = 'Mailbox-Details'; | $lang['user']['mailbox_details'] = 'Mailbox-Details'; | ||||||
| $lang['user']['change_password'] = 'Passwort ändern'; | $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']['save_changes'] = 'Änderungen speichern'; | ||||||
| $lang['user']['password_now'] = 'Aktuelles Passwort (Änderungen bestätigen):'; | $lang['user']['password_now'] = 'Aktuelles Passwort (Änderungen bestätigen)'; | ||||||
| $lang['user']['new_password_repeat'] = 'Neues Passwort (Wiederholung):'; | $lang['user']['new_password_repeat'] = 'Neues Passwort (Wiederholung)'; | ||||||
| $lang['user']['new_password_description'] = 'Mindestanforderung: 6 Zeichen lang, Buchstaben und Zahlen.'; | $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']['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']['spam_aliases'] = 'Temporäre E-Mail Aliasse'; | ||||||
| $lang['user']['alias'] = 'Alias'; | $lang['user']['alias'] = 'Alias'; | ||||||
| $lang['user']['aliases'] = 'Aliasse'; | $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']['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_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_send_as_all'] = 'Absender für folgende Domains nicht prüfen'; | ||||||
| @@ -111,7 +115,9 @@ $lang['user']['alias_valid_until'] = 'Gültig bis'; | |||||||
| $lang['user']['alias_remove_all'] = 'Alle entfernen'; | $lang['user']['alias_remove_all'] = 'Alle entfernen'; | ||||||
| $lang['user']['alias_time_left'] = 'Zeit verbleibend'; | $lang['user']['alias_time_left'] = 'Zeit verbleibend'; | ||||||
| $lang['user']['alias_full_date'] = 'd.m.Y, H:i:s T'; | $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']['alias_select_validity'] = 'Bitte Gültigkeit auswählen'; | ||||||
|  | $lang['user']['sync_jobs'] = 'Sync Jobs'; | ||||||
| $lang['user']['hour'] = 'Stunde'; | $lang['user']['hour'] = 'Stunde'; | ||||||
| $lang['user']['hours'] = 'Stunden'; | $lang['user']['hours'] = 'Stunden'; | ||||||
| $lang['user']['day'] = 'Tag'; | $lang['user']['day'] = 'Tag'; | ||||||
| @@ -134,6 +140,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_red'] = 'Rot: Die Nachricht ist eindeutig Spam und wird vom Server abgelehnt'; | ||||||
| $lang['user']['spamfilter_default_score'] = 'Standardwert:'; | $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_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_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'; | $lang['user']['tls_policy'] = 'Verschlüsselungsrichtlinie'; | ||||||
| @@ -150,6 +157,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 /> | $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".'; | 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']['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']['dashboard'] = '%s - Dashboard'; | ||||||
| $lang['start']['start_rc'] = 'Roundcube öffnen'; | $lang['start']['start_rc'] = 'Roundcube öffnen'; | ||||||
| @@ -180,10 +203,16 @@ $lang['header']['mailboxes'] = 'Mailboxen'; | |||||||
| $lang['header']['user_settings'] = 'Benutzereinstellungen'; | $lang['header']['user_settings'] = 'Benutzereinstellungen'; | ||||||
| $lang['header']['login'] = 'Anmeldung'; | $lang['header']['login'] = 'Anmeldung'; | ||||||
| $lang['header']['logged_in_as_logout'] = 'Eingeloggt als <b>%s</b> (abmelden)'; | $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['header']['locale'] = 'Sprache'; | ||||||
| $lang['mailbox']['domain'] = 'Domain'; | $lang['mailbox']['domain'] = 'Domain'; | ||||||
| $lang['mailbox']['alias'] = 'Alias'; | $lang['mailbox']['alias'] = 'Alias'; | ||||||
| $lang['mailbox']['aliases'] = 'Aliasse'; | $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']['domains'] = 'Domains'; | ||||||
| $lang['mailbox']['mailboxes'] = 'Mailboxen'; | $lang['mailbox']['mailboxes'] = 'Mailboxen'; | ||||||
| $lang['mailbox']['mailbox_quota'] = 'Max. Größe einer Mailbox'; | $lang['mailbox']['mailbox_quota'] = 'Max. Größe einer Mailbox'; | ||||||
| @@ -206,10 +235,12 @@ $lang['mailbox']['msg_num'] = 'Anzahl Nachrichten'; | |||||||
| $lang['mailbox']['remove'] = 'Entfernen'; | $lang['mailbox']['remove'] = 'Entfernen'; | ||||||
| $lang['mailbox']['edit'] = 'Bearbeiten'; | $lang['mailbox']['edit'] = 'Bearbeiten'; | ||||||
| $lang['mailbox']['archive'] = 'Archiv-Zugriff'; | $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'] = 'Domain hinzufügen'; | ||||||
| $lang['mailbox']['add_domain_alias'] = 'Domain-Alias hinzufügen'; | $lang['mailbox']['add_domain_alias'] = 'Domain-Alias hinzufügen'; | ||||||
| $lang['mailbox']['add_mailbox'] = 'Mailbox hinzufügen'; | $lang['mailbox']['add_mailbox'] = 'Mailbox hinzufügen'; | ||||||
|  | $lang['mailbox']['add_resource'] = 'Ressource hinzufügen'; | ||||||
| $lang['mailbox']['add_alias'] = 'Alias hinzufügen'; | $lang['mailbox']['add_alias'] = 'Alias hinzufügen'; | ||||||
|  |  | ||||||
| $lang['info']['no_action'] = 'Keine Aktion anwendbar'; | $lang['info']['no_action'] = 'Keine Aktion anwendbar'; | ||||||
| @@ -219,14 +250,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_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_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_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_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_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_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_alias_details'] = 'Benutzer werden keine Nachrichten mehr von dieser Adresse erhalten und versenden koennen!</b>'; | ||||||
| $lang['delete']['remove_button'] = 'Entfernen'; | $lang['delete']['remove_button'] = 'Entfernen'; | ||||||
| $lang['delete']['previous'] = 'Vorherige Seite'; | $lang['delete']['previous'] = 'Vorherige Seite'; | ||||||
|  |  | ||||||
|  | $lang['edit']['syncjob'] = 'Sync-Job bearbeiten'; | ||||||
| $lang['edit']['save'] = 'Änderungen speichern'; | $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']['archive'] = 'Archiv-Zugriff'; | ||||||
| $lang['edit']['max_mailboxes'] = 'Max. Mailboxanzahl:'; | $lang['edit']['max_mailboxes'] = 'Max. Mailboxanzahl:'; | ||||||
| $lang['edit']['title'] = 'Objekt bearbeiten'; | $lang['edit']['title'] = 'Objekt bearbeiten'; | ||||||
| @@ -264,10 +307,25 @@ $lang['edit']['dkim_txt_value'] = 'TXT-Record Wert:'; | |||||||
| $lang['edit']['previous'] = 'Vorherige Seite'; | $lang['edit']['previous'] = 'Vorherige Seite'; | ||||||
| $lang['edit']['unchanged_if_empty'] = 'Unverändert, wenn leer'; | $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 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']['title'] = 'Objekt anlegen'; | ||||||
| $lang['add']['domain'] = 'Domain'; | $lang['add']['domain'] = 'Domain'; | ||||||
| $lang['add']['active'] = 'Aktiv'; | $lang['add']['active'] = 'Aktiv'; | ||||||
|  | $lang['add']['multiple_bookings'] = 'Mehrfaches Buchen möglich'; | ||||||
| $lang['add']['save'] = 'Änderungen speichern'; | $lang['add']['save'] = 'Änderungen speichern'; | ||||||
| $lang['add']['description'] = 'Beschreibung:'; | $lang['add']['description'] = 'Beschreibung:'; | ||||||
| $lang['add']['max_aliases'] = 'Max. mögliche Aliasse:'; | $lang['add']['max_aliases'] = 'Max. mögliche Aliasse:'; | ||||||
| @@ -289,7 +347,10 @@ $lang['add']['alias_domain'] = 'Alias-Domain'; | |||||||
| $lang['add']['select'] = 'Bitte auswählen'; | $lang['add']['select'] = 'Bitte auswählen'; | ||||||
| $lang['add']['target_domain'] = 'Ziel-Domain:'; | $lang['add']['target_domain'] = 'Ziel-Domain:'; | ||||||
| $lang['add']['mailbox'] = 'Mailbox'; | $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']['mailbox_username'] = 'Benutzername (linker Teil der E-Mail-Adresse):'; | ||||||
|  | $lang['add']['resource_name'] = 'Ressourcenname:'; | ||||||
| $lang['add']['full_name'] = 'Vor- und Zuname:'; | $lang['add']['full_name'] = 'Vor- und Zuname:'; | ||||||
| $lang['add']['quota_mb'] = 'Speicherplatz (MiB):'; | $lang['add']['quota_mb'] = 'Speicherplatz (MiB):'; | ||||||
| $lang['add']['select_domain'] = 'Bitte zuerst eine Domain auswählen'; | $lang['add']['select_domain'] = 'Bitte zuerst eine Domain auswählen'; | ||||||
| @@ -310,11 +371,21 @@ $lang['login']['login'] = 'Anmelden'; | |||||||
| $lang['login']['previous'] = 'Vorherige Seite'; | $lang['login']['previous'] = 'Vorherige Seite'; | ||||||
| $lang['login']['delayed'] = 'Login wurde zur Sicherheit um %s Sekunde/n verzögert.'; | $lang['login']['delayed'] = 'Login wurde zur Sicherheit um %s Sekunde/n verzögert.'; | ||||||
|  |  | ||||||
| $lang['login']['tfa'] = 'Zwei-Faktor-Authentifizierung'; | $lang['tfa']['tfa'] = "Two-Factor Authentication"; | ||||||
| $lang['login']['tfa_details'] = 'Bitte bestätigen Sie Ihr Einmalpasswort im folgenden Feld'; | $lang['tfa']['set_tfa'] = "Konfiguriere Two-Factor Authentication Methode"; | ||||||
| $lang['login']['confirm'] = 'Bestätigen'; | $lang['tfa']['yubi_otp'] = "Yubico OTP Authentifizierung"; | ||||||
| $lang['login']['otp'] = 'Einmalpasswort'; | $lang['tfa']['u2f'] = "U2F Authentifizierung"; | ||||||
| $lang['login']['trash_login'] = 'Login verwerfen'; | $lang['tfa']['hotp'] = "HOTP Authentifizierung"; | ||||||
|  | $lang['tfa']['totp'] = "TOTP Authentifizierung"; | ||||||
|  | $lang['tfa']['none'] = "Deaktiviert"; | ||||||
|  | $lang['tfa']['delete_tfa'] = "Deaktiviere TFA"; | ||||||
|  | $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']['search_domain_da'] = 'Domains durchsuchen'; | ||||||
| $lang['admin']['restrictions'] = 'Postifx Restriktionen'; | $lang['admin']['restrictions'] = 'Postifx Restriktionen'; | ||||||
| @@ -340,9 +411,12 @@ $lang['admin']['msg_size_limit_details'] = 'Diese Einstellung wird Postfix und d | |||||||
| $lang['admin']['save'] = 'Änderungen speichern'; | $lang['admin']['save'] = 'Änderungen speichern'; | ||||||
| $lang['admin']['maintenance'] = 'Wartung und Information'; | $lang['admin']['maintenance'] = 'Wartung und Information'; | ||||||
| $lang['admin']['sys_info'] = 'Systeminformation'; | $lang['admin']['sys_info'] = 'Systeminformation'; | ||||||
| $lang['admin']['dkim_add_key'] = 'DKIM-Record hinzufügen'; | $lang['admin']['dkim_add_key'] = 'DKIM-Key hinzufügen'; | ||||||
| $lang['admin']['dkim_keys'] = 'DKIM-Records'; | $lang['admin']['dkim_keys'] = 'DKIM-Keys'; | ||||||
| $lang['admin']['dkim_key_length'] = 'DKIM Schlüssellänge (Bits)'; | $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']['add'] = 'Hinzufügen'; | ||||||
| $lang['admin']['configuration'] = 'Konfiguration'; | $lang['admin']['configuration'] = 'Konfiguration'; | ||||||
| $lang['admin']['password'] = 'Passwort'; | $lang['admin']['password'] = 'Passwort'; | ||||||
|   | |||||||
| @@ -10,13 +10,13 @@ $lang['footer']['restart_sogo'] = 'Restart SOGo'; | |||||||
| $lang['footer']['restart_now'] = 'Restart now'; | $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['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['dkim']['confirm'] = "Are you sure?"; | ||||||
| $lang['danger']['dkim_not_found'] = "DKIM record not found"; | $lang['danger']['dkim_not_found'] = "DKIM key not found"; | ||||||
| $lang['danger']['dkim_remove_failed'] = "Cannot remove selected DKIM record"; | $lang['danger']['dkim_remove_failed'] = "Cannot remove selected DKIM key"; | ||||||
| $lang['danger']['dkim_add_failed'] = "Cannot add given DKIM record"; | $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_domain_or_sel_invalid'] = "DKIM domain or selector invalid"; | ||||||
| $lang['danger']['dkim_key_length_invalid'] = "DKIM key length invalid"; | $lang['danger']['dkim_key_length_invalid'] = "DKIM key length invalid"; | ||||||
| $lang['success']['dkim_removed'] = "DKIM record has been removed"; | $lang['success']['dkim_removed'] = "DKIM key has been removed"; | ||||||
| $lang['success']['dkim_added'] = "DKIM record has been saved"; | $lang['success']['dkim_added'] = "DKIM key has been saved"; | ||||||
| $lang['danger']['access_denied'] = "Access denied or invalid form data"; | $lang['danger']['access_denied'] = "Access denied or invalid form data"; | ||||||
| $lang['danger']['whitelist_from_invalid'] = "Whitelist entry invalid"; | $lang['danger']['whitelist_from_invalid'] = "Whitelist entry invalid"; | ||||||
| $lang['danger']['domain_invalid'] = "Domain name is invalid"; | $lang['danger']['domain_invalid'] = "Domain name is invalid"; | ||||||
| @@ -41,6 +41,8 @@ $lang['success']['alias_added'] = "Alias address/es has/have been added"; | |||||||
| $lang['success']['alias_modified'] = "Changes to alias have been saved"; | $lang['success']['alias_modified'] = "Changes to alias have been saved"; | ||||||
| $lang['success']['aliasd_modified'] = "Changes to alias domain 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']['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['success']['msg_size_saved'] = "Message size limit has been set"; | ||||||
| $lang['danger']['aliasd_not_found'] = "Alias domain not found"; | $lang['danger']['aliasd_not_found'] = "Alias domain not found"; | ||||||
| $lang['danger']['targetd_not_found'] = "Target domain not found"; | $lang['danger']['targetd_not_found'] = "Target domain not found"; | ||||||
| @@ -56,14 +58,13 @@ $lang['danger']['exit_code_not_null'] = "Error: Exit code was %d"; | |||||||
| $lang['danger']['mailbox_not_available'] = "Mailbox not available"; | $lang['danger']['mailbox_not_available'] = "Mailbox not available"; | ||||||
| $lang['danger']['username_invalid'] = "Username cannot be used"; | $lang['danger']['username_invalid'] = "Username cannot be used"; | ||||||
| $lang['danger']['password_mismatch'] = "Confirmation password is not identical"; | $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 requirements (upper and lowercase letters and at least one number, min. 6 characters long)"; | ||||||
| $lang['danger']['password_empty'] = "Password must not be empty"; | $lang['danger']['password_empty'] = "Password must not be empty"; | ||||||
| $lang['danger']['login_failed'] = "Login failed"; | $lang['danger']['login_failed'] = "Login failed"; | ||||||
| $lang['danger']['mailbox_invalid'] = "Mailbox name is invalid"; | $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['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'] = "%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_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"; | $lang['danger']['is_spam_alias'] = "%s is already known as a spam alias address"; | ||||||
| @@ -73,11 +74,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_exceeded'] = "Quota exceeds the domain limit (max. %d MiB)"; | ||||||
| $lang['danger']['mailbox_quota_left_exceeded'] = "Not enough space left (space left: %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']['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']['domain_removed'] = "Domain %s has been removed"; | ||||||
| $lang['success']['alias_removed'] = "Alias-Adresse %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']['alias_domain_removed'] = "Alias domain %s has been removed"; | ||||||
| $lang['success']['domain_admin_removed'] = "Domain administrator %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']['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']['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']['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"; | $lang['danger']['mailboxes_in_use'] = "Max. mailboxes must be greater or equal to %d"; | ||||||
| @@ -86,7 +90,6 @@ $lang['danger']['sender_acl_invalid'] = "Sender ACL value is invalid"; | |||||||
| $lang['danger']['domain_not_empty'] = "Cannot remove non-empty domain"; | $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['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']['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['danger']['validity_missing'] = 'Please assign a period of validity'; | ||||||
| $lang['user']['on'] = "On"; | $lang['user']['on'] = "On"; | ||||||
| $lang['user']['off'] = "Off"; | $lang['user']['off'] = "Off"; | ||||||
| @@ -95,25 +98,28 @@ $lang['user']['user_settings'] = 'User settings'; | |||||||
| $lang['user']['mailbox_settings'] = 'Mailbox settings'; | $lang['user']['mailbox_settings'] = 'Mailbox settings'; | ||||||
| $lang['user']['mailbox_details'] = 'Mailbox details'; | $lang['user']['mailbox_details'] = 'Mailbox details'; | ||||||
| $lang['user']['change_password'] = 'Change password'; | $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']['save_changes'] = 'Save changes'; | ||||||
| $lang['user']['password_now'] = 'Current password (confirm changes):'; | $lang['user']['password_now'] = 'Current password (confirm changes)'; | ||||||
| $lang['user']['new_password_repeat'] = 'Confirmation password (repeat):'; | $lang['user']['new_password_repeat'] = 'Confirmation password (repeat)'; | ||||||
| $lang['user']['new_password_description'] = 'Requirement: 6 characters long, letters and numbers.'; | $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']['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']['spam_aliases'] = 'Temporary email aliases'; | ||||||
| $lang['user']['alias'] = 'Alias'; | $lang['user']['alias'] = 'Alias'; | ||||||
| $lang['user']['aliases'] = 'Aliases'; | $lang['user']['aliases'] = 'Aliases'; | ||||||
|  | $lang['user']['domain_aliases'] = 'Domain alias addresses'; | ||||||
| $lang['user']['is_catch_all'] = 'Catch-all for domain/s'; | $lang['user']['is_catch_all'] = 'Catch-all for domain/s'; | ||||||
| $lang['user']['aliases_also_send_as'] = 'Also allowed to send as'; | $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_send_as_all'] = 'Do not check sender access for following domain/s'; | ||||||
| $lang['user']['alias_create_random'] = 'Generate random alias'; | $lang['user']['alias_create_random'] = 'Generate random alias'; | ||||||
| $lang['user']['alias_extend_all'] = 'Extend aliases by 1 hour'; | $lang['user']['alias_extend_all'] = 'Extend aliases by 1 hour'; | ||||||
| $lang['user']['alias_valid_until'] = 'Valid until'; | $lang['user']['alias_valid_until'] = 'Valid until'; | ||||||
| $lang['user']['alias_remove_all'] = 'Remove all aliases'; | $lang['user']['alias_remove_all'] = 'Remove all aliases'; | ||||||
| $lang['user']['alias_time_left'] = 'Time left'; | $lang['user']['alias_time_left'] = 'Time left'; | ||||||
| $lang['user']['alias_full_date'] = 'd.m.Y, H:i:s T'; | $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']['alias_select_validity'] = 'Period of validity'; | ||||||
|  | $lang['user']['sync_jobs'] = 'Sync jobs'; | ||||||
| $lang['user']['hour'] = 'Hour'; | $lang['user']['hour'] = 'Hour'; | ||||||
| $lang['user']['hours'] = 'Hours'; | $lang['user']['hours'] = 'Hours'; | ||||||
| $lang['user']['day'] = 'Day'; | $lang['user']['day'] = 'Day'; | ||||||
| @@ -136,12 +142,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_red'] = 'Red: This message is spam and will be rejected by the server'; | ||||||
| $lang['user']['spamfilter_default_score'] = 'Default values:'; | $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_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_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_policy'] = 'Encryption policy'; | ||||||
| $lang['user']['tls_enforce_in'] = 'Enforce TLS incoming'; | $lang['user']['tls_enforce_in'] = 'Enforce TLS incoming'; | ||||||
| $lang['user']['tls_enforce_out'] = 'Enforce TLS outgoing'; | $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_settings'] = 'Other profile settings'; | ||||||
| $lang['user']['misc_delete_profile'] = 'Other profile settings'; | $lang['user']['misc_delete_profile'] = 'Other profile settings'; | ||||||
| @@ -152,6 +159,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 /> | $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".'; | 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']['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']['dashboard'] = '%s - dashboard'; | ||||||
| $lang['start']['start_rc'] = 'Open Roundcube'; | $lang['start']['start_rc'] = 'Open Roundcube'; | ||||||
| @@ -182,12 +205,18 @@ $lang['header']['mailboxes'] = 'Mailboxes'; | |||||||
| $lang['header']['user_settings'] = 'User settings'; | $lang['header']['user_settings'] = 'User settings'; | ||||||
| $lang['header']['login'] = 'Login'; | $lang['header']['login'] = 'Login'; | ||||||
| $lang['header']['logged_in_as_logout'] = 'Logged in as <b>%s</b> (logout)'; | $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['header']['locale'] = 'Language'; | ||||||
| $lang['mailbox']['domain'] = 'Domain'; | $lang['mailbox']['domain'] = 'Domain'; | ||||||
|  | $lang['mailbox']['multiple_bookings'] = 'Multiple bookings'; | ||||||
|  | $lang['mailbox']['kind'] = 'Kind'; | ||||||
|  | $lang['mailbox']['description'] = 'Description'; | ||||||
| $lang['mailbox']['alias'] = 'Alias'; | $lang['mailbox']['alias'] = 'Alias'; | ||||||
|  | $lang['mailbox']['resource_name'] = 'Resource name'; | ||||||
| $lang['mailbox']['aliases'] = 'Aliases'; | $lang['mailbox']['aliases'] = 'Aliases'; | ||||||
| $lang['mailbox']['domains'] = 'Domains'; | $lang['mailbox']['domains'] = 'Domains'; | ||||||
| $lang['mailbox']['mailboxes'] = 'Mailboxes'; | $lang['mailbox']['mailboxes'] = 'Mailboxes'; | ||||||
|  | $lang['mailbox']['resources'] = 'Resources'; | ||||||
| $lang['mailbox']['mailbox_quota'] = 'Max. size of a mailbox'; | $lang['mailbox']['mailbox_quota'] = 'Max. size of a mailbox'; | ||||||
| $lang['mailbox']['domain_quota'] = 'Quota'; | $lang['mailbox']['domain_quota'] = 'Quota'; | ||||||
| $lang['mailbox']['active'] = 'Active'; | $lang['mailbox']['active'] = 'Active'; | ||||||
| @@ -208,37 +237,52 @@ $lang['mailbox']['msg_num'] = 'Message #'; | |||||||
| $lang['mailbox']['remove'] = 'Remove'; | $lang['mailbox']['remove'] = 'Remove'; | ||||||
| $lang['mailbox']['edit'] = 'Edit'; | $lang['mailbox']['edit'] = 'Edit'; | ||||||
| $lang['mailbox']['archive'] = 'Archive'; | $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'] = 'Add domain'; | ||||||
| $lang['mailbox']['add_domain_alias'] = 'Add domain alias'; | $lang['mailbox']['add_domain_alias'] = 'Add domain alias'; | ||||||
| $lang['mailbox']['add_mailbox'] = 'Add mailbox'; | $lang['mailbox']['add_mailbox'] = 'Add mailbox'; | ||||||
|  | $lang['mailbox']['add_resource'] = 'Add resource'; | ||||||
| $lang['mailbox']['add_alias'] = 'Add alias'; | $lang['mailbox']['add_alias'] = 'Add alias'; | ||||||
|  |  | ||||||
| $lang['info']['no_action'] = 'No action applicable'; | $lang['info']['no_action'] = 'No action applicable'; | ||||||
|  |  | ||||||
| $lang['delete']['title'] = 'Remove object'; | $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_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_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_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_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_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_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_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_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']['remove_button'] = 'Remove'; | ||||||
| $lang['delete']['previous'] = 'Previous page'; | $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']['save'] = 'Save changes'; | ||||||
| $lang['edit']['archive'] = 'Archive access'; | $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']['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']['active'] = 'Active'; | ||||||
| $lang['edit']['target_domain'] = 'Target domain:'; | $lang['edit']['target_domain'] = 'Target domain'; | ||||||
| $lang['edit']['password'] = 'Password:'; | $lang['edit']['password'] = 'Password'; | ||||||
| $lang['edit']['ratelimit'] = 'Outgoing rate limit/h:'; | $lang['edit']['ratelimit'] = 'Outgoing rate limit/h'; | ||||||
| $lang['danger']['ratelimt_less_one'] = 'Outgoing rate limit/h must not be less than 1'; | $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_admin'] = 'Edit domain administrator'; | ||||||
| $lang['edit']['domain'] = 'Edit domain'; | $lang['edit']['domain'] = 'Edit domain'; | ||||||
| $lang['edit']['alias_domain'] = 'Alias domain'; | $lang['edit']['alias_domain'] = 'Alias domain'; | ||||||
| @@ -247,14 +291,14 @@ $lang['edit']['domains'] = 'Domains'; | |||||||
| $lang['edit']['destroy'] = 'Manual data input'; | $lang['edit']['destroy'] = 'Manual data input'; | ||||||
| $lang['edit']['alias'] = 'Edit alias'; | $lang['edit']['alias'] = 'Edit alias'; | ||||||
| $lang['edit']['mailbox'] = 'Edit mailbox'; | $lang['edit']['mailbox'] = 'Edit mailbox'; | ||||||
| $lang['edit']['description'] = 'Description:'; | $lang['edit']['description'] = 'Description'; | ||||||
| $lang['edit']['max_aliases'] = 'Max. aliases:'; | $lang['edit']['max_aliases'] = 'Max. aliases'; | ||||||
| $lang['edit']['max_quota'] = 'Max. quota per mailbox (MiB):'; | $lang['edit']['max_quota'] = 'Max. quota per mailbox (MiB)'; | ||||||
| $lang['edit']['domain_quota'] = 'Domain quota:'; | $lang['edit']['domain_quota'] = 'Domain quota'; | ||||||
| $lang['edit']['backup_mx_options'] = 'Backup MX options:'; | $lang['edit']['backup_mx_options'] = 'Backup MX options'; | ||||||
| $lang['edit']['relay_domain'] = 'Relay domain'; | $lang['edit']['relay_domain'] = 'Relay domain'; | ||||||
| $lang['edit']['relay_all'] = 'Relay all recipients'; | $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']['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']['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'; | $lang['edit']['full_name'] = 'Full name'; | ||||||
| @@ -266,13 +310,29 @@ $lang['edit']['dkim_txt_value'] = 'TXT record value:'; | |||||||
| $lang['edit']['previous'] = 'Previous page'; | $lang['edit']['previous'] = 'Previous page'; | ||||||
| $lang['edit']['unchanged_if_empty'] = 'If unchanged leave blank'; | $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'] = 'Do not check sender for domain %s'; | ||||||
|  | $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']['title'] = 'Add object'; | ||||||
| $lang['add']['domain'] = 'Domain'; | $lang['add']['domain'] = 'Domain'; | ||||||
| $lang['add']['active'] = 'Active'; | $lang['add']['active'] = 'Active'; | ||||||
|  | $lang['add']['multiple_bookings'] = 'Multiple bookings'; | ||||||
| $lang['add']['save'] = 'Save changes'; | $lang['add']['save'] = 'Save changes'; | ||||||
| $lang['add']['description'] = 'Description:'; | $lang['add']['description'] = 'Description:'; | ||||||
| $lang['add']['max_aliases'] = 'Max. possible aliases:'; | $lang['add']['max_aliases'] = 'Max. possible aliases:'; | ||||||
|  | $lang['add']['resource_name'] = 'Resource name'; | ||||||
| $lang['add']['max_mailboxes'] = 'Max. possible mailboxes:'; | $lang['add']['max_mailboxes'] = 'Max. possible mailboxes:'; | ||||||
| $lang['add']['mailbox_quota_m'] = 'Max. quota per mailbox (MiB):'; | $lang['add']['mailbox_quota_m'] = 'Max. quota per mailbox (MiB):'; | ||||||
| $lang['add']['domain_quota_m'] = 'Total domain quota (MiB):'; | $lang['add']['domain_quota_m'] = 'Total domain quota (MiB):'; | ||||||
| @@ -291,6 +351,8 @@ $lang['add']['alias_domain'] = 'Alias domain'; | |||||||
| $lang['add']['select'] = 'Please select...'; | $lang['add']['select'] = 'Please select...'; | ||||||
| $lang['add']['target_domain'] = 'Target domain:'; | $lang['add']['target_domain'] = 'Target domain:'; | ||||||
| $lang['add']['mailbox'] = 'Mailbox'; | $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']['mailbox_username'] = 'Username (left part of an email address):'; | ||||||
| $lang['add']['full_name'] = 'Full name:'; | $lang['add']['full_name'] = 'Full name:'; | ||||||
| $lang['add']['quota_mb'] = 'Quota (MiB):'; | $lang['add']['quota_mb'] = 'Quota (MiB):'; | ||||||
| @@ -312,11 +374,21 @@ $lang['login']['login'] = 'Login'; | |||||||
| $lang['login']['previous'] = "Previous page"; | $lang['login']['previous'] = "Previous page"; | ||||||
| $lang['login']['delayed'] = 'Login was delayed by %s seconds.'; | $lang['login']['delayed'] = 'Login was delayed by %s seconds.'; | ||||||
|  |  | ||||||
| $lang['login']['tfa'] = "Two-factor authentication"; | $lang['tfa']['tfa'] = "Two-factor authentication"; | ||||||
| $lang['login']['tfa_details'] = "Please confirm your one-time password in the below field"; | $lang['tfa']['set_tfa'] = "Set two-factor authentication method"; | ||||||
| $lang['login']['confirm'] = "Confirm"; | $lang['tfa']['yubi_otp'] = "Yubico OTP authentication"; | ||||||
| $lang['login']['otp'] = "One-time password"; | $lang['tfa']['u2f'] = "U2F authentication"; | ||||||
| $lang['login']['trash_login'] = "Trash login"; | $lang['tfa']['hotp'] = "HOTP authentication"; | ||||||
|  | $lang['tfa']['totp'] = "TOTP authentication"; | ||||||
|  | $lang['tfa']['none'] = "Deaktiviert"; | ||||||
|  | $lang['tfa']['delete_tfa'] = "Disable TFA"; | ||||||
|  | $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']['search_domain_da'] = 'Search domains'; | ||||||
| $lang['admin']['restrictions'] = 'Postifx Restrictions'; | $lang['admin']['restrictions'] = 'Postifx Restrictions'; | ||||||
| @@ -340,6 +412,10 @@ $lang['admin']['privacy_anon_mail'] = 'Anonymize outgoing mail'; | |||||||
| $lang['admin']['dkim_txt_name'] = 'TXT record name:'; | $lang['admin']['dkim_txt_name'] = 'TXT record name:'; | ||||||
| $lang['admin']['dkim_txt_value'] = 'TXT record value:'; | $lang['admin']['dkim_txt_value'] = 'TXT record value:'; | ||||||
| $lang['admin']['dkim_key_length'] = 'DKIM key length (bits)'; | $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']['previous'] = 'Previous page'; | ||||||
| $lang['admin']['quota_mb'] = 'Quota (MiB):'; | $lang['admin']['quota_mb'] = 'Quota (MiB):'; | ||||||
| $lang['admin']['sender_acl'] = 'Allow to send as:'; | $lang['admin']['sender_acl'] = 'Allow to send as:'; | ||||||
| @@ -349,8 +425,8 @@ $lang['admin']['msg_size_limit_details'] = 'Applying a new limit will reload Pos | |||||||
| $lang['admin']['save'] = 'Save changes'; | $lang['admin']['save'] = 'Save changes'; | ||||||
| $lang['admin']['maintenance'] = 'Maintenance and Information'; | $lang['admin']['maintenance'] = 'Maintenance and Information'; | ||||||
| $lang['admin']['sys_info'] = 'System information'; | $lang['admin']['sys_info'] = 'System information'; | ||||||
| $lang['admin']['dkim_add_key'] = 'Add DKIM record'; | $lang['admin']['dkim_add_key'] = 'Add DKIM key'; | ||||||
| $lang['admin']['dkim_keys'] = 'DKIM records'; | $lang['admin']['dkim_keys'] = 'DKIM keys'; | ||||||
| $lang['admin']['add'] = 'Add'; | $lang['admin']['add'] = 'Add'; | ||||||
| $lang['admin']['configuration'] = 'Configuration'; | $lang['admin']['configuration'] = 'Configuration'; | ||||||
| $lang['admin']['password'] = 'Password'; | $lang['admin']['password'] = 'Password'; | ||||||
| @@ -374,5 +450,5 @@ $lang['admin']['invalid_max_msg_size'] = 'Invalid max. message size'; | |||||||
| $lang['admin']['site_not_found'] = 'Cannot locate mailcow site configuration'; | $lang['admin']['site_not_found'] = 'Cannot locate mailcow site configuration'; | ||||||
| $lang['admin']['public_folder_empty'] = 'Public folder name must not be empty'; | $lang['admin']['public_folder_empty'] = 'Public folder name must not be empty'; | ||||||
| $lang['admin']['set_rr_failed'] = 'Cannot set Postfix restrictions'; | $lang['admin']['set_rr_failed'] = 'Cannot set Postfix restrictions'; | ||||||
| $lang['admin']['no_record'] = 'No Record'; | $lang['admin']['no_record'] = 'No record'; | ||||||
| ?> | ?> | ||||||
|   | |||||||
| @@ -4,7 +4,10 @@ | |||||||
| //  Dutch language file | //  Dutch language file | ||||||
| */ | */ | ||||||
| $lang['footer']['loading'] = "Even geduld a.u.b. ..."; | $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['dkim']['confirm'] = "Weet u het zeker?"; | ||||||
| $lang['danger']['dkim_not_found'] = "DKIM record niet gevonden."; | $lang['danger']['dkim_not_found'] = "DKIM record niet gevonden."; | ||||||
| $lang['danger']['dkim_remove_failed'] = "Kan geselecteerde DKIM record niet verwijderen."; | $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['success']['domain_added'] = "Domein toegevoegd: %s."; | ||||||
| $lang['danger']['alias_empty'] = "Aliasadres mag niet leeg blijven."; | $lang['danger']['alias_empty'] = "Aliasadres mag niet leeg blijven."; | ||||||
| $lang['danger']['goto_empty'] = "Doeladres 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']['policy_list_from_exists'] = "Deze invoer bestaat al."; | ||||||
| $lang['danger']['blacklist_from_invalid'] = "Zwarte lijst invoer heeft een ongeldig format."; | $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_exists'] = "Deze invoer staat op de witte lijst."; | ||||||
| $lang['danger']['whitelist_from_invalid'] = "Witte lijst invoer heeft een ongeldig format."; | $lang['danger']['whitelist_from_invalid'] = "Witte lijst invoer heeft een ongeldig format."; | ||||||
| $lang['danger']['alias_invalid'] = "Aliasadres is ongeldig."; | $lang['danger']['alias_invalid'] = "Aliasadres is ongeldig."; | ||||||
| @@ -90,10 +93,10 @@ $lang['user']['user_settings'] = 'Gebruikersinstellingen'; | |||||||
| $lang['user']['mailbox_settings'] = 'Postvakinstellingen'; | $lang['user']['mailbox_settings'] = 'Postvakinstellingen'; | ||||||
| $lang['user']['mailbox_details'] = 'Postvakdetails'; | $lang['user']['mailbox_details'] = 'Postvakdetails'; | ||||||
| $lang['user']['change_password'] = 'Verander wachtwoord'; | $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']['save_changes'] = 'Wijzigingen opslaan'; | ||||||
| $lang['user']['password_now'] = 'Huidig wachtwoord (bevestig wijzigingen):'; | $lang['user']['password_now'] = 'Huidig wachtwoord (bevestig wijzigingen)'; | ||||||
| $lang['user']['new_password_repeat'] = 'Bevestig wachtwoord (herhalen):'; | $lang['user']['new_password_repeat'] = 'Bevestig wachtwoord (herhalen)'; | ||||||
| $lang['user']['new_password_description'] = 'Vereisten: 6 karakters lang, letters en nummers.'; | $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']['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'; | $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_action'] = 'Handeling'; | ||||||
| $lang['user']['spamfilter_table_empty'] = 'Geen gegevens om weer te geven.'; | $lang['user']['spamfilter_table_empty'] = 'Geen gegevens om weer te geven.'; | ||||||
| $lang['user']['spamfilter_table_remove'] = 'verwijder'; | $lang['user']['spamfilter_table_remove'] = 'verwijder'; | ||||||
|  | $lang['user']['spamfilter_table_add'] = 'Voeg item toe'; | ||||||
| $lang['user']['spamfilter_default_score'] = 'Spamscore:'; | $lang['user']['spamfilter_default_score'] = 'Spamscore:'; | ||||||
| $lang['user']['spamfilter_green'] = 'Groen: Dit bericht is geen spam.'; | $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.'; | $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_settings'] = 'Andere profielinstellingen'; | ||||||
| $lang['user']['misc_delete_profile'] = '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_rc'] = 'Open Roundcube'; | ||||||
| $lang['start']['start_sogo'] = 'Open SOGo'; | $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.'; | $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'] = 'Wachtwoord:'; | ||||||
| $lang['add']['password_repeat'] = 'Bevestig wachtwoord (herhalen):'; | $lang['add']['password_repeat'] = 'Bevestig wachtwoord (herhalen):'; | ||||||
| $lang['add']['previous'] = 'Vorige pagina'; | $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']['title'] = 'Aanmelden'; | ||||||
| $lang['login']['administration'] = 'Beheer'; | $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_settings'] = 'Configrações da conta'; | ||||||
| $lang['user']['mailbox_details'] = 'Detalhes da conta'; | $lang['user']['mailbox_details'] = 'Detalhes da conta'; | ||||||
| $lang['user']['change_password'] = 'Alterar senha'; | $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']['save_changes'] = 'Salvar'; | ||||||
| $lang['user']['password_now'] = 'Senha atual (confirme a alteração):'; | $lang['user']['password_now'] = 'Senha atual (confirme a alteração)'; | ||||||
| $lang['user']['new_password_repeat'] = 'Confirmar senha (repetir):'; | $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']['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']['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'; | $lang['user']['spam_aliases'] = 'Apelidos temporários'; | ||||||
|   | |||||||
| @@ -1,7 +1,7 @@ | |||||||
| <?php | <?php | ||||||
| require_once "inc/prerequisites.inc.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"; | require_once "inc/header.inc.php"; | ||||||
| $_SESSION['return_to'] = $_SERVER['REQUEST_URI']; | $_SESSION['return_to'] = $_SERVER['REQUEST_URI']; | ||||||
| ?> | ?> | ||||||
| @@ -49,80 +49,32 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI']; | |||||||
| 					</thead> | 					</thead> | ||||||
| 					<tbody> | 					<tbody> | ||||||
| 					<?php | 					<?php | ||||||
| 					try { |           $domains = mailbox_get_domains(); | ||||||
| 						$stmt = $pdo->prepare("SELECT  | 	        if (!empty($domains)): | ||||||
| 								`domain`, | 					foreach ($domains as $domain): | ||||||
| 								`aliases`, |             $domaindata = mailbox_get_domain_details($domain); | ||||||
| 								`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; |  | ||||||
| 						} |  | ||||||
| 					?> | 					?> | ||||||
| 						<tr id="data"> | 						<tr id="data"> | ||||||
| 							<td><?=htmlspecialchars($row['domain']);?></td> | 							<td><?=htmlspecialchars($domaindata['domain_name']);?></td> | ||||||
| 							<td><?=intval($AliasData['count']);?> / <?=intval($row['aliases']);?></td> | 							<td><?=$domaindata['aliases_in_domain'];?> / <?=$domaindata['max_num_aliases_for_domain'];?></td> | ||||||
| 							<td><?=$MailboxData['count'];?> / <?=$row['mailboxes'];?></td> | 							<td><?=$domaindata['mboxes_in_domain'];?> / <?=$domaindata['max_num_mboxes_for_domain'];?></td> | ||||||
| 							<td><?=formatBytes(intval($row['maxquota']), 2);?></td> | 							<td><?=formatBytes($domaindata['max_quota_for_mbox']);?></td> | ||||||
| 							<td><?=formatBytes(intval($MailboxData['quota']), 2);?> / <?=formatBytes(intval($row['quota']));?></td> | 							<td><?=formatBytes($domaindata['quota_used_in_domain'], 2);?> / <?=formatBytes($domaindata['max_quota_for_domain'], 2);?></td> | ||||||
| 							<?php | 							<?php | ||||||
| 							if ($_SESSION['mailcow_cc_role'] == "admin"): | 							if ($_SESSION['mailcow_cc_role'] == "admin"): | ||||||
| 							?> | 							?> | ||||||
| 								<td><?=$row['backupmx'];?></td> | 								<td><?=$domaindata['backupmx'];?></td> | ||||||
| 							<?php | 							<?php | ||||||
| 							endif; | 							endif; | ||||||
| 							?> | 							?> | ||||||
| 							<td><?=$row['active'];?></td> | 							<td><?=$domaindata['active'];?></td> | ||||||
| 							<?php | 							<?php | ||||||
| 							if ($_SESSION['mailcow_cc_role'] == "admin"): | 							if ($_SESSION['mailcow_cc_role'] == "admin"): | ||||||
| 							?> | 							?> | ||||||
| 								<td style="text-align: right;"> | 								<td style="text-align: right;"> | ||||||
| 									<div class="btn-group"> | 									<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> | ||||||
| 										<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="/delete.php?domain=<?=urlencode($domaindata['domain_name']);?>" class="btn btn-xs btn-danger"><span class="glyphicon glyphicon-trash"></span> <?=$lang['mailbox']['remove'];?></a> | ||||||
| 									</div> | 									</div> | ||||||
| 								</td> | 								</td> | ||||||
| 							<?php | 							<?php | ||||||
| @@ -130,19 +82,19 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI']; | |||||||
| 							?> | 							?> | ||||||
| 								<td style="text-align: right;"> | 								<td style="text-align: right;"> | ||||||
| 									<div class="btn-group"> | 									<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> | 									</div> | ||||||
| 								</td> | 								</td> | ||||||
| 						</tr> | 						</tr> | ||||||
| 							<?php |               <?php | ||||||
| 							endif; |               endif; | ||||||
| 							endwhile; |             endforeach; | ||||||
| 	            else: |             else: | ||||||
| 							?> |  | ||||||
| 							  <tr id="no-data"><td colspan="8" style="text-align: center; font-style: italic;"><?=$lang['mailbox']['no_record'];?></td></tr> |  | ||||||
| 							<?php |  | ||||||
| 							endif; |  | ||||||
| 							?> | 							?> | ||||||
|  |               <tr id="no-data"><td colspan="8" style="text-align: center; font-style: italic;"><?=$lang['mailbox']['no_record_single'];?></td></tr> | ||||||
|  |             <?php | ||||||
|  |             endif; | ||||||
|  |             ?> | ||||||
| 					</tbody> | 					</tbody> | ||||||
| 						<?php | 						<?php | ||||||
| 						if ($_SESSION['mailcow_cc_role'] == "admin"): | 						if ($_SESSION['mailcow_cc_role'] == "admin"): | ||||||
| @@ -162,91 +114,6 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI']; | |||||||
| 			</div> | 			</div> | ||||||
| 		</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']['domain_aliases'];?> <span class="badge" id="numRowsDomainAlias"></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?aliasdomain"><span class="glyphicon glyphicon-plus"></span></a> |  | ||||||
| 					</div> |  | ||||||
| 				</div> |  | ||||||
| 				<div class="panel-body"> |  | ||||||
| 					<input type="text" class="form-control" id="domainaliastable-filter" data-action="filter" data-filters="#domainaliastable" placeholder="Filter" /> |  | ||||||
| 				</div> |  | ||||||
| 				<div class="table-responsive"> |  | ||||||
| 				<table class="table table-striped sortable-theme-bootstrap" data-sortable id="domainaliastable"> |  | ||||||
| 					<thead> |  | ||||||
| 						<tr> |  | ||||||
| 							<th class="sort-table" style="min-width: 67px;"><?=$lang['mailbox']['alias'];?></th> |  | ||||||
| 							<th class="sort-table" style="min-width: 127px;"><?=$lang['mailbox']['target_domain'];?></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  |  | ||||||
| 								`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: |  | ||||||
| 	        ?> |  | ||||||
| 						<tr id="no-data"><td colspan="4" 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?aliasdomain"><?=$lang['mailbox']['add_domain_alias'];?></a> |  | ||||||
| 							</td> |  | ||||||
| 						</tr> |  | ||||||
| 					</tfoot> |  | ||||||
| 				</table> |  | ||||||
| 				</div> |  | ||||||
| 			</div> |  | ||||||
| 		</div> |  | ||||||
| 	</div> |  | ||||||
| 	<div class="row"> | 	<div class="row"> | ||||||
| 		<div class="col-md-12"> | 		<div class="col-md-12"> | ||||||
| 			<div class="panel panel-default"> | 			<div class="panel panel-default"> | ||||||
| @@ -278,91 +145,45 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI']; | |||||||
| 					</thead> | 					</thead> | ||||||
| 					<tbody> | 					<tbody> | ||||||
| 						<?php | 						<?php | ||||||
| 						try { |             foreach (mailbox_get_domains() as $domain) { | ||||||
| 							$stmt = $pdo->prepare("SELECT |               $mailboxes = mailbox_get_mailboxes($domain); | ||||||
| 									`domain`.`backupmx`, |               if (!empty($mailboxes)) { | ||||||
| 									`mailbox`.`username`, |                 foreach ($mailboxes as $mailbox) { | ||||||
| 									`mailbox`.`name`, |                   $mailboxdata = mailbox_get_mailbox_details($mailbox); | ||||||
| 									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"> | 						<tr id="data"> | ||||||
| 							<?php | 							<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> | ||||||
| 							if ($row['backupmx'] == "0"): | 							<td><?=htmlspecialchars($mailboxdata['name'], ENT_QUOTES, 'UTF-8');?></td> | ||||||
| 							?> | 							<td><?=htmlspecialchars($mailboxdata['domain']);?></td> | ||||||
| 								<td><?=htmlspecialchars($row['username']);?></td> | 							<td><?=formatBytes($mailboxdata['quota_used'], 2);?> / <?=formatBytes($mailboxdata['quota'], 2);?></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;"> | 							<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"> | ||||||
| 									<div class="progress-bar <?=$pbar;?>" role="progressbar" aria-valuenow="<?=$percentInUse;?>" aria-valuemin="0" aria-valuemax="100" style="min-width:2em;width: <?=$percentInUse;?>%;"> | 									<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'];?>%;"> | ||||||
| 										<?=$percentInUse;?>% | 										<?=$mailboxdata['percent_in_use'];?>% | ||||||
| 									</div> | 									</div> | ||||||
| 								</div> | 								</div> | ||||||
| 							</td> | 							</td> | ||||||
| 							<td><?=$row['messages'];?></td> | 							<td><?=$mailboxdata['messages'];?></td> | ||||||
| 							<td><?=$row['active'];?></td> | 							<td><?=$mailboxdata['active'];?></td> | ||||||
| 							<td style="text-align: right;"> | 							<td style="text-align: right;"> | ||||||
| 								<div class="btn-group"> | 								<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="/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($row['username']);?>" class="btn btn-xs btn-danger"><span class="glyphicon glyphicon-trash"></span> <?=$lang['mailbox']['remove'];?></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> | 								</div> | ||||||
| 							</td> | 							</td> | ||||||
| 						</tr> | 						</tr> | ||||||
| 						<?php | 						<?php | ||||||
| 						endwhile; |                 } | ||||||
| 	          else: |               } | ||||||
| 						?> |               else { | ||||||
| 						  <tr id="no-data"><td colspan="8" style="text-align: center; font-style: italic;"><?=$lang['mailbox']['no_record'];?></td></tr> |                   ?> | ||||||
| 						<?php |                   <tr id="no-data"><td colspan="8" style="text-align: center; font-style: italic;"><?=sprintf($lang['mailbox']['no_record'], $domain);?></td></tr> | ||||||
| 						endif; |                   <?php | ||||||
|  |               } | ||||||
|  |             } | ||||||
| 						?> | 						?> | ||||||
| 					</tbody> | 					</tbody> | ||||||
| 					<tfoot> | 					<tfoot> | ||||||
| @@ -377,6 +198,145 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI']; | |||||||
| 			</div> | 			</div> | ||||||
| 		</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 | ||||||
|  |             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="8" style="text-align: center; font-style: italic;"><?=sprintf($lang['mailbox']['no_record'], $domain);?></td></tr> | ||||||
|  |                   <?php | ||||||
|  |               } | ||||||
|  |             } | ||||||
|  | 						?> | ||||||
|  | 					</tbody> | ||||||
|  | 					<tfoot> | ||||||
|  | 						<tr id="no-data"> | ||||||
|  | 							<td colspan="8" 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"> | ||||||
|  | 				<div class="panel-heading"> | ||||||
|  | 					<h3 class="panel-title"><?=$lang['mailbox']['domain_aliases'];?> <span class="badge" id="numRowsDomainAlias"></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?aliasdomain"><span class="glyphicon glyphicon-plus"></span></a> | ||||||
|  | 					</div> | ||||||
|  | 				</div> | ||||||
|  | 				<div class="panel-body"> | ||||||
|  | 					<input type="text" class="form-control" id="domainaliastable-filter" data-action="filter" data-filters="#domainaliastable" placeholder="Filter" /> | ||||||
|  | 				</div> | ||||||
|  | 				<div class="table-responsive"> | ||||||
|  | 				<table class="table table-striped sortable-theme-bootstrap" data-sortable id="domainaliastable"> | ||||||
|  | 					<thead> | ||||||
|  | 						<tr> | ||||||
|  | 							<th class="sort-table" style="min-width: 67px;"><?=$lang['mailbox']['alias'];?></th> | ||||||
|  | 							<th class="sort-table" style="min-width: 127px;"><?=$lang['mailbox']['target_domain'];?></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 | ||||||
|  |           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="8" style="text-align: center; font-style: italic;"><?=sprintf($lang['mailbox']['no_record'], $domain);?></td></tr> | ||||||
|  | 	        <?php | ||||||
|  |             } | ||||||
|  |           } | ||||||
|  |           ?> | ||||||
|  | 					</tbody> | ||||||
|  | 					<tfoot> | ||||||
|  | 						<tr id="no-data"> | ||||||
|  | 							<td colspan="8" style="text-align: center; border-top: 1px solid #e7e7e7;"> | ||||||
|  | 								<a href="/add.php?aliasdomain"><?=$lang['mailbox']['add_domain_alias'];?></a> | ||||||
|  | 							</td> | ||||||
|  | 						</tr> | ||||||
|  | 					</tfoot> | ||||||
|  | 				</table> | ||||||
|  | 				</div> | ||||||
|  | 			</div> | ||||||
|  | 		</div> | ||||||
|  | 	</div> | ||||||
|  |  | ||||||
| 	<div class="row"> | 	<div class="row"> | ||||||
| 		<div class="col-md-12"> | 		<div class="col-md-12"> | ||||||
| 			<div class="panel panel-default"> | 			<div class="panel panel-default"> | ||||||
| @@ -405,76 +365,42 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI']; | |||||||
| 					</thead> | 					</thead> | ||||||
| 					<tbody> | 					<tbody> | ||||||
| 					<?php | 					<?php | ||||||
| 					try { |           foreach (array_merge(mailbox_get_domains(), mailbox_get_alias_domains()) as $domain) { | ||||||
| 						$stmt = $pdo->prepare("SELECT |             $aliases = mailbox_get_aliases($domain); | ||||||
| 								`address`, |             if (!empty($aliases)) { | ||||||
| 								`goto`, |               foreach ($aliases as $alias) { | ||||||
| 								`domain`, |                 $aliasdata = mailbox_get_alias_details($alias); | ||||||
| 								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)): |  | ||||||
| 					?> | 					?> | ||||||
| 						<tr id="data"> | 						<tr id="data"> | ||||||
| 							<td> | 							<td> | ||||||
| 							<?php | 							<?= ($aliasdata['is_catch_all'] == "1") ? '<span class="glyphicon glyphicon-pushpin" aria-hidden="true"></span> Catch-all ' . htmlspecialchars($aliasdata['address']) : htmlspecialchars($aliasdata['address']); ?> | ||||||
| 							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; |  | ||||||
| 							?> |  | ||||||
| 							</td> | 							</td> | ||||||
| 							<td> | 							<td> | ||||||
| 							<?php | 							<?php | ||||||
| 							foreach(explode(",", $row['goto']) as $goto) { | 							foreach(explode(",", $aliasdata['goto']) as $goto) { | ||||||
| 								echo nl2br(htmlspecialchars($goto.PHP_EOL)); | 								echo nl2br(htmlspecialchars($goto.PHP_EOL)); | ||||||
| 							} | 							} | ||||||
| 							?> | 							?> | ||||||
| 							</td> | 							</td> | ||||||
| 							<td><?=htmlspecialchars($row['domain']);?></td> | 							<td><?=htmlspecialchars($aliasdata['domain']);?></td> | ||||||
| 							<td><?=$row['active'];?></td> | 							<td><?=$aliasdata['active'];?></td> | ||||||
| 							<td style="text-align: right;"> | 							<td style="text-align: right;"> | ||||||
| 								<div class="btn-group"> | 								<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="/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($row['address']);?>" class="btn btn-xs btn-danger"><span class="glyphicon glyphicon-trash"></span> <?=$lang['mailbox']['remove'];?></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> | 								</div> | ||||||
| 							</td> | 							</td> | ||||||
| 						</tr> | 						</tr> | ||||||
| 					<?php | 						<?php | ||||||
| 					endwhile; |                 } | ||||||
| 	        else: |               } | ||||||
| 					?> |               else { | ||||||
| 					  <tr id="no-data"><td colspan="5" style="text-align: center; font-style: italic;"><?=$lang['mailbox']['no_record'];?></td></tr> |                   ?> | ||||||
| 					<?php |                   <tr id="no-data"><td colspan="8" style="text-align: center; font-style: italic;"><?=sprintf($lang['mailbox']['no_record'], $domain);?></td></tr> | ||||||
| 					endif;	 |                   <?php | ||||||
| 					?> |               } | ||||||
|  |             } | ||||||
|  | 						?> | ||||||
| 					</tbody> | 					</tbody> | ||||||
| 					<tfoot> | 					<tfoot> | ||||||
| 						<tr id="no-data"> | 						<tr id="no-data"> | ||||||
|   | |||||||
							
								
								
									
										157
									
								
								data/web/u2f_api.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										157
									
								
								data/web/u2f_api.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,157 @@ | |||||||
|  | <?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); | ||||||
|  |  | ||||||
|  | $scheme = isset($_SERVER['HTTPS']) ? "https://" : "http://"; | ||||||
|  | $u2f = new u2flib_server\U2F($scheme . $_SERVER['HTTP_HOST']); | ||||||
|  |  | ||||||
|  | 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,108 @@ | |||||||
| <?php | <?php | ||||||
| require_once("inc/prerequisites.inc.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']; | ||||||
|  | 	$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-md-9 col-xs-7"> | ||||||
|  |       <p><?=get_tfa()['pretty'];?></p> | ||||||
|  |       </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="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"); | 	require_once("inc/header.inc.php"); | ||||||
| 	$_SESSION['return_to'] = $_SERVER['REQUEST_URI']; | 	$_SESSION['return_to'] = $_SERVER['REQUEST_URI']; | ||||||
| 	$username = $_SESSION['mailcow_cc_username']; | 	$username = $_SESSION['mailcow_cc_username']; | ||||||
| 	$get_tls_policy = get_tls_policy($_SESSION['mailcow_cc_username']); | 	$get_tls_policy = get_tls_policy($_SESSION['mailcow_cc_username']); | ||||||
| ?> | ?> | ||||||
| <div class="container"> | <div class="container"> | ||||||
| <h3><?=$lang['user']['mailbox_settings'];?></h3> | <h3><?=$lang['user']['user_settings'];?></h3> | ||||||
| <p class="help-block"><?=$lang['user']['did_you_know'];?></p> |  | ||||||
|  |  | ||||||
| <div class="panel panel-default"> | <div class="panel panel-default"> | ||||||
| <div class="panel-heading"><?=$lang['user']['mailbox_details'];?></div> | <div class="panel-heading"><?=$lang['user']['mailbox_details'];?></div> | ||||||
| <div class="panel-body"> | <div class="panel-body"> | ||||||
|   <form class="form-horizontal" role="form" method="post" autocomplete="off"> |   <div class="row"> | ||||||
|     <div class="form-group"> |     <div class="col-sm-offset-3 col-sm-9"> | ||||||
|       <div class="col-sm-offset-3 col-sm-10"> |       <p><a href="#pwChangeModal" data-toggle="modal">[<?=$lang['user']['change_password'];?>]</a></p> | ||||||
|         <div class="checkbox"> |  | ||||||
|           <label><input type="checkbox" name="togglePwNew" id="togglePwNew"> <?=$lang['user']['change_password'];?></label> |  | ||||||
|         </div> |  | ||||||
|       </div> |  | ||||||
|     </div> |     </div> | ||||||
|     <div class="passFields"> |   </div> | ||||||
|       <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> |  | ||||||
|   <hr> |   <hr> | ||||||
|   <?php // Get user information about aliases |   <?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="row"> | ||||||
|     <div class="col-md-3 col-xs-5 text-right"><?=$lang['user']['aliases'];?>:</div> |     <div class="col-md-3 col-xs-5 text-right"><?=$lang['user']['aliases'];?>:</div> | ||||||
|     <div class="col-md-9 col-xs-7"> |     <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> |   </div> | ||||||
|   <div class="row"> |   <div class="row"> | ||||||
|     <div class="col-md-3 col-xs-5 text-right"><?=$lang['user']['aliases_also_send_as'];?>:</div> |     <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"> |     <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> |   </div> | ||||||
|   <div class="row"> |   <div class="row"> | ||||||
|     <div class="col-md-3 col-xs-5 text-right"><?=$lang['user']['aliases_send_as_all'];?>:</div> |     <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"> |     <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> |   </div> | ||||||
|   <div class="row"> |   <div class="row"> | ||||||
|     <div class="col-md-3 col-xs-5 text-right"><?=$lang['user']['is_catch_all'];?>:</div> |     <div class="col-md-3 col-xs-5 text-right"><?=$lang['user']['is_catch_all'];?>:</div> | ||||||
|     <div class="col-md-9 col-xs-7"> |     <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> | ||||||
|   </div> |   </div> | ||||||
|   <hr> |   <hr> | ||||||
|   <?php // Show tagging options ?> |   <?php // Show tagging options ?> | ||||||
|   <form class="form-horizontal" role="form" method="post"> |   <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="row"> | ||||||
|     <div class="col-md-3 col-xs-5 text-right"><?=$lang['user']['tag_handling'];?>:</div> |     <div class="col-md-3 col-xs-5 text-right"><?=$lang['user']['tag_handling'];?>:</div> | ||||||
|     <div class="col-md-9 col-xs-7"> |     <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()"> |     <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="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> |       <option value="subject" <?=($get_tagging_options == "1") ? 'selected' : null; ?>><?=$lang['user']['tag_in_subject'];?></option> | ||||||
| @@ -94,6 +112,16 @@ if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == 'user | |||||||
|     </div> |     </div> | ||||||
|   </div> |   </div> | ||||||
|   </form> |   </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> | ||||||
| </div> | </div> | ||||||
|  |  | ||||||
| @@ -102,56 +130,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" 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="#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="#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> | </ul> | ||||||
| <hr> | <hr> | ||||||
|  |  | ||||||
| <div class="tab-content"> | <div class="tab-content"> | ||||||
| 	<div role="tabpanel" class="tab-pane active" id="SpamAliases"> | 	<div role="tabpanel" class="tab-pane active" id="SpamAliases"> | ||||||
| 		<form class="form-horizontal" role="form" method="post"> | 		<div class="row"> | ||||||
| 		<div class="table-responsive"> | 			<div class="col-xs-6"> | ||||||
| 		<table class="table table-striped sortable-theme-bootstrap" data-sortable id="timelimitedaliases"> | 				<p><b><?=$lang['user']['alias'];?></b></p> | ||||||
| 			<thead> | 			</div> | ||||||
| 			<tr> | 			<div class="col-xs-2"> | ||||||
| 				<th class="sort-table" style="min-width: 96px;"><?=$lang['user']['alias'];?></th> | 				<p><b><?=$lang['user']['alias_valid_until'];?></b></p> | ||||||
| 				<th class="sort-table" style="min-width: 135px;"><?=$lang['user']['alias_valid_until'];?></th> | 			</div> | ||||||
| 			</tr> | 			<div class="col-xs-2"> | ||||||
| 			</thead> |         <p><b><?=$lang['user']['action'];?></b></p> | ||||||
| 			<tbody> | 			</div> | ||||||
|  |     </div> | ||||||
| 			<?php | 			<?php | ||||||
| 			try { |       $get_time_limited_aliases = get_time_limited_aliases($username); | ||||||
| 				$stmt = $pdo->prepare("SELECT `address`, |       if (!empty($get_time_limited_aliases)): | ||||||
| 					`goto`, |         foreach ($get_time_limited_aliases as $row): | ||||||
| 					`validity` |         ?> | ||||||
| 						FROM `spamalias` | 		<div class="row"> | ||||||
| 							WHERE `goto` = :username |       <div class="col-xs-6"> | ||||||
| 								AND `validity` >= :unixnow"); |         <p><?=htmlspecialchars($row['address']);?></p> | ||||||
| 				$stmt->execute(array(':username' => $username, ':unixnow' => time())); |       </div> | ||||||
| 				$rows = $stmt->fetchAll(PDO::FETCH_ASSOC); |       <div class="col-xs-2"> | ||||||
| 			} |         <p><?=htmlspecialchars(date($lang['user']['alias_full_date'], $row['validity']));?></p> | ||||||
| 			catch(PDOException $e) { |       </div> | ||||||
| 				$_SESSION['return'] = array( |       <div class="col-xs-1"> | ||||||
| 					'type' => 'danger', |         <form class="form-inline" role="form" method="post"> | ||||||
| 					'msg' => 'MySQL: '.$e |           <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']);?>"> | ||||||
| 			if(!empty($rows)): |         </form> | ||||||
| 			while ($row = array_shift($rows)): |       </div> | ||||||
| 			?> |       <div class="col-xs-1"> | ||||||
| 				<tr id="data"> |         <form class="form-inline" role="form" method="post"> | ||||||
| 				<td><?=htmlspecialchars($row['address']);?></td> |           <a href="#" onclick="$(this).closest('form').submit()"><span class="glyphicon glyphicon-time"></span> + 1h</a> | ||||||
| 				<td><?=htmlspecialchars(date($lang['user']['alias_full_date'], $row['validity']));?></td> |           <input type="hidden" name="set_time_limited_aliases" value="extend"> | ||||||
| 				</tr> |           <input type="hidden" name="item" value="<?=htmlspecialchars($row['address']);?>"> | ||||||
| 			<?php |         </form> | ||||||
| 			endwhile; |       </div> | ||||||
|  |     </div> | ||||||
|  |         <?php | ||||||
|  |         endforeach; | ||||||
| 			else: | 			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 | 			<?php | ||||||
| 			endif;	 | 			endif;	 | ||||||
| 			?> | 			?> | ||||||
| 			</tbody> |     <form class="form-horizontal" role="form" method="post"> | ||||||
| 		</table> |  | ||||||
| 		</div> |  | ||||||
| 		<div class="form-group"> | 		<div class="form-group"> | ||||||
| 			<div class="col-sm-9"> | 			<div class="col-sm-9"> | ||||||
| 				<select id="validity" name="validity" title="<?=$lang['user']['alias_select_validity'];?>"> | 				<select id="validity" name="validity" title="<?=$lang['user']['alias_select_validity'];?>"> | ||||||
| @@ -161,16 +194,16 @@ if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == 'user | |||||||
| 					<option value="168">1 <?=$lang['user']['week'];?></option> | 					<option value="168">1 <?=$lang['user']['week'];?></option> | ||||||
| 					<option value="672">4 <?=$lang['user']['weeks'];?></option> | 					<option value="672">4 <?=$lang['user']['weeks'];?></option> | ||||||
| 				</select> | 				</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> | 		</div> | ||||||
| 		<div class="form-group"> | 		<div class="form-group"> | ||||||
| 			<div class="col-sm-12"> | 			<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'];?> | 					<span class="glyphicon glyphicon-remove" aria-hidden="true"></span> <?=$lang['user']['alias_remove_all'];?> | ||||||
| 				</button> | 				</button> | ||||||
| 				<button style="border-color:#f5f5f5;background:none;color:grey" type="submit" name="trigger_set_time_limited_aliases" value="extend" class="btn btn-sm"> | 				<button type="submit" name="set_time_limited_aliases" value="extendall" class="btn-default btn btn-sm"> | ||||||
| 					<span class="glyphicon glyphicon-hourglass" aria-hidden="true"></span> <?=$lang['user']['alias_extend_all'];?> | 					<span class="glyphicon glyphicon-time" aria-hidden="true"></span> <?=$lang['user']['alias_extend_all'];?> | ||||||
| 				</button> | 				</button> | ||||||
| 			</div> | 			</div> | ||||||
| 		</div> | 		</div> | ||||||
| @@ -189,7 +222,7 @@ if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == 'user | |||||||
| 						data-slider-range="true" | 						data-slider-range="true" | ||||||
| 						data-slider-tooltip='always' | 						data-slider-tooltip='always' | ||||||
| 						data-slider-id="slider1" | 						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" /> | 						data-slider-step="1" /> | ||||||
| 					<br /><br /> | 					<br /><br /> | ||||||
| 					<ul> | 					<ul> | ||||||
| @@ -203,7 +236,7 @@ if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == 'user | |||||||
| 			</div> | 			</div> | ||||||
| 			<div class="form-group"> | 			<div class="form-group"> | ||||||
| 				<div class="col-sm-offset-2 col-sm-10"> | 				<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> | ||||||
| 			</div> | 			</div> | ||||||
| 		</form> | 		</form> | ||||||
| @@ -217,49 +250,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 class="col-sm-6"><b><?=$lang['user']['spamfilter_table_action'];?></b></div> | ||||||
| 				</div> | 				</div> | ||||||
| 				<?php | 				<?php | ||||||
| 				try { |         $get_policy_list = get_policy_list($username); | ||||||
| 					$stmt = $pdo->prepare("SELECT `value`, `prefid` FROM `filterconf` WHERE `option`='whitelist_from' AND `object`= :username"); | 				if (empty($get_policy_list['whitelist'])): | ||||||
| 					$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): |  | ||||||
| 				?> | 				?> | ||||||
| 					<div class="row"> | 					<div class="row"> | ||||||
| 						<div class="col-sm-12"><i><?=$lang['user']['spamfilter_table_empty'];?></i></div> | 						<div class="col-sm-12"><i><?=$lang['user']['spamfilter_table_empty'];?></i></div> | ||||||
| 					</div> | 					</div> | ||||||
| 				<?php | 				<?php | ||||||
| 				endif; | 				else: | ||||||
| 				while ($whitelistRow = array_shift($rows)): |           foreach($get_policy_list['whitelist'] as $wl): | ||||||
| 				?> |           ?> | ||||||
| 				<div class="row striped"> |           <div class="row striped"> | ||||||
| 					<form class="form-inline" method="post"> |             <form class="form-inline" method="post"> | ||||||
| 					<div class="col-xs-6"><code><?=$whitelistRow['value'];?></code></div> |             <div class="col-xs-6"><code><?=$wl['value'];?></code></div> | ||||||
| 					<div class="col-xs-6"> |             <div class="col-xs-6"> | ||||||
| 						<input type="hidden" name="prefid" value="<?=$whitelistRow['prefid'];?>"> |               <input type="hidden" name="delete_prefid" value="<?=$wl['prefid'];?>"> | ||||||
| 						<?php |               <?php | ||||||
| 						if ($whitelistRow['username'] != array_pop(explode('@', $username))): |               if (filter_var($wl['object'], FILTER_VALIDATE_EMAIL)): | ||||||
| 						?> |               ?> | ||||||
| 							<input type="hidden" name="trigger_set_policy_list"> |                 <input type="hidden" name="delete_policy_list_item"> | ||||||
| 							<a href="#n" onclick="$(this).closest('form').submit()"><?=$lang['user']['spamfilter_table_remove'];?></a> |                 <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 |               <?php | ||||||
| 						else: |               else: | ||||||
| 						?> |               ?> | ||||||
| 							<span style="cursor:not-allowed"><?=$lang['user']['spamfilter_table_domain_policy'];?></span> |                 <span style="cursor:not-allowed"><?=$lang['user']['spamfilter_table_domain_policy'];?></span> | ||||||
| 						<?php |               <?php | ||||||
| 						endif; |               endif; | ||||||
| 						?> |               ?> | ||||||
| 					</div> |             </div> | ||||||
| 					</form> |             </form> | ||||||
| 				</div> |           </div> | ||||||
|  |           <?php | ||||||
| 				<?php |           endforeach; | ||||||
| 				endwhile; |         endif; | ||||||
| 				?> | 				?> | ||||||
| 				<hr style="margin:5px 0px 7px 0px"> | 				<hr style="margin:5px 0px 7px 0px"> | ||||||
| 				<div class="row"> | 				<div class="row"> | ||||||
| @@ -269,7 +292,7 @@ if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == 'user | |||||||
| 						<input type="hidden" name="object_list" value="wl"> | 						<input type="hidden" name="object_list" value="wl"> | ||||||
| 					</div> | 					</div> | ||||||
| 					<div class="col-xs-6"> | 					<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> | 					</div> | ||||||
| 					</form> | 					</form> | ||||||
| 				</div> | 				</div> | ||||||
| @@ -282,48 +305,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 class="col-sm-6"><b><?=$lang['user']['spamfilter_table_action'];?></b></div> | ||||||
| 				</div> | 				</div> | ||||||
| 				<?php | 				<?php | ||||||
| 				try { | 				if (empty($get_policy_list['blacklist'])): | ||||||
| 					$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): |  | ||||||
| 				?> | 				?> | ||||||
| 					<div class="row"> | 					<div class="row"> | ||||||
| 						<div class="col-sm-12"><i><?=$lang['user']['spamfilter_table_empty'];?></i></div> | 						<div class="col-sm-12"><i><?=$lang['user']['spamfilter_table_empty'];?></i></div> | ||||||
| 					</div> | 					</div> | ||||||
| 				<?php | 				<?php | ||||||
| 				endif; | 				else: | ||||||
| 				while ($blacklistRow = array_shift($rows)): |           foreach($get_policy_list['blacklist'] as $bl): | ||||||
| 				?> |           ?> | ||||||
| 				<div class="row striped"> |           <div class="row striped"> | ||||||
| 					<form class="form-inline" method="post"> |             <form class="form-inline" method="post"> | ||||||
| 					<div class="col-xs-6"><code><?=$blacklistRow['value'];?></code></div> |             <div class="col-xs-6"><code><?=$bl['value'];?></code></div> | ||||||
| 					<div class="col-xs-6"> |             <div class="col-xs-6"> | ||||||
| 						<input type="hidden" name="prefid" value="<?=$blacklistRow['prefid'];?>"> |               <?php | ||||||
| 						<?php |               if (filter_var($bl['object'], FILTER_VALIDATE_EMAIL)): | ||||||
| 						if ($blacklistRow['username'] != array_pop(explode('@', $username))): |               ?> | ||||||
| 						?> |                 <input type="hidden" name="delete_prefid" value="<?=$bl['prefid'];?>"> | ||||||
| 							<input type="hidden" name="trigger_set_policy_list"> |                 <input type="hidden" name="delete_policy_list_item"> | ||||||
| 							<a href="#n" onclick="$(this).closest('form').submit()"><?=$lang['user']['spamfilter_table_remove'];?></a> |                 <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 |               <?php | ||||||
| 						else: |               else: | ||||||
| 						?> |               ?> | ||||||
| 							<span style="cursor:not-allowed"><?=$lang['user']['spamfilter_table_domain_policy'];?></span> |                 <span style="cursor:not-allowed"><?=$lang['user']['spamfilter_table_domain_policy'];?></span> | ||||||
| 						<?php |               <?php | ||||||
| 						endif; |               endif; | ||||||
| 						?> |               ?> | ||||||
| 					</div> |             </div> | ||||||
| 					</form> |             </form> | ||||||
| 				</div> |           </div> | ||||||
| 				<?php |           <?php | ||||||
| 				endwhile; |           endforeach; | ||||||
|  |         endif; | ||||||
| 				?> | 				?> | ||||||
| 				<hr style="margin:5px 0px 7px 0px"> | 				<hr style="margin:5px 0px 7px 0px"> | ||||||
| 				<div class="row"> | 				<div class="row"> | ||||||
| @@ -333,7 +346,7 @@ if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == 'user | |||||||
| 						<input type="hidden" name="object_list" value="bl"> | 						<input type="hidden" name="object_list" value="bl"> | ||||||
| 					</div> | 					</div> | ||||||
| 					<div class="col-xs-6"> | 					<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> | 					</div> | ||||||
| 					</form> | 					</form> | ||||||
| 				</div> | 				</div> | ||||||
| @@ -359,19 +372,144 @@ if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == 'user | |||||||
| 			</div> | 			</div> | ||||||
| 			<div class="form-group"> | 			<div class="form-group"> | ||||||
| 				<div class="col-sm-12"> | 				<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> | ||||||
| 			</div> | 			</div> | ||||||
| 		</form> | 		</form> | ||||||
| 	</div> | 	</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: 35px;"><?=$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> | </div> | ||||||
| <br /> |  | ||||||
| </div> <!-- /container --> | </div> <!-- /container --> | ||||||
| <script src="js/sorttable.js"></script> | <script src="js/sorttable.js"></script> | ||||||
| <script src="js/user.js"></script> | <script src="js/user.js"></script> | ||||||
| <?php | <?php | ||||||
| require_once("inc/footer.inc.php"); | require_once("inc/footer.inc.php"); | ||||||
| } else { | } | ||||||
|  | else { | ||||||
| 	header('Location: /'); | 	header('Location: /'); | ||||||
| 	exit(); | 	exit(); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -135,9 +135,8 @@ services: | |||||||
|       volumes: |       volumes: | ||||||
|         - ./data/conf/dovecot:/etc/dovecot |         - ./data/conf/dovecot:/etc/dovecot | ||||||
|         - ./data/assets/ssl:/etc/ssl/mail/:ro |         - ./data/assets/ssl:/etc/ssl/mail/:ro | ||||||
|  |         - ./data/conf/sogo/:/etc/sogo/ | ||||||
|         - vmail-vol-1:/var/vmail |         - vmail-vol-1:/var/vmail | ||||||
|       volumes_from: |  | ||||||
|         - sogo-mailcow |  | ||||||
|       environment: |       environment: | ||||||
|         - DBNAME=${DBNAME} |         - DBNAME=${DBNAME} | ||||||
|         - DBUSER=${DBUSER} |         - DBUSER=${DBUSER} | ||||||
| @@ -203,18 +202,19 @@ services: | |||||||
|         - php-fpm-mailcow |         - php-fpm-mailcow | ||||||
|         - rspamd-mailcow |         - rspamd-mailcow | ||||||
|       image: nginx:mainline |       image: nginx:mainline | ||||||
|       volumes_from: |       command: /bin/bash -c "envsubst < /etc/nginx/conf.d/listen.template > /etc/nginx/conf.d/listen.active && nginx -g 'daemon off;'" | ||||||
|         - sogo-mailcow |       environment: | ||||||
|  |         - HTTPS_PORT=${HTTPS_PORT} | ||||||
|       volumes: |       volumes: | ||||||
|         - ./data/web:/web:ro |         - ./data/web:/web:ro | ||||||
|         - ./data/conf/rspamd/dynmaps:/dynmaps:ro |         - ./data/conf/rspamd/dynmaps:/dynmaps:ro | ||||||
|         - ./data/assets/ssl/:/etc/ssl/mail/: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: |       dns: | ||||||
|         - 172.22.1.254 |         - 172.22.1.254 | ||||||
|       dns_search: mailcow-network |       dns_search: mailcow-network | ||||||
|       ports: |       ports: | ||||||
|         - "443:443" |         - "${HTTPS_PORT}:${HTTPS_PORT}" | ||||||
|       restart: always |       restart: always | ||||||
|       networks: |       networks: | ||||||
|         mailcow-network: |         mailcow-network: | ||||||
| @@ -228,7 +228,6 @@ networks: | |||||||
|       driver: default |       driver: default | ||||||
|       config: |       config: | ||||||
|         - subnet: 172.22.1.0/24 |         - subnet: 172.22.1.0/24 | ||||||
|           gateway: 172.22.1.1 |  | ||||||
|  |  | ||||||
| volumes: | volumes: | ||||||
|   vmail-vol-1: |   vmail-vol-1: | ||||||
|   | |||||||
| @@ -44,6 +44,7 @@ DBROOT=$(</dev/urandom tr -dc A-Za-z0-9 | head -c 28) | |||||||
| # ------------------------------ | # ------------------------------ | ||||||
| # You should leave that alone | # You should leave that alone | ||||||
| # Can also be 11.22.33.44:25 or 0.0.0.0:465 etc. for specific bindings | # Can also be 11.22.33.44:25 or 0.0.0.0:465 etc. for specific bindings | ||||||
|  | HTTPS_PORT=443 | ||||||
| SMTP_PORT=25 | SMTP_PORT=25 | ||||||
| SMTPS_PORT=465 | SMTPS_PORT=465 | ||||||
| SUBMISSION_PORT=587 | SUBMISSION_PORT=587 | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user