NobGit
public nobgit read

NobMail

Based on mailcow: dockerized

Languages

Repository composition by tracked source files.

PHP
PHP 49% JavaScript 35% HTML 9% CSS 4% Shell 2% Python 1% Lua 0% Perl 0% Ruby 0% SCSS 0%
Create file Wiki Documentation
Clone
https://nobgit.com/orgs/nobgit/nobmail.git
ssh://[email protected]:2222/orgs/nobgit/nobmail.git

Commit

Docs

d891bc88
andryyy <[email protected]> 9 years ago
.env                                               |    1 +
 .gitignore                                         |    8 +
 README.md                                          |    5 +
 css/highlight.css                                  |  124 -
 css/theme.css                                      |   12 -
 css/theme_extra.css                                |  178 -
 data/Dockerfiles/dovecot/Dockerfile                |   87 +
 data/Dockerfiles/dovecot/docker-entrypoint.sh      |   28 +
 data/Dockerfiles/dovecot/imapsync                  | 9488 ++++++++++++++++++++
 data/Dockerfiles/dovecot/imapsync_cron.pl          |   72 +
 data/Dockerfiles/dovecot/postlogin.sh              |    4 +
 data/Dockerfiles/dovecot/rspamd-pipe               |    8 +
 data/Dockerfiles/dovecot/supervisord.conf          |   21 +
 .nojekyll => data/Dockerfiles/memcached/.empty     |    0
 data/Dockerfiles/mysql/.empty                      |    0
 data/Dockerfiles/nginx/.empty                      |    0
 data/Dockerfiles/pdns/Dockerfile                   |   26 +
 data/Dockerfiles/php-fpm/Dockerfile                |   18 +
 data/Dockerfiles/php-fpm/docker-entrypoint.sh      |    7 +
 data/Dockerfiles/postfix/Dockerfile                |   33 +
 data/Dockerfiles/postfix/postfix.sh                |   16 +
 data/Dockerfiles/postfix/supervisord.conf          |   17 +
 data/Dockerfiles/redis/.empty                      |    0
 data/Dockerfiles/rmilter/Dockerfile                |   26 +
 data/Dockerfiles/rmilter/supervisord.conf          |   18 +
 data/Dockerfiles/rspamd/Dockerfile                 |   27 +
 data/Dockerfiles/sogo/Dockerfile                   |   51 +
 data/Dockerfiles/sogo/reconf-domains.sh            |   98 +
 data/Dockerfiles/sogo/supervisord.conf             |   46 +
 data/assets/passwd/generate_passwords.sh           |    4 +
 data/assets/ssl/ca-key.pem                         |   27 +
 data/assets/ssl/ca.pem                             |   18 +
 data/assets/ssl/cert.pem                           |   19 +
 data/assets/ssl/dhparams.pem                       |    8 +
 data/assets/ssl/key.pem                            |   27 +
 data/conf/dovecot/dovecot.conf                     |  247 +
 data/conf/dovecot/sieve_after                      |   24 +
 data/conf/dovecot/sql/dovecot-dict-sql.conf        |   15 +
 data/conf/dovecot/sql/dovecot-mysql.conf           |    6 +
 data/conf/mysql/my.cnf                             |   14 +
 data/conf/nginx/dynmaps.conf                       |   18 +
 data/conf/nginx/listen.template                    |    1 +
 data/conf/nginx/site.conf                          |  136 +
 data/conf/pdns/pdns_custom.lua                     |    1 +
 data/conf/pdns/recursor.conf                       |   41 +
 data/conf/postfix/main.cf                          |   93 +
 data/conf/postfix/master.cf                        |   45 +
 data/conf/postfix/postscreen_access.cidr           |  654 ++
 data/conf/postfix/smtp_dsn_filter                  |    6 +
 .../conf/postfix/sql/mysql_relay_recipient_maps.cf |    5 +
 .../postfix/sql/mysql_tls_enforce_in_policy.cf     |    5 +
 .../postfix/sql/mysql_tls_enforce_out_policy.cf    |    5 +
 .../mysql_virtual_alias_domain_catchall_maps.cf    |    6 +
 .../sql/mysql_virtual_alias_domain_mailbox_maps.cf |    5 +
 .../postfix/sql/mysql_virtual_alias_domain_maps.cf |    5 +
 data/conf/postfix/sql/mysql_virtual_alias_maps.cf  |    5 +
 .../conf/postfix/sql/mysql_virtual_domains_maps.cf |    5 +
 .../conf/postfix/sql/mysql_virtual_mailbox_maps.cf |    5 +
 .../postfix/sql/mysql_virtual_relay_domain_maps.cf |    5 +
 data/conf/postfix/sql/mysql_virtual_sender_acl.cf  |    5 +
 .../postfix/sql/mysql_virtual_spamalias_maps.cf    |    5 +
 data/conf/rmilter/rmilter.conf                     |   42 +
 data/conf/rspamd/dynmaps/authoritative.php         |   22 +
 data/conf/rspamd/dynmaps/settings.php              |  224 +
 data/conf/rspamd/dynmaps/tags.php                  |   22 +
 data/conf/rspamd/dynmaps/vars.inc.php              |    1 +
 data/conf/rspamd/local.d/dkim.conf                 |   34 +
 data/conf/rspamd/local.d/metrics.conf              |   19 +
 data/conf/rspamd/local.d/options.inc               |    3 +
 data/conf/rspamd/local.d/redis.conf                |    1 +
 data/conf/rspamd/local.d/rspamd.conf.local         |    1 +
 data/conf/rspamd/local.d/statistic.conf            |   59 +
 data/conf/rspamd/lua/rspamd.local.lua              |   75 +
 data/conf/rspamd/override.d/logging.inc            |    3 +
 data/conf/rspamd/override.d/worker-controller.inc  |    7 +
 data/conf/rspamd/override.d/worker-normal.inc      |    1 +
 data/conf/sogo/sogo.conf                           |   76 +
 data/web/add.php                                   |  385 +
 data/web/admin.php                                 |  311 +
 data/web/autoconfig.php                            |   69 +
 data/web/autodiscover.php                          |  142 +
 data/web/call_sogo_ctrl.php                        |   40 +
 data/web/css/bootstrap-select.min.css              |    6 +
 data/web/css/bootstrap-slider.min.css              |   41 +
 data/web/css/bootstrap-switch.min.css              |   10 +
 data/web/css/mailbox.css                           |   19 +
 data/web/css/mailcow.css                           |   46 +
 data/web/css/tables.css                            |   79 +
 data/web/delete.php                                |  212 +
 data/web/edit.php                                  |  664 ++
 data/web/favicon.png                               |  Bin 0 -> 6856 bytes
 data/web/img/cow_mailcow.svg                       |  197 +
 data/web/img/yubi.ico                              |  Bin 0 -> 1150 bytes
 data/web/inc/footer.inc.php                        |  225 +
 data/web/inc/functions.inc.php                     | 4922 ++++++++++
 data/web/inc/header.inc.php                        |  104 +
 data/web/inc/init.sql                              |  281 +
 data/web/inc/languages.min.css                     |    1 +
 data/web/inc/languages.png                         |  Bin 0 -> 45164 bytes
 data/web/inc/lib/U2F.php                           |  506 ++
 data/web/inc/lib/Yubico.php                        |  475 +
 data/web/inc/prerequisites.inc.php                 |  103 +
 data/web/inc/tfa_modals.php                        |  133 +
 data/web/inc/triggers.inc.php                      |  173 +
 data/web/inc/vars.inc.php                          |   38 +
 data/web/index.php                                 |   90 +
 data/web/js/add.js                                 |   18 +
 data/web/js/admin.js                               |   31 +
 data/web/js/bootstrap-select.min.js                |    9 +
 data/web/js/bootstrap-slider.min.js                |    5 +
 data/web/js/bootstrap-switch.min.js                |   10 +
 data/web/js/index.js                               |    3 +
 data/web/js/mailbox.js                             |   54 +
 data/web/js/sorttable.js                           |  236 +
 data/web/js/u2f-api.js                             |  651 ++
 data/web/js/user.js                                |   33 +
 data/web/json_api.php                              |   57 +
 data/web/lang/lang.de.php                          |  448 +
 data/web/lang/lang.en.php                          |  458 +
 data/web/lang/lang.es.php                          |  378 +
 data/web/lang/lang.nl.php                          |  369 +
 data/web/lang/lang.pt.php                          |  355 +
 data/web/mailbox.php                               |  426 +
 data/web/robots.txt                                |    2 +
 data/web/u2f_api.php                               |  157 +
 data/web/user.php                                  |  529 ++
 docker-compose.yml                                 |  241 +
 docs/first_steps.md                                |   76 +
 docs/index.md                                      |   42 +
 docs/install.md                                    |   61 +
 docs/u_and_e.md                                    |  390 +
 first_steps/index.html                             |  255 -
 fonts/fontawesome-webfont.eot                      |  Bin 37405 -> 0 bytes
 fonts/fontawesome-webfont.svg                      |  399 -
 fonts/fontawesome-webfont.ttf                      |  Bin 79076 -> 0 bytes
 fonts/fontawesome-webfont.woff                     |  Bin 43572 -> 0 bytes
 generate_config.sh                                 |   59 +
 img/favicon.ico                                    |  Bin 1150 -> 0 bytes
 index.html                                         |  207 -
 install/index.html                                 |  227 -
 js/highlight.pack.js                               |    2 -
 js/jquery-2.1.1.min.js                             |    4 -
 js/modernizr-2.8.3.min.js                          |    1 -
 js/theme.js                                        |   55 -
 mkdocs.yml                                         |    9 +
 mkdocs/js/lunr.min.js                              |    7 -
 mkdocs/js/mustache.min.js                          |    1 -
 mkdocs/js/require.js                               |   36 -
 mkdocs/js/search-results-template.mustache         |    4 -
 mkdocs/js/search.js                                |   88 -
 mkdocs/js/text.js                                  |  390 -
 mkdocs/search_index.json                           |  199 -
 search.html                                        |  150 -
 sitemap.xml                                        |   36 -
 u_and_e/index.html                                 |  541 --
 155 files changed, 26539 insertions(+), 2916 deletions(-)
 create mode 120000 .env
 create mode 100644 .gitignore
 create mode 100644 README.md
 delete mode 100644 css/highlight.css
 delete mode 100644 css/theme.css
 delete mode 100644 css/theme_extra.css
 create mode 100644 data/Dockerfiles/dovecot/Dockerfile
 create mode 100755 data/Dockerfiles/dovecot/docker-entrypoint.sh
 create mode 100755 data/Dockerfiles/dovecot/imapsync
 create mode 100755 data/Dockerfiles/dovecot/imapsync_cron.pl
 create mode 100755 data/Dockerfiles/dovecot/postlogin.sh
 create mode 100755 data/Dockerfiles/dovecot/rspamd-pipe
 create mode 100644 data/Dockerfiles/dovecot/supervisord.conf
 rename .nojekyll => data/Dockerfiles/memcached/.empty (100%)
 create mode 100644 data/Dockerfiles/mysql/.empty
 create mode 100644 data/Dockerfiles/nginx/.empty
 create mode 100644 data/Dockerfiles/pdns/Dockerfile
 create mode 100644 data/Dockerfiles/php-fpm/Dockerfile
 create mode 100755 data/Dockerfiles/php-fpm/docker-entrypoint.sh
 create mode 100644 data/Dockerfiles/postfix/Dockerfile
 create mode 100755 data/Dockerfiles/postfix/postfix.sh
 create mode 100644 data/Dockerfiles/postfix/supervisord.conf
 create mode 100644 data/Dockerfiles/redis/.empty
 create mode 100644 data/Dockerfiles/rmilter/Dockerfile
 create mode 100644 data/Dockerfiles/rmilter/supervisord.conf
 create mode 100644 data/Dockerfiles/rspamd/Dockerfile
 create mode 100644 data/Dockerfiles/sogo/Dockerfile
 create mode 100755 data/Dockerfiles/sogo/reconf-domains.sh
 create mode 100644 data/Dockerfiles/sogo/supervisord.conf
 create mode 100755 data/assets/passwd/generate_passwords.sh
 create mode 100644 data/assets/ssl/ca-key.pem
 create mode 100644 data/assets/ssl/ca.pem
 create mode 100644 data/assets/ssl/cert.pem
 create mode 100644 data/assets/ssl/dhparams.pem
 create mode 100644 data/assets/ssl/key.pem
 create mode 100644 data/conf/dovecot/dovecot.conf
 create mode 100644 data/conf/dovecot/sieve_after
 create mode 100644 data/conf/dovecot/sql/dovecot-dict-sql.conf
 create mode 100644 data/conf/dovecot/sql/dovecot-mysql.conf
 create mode 100644 data/conf/mysql/my.cnf
 create mode 100644 data/conf/nginx/dynmaps.conf
 create mode 100644 data/conf/nginx/listen.template
 create mode 100644 data/conf/nginx/site.conf
 create mode 100644 data/conf/pdns/pdns_custom.lua
 create mode 100644 data/conf/pdns/recursor.conf
 create mode 100644 data/conf/postfix/main.cf
 create mode 100644 data/conf/postfix/master.cf
 create mode 100644 data/conf/postfix/postscreen_access.cidr
 create mode 100644 data/conf/postfix/smtp_dsn_filter
 create mode 100644 data/conf/postfix/sql/mysql_relay_recipient_maps.cf
 create mode 100644 data/conf/postfix/sql/mysql_tls_enforce_in_policy.cf
 create mode 100644 data/conf/postfix/sql/mysql_tls_enforce_out_policy.cf
 create mode 100644 data/conf/postfix/sql/mysql_virtual_alias_domain_catchall_maps.cf
 create mode 100644 data/conf/postfix/sql/mysql_virtual_alias_domain_mailbox_maps.cf
 create mode 100644 data/conf/postfix/sql/mysql_virtual_alias_domain_maps.cf
 create mode 100644 data/conf/postfix/sql/mysql_virtual_alias_maps.cf
 create mode 100644 data/conf/postfix/sql/mysql_virtual_domains_maps.cf
 create mode 100644 data/conf/postfix/sql/mysql_virtual_mailbox_maps.cf
 create mode 100644 data/conf/postfix/sql/mysql_virtual_relay_domain_maps.cf
 create mode 100644 data/conf/postfix/sql/mysql_virtual_sender_acl.cf
 create mode 100644 data/conf/postfix/sql/mysql_virtual_spamalias_maps.cf
 create mode 100644 data/conf/rmilter/rmilter.conf
 create mode 100644 data/conf/rspamd/dynmaps/authoritative.php
 create mode 100644 data/conf/rspamd/dynmaps/settings.php
 create mode 100644 data/conf/rspamd/dynmaps/tags.php
 create mode 120000 data/conf/rspamd/dynmaps/vars.inc.php
 create mode 100644 data/conf/rspamd/local.d/dkim.conf
 create mode 100644 data/conf/rspamd/local.d/metrics.conf
 create mode 100644 data/conf/rspamd/local.d/options.inc
 create mode 100644 data/conf/rspamd/local.d/redis.conf
 create mode 100644 data/conf/rspamd/local.d/rspamd.conf.local
 create mode 100644 data/conf/rspamd/local.d/statistic.conf
 create mode 100644 data/conf/rspamd/lua/rspamd.local.lua
 create mode 100644 data/conf/rspamd/override.d/logging.inc
 create mode 100644 data/conf/rspamd/override.d/worker-controller.inc
 create mode 100644 data/conf/rspamd/override.d/worker-normal.inc
 create mode 100644 data/conf/sogo/sogo.conf
 create mode 100644 data/web/add.php
 create mode 100644 data/web/admin.php
 create mode 100644 data/web/autoconfig.php
 create mode 100644 data/web/autodiscover.php
 create mode 100644 data/web/call_sogo_ctrl.php
 create mode 100644 data/web/css/bootstrap-select.min.css
 create mode 100644 data/web/css/bootstrap-slider.min.css
 create mode 100644 data/web/css/bootstrap-switch.min.css
 create mode 100644 data/web/css/mailbox.css
 create mode 100644 data/web/css/mailcow.css
 create mode 100644 data/web/css/tables.css
 create mode 100644 data/web/delete.php
 create mode 100644 data/web/edit.php
 create mode 100644 data/web/favicon.png
 create mode 100644 data/web/img/cow_mailcow.svg
 create mode 100644 data/web/img/yubi.ico
 create mode 100644 data/web/inc/footer.inc.php
 create mode 100644 data/web/inc/functions.inc.php
 create mode 100644 data/web/inc/header.inc.php
 create mode 100644 data/web/inc/init.sql
 create mode 100644 data/web/inc/languages.min.css
 create mode 100644 data/web/inc/languages.png
 create mode 100644 data/web/inc/lib/U2F.php
 create mode 100644 data/web/inc/lib/Yubico.php
 create mode 100644 data/web/inc/prerequisites.inc.php
 create mode 100644 data/web/inc/tfa_modals.php
 create mode 100644 data/web/inc/triggers.inc.php
 create mode 100644 data/web/inc/vars.inc.php
 create mode 100644 data/web/index.php
 create mode 100644 data/web/js/add.js
 create mode 100644 data/web/js/admin.js
 create mode 100644 data/web/js/bootstrap-select.min.js
 create mode 100644 data/web/js/bootstrap-slider.min.js
 create mode 100644 data/web/js/bootstrap-switch.min.js
 create mode 100644 data/web/js/index.js
 create mode 100644 data/web/js/mailbox.js
 create mode 100644 data/web/js/sorttable.js
 create mode 100644 data/web/js/u2f-api.js
 create mode 100644 data/web/js/user.js
 create mode 100644 data/web/json_api.php
 create mode 100644 data/web/lang/lang.de.php
 create mode 100644 data/web/lang/lang.en.php
 create mode 100644 data/web/lang/lang.es.php
 create mode 100644 data/web/lang/lang.nl.php
 create mode 100644 data/web/lang/lang.pt.php
 create mode 100644 data/web/mailbox.php
 create mode 100644 data/web/robots.txt
 create mode 100644 data/web/u2f_api.php
 create mode 100644 data/web/user.php
 create mode 100644 docker-compose.yml
 create mode 100644 docs/first_steps.md
 create mode 100644 docs/index.md
 create mode 100644 docs/install.md
 create mode 100644 docs/u_and_e.md
 delete mode 100644 first_steps/index.html
 delete mode 100755 fonts/fontawesome-webfont.eot
 delete mode 100755 fonts/fontawesome-webfont.svg
 delete mode 100755 fonts/fontawesome-webfont.ttf
 delete mode 100755 fonts/fontawesome-webfont.woff
 create mode 100755 generate_config.sh
 delete mode 100644 img/favicon.ico
 delete mode 100644 index.html
 delete mode 100644 install/index.html
 delete mode 100644 js/highlight.pack.js
 delete mode 100644 js/jquery-2.1.1.min.js
 delete mode 100644 js/modernizr-2.8.3.min.js
 delete mode 100644 js/theme.js
 create mode 100644 mkdocs.yml
 delete mode 100644 mkdocs/js/lunr.min.js
 delete mode 100644 mkdocs/js/mustache.min.js
 delete mode 100644 mkdocs/js/require.js
 delete mode 100644 mkdocs/js/search-results-template.mustache
 delete mode 100644 mkdocs/js/search.js
 delete mode 100644 mkdocs/js/text.js
 delete mode 100644 mkdocs/search_index.json
 delete mode 100644 search.html
 delete mode 100644 sitemap.xml
 delete mode 100644 u_and_e/index.html

Diff

diff --git a/.env b/.env
new file mode 120000
index 00000000..9400869f
--- /dev/null
+++ b/.env
@@ -0,0 +1 @@
+mailcow.conf
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 00000000..e088e56d
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,8 @@
+rebuild-images.sh
+data/conf/sogo/sieve.creds
+data/conf/dovecot/dovecot-master.passwd
+mailcow.conf
+mailcow.conf_backup
+data/conf/nginx/listen*active
+data/web/inc/vars.local.inc.php
+site/
diff --git a/README.md b/README.md
new file mode 100644
index 00000000..bd305b38
--- /dev/null
+++ b/README.md
@@ -0,0 +1,5 @@
+# mailcow: dockerized - 🐮 + 🐋 = 💕
+
+[![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=JWBSYHF4SMC68)
+
+Please see the official documentation for instructions => [mailcow.email/dockerized](https://mailcow.email/dockerized)
diff --git a/css/highlight.css b/css/highlight.css
deleted file mode 100644
index 0ae40a72..00000000
--- a/css/highlight.css
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
-This is the GitHub theme for highlight.js
-
-github.com style (c) Vasily Polovnyov <[email protected]>
-
-*/
-
-.hljs {
-  display: block;
-  overflow-x: auto;
-  color: #333;
-  -webkit-text-size-adjust: none;
-}
-
-.hljs-comment,
-.diff .hljs-header,
-.hljs-javadoc {
-  color: #998;
-  font-style: italic;
-}
-
-.hljs-keyword,
-.css .rule .hljs-keyword,
-.hljs-winutils,
-.nginx .hljs-title,
-.hljs-subst,
-.hljs-request,
-.hljs-status {
-  color: #333;
-  font-weight: bold;
-}
-
-.hljs-number,
-.hljs-hexcolor,
-.ruby .hljs-constant {
-  color: #008080;
-}
-
-.hljs-string,
-.hljs-tag .hljs-value,
-.hljs-phpdoc,
-.hljs-dartdoc,
-.tex .hljs-formula {
-  color: #d14;
-}
-
-.hljs-title,
-.hljs-id,
-.scss .hljs-preprocessor {
-  color: #900;
-  font-weight: bold;
-}
-
-.hljs-list .hljs-keyword,
-.hljs-subst {
-  font-weight: normal;
-}
-
-.hljs-class .hljs-title,
-.hljs-type,
-.vhdl .hljs-literal,
-.tex .hljs-command {
-  color: #458;
-  font-weight: bold;
-}
-
-.hljs-tag,
-.hljs-tag .hljs-title,
-.hljs-rule .hljs-property,
-.django .hljs-tag .hljs-keyword {
-  color: #000080;
-  font-weight: normal;
-}
-
-.hljs-attribute,
-.hljs-variable,
-.lisp .hljs-body,
-.hljs-name {
-  color: #008080;
-}
-
-.hljs-regexp {
-  color: #009926;
-}
-
-.hljs-symbol,
-.ruby .hljs-symbol .hljs-string,
-.lisp .hljs-keyword,
-.clojure .hljs-keyword,
-.scheme .hljs-keyword,
-.tex .hljs-special,
-.hljs-prompt {
-  color: #990073;
-}
-
-.hljs-built_in {
-  color: #0086b3;
-}
-
-.hljs-preprocessor,
-.hljs-pragma,
-.hljs-pi,
-.hljs-doctype,
-.hljs-shebang,
-.hljs-cdata {
-  color: #999;
-  font-weight: bold;
-}
-
-.hljs-deletion {
-  background: #fdd;
-}
-
-.hljs-addition {
-  background: #dfd;
-}
-
-.diff .hljs-change {
-  background: #0086b3;
-}
-
-.hljs-chunk {
-  color: #aaa;
-}
diff --git a/css/theme.css b/css/theme.css
deleted file mode 100644
index 099a2d82..00000000
--- a/css/theme.css
+++ /dev/null
@@ -1,12 +0,0 @@
-/*
- * This file is copied from the upstream ReadTheDocs Sphinx
- * theme. To aid upgradability this file should *not* be edited.
- * modifications we need should be included in theme_extra.css.
- *
- * https://github.com/rtfd/readthedocs.org/blob/master/readthedocs/core/static/core/css/theme.css
- */
-
-*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block}audio,canvas,video{display:inline-block;*display:inline;*zoom:1}audio:not([controls]){display:none}[hidden]{display:none}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}a:hover,a:active{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:bold}blockquote{margin:0}dfn{font-style:italic}ins{background:#ff9;color:#000;text-decoration:none}mark{background:#ff0;color:#000;font-style:italic;font-weight:bold}pre,code,.rst-content tt,kbd,samp{font-family:monospace,serif;_font-family:"courier new",monospace;font-size:1em}pre{white-space:pre}q{quotes:none}q:before,q:after{content:"";content:none}small{font-size:85%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}ul,ol,dl{margin:0;padding:0;list-style:none;list-style-image:none}li{list-style:none}dd{margin:0}img{border:0;-ms-interpolation-mode:bicubic;vertical-align:middle;max-width:100%}svg:not(:root){overflow:hidden}figure{margin:0}form{margin:0}fieldset{border:0;margin:0;padding:0}label{cursor:pointer}legend{border:0;*margin-left:-7px;padding:0;white-space:normal}button,input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}button,input{line-height:normal}button,input[type="button"],input[type="reset"],input[type="submit"]{cursor:pointer;-webkit-appearance:button;*overflow:visible}button[disabled],input[disabled]{cursor:default}input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0;*width:13px;*height:13px}input[type="search"]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type="search"]::-webkit-search-decoration,input[type="search"]::-webkit-search-cancel-button{-webkit-appearance:none}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}textarea{overflow:auto;vertical-align:top;resize:vertical}table{border-collapse:collapse;border-spacing:0}td{vertical-align:top}.chromeframe{margin:0.2em 0;background:#ccc;color:#000;padding:0.2em 0}.ir{display:block;border:0;text-indent:-999em;overflow:hidden;background-color:transparent;background-repeat:no-repeat;text-align:left;direction:ltr;*line-height:0}.ir br{display:none}.hidden{display:none !important;visibility:hidden}.visuallyhidden{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.visuallyhidden.focusable:active,.visuallyhidden.focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}.invisible{visibility:hidden}.relative{position:relative}big,small{font-size:100%}@media print{html,body,section{background:none !important}*{box-shadow:none !important;text-shadow:none !important;filter:none !important;-ms-filter:none !important}a,a:visited{text-decoration:underline}.ir a:after,a[href^="javascript:"]:after,a[href^="#"]:after{content:""}pre,blockquote{page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100% !important}@page{margin:0.5cm}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}}.fa:before,.rst-content .admonition-title:before,.rst-content h1 .headerlink:before,.rst-content h2 .headerlink:before,.rst-content h3 .headerlink:before,.rst-content h4 .headerlink:before,.rst-content h5 .headerlink:before,.rst-content h6 .headerlink:before,.rst-content dl dt .headerlink:before,.icon:before,.wy-dropdown .caret:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.wy-alert,.rst-content .note,.rst-content .attention,.rst-content .caution,.rst-content .danger,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .tip,.rst-content .warning,.rst-content .seealso,.rst-content .admonition-todo,.btn,input[type="text"],input[type="password"],input[type="email"],input[type="url"],input[type="date"],input[type="month"],input[type="time"],input[type="datetime"],input[type="datetime-local"],input[type="week"],input[type="number"],input[type="search"],input[type="tel"],input[type="color"],select,textarea,.wy-menu-vertical li.on a,.wy-menu-vertical li.current>a,.wy-side-nav-search>a,.wy-side-nav-search .wy-dropdown>a,.wy-nav-top a{-webkit-font-smoothing:antialiased}.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;content:""}.clearfix:after{clear:both}/*!
- *  Font Awesome 4.1.0 by @davegandy - http://fontawesome.io - @fontawesome
- *  License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License)
- */@font-face{font-family:'FontAwesome';src:url("../fonts/fontawesome-webfont.eot?v=4.1.0");src:url("../fonts/fontawesome-webfont.eot?#iefix&v=4.1.0") format("embedded-opentype"),url("../fonts/fontawesome-webfont.woff?v=4.1.0") format("woff"),url("../fonts/fontawesome-webfont.ttf?v=4.1.0") format("truetype"),url("../fonts/fontawesome-webfont.svg?v=4.1.0#fontawesomeregular") format("svg");font-weight:normal;font-style:normal}.fa,.rst-content .admonition-title,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content dl dt .headerlink,.icon{display:inline-block;font-family:FontAwesome;font-style:normal;font-weight:normal;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333em;line-height:0.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14286em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14286em;width:2.14286em;top:0.14286em;text-align:center}.fa-li.fa-lg{left:-1.85714em}.fa-border{padding:.2em .25em .15em;border:solid 0.08em #eee;border-radius:.1em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left,.rst-content .pull-left.admonition-title,.rst-content h1 .pull-left.headerlink,.rst-content h2 .pull-left.headerlink,.rst-content h3 .pull-left.headerlink,.rst-content h4 .pull-left.headerlink,.rst-content h5 .pull-left.headerlink,.rst-content h6 .pull-left.headerlink,.rst-content dl dt .pull-left.headerlink,.pull-left.icon{margin-right:.3em}.fa.pull-right,.rst-content .pull-right.admonition-title,.rst-content h1 .pull-right.headerlink,.rst-content h2 .pull-right.headerlink,.rst-content h3 .pull-right.headerlink,.rst-content h4 .pull-right.headerlink,.rst-content h5 .pull-right.headerlink,.rst-content h6 .pull-right.headerlink,.rst-content dl dt .pull-right.headerlink,.pull-right.icon{margin-left:.3em}.fa-spin{-webkit-animation:spin 2s infinite linear;-moz-animation:spin 2s infinite linear;-o-animation:spin 2s infinite linear;animation:spin 2s infinite linear}@-moz-keyframes spin{0%{-moz-transform:rotate(0deg)}100%{-moz-transform:rotate(359deg)}}@-webkit-keyframes spin{0%{-webkit-transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg)}}@-o-keyframes spin{0%{-o-transform:rotate(0deg)}100%{-o-transform:rotate(359deg)}}@keyframes spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1);-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2);-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-ms-transform:rotate(180deg);-o-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=3);-webkit-transform:rotate(270deg);-moz-transform:rotate(270deg);-ms-transform:rotate(270deg);-o-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=0);-webkit-transform:scale(-1, 1);-moz-transform:scale(-1, 1);-ms-transform:scale(-1, 1);-o-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2);-webkit-transform:scale(1, -1);-moz-transform:scale(1, -1);-ms-transform:scale(1, -1);-o-transform:scale(1, -1);transform:scale(1, -1)}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:""}.fa-music:before{content:""}.fa-search:before,.icon-search:before{content:""}.fa-envelope-o:before{content:""}.fa-heart:before{content:""}.fa-star:before{content:""}.fa-star-o:before{content:""}.fa-user:before{content:""}.fa-film:before{content:""}.fa-th-large:before{content:""}.fa-th:before{content:""}.fa-th-list:before{content:""}.fa-check:before{content:""}.fa-times:before{content:""}.fa-search-plus:before{content:""}.fa-search-minus:before{content:""}.fa-power-off:before{content:""}.fa-signal:before{content:""}.fa-gear:before,.fa-cog:before{content:""}.fa-trash-o:before{content:""}.fa-home:before,.icon-home:before{content:""}.fa-file-o:before{content:""}.fa-clock-o:before{content:""}.fa-road:before{content:""}.fa-download:before{content:""}.fa-arrow-circle-o-down:before{content:""}.fa-arrow-circle-o-up:before{content:""}.fa-inbox:before{content:""}.fa-play-circle-o:before{content:""}.fa-rotate-right:before,.fa-repeat:before{content:""}.fa-refresh:before{content:""}.fa-list-alt:before{content:""}.fa-lock:before{content:""}.fa-flag:before{content:""}.fa-headphones:before{content:""}.fa-volume-off:before{content:""}.fa-volume-down:before{content:""}.fa-volume-up:before{content:""}.fa-qrcode:before{content:""}.fa-barcode:before{content:""}.fa-tag:before{content:""}.fa-tags:before{content:""}.fa-book:before,.icon-book:before{content:""}.fa-bookmark:before{content:""}.fa-print:before{content:""}.fa-camera:before{content:""}.fa-font:before{content:""}.fa-bold:before{content:""}.fa-italic:before{content:""}.fa-text-height:before{content:""}.fa-text-width:before{content:""}.fa-align-left:before{content:""}.fa-align-center:before{content:""}.fa-align-right:before{content:""}.fa-align-justify:before{content:""}.fa-list:before{content:""}.fa-dedent:before,.fa-outdent:before{content:""}.fa-indent:before{content:""}.fa-video-camera:before{content:""}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:""}.fa-pencil:before{content:""}.fa-map-marker:before{content:""}.fa-adjust:before{content:""}.fa-tint:before{content:""}.fa-edit:before,.fa-pencil-square-o:before{content:""}.fa-share-square-o:before{content:""}.fa-check-square-o:before{content:""}.fa-arrows:before{content:""}.fa-step-backward:before{content:""}.fa-fast-backward:before{content:""}.fa-backward:before{content:""}.fa-play:before{content:""}.fa-pause:before{content:""}.fa-stop:before{content:""}.fa-forward:before{content:""}.fa-fast-forward:before{content:""}.fa-step-forward:before{content:""}.fa-eject:before{content:""}.fa-chevron-left:before{content:""}.fa-chevron-right:before{content:""}.fa-plus-circle:before{content:""}.fa-minus-circle:before{content:""}.fa-times-circle:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before{content:""}.fa-check-circle:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before{content:""}.fa-question-circle:before{content:""}.fa-info-circle:before{content:""}.fa-crosshairs:before{content:""}.fa-times-circle-o:before{content:""}.fa-check-circle-o:before{content:""}.fa-ban:before{content:""}.fa-arrow-left:before{content:""}.fa-arrow-right:before{content:""}.fa-arrow-up:before{content:""}.fa-arrow-down:before{content:""}.fa-mail-forward:before,.fa-share:before{content:""}.fa-expand:before{content:""}.fa-compress:before{content:""}.fa-plus:before{content:""}.fa-minus:before{content:""}.fa-asterisk:before{content:""}.fa-exclamation-circle:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.rst-content .admonition-title:before{content:""}.fa-gift:before{content:""}.fa-leaf:before{content:""}.fa-fire:before,.icon-fire:before{content:""}.fa-eye:before{content:""}.fa-eye-slash:before{content:""}.fa-warning:before,.fa-exclamation-triangle:before{content:""}.fa-plane:before{content:""}.fa-calendar:before{content:""}.fa-random:before{content:""}.fa-comment:before{content:""}.fa-magnet:before{content:""}.fa-chevron-up:before{content:""}.fa-chevron-down:before{content:""}.fa-retweet:before{content:""}.fa-shopping-cart:before{content:""}.fa-folder:before{content:""}.fa-folder-open:before{content:""}.fa-arrows-v:before{content:""}.fa-arrows-h:before{content:""}.fa-bar-chart-o:before{content:""}.fa-twitter-square:before{content:""}.fa-facebook-square:before{content:""}.fa-camera-retro:before{content:""}.fa-key:before{content:""}.fa-gears:before,.fa-cogs:before{content:""}.fa-comments:before{content:""}.fa-thumbs-o-up:before{content:""}.fa-thumbs-o-down:before{content:""}.fa-star-half:before{content:""}.fa-heart-o:before{content:""}.fa-sign-out:before{content:""}.fa-linkedin-square:before{content:""}.fa-thumb-tack:before{content:""}.fa-external-link:before{content:""}.fa-sign-in:before{content:""}.fa-trophy:before{content:""}.fa-github-square:before{content:""}.fa-upload:before{content:""}.fa-lemon-o:before{content:""}.fa-phone:before{content:""}.fa-square-o:before{content:""}.fa-bookmark-o:before{content:""}.fa-phone-square:before{content:""}.fa-twitter:before{content:""}.fa-facebook:before{content:""}.fa-github:before,.icon-github:before{content:""}.fa-unlock:before{content:""}.fa-credit-card:before{content:""}.fa-rss:before{content:""}.fa-hdd-o:before{content:""}.fa-bullhorn:before{content:""}.fa-bell:before{content:""}.fa-certificate:before{content:""}.fa-hand-o-right:before{content:""}.fa-hand-o-left:before{content:""}.fa-hand-o-up:before{content:""}.fa-hand-o-down:before{content:""}.fa-arrow-circle-left:before,.icon-circle-arrow-left:before{content:""}.fa-arrow-circle-right:before,.icon-circle-arrow-right:before{content:""}.fa-arrow-circle-up:before{content:""}.fa-arrow-circle-down:before{content:""}.fa-globe:before{content:""}.fa-wrench:before{content:""}.fa-tasks:before{content:""}.fa-filter:before{content:""}.fa-briefcase:before{content:""}.fa-arrows-alt:before{content:""}.fa-group:before,.fa-users:before{content:""}.fa-chain:before,.fa-link:before,.icon-link:before{content:""}.fa-cloud:before{content:""}.fa-flask:before{content:""}.fa-cut:before,.fa-scissors:before{content:""}.fa-copy:before,.fa-files-o:before{content:""}.fa-paperclip:before{content:""}.fa-save:before,.fa-floppy-o:before{content:""}.fa-square:before{content:""}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:""}.fa-list-ul:before{content:""}.fa-list-ol:before{content:""}.fa-strikethrough:before{content:""}.fa-underline:before{content:""}.fa-table:before{content:""}.fa-magic:before{content:""}.fa-truck:before{content:""}.fa-pinterest:before{content:""}.fa-pinterest-square:before{content:""}.fa-google-plus-square:before{content:""}.fa-google-plus:before{content:""}.fa-money:before{content:""}.fa-caret-down:before,.wy-dropdown .caret:before,.icon-caret-down:before{content:""}.fa-caret-up:before{content:""}.fa-caret-left:before{content:""}.fa-caret-right:before{content:""}.fa-columns:before{content:""}.fa-unsorted:before,.fa-sort:before{content:""}.fa-sort-down:before,.fa-sort-desc:before{content:""}.fa-sort-up:before,.fa-sort-asc:before{content:""}.fa-envelope:before{content:""}.fa-linkedin:before{content:""}.fa-rotate-left:before,.fa-undo:before{content:""}.fa-legal:before,.fa-gavel:before{content:""}.fa-dashboard:before,.fa-tachometer:before{content:""}.fa-comment-o:before{content:""}.fa-comments-o:before{content:""}.fa-flash:before,.fa-bolt:before{content:""}.fa-sitemap:before{content:""}.fa-umbrella:before{content:""}.fa-paste:before,.fa-clipboard:before{content:""}.fa-lightbulb-o:before{content:""}.fa-exchange:before{content:""}.fa-cloud-download:before{content:""}.fa-cloud-upload:before{content:""}.fa-user-md:before{content:""}.fa-stethoscope:before{content:""}.fa-suitcase:before{content:""}.fa-bell-o:before{content:""}.fa-coffee:before{content:""}.fa-cutlery:before{content:""}.fa-file-text-o:before{content:""}.fa-building-o:before{content:""}.fa-hospital-o:before{content:""}.fa-ambulance:before{content:""}.fa-medkit:before{content:""}.fa-fighter-jet:before{content:""}.fa-beer:before{content:""}.fa-h-square:before{content:""}.fa-plus-square:before{content:""}.fa-angle-double-left:before{content:""}.fa-angle-double-right:before{content:""}.fa-angle-double-up:before{content:""}.fa-angle-double-down:before{content:""}.fa-angle-left:before{content:""}.fa-angle-right:before{content:""}.fa-angle-up:before{content:""}.fa-angle-down:before{content:""}.fa-desktop:before{content:""}.fa-laptop:before{content:""}.fa-tablet:before{content:""}.fa-mobile-phone:before,.fa-mobile:before{content:""}.fa-circle-o:before{content:""}.fa-quote-left:before{content:""}.fa-quote-right:before{content:""}.fa-spinner:before{content:""}.fa-circle:before{content:""}.fa-mail-reply:before,.fa-reply:before{content:""}.fa-github-alt:before{content:""}.fa-folder-o:before{content:""}.fa-folder-open-o:before{content:""}.fa-smile-o:before{content:""}.fa-frown-o:before{content:""}.fa-meh-o:before{content:""}.fa-gamepad:before{content:""}.fa-keyboard-o:before{content:""}.fa-flag-o:before{content:""}.fa-flag-checkered:before{content:""}.fa-terminal:before{content:""}.fa-code:before{content:""}.fa-mail-reply-all:before,.fa-reply-all:before{content:""}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:""}.fa-location-arrow:before{content:""}.fa-crop:before{content:""}.fa-code-fork:before{content:""}.fa-unlink:before,.fa-chain-broken:before{content:""}.fa-question:before{content:""}.fa-info:before{content:""}.fa-exclamation:before{content:""}.fa-superscript:before{content:""}.fa-subscript:before{content:""}.fa-eraser:before{content:""}.fa-puzzle-piece:before{content:""}.fa-microphone:before{content:""}.fa-microphone-slash:before{content:""}.fa-shield:before{content:""}.fa-calendar-o:before{content:""}.fa-fire-extinguisher:before{content:""}.fa-rocket:before{content:""}.fa-maxcdn:before{content:""}.fa-chevron-circle-left:before{content:""}.fa-chevron-circle-right:before{content:""}.fa-chevron-circle-up:before{content:""}.fa-chevron-circle-down:before{content:""}.fa-html5:before{content:""}.fa-css3:before{content:""}.fa-anchor:before{content:""}.fa-unlock-alt:before{content:""}.fa-bullseye:before{content:""}.fa-ellipsis-h:before{content:""}.fa-ellipsis-v:before{content:""}.fa-rss-square:before{content:""}.fa-play-circle:before{content:""}.fa-ticket:before{content:""}.fa-minus-square:before{content:""}.fa-minus-square-o:before{content:""}.fa-level-up:before{content:""}.fa-level-down:before{content:""}.fa-check-square:before{content:""}.fa-pencil-square:before{content:""}.fa-external-link-square:before{content:""}.fa-share-square:before{content:""}.fa-compass:before{content:""}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:""}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:""}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:""}.fa-euro:before,.fa-eur:before{content:""}.fa-gbp:before{content:""}.fa-dollar:before,.fa-usd:before{content:""}.fa-rupee:before,.fa-inr:before{content:""}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:""}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:""}.fa-won:before,.fa-krw:before{content:""}.fa-bitcoin:before,.fa-btc:before{content:""}.fa-file:before{content:""}.fa-file-text:before{content:""}.fa-sort-alpha-asc:before{content:""}.fa-sort-alpha-desc:before{content:""}.fa-sort-amount-asc:before{content:""}.fa-sort-amount-desc:before{content:""}.fa-sort-numeric-asc:before{content:""}.fa-sort-numeric-desc:before{content:""}.fa-thumbs-up:before{content:""}.fa-thumbs-down:before{content:""}.fa-youtube-square:before{content:""}.fa-youtube:before{content:""}.fa-xing:before{content:""}.fa-xing-square:before{content:""}.fa-youtube-play:before{content:""}.fa-dropbox:before{content:""}.fa-stack-overflow:before{content:""}.fa-instagram:before{content:""}.fa-flickr:before{content:""}.fa-adn:before{content:""}.fa-bitbucket:before,.icon-bitbucket:before{content:""}.fa-bitbucket-square:before{content:""}.fa-tumblr:before{content:""}.fa-tumblr-square:before{content:""}.fa-long-arrow-down:before{content:""}.fa-long-arrow-up:before{content:""}.fa-long-arrow-left:before{content:""}.fa-long-arrow-right:before{content:""}.fa-apple:before{content:""}.fa-windows:before{content:""}.fa-android:before{content:""}.fa-linux:before{content:""}.fa-dribbble:before{content:""}.fa-skype:before{content:""}.fa-foursquare:before{content:""}.fa-trello:before{content:""}.fa-female:before{content:""}.fa-male:before{content:""}.fa-gittip:before{content:""}.fa-sun-o:before{content:""}.fa-moon-o:before{content:""}.fa-archive:before{content:""}.fa-bug:before{content:""}.fa-vk:before{content:""}.fa-weibo:before{content:""}.fa-renren:before{content:""}.fa-pagelines:before{content:""}.fa-stack-exchange:before{content:""}.fa-arrow-circle-o-right:before{content:""}.fa-arrow-circle-o-left:before{content:""}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:""}.fa-dot-circle-o:before{content:""}.fa-wheelchair:before{content:""}.fa-vimeo-square:before{content:""}.fa-turkish-lira:before,.fa-try:before{content:""}.fa-plus-square-o:before{content:""}.fa-space-shuttle:before{content:""}.fa-slack:before{content:""}.fa-envelope-square:before{content:""}.fa-wordpress:before{content:""}.fa-openid:before{content:""}.fa-institution:before,.fa-bank:before,.fa-university:before{content:""}.fa-mortar-board:before,.fa-graduation-cap:before{content:""}.fa-yahoo:before{content:""}.fa-google:before{content:""}.fa-reddit:before{content:""}.fa-reddit-square:before{content:""}.fa-stumbleupon-circle:before{content:""}.fa-stumbleupon:before{content:""}.fa-delicious:before{content:""}.fa-digg:before{content:""}.fa-pied-piper-square:before,.fa-pied-piper:before{content:""}.fa-pied-piper-alt:before{content:""}.fa-drupal:before{content:""}.fa-joomla:before{content:""}.fa-language:before{content:""}.fa-fax:before{content:""}.fa-building:before{content:""}.fa-child:before{content:""}.fa-paw:before{content:""}.fa-spoon:before{content:""}.fa-cube:before{content:""}.fa-cubes:before{content:""}.fa-behance:before{content:""}.fa-behance-square:before{content:""}.fa-steam:before{content:""}.fa-steam-square:before{content:""}.fa-recycle:before{content:""}.fa-automobile:before,.fa-car:before{content:""}.fa-cab:before,.fa-taxi:before{content:""}.fa-tree:before{content:""}.fa-spotify:before{content:""}.fa-deviantart:before{content:""}.fa-soundcloud:before{content:""}.fa-database:before{content:""}.fa-file-pdf-o:before{content:""}.fa-file-word-o:before{content:""}.fa-file-excel-o:before{content:""}.fa-file-powerpoint-o:before{content:""}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:""}.fa-file-zip-o:before,.fa-file-archive-o:before{content:""}.fa-file-sound-o:before,.fa-file-audio-o:before{content:""}.fa-file-movie-o:before,.fa-file-video-o:before{content:""}.fa-file-code-o:before{content:""}.fa-vine:before{content:""}.fa-codepen:before{content:""}.fa-jsfiddle:before{content:""}.fa-life-bouy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:""}.fa-circle-o-notch:before{content:""}.fa-ra:before,.fa-rebel:before{content:""}.fa-ge:before,.fa-empire:before{content:""}.fa-git-square:before{content:""}.fa-git:before{content:""}.fa-hacker-news:before{content:""}.fa-tencent-weibo:before{content:""}.fa-qq:before{content:""}.fa-wechat:before,.fa-weixin:before{content:""}.fa-send:before,.fa-paper-plane:before{content:""}.fa-send-o:before,.fa-paper-plane-o:before{content:""}.fa-history:before{content:""}.fa-circle-thin:before{content:""}.fa-header:before{content:""}.fa-paragraph:before{content:""}.fa-sliders:before{content:""}.fa-share-alt:before{content:""}.fa-share-alt-square:before{content:""}.fa-bomb:before{content:""}.fa,.rst-content .admonition-title,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content dl dt .headerlink,.icon,.wy-dropdown .caret,.wy-inline-validate.wy-inline-validate-success .wy-input-context,.wy-inline-validate.wy-inline-validate-danger .wy-input-context,.wy-inline-validate.wy-inline-validate-warning .wy-input-context,.wy-inline-validate.wy-inline-validate-info .wy-input-context{font-family:inherit}.fa:before,.rst-content .admonition-title:before,.rst-content h1 .headerlink:before,.rst-content h2 .headerlink:before,.rst-content h3 .headerlink:before,.rst-content h4 .headerlink:before,.rst-content h5 .headerlink:before,.rst-content h6 .headerlink:before,.rst-content dl dt .headerlink:before,.icon:before,.wy-dropdown .caret:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before{font-family:"FontAwesome";display:inline-block;font-style:normal;font-weight:normal;line-height:1;text-decoration:inherit}a .fa,a .rst-content .admonition-title,.rst-content a .admonition-title,a .rst-content h1 .headerlink,.rst-content h1 a .headerlink,a .rst-content h2 .headerlink,.rst-content h2 a .headerlink,a .rst-content h3 .headerlink,.rst-content h3 a .headerlink,a .rst-content h4 .headerlink,.rst-content h4 a .headerlink,a .rst-content h5 .headerlink,.rst-content h5 a .headerlink,a .rst-content h6 .headerlink,.rst-content h6 a .headerlink,a .rst-content dl dt .headerlink,.rst-content dl dt a .headerlink,a .icon{display:inline-block;text-decoration:inherit}.btn .fa,.btn .rst-content .admonition-title,.rst-content .btn .admonition-title,.btn .rst-content h1 .headerlink,.rst-content h1 .btn .headerlink,.btn .rst-content h2 .headerlink,.rst-content h2 .btn .headerlink,.btn .rst-content h3 .headerlink,.rst-content h3 .btn .headerlink,.btn .rst-content h4 .headerlink,.rst-content h4 .btn .headerlink,.btn .rst-content h5 .headerlink,.rst-content h5 .btn .headerlink,.btn .rst-content h6 .headerlink,.rst-content h6 .btn .headerlink,.btn .rst-content dl dt .headerlink,.rst-content dl dt .btn .headerlink,.btn .icon,.nav .fa,.nav .rst-content .admonition-title,.rst-content .nav .admonition-title,.nav .rst-content h1 .headerlink,.rst-content h1 .nav .headerlink,.nav .rst-content h2 .headerlink,.rst-content h2 .nav .headerlink,.nav .rst-content h3 .headerlink,.rst-content h3 .nav .headerlink,.nav .rst-content h4 .headerlink,.rst-content h4 .nav .headerlink,.nav .rst-content h5 .headerlink,.rst-content h5 .nav .headerlink,.nav .rst-content h6 .headerlink,.rst-content h6 .nav .headerlink,.nav .rst-content dl dt .headerlink,.rst-content dl dt .nav .headerlink,.nav .icon{display:inline}.btn .fa.fa-large,.btn .rst-content .fa-large.admonition-title,.rst-content .btn .fa-large.admonition-title,.btn .rst-content h1 .fa-large.headerlink,.rst-content h1 .btn .fa-large.headerlink,.btn .rst-content h2 .fa-large.headerlink,.rst-content h2 .btn .fa-large.headerlink,.btn .rst-content h3 .fa-large.headerlink,.rst-content h3 .btn .fa-large.headerlink,.btn .rst-content h4 .fa-large.headerlink,.rst-content h4 .btn .fa-large.headerlink,.btn .rst-content h5 .fa-large.headerlink,.rst-content h5 .btn .fa-large.headerlink,.btn .rst-content h6 .fa-large.headerlink,.rst-content h6 .btn .fa-large.headerlink,.btn .rst-content dl dt .fa-large.headerlink,.rst-content dl dt .btn .fa-large.headerlink,.btn .fa-large.icon,.nav .fa.fa-large,.nav .rst-content .fa-large.admonition-title,.rst-content .nav .fa-large.admonition-title,.nav .rst-content h1 .fa-large.headerlink,.rst-content h1 .nav .fa-large.headerlink,.nav .rst-content h2 .fa-large.headerlink,.rst-content h2 .nav .fa-large.headerlink,.nav .rst-content h3 .fa-large.headerlink,.rst-content h3 .nav .fa-large.headerlink,.nav .rst-content h4 .fa-large.headerlink,.rst-content h4 .nav .fa-large.headerlink,.nav .rst-content h5 .fa-large.headerlink,.rst-content h5 .nav .fa-large.headerlink,.nav .rst-content h6 .fa-large.headerlink,.rst-content h6 .nav .fa-large.headerlink,.nav .rst-content dl dt .fa-large.headerlink,.rst-content dl dt .nav .fa-large.headerlink,.nav .fa-large.icon{line-height:0.9em}.btn .fa.fa-spin,.btn .rst-content .fa-spin.admonition-title,.rst-content .btn .fa-spin.admonition-title,.btn .rst-content h1 .fa-spin.headerlink,.rst-content h1 .btn .fa-spin.headerlink,.btn .rst-content h2 .fa-spin.headerlink,.rst-content h2 .btn .fa-spin.headerlink,.btn .rst-content h3 .fa-spin.headerlink,.rst-content h3 .btn .fa-spin.headerlink,.btn .rst-content h4 .fa-spin.headerlink,.rst-content h4 .btn .fa-spin.headerlink,.btn .rst-content h5 .fa-spin.headerlink,.rst-content h5 .btn .fa-spin.headerlink,.btn .rst-content h6 .fa-spin.headerlink,.rst-content h6 .btn .fa-spin.headerlink,.btn .rst-content dl dt .fa-spin.headerlink,.rst-content dl dt .btn .fa-spin.headerlink,.btn .fa-spin.icon,.nav .fa.fa-spin,.nav .rst-content .fa-spin.admonition-title,.rst-content .nav .fa-spin.admonition-title,.nav .rst-content h1 .fa-spin.headerlink,.rst-content h1 .nav .fa-spin.headerlink,.nav .rst-content h2 .fa-spin.headerlink,.rst-content h2 .nav .fa-spin.headerlink,.nav .rst-content h3 .fa-spin.headerlink,.rst-content h3 .nav .fa-spin.headerlink,.nav .rst-content h4 .fa-spin.headerlink,.rst-content h4 .nav .fa-spin.headerlink,.nav .rst-content h5 .fa-spin.headerlink,.rst-content h5 .nav .fa-spin.headerlink,.nav .rst-content h6 .fa-spin.headerlink,.rst-content h6 .nav .fa-spin.headerlink,.nav .rst-content dl dt .fa-spin.headerlink,.rst-content dl dt .nav .fa-spin.headerlink,.nav .fa-spin.icon{display:inline-block}.btn.fa:before,.rst-content .btn.admonition-title:before,.rst-content h1 .btn.headerlink:before,.rst-content h2 .btn.headerlink:before,.rst-content h3 .btn.headerlink:before,.rst-content h4 .btn.headerlink:before,.rst-content h5 .btn.headerlink:before,.rst-content h6 .btn.headerlink:before,.rst-content dl dt .btn.headerlink:before,.btn.icon:before{opacity:0.5;-webkit-transition:opacity 0.05s ease-in;-moz-transition:opacity 0.05s ease-in;transition:opacity 0.05s ease-in}.btn.fa:hover:before,.rst-content .btn.admonition-title:hover:before,.rst-content h1 .btn.headerlink:hover:before,.rst-content h2 .btn.headerlink:hover:before,.rst-content h3 .btn.headerlink:hover:before,.rst-content h4 .btn.headerlink:hover:before,.rst-content h5 .btn.headerlink:hover:before,.rst-content h6 .btn.headerlink:hover:before,.rst-content dl dt .btn.headerlink:hover:before,.btn.icon:hover:before{opacity:1}.btn-mini .fa:before,.btn-mini .rst-content .admonition-title:before,.rst-content .btn-mini .admonition-title:before,.btn-mini .rst-content h1 .headerlink:before,.rst-content h1 .btn-mini .headerlink:before,.btn-mini .rst-content h2 .headerlink:before,.rst-content h2 .btn-mini .headerlink:before,.btn-mini .rst-content h3 .headerlink:before,.rst-content h3 .btn-mini .headerlink:before,.btn-mini .rst-content h4 .headerlink:before,.rst-content h4 .btn-mini .headerlink:before,.btn-mini .rst-content h5 .headerlink:before,.rst-content h5 .btn-mini .headerlink:before,.btn-mini .rst-content h6 .headerlink:before,.rst-content h6 .btn-mini .headerlink:before,.btn-mini .rst-content dl dt .headerlink:before,.rst-content dl dt .btn-mini .headerlink:before,.btn-mini .icon:before{font-size:14px;vertical-align:-15%}.wy-alert,.rst-content .note,.rst-content .attention,.rst-content .caution,.rst-content .danger,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .tip,.rst-content .warning,.rst-content .seealso,.rst-content .admonition-todo{padding:12px;line-height:24px;margin-bottom:24px;background:#e7f2fa}.wy-alert-title,.rst-content .admonition-title{color:#fff;font-weight:bold;display:block;color:#fff;background:#6ab0de;margin:-12px;padding:6px 12px;margin-bottom:12px}.wy-alert.wy-alert-danger,.rst-content .wy-alert-danger.note,.rst-content .wy-alert-danger.attention,.rst-content .wy-alert-danger.caution,.rst-content .danger,.rst-content .error,.rst-content .wy-alert-danger.hint,.rst-content .wy-alert-danger.important,.rst-content .wy-alert-danger.tip,.rst-content .wy-alert-danger.warning,.rst-content .wy-alert-danger.seealso,.rst-content .wy-alert-danger.admonition-todo{background:#fdf3f2}.wy-alert.wy-alert-danger .wy-alert-title,.rst-content .wy-alert-danger.note .wy-alert-title,.rst-content .wy-alert-danger.attention .wy-alert-title,.rst-content .wy-alert-danger.caution .wy-alert-title,.rst-content .danger .wy-alert-title,.rst-content .error .wy-alert-title,.rst-content .wy-alert-danger.hint .wy-alert-title,.rst-content .wy-alert-danger.important .wy-alert-title,.rst-content .wy-alert-danger.tip .wy-alert-title,.rst-content .wy-alert-danger.warning .wy-alert-title,.rst-content .wy-alert-danger.seealso .wy-alert-title,.rst-content .wy-alert-danger.admonition-todo .wy-alert-title,.wy-alert.wy-alert-danger .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-danger .admonition-title,.rst-content .wy-alert-danger.note .admonition-title,.rst-content .wy-alert-danger.attention .admonition-title,.rst-content .wy-alert-danger.caution .admonition-title,.rst-content .danger .admonition-title,.rst-content .error .admonition-title,.rst-content .wy-alert-danger.hint .admonition-title,.rst-content .wy-alert-danger.important .admonition-title,.rst-content .wy-alert-danger.tip .admonition-title,.rst-content .wy-alert-danger.warning .admonition-title,.rst-content .wy-alert-danger.seealso .admonition-title,.rst-content .wy-alert-danger.admonition-todo .admonition-title{background:#f29f97}.wy-alert.wy-alert-warning,.rst-content .wy-alert-warning.note,.rst-content .attention,.rst-content .caution,.rst-content .wy-alert-warning.danger,.rst-content .wy-alert-warning.error,.rst-content .wy-alert-warning.hint,.rst-content .wy-alert-warning.important,.rst-content .wy-alert-warning.tip,.rst-content .warning,.rst-content .wy-alert-warning.seealso,.rst-content .admonition-todo{background:#ffedcc}.wy-alert.wy-alert-warning .wy-alert-title,.rst-content .wy-alert-warning.note .wy-alert-title,.rst-content .attention .wy-alert-title,.rst-content .caution .wy-alert-title,.rst-content .wy-alert-warning.danger .wy-alert-title,.rst-content .wy-alert-warning.error .wy-alert-title,.rst-content .wy-alert-warning.hint .wy-alert-title,.rst-content .wy-alert-warning.important .wy-alert-title,.rst-content .wy-alert-warning.tip .wy-alert-title,.rst-content .warning .wy-alert-title,.rst-content .wy-alert-warning.seealso .wy-alert-title,.rst-content .admonition-todo .wy-alert-title,.wy-alert.wy-alert-warning .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-warning .admonition-title,.rst-content .wy-alert-warning.note .admonition-title,.rst-content .attention .admonition-title,.rst-content .caution .admonition-title,.rst-content .wy-alert-warning.danger .admonition-title,.rst-content .wy-alert-warning.error .admonition-title,.rst-content .wy-alert-warning.hint .admonition-title,.rst-content .wy-alert-warning.important .admonition-title,.rst-content .wy-alert-warning.tip .admonition-title,.rst-content .warning .admonition-title,.rst-content .wy-alert-warning.seealso .admonition-title,.rst-content .admonition-todo .admonition-title{background:#f0b37e}.wy-alert.wy-alert-info,.rst-content .note,.rst-content .wy-alert-info.attention,.rst-content .wy-alert-info.caution,.rst-content .wy-alert-info.danger,.rst-content .wy-alert-info.error,.rst-content .wy-alert-info.hint,.rst-content .wy-alert-info.important,.rst-content .wy-alert-info.tip,.rst-content .wy-alert-info.warning,.rst-content .seealso,.rst-content .wy-alert-info.admonition-todo{background:#e7f2fa}.wy-alert.wy-alert-info .wy-alert-title,.rst-content .note .wy-alert-title,.rst-content .wy-alert-info.attention .wy-alert-title,.rst-content .wy-alert-info.caution .wy-alert-title,.rst-content .wy-alert-info.danger .wy-alert-title,.rst-content .wy-alert-info.error .wy-alert-title,.rst-content .wy-alert-info.hint .wy-alert-title,.rst-content .wy-alert-info.important .wy-alert-title,.rst-content .wy-alert-info.tip .wy-alert-title,.rst-content .wy-alert-info.warning .wy-alert-title,.rst-content .seealso .wy-alert-title,.rst-content .wy-alert-info.admonition-todo .wy-alert-title,.wy-alert.wy-alert-info .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-info .admonition-title,.rst-content .note .admonition-title,.rst-content .wy-alert-info.attention .admonition-title,.rst-content .wy-alert-info.caution .admonition-title,.rst-content .wy-alert-info.danger .admonition-title,.rst-content .wy-alert-info.error .admonition-title,.rst-content .wy-alert-info.hint .admonition-title,.rst-content .wy-alert-info.important .admonition-title,.rst-content .wy-alert-info.tip .admonition-title,.rst-content .wy-alert-info.warning .admonition-title,.rst-content .seealso .admonition-title,.rst-content .wy-alert-info.admonition-todo .admonition-title{background:#6ab0de}.wy-alert.wy-alert-success,.rst-content .wy-alert-success.note,.rst-content .wy-alert-success.attention,.rst-content .wy-alert-success.caution,.rst-content .wy-alert-success.danger,.rst-content .wy-alert-success.error,.rst-content .hint,.rst-content .important,.rst-content .tip,.rst-content .wy-alert-success.warning,.rst-content .wy-alert-success.seealso,.rst-content .wy-alert-success.admonition-todo{background:#dbfaf4}.wy-alert.wy-alert-success .wy-alert-title,.rst-content .wy-alert-success.note .wy-alert-title,.rst-content .wy-alert-success.attention .wy-alert-title,.rst-content .wy-alert-success.caution .wy-alert-title,.rst-content .wy-alert-success.danger .wy-alert-title,.rst-content .wy-alert-success.error .wy-alert-title,.rst-content .hint .wy-alert-title,.rst-content .important .wy-alert-title,.rst-content .tip .wy-alert-title,.rst-content .wy-alert-success.warning .wy-alert-title,.rst-content .wy-alert-success.seealso .wy-alert-title,.rst-content .wy-alert-success.admonition-todo .wy-alert-title,.wy-alert.wy-alert-success .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-success .admonition-title,.rst-content .wy-alert-success.note .admonition-title,.rst-content .wy-alert-success.attention .admonition-title,.rst-content .wy-alert-success.caution .admonition-title,.rst-content .wy-alert-success.danger .admonition-title,.rst-content .wy-alert-success.error .admonition-title,.rst-content .hint .admonition-title,.rst-content .important .admonition-title,.rst-content .tip .admonition-title,.rst-content .wy-alert-success.warning .admonition-title,.rst-content .wy-alert-success.seealso .admonition-title,.rst-content .wy-alert-success.admonition-todo .admonition-title{background:#1abc9c}.wy-alert.wy-alert-neutral,.rst-content .wy-alert-neutral.note,.rst-content .wy-alert-neutral.attention,.rst-content .wy-alert-neutral.caution,.rst-content .wy-alert-neutral.danger,.rst-content .wy-alert-neutral.error,.rst-content .wy-alert-neutral.hint,.rst-content .wy-alert-neutral.important,.rst-content .wy-alert-neutral.tip,.rst-content .wy-alert-neutral.warning,.rst-content .wy-alert-neutral.seealso,.rst-content .wy-alert-neutral.admonition-todo{background:#f3f6f6}.wy-alert.wy-alert-neutral .wy-alert-title,.rst-content .wy-alert-neutral.note .wy-alert-title,.rst-content .wy-alert-neutral.attention .wy-alert-title,.rst-content .wy-alert-neutral.caution .wy-alert-title,.rst-content .wy-alert-neutral.danger .wy-alert-title,.rst-content .wy-alert-neutral.error .wy-alert-title,.rst-content .wy-alert-neutral.hint .wy-alert-title,.rst-content .wy-alert-neutral.important .wy-alert-title,.rst-content .wy-alert-neutral.tip .wy-alert-title,.rst-content .wy-alert-neutral.warning .wy-alert-title,.rst-content .wy-alert-neutral.seealso .wy-alert-title,.rst-content .wy-alert-neutral.admonition-todo .wy-alert-title,.wy-alert.wy-alert-neutral .rst-content .admonition-title,.rst-content .wy-alert.wy-alert-neutral .admonition-title,.rst-content .wy-alert-neutral.note .admonition-title,.rst-content .wy-alert-neutral.attention .admonition-title,.rst-content .wy-alert-neutral.caution .admonition-title,.rst-content .wy-alert-neutral.danger .admonition-title,.rst-content .wy-alert-neutral.error .admonition-title,.rst-content .wy-alert-neutral.hint .admonition-title,.rst-content .wy-alert-neutral.important .admonition-title,.rst-content .wy-alert-neutral.tip .admonition-title,.rst-content .wy-alert-neutral.warning .admonition-title,.rst-content .wy-alert-neutral.seealso .admonition-title,.rst-content .wy-alert-neutral.admonition-todo .admonition-title{color:#404040;background:#e1e4e5}.wy-alert.wy-alert-neutral a,.rst-content .wy-alert-neutral.note a,.rst-content .wy-alert-neutral.attention a,.rst-content .wy-alert-neutral.caution a,.rst-content .wy-alert-neutral.danger a,.rst-content .wy-alert-neutral.error a,.rst-content .wy-alert-neutral.hint a,.rst-content .wy-alert-neutral.important a,.rst-content .wy-alert-neutral.tip a,.rst-content .wy-alert-neutral.warning a,.rst-content .wy-alert-neutral.seealso a,.rst-content .wy-alert-neutral.admonition-todo a{color:#2980B9}.wy-alert p:last-child,.rst-content .note p:last-child,.rst-content .attention p:last-child,.rst-content .caution p:last-child,.rst-content .danger p:last-child,.rst-content .error p:last-child,.rst-content .hint p:last-child,.rst-content .important p:last-child,.rst-content .tip p:last-child,.rst-content .warning p:last-child,.rst-content .seealso p:last-child,.rst-content .admonition-todo p:last-child{margin-bottom:0}.wy-tray-container{position:fixed;bottom:0px;left:0;z-index:600}.wy-tray-container li{display:block;width:300px;background:transparent;color:#fff;text-align:center;box-shadow:0 5px 5px 0 rgba(0,0,0,0.1);padding:0 24px;min-width:20%;opacity:0;height:0;line-height:56px;overflow:hidden;-webkit-transition:all 0.3s ease-in;-moz-transition:all 0.3s ease-in;transition:all 0.3s ease-in}.wy-tray-container li.wy-tray-item-success{background:#27AE60}.wy-tray-container li.wy-tray-item-info{background:#2980B9}.wy-tray-container li.wy-tray-item-warning{background:#E67E22}.wy-tray-container li.wy-tray-item-danger{background:#E74C3C}.wy-tray-container li.on{opacity:1;height:56px}@media screen and (max-width: 768px){.wy-tray-container{bottom:auto;top:0;width:100%}.wy-tray-container li{width:100%}}button{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle;cursor:pointer;line-height:normal;-webkit-appearance:button;*overflow:visible}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}button[disabled]{cursor:default}.btn{display:inline-block;border-radius:2px;line-height:normal;white-space:nowrap;text-align:center;cursor:pointer;font-size:100%;padding:6px 12px 8px 12px;color:#fff;border:1px solid rgba(0,0,0,0.1);background-color:#27AE60;text-decoration:none;font-weight:normal;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;box-shadow:0px 1px 2px -1px rgba(255,255,255,0.5) inset,0px -2px 0px 0px rgba(0,0,0,0.1) inset;outline-none:false;vertical-align:middle;*display:inline;zoom:1;-webkit-user-drag:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-transition:all 0.1s linear;-moz-transition:all 0.1s linear;transition:all 0.1s linear}.btn-hover{background:#2e8ece;color:#fff}.btn:hover{background:#2cc36b;color:#fff}.btn:focus{background:#2cc36b;outline:0}.btn:active{box-shadow:0px -1px 0px 0px rgba(0,0,0,0.05) inset,0px 2px 0px 0px rgba(0,0,0,0.1) inset;padding:8px 12px 6px 12px}.btn:visited{color:#fff}.btn:disabled{background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=40);opacity:0.4;cursor:not-allowed;box-shadow:none}.btn-disabled{background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=40);opacity:0.4;cursor:not-allowed;box-shadow:none}.btn-disabled:hover,.btn-disabled:focus,.btn-disabled:active{background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=40);opacity:0.4;cursor:not-allowed;box-shadow:none}.btn::-moz-focus-inner{padding:0;border:0}.btn-small{font-size:80%}.btn-info{background-color:#2980B9 !important}.btn-info:hover{background-color:#2e8ece !important}.btn-neutral{background-color:#f3f6f6 !important;color:#404040 !important}.btn-neutral:hover{background-color:#e5ebeb !important;color:#404040}.btn-neutral:visited{color:#404040 !important}.btn-success{background-color:#27AE60 !important}.btn-success:hover{background-color:#295 !important}.btn-danger{background-color:#E74C3C !important}.btn-danger:hover{background-color:#ea6153 !important}.btn-warning{background-color:#E67E22 !important}.btn-warning:hover{background-color:#e98b39 !important}.btn-invert{background-color:#222}.btn-invert:hover{background-color:#2f2f2f !important}.btn-link{background-color:transparent !important;color:#2980B9;box-shadow:none;border-color:transparent !important}.btn-link:hover{background-color:transparent !important;color:#409ad5 !important;box-shadow:none}.btn-link:active{background-color:transparent !important;color:#409ad5 !important;box-shadow:none}.btn-link:visited{color:#9B59B6}.wy-btn-group .btn,.wy-control .btn{vertical-align:middle}.wy-btn-group{margin-bottom:24px;*zoom:1}.wy-btn-group:before,.wy-btn-group:after{display:table;content:""}.wy-btn-group:after{clear:both}.wy-dropdown{position:relative;display:inline-block}.wy-dropdown-menu{position:absolute;left:0;display:none;float:left;top:100%;min-width:100%;background:#fcfcfc;z-index:100;border:solid 1px #cfd7dd;box-shadow:0 2px 2px 0 rgba(0,0,0,0.1);padding:12px}.wy-dropdown-menu>dd>a{display:block;clear:both;color:#404040;white-space:nowrap;font-size:90%;padding:0 12px;cursor:pointer}.wy-dropdown-menu>dd>a:hover{background:#2980B9;color:#fff}.wy-dropdown-menu>dd.divider{border-top:solid 1px #cfd7dd;margin:6px 0}.wy-dropdown-menu>dd.search{padding-bottom:12px}.wy-dropdown-menu>dd.search input[type="search"]{width:100%}.wy-dropdown-menu>dd.call-to-action{background:#e3e3e3;text-transform:uppercase;font-weight:500;font-size:80%}.wy-dropdown-menu>dd.call-to-action:hover{background:#e3e3e3}.wy-dropdown-menu>dd.call-to-action .btn{color:#fff}.wy-dropdown.wy-dropdown-up .wy-dropdown-menu{bottom:100%;top:auto;left:auto;right:0}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu{background:#fcfcfc;margin-top:2px}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a{padding:6px 12px}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a:hover{background:#2980B9;color:#fff}.wy-dropdown.wy-dropdown-left .wy-dropdown-menu{right:0;text-align:right}.wy-dropdown-arrow:before{content:" ";border-bottom:5px solid #f5f5f5;border-left:5px solid transparent;border-right:5px solid transparent;position:absolute;display:block;top:-4px;left:50%;margin-left:-3px}.wy-dropdown-arrow.wy-dropdown-arrow-left:before{left:11px}.wy-form-stacked select{display:block}.wy-form-aligned input,.wy-form-aligned textarea,.wy-form-aligned select,.wy-form-aligned .wy-help-inline,.wy-form-aligned label{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}.wy-form-aligned .wy-control-group>label{display:inline-block;vertical-align:middle;width:10em;margin:6px 12px 0 0;float:left}.wy-form-aligned .wy-control{float:left}.wy-form-aligned .wy-control label{display:block}.wy-form-aligned .wy-control select{margin-top:6px}fieldset{border:0;margin:0;padding:0}legend{display:block;width:100%;border:0;padding:0;white-space:normal;margin-bottom:24px;font-size:150%;*margin-left:-7px}label{display:block;margin:0 0 0.3125em 0;color:#999;font-size:90%}input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}.wy-control-group{margin-bottom:24px;*zoom:1;max-width:68em;margin-left:auto;margin-right:auto;*zoom:1}.wy-control-group:before,.wy-control-group:after{display:table;content:""}.wy-control-group:after{clear:both}.wy-control-group:before,.wy-control-group:after{display:table;content:""}.wy-control-group:after{clear:both}.wy-control-group.wy-control-group-required>label:after{content:" *";color:#E74C3C}.wy-control-group .wy-form-full,.wy-control-group .wy-form-halves,.wy-control-group .wy-form-thirds{padding-bottom:12px}.wy-control-group .wy-form-full select,.wy-control-group .wy-form-halves select,.wy-control-group .wy-form-thirds select{width:100%}.wy-control-group .wy-form-full input[type="text"],.wy-control-group .wy-form-full input[type="password"],.wy-control-group .wy-form-full input[type="email"],.wy-control-group .wy-form-full input[type="url"],.wy-control-group .wy-form-full input[type="date"],.wy-control-group .wy-form-full input[type="month"],.wy-control-group .wy-form-full input[type="time"],.wy-control-group .wy-form-full input[type="datetime"],.wy-control-group .wy-form-full input[type="datetime-local"],.wy-control-group .wy-form-full input[type="week"],.wy-control-group .wy-form-full input[type="number"],.wy-control-group .wy-form-full input[type="search"],.wy-control-group .wy-form-full input[type="tel"],.wy-control-group .wy-form-full input[type="color"],.wy-control-group .wy-form-halves input[type="text"],.wy-control-group .wy-form-halves input[type="password"],.wy-control-group .wy-form-halves input[type="email"],.wy-control-group .wy-form-halves input[type="url"],.wy-control-group .wy-form-halves input[type="date"],.wy-control-group .wy-form-halves input[type="month"],.wy-control-group .wy-form-halves input[type="time"],.wy-control-group .wy-form-halves input[type="datetime"],.wy-control-group .wy-form-halves input[type="datetime-local"],.wy-control-group .wy-form-halves input[type="week"],.wy-control-group .wy-form-halves input[type="number"],.wy-control-group .wy-form-halves input[type="search"],.wy-control-group .wy-form-halves input[type="tel"],.wy-control-group .wy-form-halves input[type="color"],.wy-control-group .wy-form-thirds input[type="text"],.wy-control-group .wy-form-thirds input[type="password"],.wy-control-group .wy-form-thirds input[type="email"],.wy-control-group .wy-form-thirds input[type="url"],.wy-control-group .wy-form-thirds input[type="date"],.wy-control-group .wy-form-thirds input[type="month"],.wy-control-group .wy-form-thirds input[type="time"],.wy-control-group .wy-form-thirds input[type="datetime"],.wy-control-group .wy-form-thirds input[type="datetime-local"],.wy-control-group .wy-form-thirds input[type="week"],.wy-control-group .wy-form-thirds input[type="number"],.wy-control-group .wy-form-thirds input[type="search"],.wy-control-group .wy-form-thirds input[type="tel"],.wy-control-group .wy-form-thirds input[type="color"]{width:100%}.wy-control-group .wy-form-full{float:left;display:block;margin-right:2.35765%;width:100%;margin-right:0}.wy-control-group .wy-form-full:last-child{margin-right:0}.wy-control-group .wy-form-halves{float:left;display:block;margin-right:2.35765%;width:48.82117%}.wy-control-group .wy-form-halves:last-child{margin-right:0}.wy-control-group .wy-form-halves:nth-of-type(2n){margin-right:0}.wy-control-group .wy-form-halves:nth-of-type(2n+1){clear:left}.wy-control-group .wy-form-thirds{float:left;display:block;margin-right:2.35765%;width:31.76157%}.wy-control-group .wy-form-thirds:last-child{margin-right:0}.wy-control-group .wy-form-thirds:nth-of-type(3n){margin-right:0}.wy-control-group .wy-form-thirds:nth-of-type(3n+1){clear:left}.wy-control-group.wy-control-group-no-input .wy-control{margin:6px 0 0 0;font-size:90%}.wy-control-no-input{display:inline-block;margin:6px 0 0 0;font-size:90%}.wy-control-group.fluid-input input[type="text"],.wy-control-group.fluid-input input[type="password"],.wy-control-group.fluid-input input[type="email"],.wy-control-group.fluid-input input[type="url"],.wy-control-group.fluid-input input[type="date"],.wy-control-group.fluid-input input[type="month"],.wy-control-group.fluid-input input[type="time"],.wy-control-group.fluid-input input[type="datetime"],.wy-control-group.fluid-input input[type="datetime-local"],.wy-control-group.fluid-input input[type="week"],.wy-control-group.fluid-input input[type="number"],.wy-control-group.fluid-input input[type="search"],.wy-control-group.fluid-input input[type="tel"],.wy-control-group.fluid-input input[type="color"]{width:100%}.wy-form-message-inline{display:inline-block;padding-left:0.3em;color:#666;vertical-align:middle;font-size:90%}.wy-form-message{display:block;color:#999;font-size:70%;margin-top:0.3125em;font-style:italic}input{line-height:normal}input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;*overflow:visible}input[type="text"],input[type="password"],input[type="email"],input[type="url"],input[type="date"],input[type="month"],input[type="time"],input[type="datetime"],input[type="datetime-local"],input[type="week"],input[type="number"],input[type="search"],input[type="tel"],input[type="color"]{-webkit-appearance:none;padding:6px;display:inline-block;border:1px solid #ccc;font-size:80%;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;box-shadow:inset 0 1px 3px #ddd;border-radius:0;-webkit-transition:border 0.3s linear;-moz-transition:border 0.3s linear;transition:border 0.3s linear}input[type="datetime-local"]{padding:0.34375em 0.625em}input[disabled]{cursor:default}input[type="checkbox"],input[type="radio"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0;margin-right:0.3125em;*height:13px;*width:13px}input[type="search"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}input[type="text"]:focus,input[type="password"]:focus,input[type="email"]:focus,input[type="url"]:focus,input[type="date"]:focus,input[type="month"]:focus,input[type="time"]:focus,input[type="datetime"]:focus,input[type="datetime-local"]:focus,input[type="week"]:focus,input[type="number"]:focus,input[type="search"]:focus,input[type="tel"]:focus,input[type="color"]:focus{outline:0;outline:thin dotted \9;border-color:#333}input.no-focus:focus{border-color:#ccc !important}input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:thin dotted #333;outline:1px auto #129FEA}input[type="text"][disabled],input[type="password"][disabled],input[type="email"][disabled],input[type="url"][disabled],input[type="date"][disabled],input[type="month"][disabled],input[type="time"][disabled],input[type="datetime"][disabled],input[type="datetime-local"][disabled],input[type="week"][disabled],input[type="number"][disabled],input[type="search"][disabled],input[type="tel"][disabled],input[type="color"][disabled]{cursor:not-allowed;background-color:#f3f6f6;color:#cad2d3}input:focus:invalid,textarea:focus:invalid,select:focus:invalid{color:#E74C3C;border:1px solid #E74C3C}input:focus:invalid:focus,textarea:focus:invalid:focus,select:focus:invalid:focus{border-color:#E74C3C}input[type="file"]:focus:invalid:focus,input[type="radio"]:focus:invalid:focus,input[type="checkbox"]:focus:invalid:focus{outline-color:#E74C3C}input.wy-input-large{padding:12px;font-size:100%}textarea{overflow:auto;vertical-align:top;width:100%;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif}select,textarea{padding:0.5em 0.625em;display:inline-block;border:1px solid #ccc;font-size:80%;box-shadow:inset 0 1px 3px #ddd;-webkit-transition:border 0.3s linear;-moz-transition:border 0.3s linear;transition:border 0.3s linear}select{border:1px solid #ccc;background-color:#fff}select[multiple]{height:auto}select:focus,textarea:focus{outline:0}select[disabled],textarea[disabled],input[readonly],select[readonly],textarea[readonly]{cursor:not-allowed;background-color:#fff;color:#cad2d3;border-color:transparent}.wy-checkbox,.wy-radio{margin:6px 0;color:#404040;display:block}.wy-checkbox input,.wy-radio input{vertical-align:baseline}.wy-form-message-inline{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}.wy-input-prefix,.wy-input-suffix{white-space:nowrap}.wy-input-prefix .wy-input-context,.wy-input-suffix .wy-input-context{padding:6px;display:inline-block;font-size:80%;background-color:#f3f6f6;border:solid 1px #ccc;color:#999}.wy-input-suffix .wy-input-context{border-left:0}.wy-input-prefix .wy-input-context{border-right:0}.wy-control-group.wy-control-group-error .wy-form-message,.wy-control-group.wy-control-group-error>label{color:#E74C3C}.wy-control-group.wy-control-group-error input[type="text"],.wy-control-group.wy-control-group-error input[type="password"],.wy-control-group.wy-control-group-error input[type="email"],.wy-control-group.wy-control-group-error input[type="url"],.wy-control-group.wy-control-group-error input[type="date"],.wy-control-group.wy-control-group-error input[type="month"],.wy-control-group.wy-control-group-error input[type="time"],.wy-control-group.wy-control-group-error input[type="datetime"],.wy-control-group.wy-control-group-error input[type="datetime-local"],.wy-control-group.wy-control-group-error input[type="week"],.wy-control-group.wy-control-group-error input[type="number"],.wy-control-group.wy-control-group-error input[type="search"],.wy-control-group.wy-control-group-error input[type="tel"],.wy-control-group.wy-control-group-error input[type="color"]{border:solid 1px #E74C3C}.wy-control-group.wy-control-group-error textarea{border:solid 1px #E74C3C}.wy-inline-validate{white-space:nowrap}.wy-inline-validate .wy-input-context{padding:0.5em 0.625em;display:inline-block;font-size:80%}.wy-inline-validate.wy-inline-validate-success .wy-input-context{color:#27AE60}.wy-inline-validate.wy-inline-validate-danger .wy-input-context{color:#E74C3C}.wy-inline-validate.wy-inline-validate-warning .wy-input-context{color:#E67E22}.wy-inline-validate.wy-inline-validate-info .wy-input-context{color:#2980B9}.rotate-90{-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.rotate-180{-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-ms-transform:rotate(180deg);-o-transform:rotate(180deg);transform:rotate(180deg)}.rotate-270{-webkit-transform:rotate(270deg);-moz-transform:rotate(270deg);-ms-transform:rotate(270deg);-o-transform:rotate(270deg);transform:rotate(270deg)}.mirror{-webkit-transform:scaleX(-1);-moz-transform:scaleX(-1);-ms-transform:scaleX(-1);-o-transform:scaleX(-1);transform:scaleX(-1)}.mirror.rotate-90{-webkit-transform:scaleX(-1) rotate(90deg);-moz-transform:scaleX(-1) rotate(90deg);-ms-transform:scaleX(-1) rotate(90deg);-o-transform:scaleX(-1) rotate(90deg);transform:scaleX(-1) rotate(90deg)}.mirror.rotate-180{-webkit-transform:scaleX(-1) rotate(180deg);-moz-transform:scaleX(-1) rotate(180deg);-ms-transform:scaleX(-1) rotate(180deg);-o-transform:scaleX(-1) rotate(180deg);transform:scaleX(-1) rotate(180deg)}.mirror.rotate-270{-webkit-transform:scaleX(-1) rotate(270deg);-moz-transform:scaleX(-1) rotate(270deg);-ms-transform:scaleX(-1) rotate(270deg);-o-transform:scaleX(-1) rotate(270deg);transform:scaleX(-1) rotate(270deg)}@media only screen and (max-width: 480px){.wy-form button[type="submit"]{margin:0.7em 0 0}.wy-form input[type="text"],.wy-form input[type="password"],.wy-form input[type="email"],.wy-form input[type="url"],.wy-form input[type="date"],.wy-form input[type="month"],.wy-form input[type="time"],.wy-form input[type="datetime"],.wy-form input[type="datetime-local"],.wy-form input[type="week"],.wy-form input[type="number"],.wy-form input[type="search"],.wy-form input[type="tel"],.wy-form input[type="color"]{margin-bottom:0.3em;display:block}.wy-form label{margin-bottom:0.3em;display:block}.wy-form input[type="password"],.wy-form input[type="email"],.wy-form input[type="url"],.wy-form input[type="date"],.wy-form input[type="month"],.wy-form input[type="time"],.wy-form input[type="datetime"],.wy-form input[type="datetime-local"],.wy-form input[type="week"],.wy-form input[type="number"],.wy-form input[type="search"],.wy-form input[type="tel"],.wy-form input[type="color"]{margin-bottom:0}.wy-form-aligned .wy-control-group label{margin-bottom:0.3em;text-align:left;display:block;width:100%}.wy-form-aligned .wy-control{margin:1.5em 0 0 0}.wy-form .wy-help-inline,.wy-form-message-inline,.wy-form-message{display:block;font-size:80%;padding:6px 0}}@media screen and (max-width: 768px){.tablet-hide{display:none}}@media screen and (max-width: 480px){.mobile-hide{display:none}}.float-left{float:left}.float-right{float:right}.full-width{width:100%}.wy-table,.rst-content table.docutils,.rst-content table.field-list{border-collapse:collapse;border-spacing:0;empty-cells:show;margin-bottom:24px}.wy-table caption,.rst-content table.docutils caption,.rst-content table.field-list caption{color:#000;font:italic 85%/1 arial,sans-serif;padding:1em 0;text-align:center}.wy-table td,.rst-content table.docutils td,.rst-content table.field-list td,.wy-table th,.rst-content table.docutils th,.rst-content table.field-list th{font-size:90%;margin:0;overflow:visible;padding:8px 16px}.wy-table td:first-child,.rst-content table.docutils td:first-child,.rst-content table.field-list td:first-child,.wy-table th:first-child,.rst-content table.docutils th:first-child,.rst-content table.field-list th:first-child{border-left-width:0}.wy-table thead,.rst-content table.docutils thead,.rst-content table.field-list thead{color:#000;text-align:left;vertical-align:bottom;white-space:nowrap}.wy-table thead th,.rst-content table.docutils thead th,.rst-content table.field-list thead th{font-weight:bold;border-bottom:solid 2px #e1e4e5}.wy-table td,.rst-content table.docutils td,.rst-content table.field-list td{background-color:transparent;vertical-align:middle}.wy-table td p,.rst-content table.docutils td p,.rst-content table.field-list td p{line-height:18px}.wy-table td p:last-child,.rst-content table.docutils td p:last-child,.rst-content table.field-list td p:last-child{margin-bottom:0}.wy-table .wy-table-cell-min,.rst-content table.docutils .wy-table-cell-min,.rst-content table.field-list .wy-table-cell-min{width:1%;padding-right:0}.wy-table .wy-table-cell-min input[type=checkbox],.rst-content table.docutils .wy-table-cell-min input[type=checkbox],.rst-content table.field-list .wy-table-cell-min input[type=checkbox],.wy-table .wy-table-cell-min input[type=checkbox],.rst-content table.docutils .wy-table-cell-min input[type=checkbox],.rst-content table.field-list .wy-table-cell-min input[type=checkbox]{margin:0}.wy-table-secondary{color:gray;font-size:90%}.wy-table-tertiary{color:gray;font-size:80%}.wy-table-odd td,.wy-table-striped tr:nth-child(2n-1) td,.rst-content table.docutils:not(.field-list) tr:nth-child(2n-1) td{background-color:#f3f6f6}.wy-table-backed{background-color:#f3f6f6}.wy-table-bordered-all,.rst-content table.docutils{border:1px solid #e1e4e5}.wy-table-bordered-all td,.rst-content table.docutils td{border-bottom:1px solid #e1e4e5;border-left:1px solid #e1e4e5}.wy-table-bordered-all tbody>tr:last-child td,.rst-content table.docutils tbody>tr:last-child td{border-bottom-width:0}.wy-table-bordered{border:1px solid #e1e4e5}.wy-table-bordered-rows td{border-bottom:1px solid #e1e4e5}.wy-table-bordered-rows tbody>tr:last-child td{border-bottom-width:0}.wy-table-horizontal tbody>tr:last-child td{border-bottom-width:0}.wy-table-horizontal td,.wy-table-horizontal th{border-width:0 0 1px 0;border-bottom:1px solid #e1e4e5}.wy-table-horizontal tbody>tr:last-child td{border-bottom-width:0}.wy-table-responsive{margin-bottom:24px;max-width:100%;overflow:auto}.wy-table-responsive table{margin-bottom:0 !important}.wy-table-responsive table td,.wy-table-responsive table th{white-space:nowrap}a{color:#2980B9;text-decoration:none}a:hover{color:#3091d1}a:visited{color:#9B59B6}html{height:100%;overflow-x:hidden}body{font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;font-weight:normal;color:#404040;min-height:100%;overflow-x:hidden;background:#edf0f2}.wy-text-left{text-align:left}.wy-text-center{text-align:center}.wy-text-right{text-align:right}.wy-text-large{font-size:120%}.wy-text-normal{font-size:100%}.wy-text-small,small{font-size:80%}.wy-text-strike{text-decoration:line-through}.wy-text-warning{color:#E67E22 !important}a.wy-text-warning:hover{color:#eb9950 !important}.wy-text-info{color:#2980B9 !important}a.wy-text-info:hover{color:#409ad5 !important}.wy-text-success{color:#27AE60 !important}a.wy-text-success:hover{color:#36d278 !important}.wy-text-danger{color:#E74C3C !important}a.wy-text-danger:hover{color:#ed7669 !important}.wy-text-neutral{color:#404040 !important}a.wy-text-neutral:hover{color:#595959 !important}h1,h2,h3,h4,h5,h6,legend{margin-top:0;font-weight:700;font-family:"Roboto Slab","ff-tisa-web-pro","Georgia",Arial,sans-serif}p{line-height:24px;margin:0;font-size:16px;margin-bottom:24px}h1{font-size:175%}h2{font-size:150%}h3{font-size:125%}h4{font-size:115%}h5{font-size:110%}h6{font-size:100%}hr{display:block;height:1px;border:0;border-top:1px solid #e1e4e5;margin:24px 0;padding:0}code,.rst-content tt{white-space:nowrap;max-width:100%;background:#fff;border:solid 1px #e1e4e5;font-size:75%;padding:0 5px;font-family:Consolas,"Andale Mono WT","Andale Mono","Lucida Console","Lucida Sans Typewriter","DejaVu Sans Mono","Bitstream Vera Sans Mono","Liberation Mono","Nimbus Mono L",Monaco,"Courier New",Courier,monospace;color:#E74C3C;overflow-x:auto}code.code-large,.rst-content tt.code-large{font-size:90%}.wy-plain-list-disc,.rst-content .section ul,.rst-content .toctree-wrapper ul,article ul{list-style:disc;line-height:24px;margin-bottom:24px}.wy-plain-list-disc li,.rst-content .section ul li,.rst-content .toctree-wrapper ul li,article ul li{list-style:disc;margin-left:24px}.wy-plain-list-disc li p:last-child,.rst-content .section ul li p:last-child,.rst-content .toctree-wrapper ul li p:last-child,article ul li p:last-child{margin-bottom:0}.wy-plain-list-disc li ul,.rst-content .section ul li ul,.rst-content .toctree-wrapper ul li ul,article ul li ul{margin-bottom:0}.wy-plain-list-disc li li,.rst-content .section ul li li,.rst-content .toctree-wrapper ul li li,article ul li li{list-style:circle}.wy-plain-list-disc li li li,.rst-content .section ul li li li,.rst-content .toctree-wrapper ul li li li,article ul li li li{list-style:square}.wy-plain-list-disc li ol li,.rst-content .section ul li ol li,.rst-content .toctree-wrapper ul li ol li,article ul li ol li{list-style:decimal}.wy-plain-list-decimal,.rst-content .section ol,.rst-content ol.arabic,article ol{list-style:decimal;line-height:24px;margin-bottom:24px}.wy-plain-list-decimal li,.rst-content .section ol li,.rst-content ol.arabic li,article ol li{list-style:decimal;margin-left:24px}.wy-plain-list-decimal li p:last-child,.rst-content .section ol li p:last-child,.rst-content ol.arabic li p:last-child,article ol li p:last-child{margin-bottom:0}.wy-plain-list-decimal li ul,.rst-content .section ol li ul,.rst-content ol.arabic li ul,article ol li ul{margin-bottom:0}.wy-plain-list-decimal li ul li,.rst-content .section ol li ul li,.rst-content ol.arabic li ul li,article ol li ul li{list-style:disc}.codeblock-example{border:1px solid #e1e4e5;border-bottom:none;padding:24px;padding-top:48px;font-weight:500;background:#fff;position:relative}.codeblock-example:after{content:"Example";position:absolute;top:0px;left:0px;background:#9B59B6;color:#fff;padding:6px 12px}.codeblock-example.prettyprint-example-only{border:1px solid #e1e4e5;margin-bottom:24px}.codeblock,pre.literal-block,.rst-content .literal-block,.rst-content pre.literal-block,div[class^='highlight']{border:1px solid #e1e4e5;padding:0px;overflow-x:auto;background:#fff;margin:1px 0 24px 0}.codeblock div[class^='highlight'],pre.literal-block div[class^='highlight'],.rst-content .literal-block div[class^='highlight'],div[class^='highlight'] div[class^='highlight']{border:none;background:none;margin:0}div[class^='highlight'] td.code{width:100%}.linenodiv pre{border-right:solid 1px #e6e9ea;margin:0;padding:12px 12px;font-family:Consolas,"Andale Mono WT","Andale Mono","Lucida Console","Lucida Sans Typewriter","DejaVu Sans Mono","Bitstream Vera Sans Mono","Liberation Mono","Nimbus Mono L",Monaco,"Courier New",Courier,monospace;font-size:12px;line-height:1.5;color:#d9d9d9}div[class^='highlight'] pre{white-space:pre;margin:0;padding:12px 12px;font-family:Consolas,"Andale Mono WT","Andale Mono","Lucida Console","Lucida Sans Typewriter","DejaVu Sans Mono","Bitstream Vera Sans Mono","Liberation Mono","Nimbus Mono L",Monaco,"Courier New",Courier,monospace;font-size:12px;line-height:1.5;display:block;overflow:auto;color:#404040}@media print{.codeblock,pre.literal-block,.rst-content .literal-block,.rst-content pre.literal-block,div[class^='highlight'],div[class^='highlight'] pre{white-space:pre-wrap}}.hll{background-color:#ffc;margin:0 -12px;padding:0 12px;display:block}.c{color:#998;font-style:italic}.err{color:#a61717;background-color:#e3d2d2}.k{font-weight:bold}.o{font-weight:bold}.cm{color:#998;font-style:italic}.cp{color:#999;font-weight:bold}.c1{color:#998;font-style:italic}.cs{color:#999;font-weight:bold;font-style:italic}.gd{color:#000;background-color:#fdd}.gd .x{color:#000;background-color:#faa}.ge{font-style:italic}.gr{color:#a00}.gh{color:#999}.gi{color:#000;background-color:#dfd}.gi .x{color:#000;background-color:#afa}.go{color:#888}.gp{color:#555}.gs{font-weight:bold}.gu{color:purple;font-weight:bold}.gt{color:#a00}.kc{font-weight:bold}.kd{font-weight:bold}.kn{font-weight:bold}.kp{font-weight:bold}.kr{font-weight:bold}.kt{color:#458;font-weight:bold}.m{color:#099}.s{color:#d14}.n{color:#333}.na{color:teal}.nb{color:#0086b3}.nc{color:#458;font-weight:bold}.no{color:teal}.ni{color:purple}.ne{color:#900;font-weight:bold}.nf{color:#900;font-weight:bold}.nn{color:#555}.nt{color:navy}.nv{color:teal}.ow{font-weight:bold}.w{color:#bbb}.mf{color:#099}.mh{color:#099}.mi{color:#099}.mo{color:#099}.sb{color:#d14}.sc{color:#d14}.sd{color:#d14}.s2{color:#d14}.se{color:#d14}.sh{color:#d14}.si{color:#d14}.sx{color:#d14}.sr{color:#009926}.s1{color:#d14}.ss{color:#990073}.bp{color:#999}.vc{color:teal}.vg{color:teal}.vi{color:teal}.il{color:#099}.gc{color:#999;background-color:#EAF2F5}.wy-breadcrumbs li{display:inline-block}.wy-breadcrumbs li.wy-breadcrumbs-aside{float:right}.wy-breadcrumbs li a{display:inline-block;padding:5px}.wy-breadcrumbs li a:first-child{padding-left:0}.wy-breadcrumbs-extra{margin-bottom:0;color:#b3b3b3;font-size:80%;display:inline-block}@media screen and (max-width: 480px){.wy-breadcrumbs-extra{display:none}.wy-breadcrumbs li.wy-breadcrumbs-aside{display:none}}@media print{.wy-breadcrumbs li.wy-breadcrumbs-aside{display:none}}.wy-affix{position:fixed;top:1.618em}.wy-menu a:hover{text-decoration:none}.wy-menu-horiz{*zoom:1}.wy-menu-horiz:before,.wy-menu-horiz:after{display:table;content:""}.wy-menu-horiz:after{clear:both}.wy-menu-horiz ul,.wy-menu-horiz li{display:inline-block}.wy-menu-horiz li:hover{background:rgba(255,255,255,0.1)}.wy-menu-horiz li.divide-left{border-left:solid 1px #404040}.wy-menu-horiz li.divide-right{border-right:solid 1px #404040}.wy-menu-horiz a{height:32px;display:inline-block;line-height:32px;padding:0 16px}.wy-menu-vertical header{height:32px;display:inline-block;line-height:32px;padding:0 1.618em;display:block;font-weight:bold;text-transform:uppercase;font-size:80%;color:#2980B9;white-space:nowrap}.wy-menu-vertical ul{margin-bottom:0}.wy-menu-vertical li.divide-top{border-top:solid 1px #404040}.wy-menu-vertical li.divide-bottom{border-bottom:solid 1px #404040}.wy-menu-vertical li.current{background:#e3e3e3}.wy-menu-vertical li.current a{color:gray;border-right:solid 1px #c9c9c9;padding:0.4045em 2.427em}.wy-menu-vertical li.current a:hover{background:#d6d6d6}.wy-menu-vertical li.on a,.wy-menu-vertical li.current>a{color:#404040;padding:0.4045em 1.618em;font-weight:bold;position:relative;background:#fcfcfc;border:none;border-bottom:solid 1px #c9c9c9;border-top:solid 1px #c9c9c9;padding-left:1.618em -4px}.wy-menu-vertical li.on a:hover,.wy-menu-vertical li.current>a:hover{background:#fcfcfc}.wy-menu-vertical li.toctree-l2.current>a{background:#c9c9c9;padding:0.4045em 2.427em}.wy-menu-vertical li.current ul{display:block}.wy-menu-vertical li ul{margin-bottom:0;display:none}.wy-menu-vertical .local-toc li ul{display:block}.wy-menu-vertical li ul li a{margin-bottom:0;color:#b3b3b3;font-weight:normal}.wy-menu-vertical a{display:inline-block;line-height:18px;padding:0.4045em 1.618em;display:block;position:relative;font-size:90%;color:#b3b3b3}.wy-menu-vertical a:hover{background-color:#4e4a4a;cursor:pointer}.wy-menu-vertical a:active{background-color:#2980B9;cursor:pointer;color:#fff}.wy-side-nav-search{z-index:200;background-color:#2980B9;text-align:center;padding:0.809em;display:block;color:#fcfcfc;margin-bottom:0.809em}.wy-side-nav-search input[type=text]{width:100%;border-radius:50px;padding:6px 12px;border-color:#2472a4}.wy-side-nav-search img{display:block;margin:auto auto 0.809em auto;height:45px;width:45px;background-color:#2980B9;padding:5px;border-radius:100%}.wy-side-nav-search>a,.wy-side-nav-search .wy-dropdown>a{color:#fcfcfc;font-size:100%;font-weight:bold;display:inline-block;padding:4px 6px;margin-bottom:0.809em}.wy-side-nav-search>a:hover,.wy-side-nav-search .wy-dropdown>a:hover{background:rgba(255,255,255,0.1)}.wy-nav .wy-menu-vertical header{color:#2980B9}.wy-nav .wy-menu-vertical a{color:#b3b3b3}.wy-nav .wy-menu-vertical a:hover{background-color:#2980B9;color:#fff}[data-menu-wrap]{-webkit-transition:all 0.2s ease-in;-moz-transition:all 0.2s ease-in;transition:all 0.2s ease-in;position:absolute;opacity:1;width:100%;opacity:0}[data-menu-wrap].move-center{left:0;right:auto;opacity:1}[data-menu-wrap].move-left{right:auto;left:-100%;opacity:0}[data-menu-wrap].move-right{right:-100%;left:auto;opacity:0}.wy-body-for-nav{background:left repeat-y #fcfcfc;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDoxOERBMTRGRDBFMUUxMUUzODUwMkJCOThDMEVFNURFMCIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDoxOERBMTRGRTBFMUUxMUUzODUwMkJCOThDMEVFNURFMCI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjE4REExNEZCMEUxRTExRTM4NTAyQkI5OEMwRUU1REUwIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjE4REExNEZDMEUxRTExRTM4NTAyQkI5OEMwRUU1REUwIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+EwrlwAAAAA5JREFUeNpiMDU0BAgwAAE2AJgB9BnaAAAAAElFTkSuQmCC);background-size:300px 1px}.wy-grid-for-nav{position:absolute;width:100%;height:100%}.wy-nav-side{position:absolute;top:0;left:0;width:300px;overflow:hidden;min-height:100%;background:#343131;z-index:200}.wy-nav-top{display:none;background:#2980B9;color:#fff;padding:0.4045em 0.809em;position:relative;line-height:50px;text-align:center;font-size:100%;*zoom:1}.wy-nav-top:before,.wy-nav-top:after{display:table;content:""}.wy-nav-top:after{clear:both}.wy-nav-top a{color:#fff;font-weight:bold}.wy-nav-top img{margin-right:12px;height:45px;width:45px;background-color:#2980B9;padding:5px;border-radius:100%}.wy-nav-top i{font-size:30px;float:left;cursor:pointer}.wy-nav-content-wrap{margin-left:300px;background:#fcfcfc;min-height:100%}.wy-nav-content{padding:1.618em 3.236em;height:100%;max-width:800px;margin:auto}.wy-body-mask{position:fixed;width:100%;height:100%;background:rgba(0,0,0,0.2);display:none;z-index:499}.wy-body-mask.on{display:block}footer{color:#999}footer p{margin-bottom:12px}.rst-footer-buttons{*zoom:1}.rst-footer-buttons:before,.rst-footer-buttons:after{display:table;content:""}.rst-footer-buttons:after{clear:both}#search-results .search li{margin-bottom:24px;border-bottom:solid 1px #e1e4e5;padding-bottom:24px}#search-results .search li:first-child{border-top:solid 1px #e1e4e5;padding-top:24px}#search-results .search li a{font-size:120%;margin-bottom:12px;display:inline-block}#search-results .context{color:gray;font-size:90%}@media screen and (max-width: 768px){.wy-body-for-nav{background:#fcfcfc}.wy-nav-top{display:block}.wy-nav-side{left:-300px}.wy-nav-side.shift{width:85%;left:0}.wy-nav-content-wrap{margin-left:0}.wy-nav-content-wrap .wy-nav-content{padding:1.618em}.wy-nav-content-wrap.shift{position:fixed;min-width:100%;left:85%;top:0;height:100%;overflow:hidden}}@media screen and (min-width: 1400px){.wy-nav-content-wrap{background:rgba(0,0,0,0.05)}.wy-nav-content{margin:0;background:#fcfcfc}}@media print{.rst-versions,footer,.wy-nav-side{display:none}.wy-nav-content-wrap{margin-left:0}}nav.stickynav{position:fixed;top:0}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;border-top:solid 10px #343131;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;z-index:400}.rst-versions a{color:#2980B9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27AE60;*zoom:1}.rst-versions .rst-current-version:before,.rst-versions .rst-current-version:after{display:table;content:""}.rst-versions .rst-current-version:after{clear:both}.rst-versions .rst-current-version .fa,.rst-versions .rst-current-version .rst-content .admonition-title,.rst-content .rst-versions .rst-current-version .admonition-title,.rst-versions .rst-current-version .rst-content h1 .headerlink,.rst-content h1 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h2 .headerlink,.rst-content h2 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h3 .headerlink,.rst-content h3 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h4 .headerlink,.rst-content h4 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h5 .headerlink,.rst-content h5 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h6 .headerlink,.rst-content h6 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content dl dt .headerlink,.rst-content dl dt .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .icon{color:#fcfcfc}.rst-versions .rst-current-version .fa-book,.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#E74C3C;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#F1C40F;color:#000}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:gray;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:solid 1px #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px}.rst-versions.rst-badge .icon-book{float:none}.rst-versions.rst-badge .fa-book,.rst-versions.rst-badge .icon-book{float:none}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book,.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge .rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width: 768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}img{width:100%;height:auto}}.rst-content img{max-width:100%;height:auto !important}.rst-content div.figure{margin-bottom:24px}.rst-content div.figure.align-center{text-align:center}.rst-content .section>img{margin-bottom:24px}.rst-content blockquote{margin-left:24px;line-height:24px;margin-bottom:24px}.rst-content .note .last,.rst-content .attention .last,.rst-content .caution .last,.rst-content .danger .last,.rst-content .error .last,.rst-content .hint .last,.rst-content .important .last,.rst-content .tip .last,.rst-content .warning .last,.rst-content .seealso .last,.rst-content .admonition-todo .last{margin-bottom:0}.rst-content .admonition-title:before{margin-right:4px}.rst-content .admonition table{border-color:rgba(0,0,0,0.1)}.rst-content .admonition table td,.rst-content .admonition table th{background:transparent !important;border-color:rgba(0,0,0,0.1) !important}.rst-content .section ol.loweralpha,.rst-content .section ol.loweralpha li{list-style:lower-alpha}.rst-content .section ol.upperalpha,.rst-content .section ol.upperalpha li{list-style:upper-alpha}.rst-content .section ol p,.rst-content .section ul p{margin-bottom:12px}.rst-content .line-block{margin-left:24px}.rst-content .topic-title{font-weight:bold;margin-bottom:12px}.rst-content .toc-backref{color:#404040}.rst-content .align-right{float:right;margin:0px 0px 24px 24px}.rst-content .align-left{float:left;margin:0px 24px 24px 0px}.rst-content .align-center{margin:auto;display:block}.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content dl dt .headerlink{display:none;visibility:hidden;font-size:14px}.rst-content h1 .headerlink:after,.rst-content h2 .headerlink:after,.rst-content h3 .headerlink:after,.rst-content h4 .headerlink:after,.rst-content h5 .headerlink:after,.rst-content h6 .headerlink:after,.rst-content dl dt .headerlink:after{visibility:visible;content:"";font-family:FontAwesome;display:inline-block}.rst-content h1:hover .headerlink,.rst-content h2:hover .headerlink,.rst-content h3:hover .headerlink,.rst-content h4:hover .headerlink,.rst-content h5:hover .headerlink,.rst-content h6:hover .headerlink,.rst-content dl dt:hover .headerlink{display:inline-block}.rst-content .sidebar{float:right;width:40%;display:block;margin:0 0 24px 24px;padding:24px;background:#f3f6f6;border:solid 1px #e1e4e5}.rst-content .sidebar p,.rst-content .sidebar ul,.rst-content .sidebar dl{font-size:90%}.rst-content .sidebar .last{margin-bottom:0}.rst-content .sidebar .sidebar-title{display:block;font-family:"Roboto Slab","ff-tisa-web-pro","Georgia",Arial,sans-serif;font-weight:bold;background:#e1e4e5;padding:6px 12px;margin:-24px;margin-bottom:24px;font-size:100%}.rst-content .highlighted{background:#F1C40F;display:inline-block;font-weight:bold;padding:0 6px}.rst-content .footnote-reference,.rst-content .citation-reference{vertical-align:super;font-size:90%}.rst-content table.docutils.citation,.rst-content table.docutils.footnote{background:none;border:none;color:#999}.rst-content table.docutils.citation td,.rst-content table.docutils.citation tr,.rst-content table.docutils.footnote td,.rst-content table.docutils.footnote tr{border:none;background-color:transparent !important;white-space:normal}.rst-content table.docutils.citation td.label,.rst-content table.docutils.footnote td.label{padding-left:0;padding-right:0;vertical-align:top}.rst-content table.field-list{border:none}.rst-content table.field-list td{border:none;padding-top:5px}.rst-content table.field-list td>strong{display:inline-block;margin-top:3px}.rst-content table.field-list .field-name{padding-right:10px;text-align:left;white-space:nowrap}.rst-content table.field-list .field-body{text-align:left;padding-left:0}.rst-content tt{color:#000}.rst-content tt big,.rst-content tt em{font-size:100% !important;line-height:normal}.rst-content tt .xref,a .rst-content tt{font-weight:bold}.rst-content a tt{color:#2980B9}.rst-content dl{margin-bottom:24px}.rst-content dl dt{font-weight:bold}.rst-content dl p,.rst-content dl table,.rst-content dl ul,.rst-content dl ol{margin-bottom:12px !important}.rst-content dl dd{margin:0 0 12px 24px}.rst-content dl:not(.docutils){margin-bottom:24px}.rst-content dl:not(.docutils) dt{display:inline-block;margin:6px 0;font-size:90%;line-height:normal;background:#e7f2fa;color:#2980B9;border-top:solid 3px #6ab0de;padding:6px;position:relative}.rst-content dl:not(.docutils) dt:before{color:#6ab0de}.rst-content dl:not(.docutils) dt .headerlink{color:#404040;font-size:100% !important}.rst-content dl:not(.docutils) dl dt{margin-bottom:6px;border:none;border-left:solid 3px #ccc;background:#f0f0f0;color:gray}.rst-content dl:not(.docutils) dl dt .headerlink{color:#404040;font-size:100% !important}.rst-content dl:not(.docutils) dt:first-child{margin-top:0}.rst-content dl:not(.docutils) tt{font-weight:bold}.rst-content dl:not(.docutils) tt.descname,.rst-content dl:not(.docutils) tt.descclassname{background-color:transparent;border:none;padding:0;font-size:100% !important}.rst-content dl:not(.docutils) tt.descname{font-weight:bold}.rst-content dl:not(.docutils) .optional{display:inline-block;padding:0 4px;color:#000;font-weight:bold}.rst-content dl:not(.docutils) .property{display:inline-block;padding-right:8px}.rst-content .viewcode-link,.rst-content .viewcode-back{display:inline-block;color:#27AE60;font-size:80%;padding-left:24px}.rst-content .viewcode-back{display:block;float:right}.rst-content p.rubric{margin-bottom:12px;font-weight:bold}@media screen and (max-width: 480px){.rst-content .sidebar{width:100%}}span[id*='MathJax-Span']{color:#404040}.math{text-align:center}
diff --git a/css/theme_extra.css b/css/theme_extra.css
deleted file mode 100644
index b8b06d7f..00000000
--- a/css/theme_extra.css
+++ /dev/null
@@ -1,178 +0,0 @@
-/*
- * Sphinx doesn't have support for section dividers like we do in
- * MkDocs, this styles the section titles in the nav
- *
- * https://github.com/mkdocs/mkdocs/issues/175
- */
-.wy-menu-vertical span {
-    line-height: 18px;
-    padding: 0.4045em 1.618em;
-    display: block;
-    position: relative;
-    font-size: 90%;
-    color: #838383;
-}
-
-.wy-menu-vertical .subnav a {
-    padding: 0.4045em 2.427em;
-}
-
-/*
- * Long navigations run off the bottom of the screen as the nav
- * area doesn't scroll.
- *
- * https://github.com/mkdocs/mkdocs/pull/202
- */
-.wy-nav-side {
-    height: 100%;
-    overflow-y: auto;
-}
-
-/*
- * readthedocs theme hides nav items when the window height is
- * too small to contain them.
- *
- * https://github.com/mkdocs/mkdocs/issues/#348
- */
-.wy-menu-vertical ul {
-  margin-bottom: 2em;
-}
-
-/*
- * Wrap inline code samples otherwise they shoot of the side and
- * can't be read at all.
- *
- * https://github.com/mkdocs/mkdocs/issues/313
- * https://github.com/mkdocs/mkdocs/issues/233
- * https://github.com/mkdocs/mkdocs/issues/834
- */
-code {
-    white-space: pre-wrap;
-    word-wrap: break-word;
-    padding: 2px 5px;
-}
-
-/**
- * Make code blocks display as blocks and give them the appropriate
- * font size and padding.
- *
- * https://github.com/mkdocs/mkdocs/issues/855
- * https://github.com/mkdocs/mkdocs/issues/834
- * https://github.com/mkdocs/mkdocs/issues/233
- */
-pre code {
-  white-space: pre;
-  word-wrap: normal;
-  display: block;
-  padding: 12px;
-  font-size: 12px;
-}
-
-/*
- * Fix link colors when the link text is inline code.
- *
- * https://github.com/mkdocs/mkdocs/issues/718
- */
-a code {
-    color: #2980B9;
-}
-a:hover code {
-    color: #3091d1;
-}
-a:visited code {
-    color: #9B59B6;
-}
-
-/*
- * The CSS classes from highlight.js seem to clash with the
- * ReadTheDocs theme causing some code to be incorrectly made
- * bold and italic.
- *
- * https://github.com/mkdocs/mkdocs/issues/411
- */
-pre .cs, pre .c {
-    font-weight: inherit;
-    font-style: inherit;
-}
-
-/*
- * Fix some issues with the theme and non-highlighted code
- * samples. Without and highlighting styles attached the
- * formatting is broken.
- *
- * https://github.com/mkdocs/mkdocs/issues/319
- */
-.no-highlight {
-  display: block;
-  padding: 0.5em;
-  color: #333;
-}
-
-
-/*
- * Additions specific to the search functionality provided by MkDocs
- */
-
-.search-results article {
-    margin-top: 23px;
-    border-top: 1px solid #E1E4E5;
-    padding-top: 24px;
-}
-
-.search-results article:first-child {
-    border-top: none;
-}
-
-form .search-query {
-    width: 100%;
-    border-radius: 50px;
-    padding: 6px 12px;  /* csslint allow: box-model */
-    border-color: #D1D4D5;
-}
-
-.wy-menu-vertical li ul {
-    display: inherit;
-}
-
-.wy-menu-vertical li ul.subnav ul.subnav{
-    padding-left: 1em;
-}
-
-.wy-menu-vertical .subnav li.current > a {
-    padding-left: 2.42em;
-}
-.wy-menu-vertical .subnav li.current > ul li a {
-    padding-left: 3.23em;
-}
-
-/*
- * Improve inline code blocks within admonitions.
- *
- * https://github.com/mkdocs/mkdocs/issues/656
- */
- .admonition code {
-  color: #404040;
-  border: 1px solid #c7c9cb;
-  border: 1px solid rgba(0, 0, 0, 0.2);
-  background: #f8fbfd;
-  background: rgba(255, 255, 255, 0.7);
-}
-
-/*
- * Account for wide tables which go off the side.
- * Override borders to avoid wierdness on narrow tables.
- * 
- * https://github.com/mkdocs/mkdocs/issues/834
- * https://github.com/mkdocs/mkdocs/pull/1034
- */
-.rst-content .section .docutils {
-    width: 100%;
-    overflow: auto;
-    display: block;
-    border: none;
-}
-
-td, th {
-   border: 1px solid #e1e4e5 !important; /* csslint allow: important */
-   border-collapse: collapse;
-}
diff --git a/data/Dockerfiles/dovecot/Dockerfile b/data/Dockerfiles/dovecot/Dockerfile
new file mode 100644
index 00000000..b98cd908
--- /dev/null
+++ b/data/Dockerfiles/dovecot/Dockerfile
@@ -0,0 +1,87 @@
+From ubuntu:xenial
+MAINTAINER Andre Peters <[email protected]>
+
+ENV DEBIAN_FRONTEND noninteractive
+ENV LC_ALL C
+
+RUN dpkg-divert --local --rename --add /sbin/initctl \
+    && ln -sf /bin/true /sbin/initctl \
+    && dpkg-divert --local --rename --add /usr/bin/ischroot \
+    && ln -sf /bin/true /usr/bin/ischroot
+
+RUN apt-get update
+RUN apt-get -y install dovecot-common \
+	dovecot-core \
+	dovecot-imapd \
+	dovecot-lmtpd \
+	dovecot-managesieved \
+	dovecot-sieve \
+	dovecot-mysql \
+	dovecot-pop3d \
+	dovecot-dev \
+	syslog-ng \
+	syslog-ng-core \
+	ca-certificates \
+	supervisor \
+	wget \
+	curl \
+	build-essential \
+	autotools-dev \
+	automake \
+	libauthen-ntlm-perl \
+	libcrypt-ssleay-perl \
+	libdigest-hmac-perl \
+	libfile-copy-recursive-perl \
+	libio-compress-perl \
+	libio-socket-inet6-perl \
+	libio-socket-ssl-perl \
+	libio-tee-perl \
+	libmodule-scandeps-perl \
+	libnet-ssleay-perl \
+	libpar-packer-perl \
+	libreadonly-perl \
+	libterm-readkey-perl \
+	libtest-pod-perl \
+	libtest-simple-perl \
+	libunicode-string-perl \
+	liburi-perl \
+	libdbi-perl \
+	liblockfile-simple-perl \
+	libdbd-mysql-perl \
+	libipc-run-perl \
+	make \
+	cpanminus
+
+RUN sed -i -E 's/^(\s*)system\(\);/\1unix-stream("\/dev\/log");/' /etc/syslog-ng/syslog-ng.conf
+RUN cpanm Data::Uniqid Mail::IMAPClient String::Util
+RUN echo '* * * * *   root   /usr/local/bin/imapsync_cron.pl' > /etc/cron.d/imapsync
+RUN echo '30 3 * * *   vmail  /usr/bin/doveadm quota recalc -A' > /etc/cron.d/dovecot-sync
+
+WORKDIR /tmp
+
+RUN wget http://hg.dovecot.org/dovecot-antispam-plugin/archive/tip.tar.gz -O - | tar xvz  \
+	&& cd /tmp/dovecot-antispam* \
+	&& ./autogen.sh \
+	&& ./configure --prefix=/usr \
+	&& make \
+	&& make install
+
+COPY ./imapsync /usr/local/bin/imapsync
+COPY ./postlogin.sh /usr/local/bin/postlogin.sh
+COPY ./imapsync_cron.pl /usr/local/bin/imapsync_cron.pl
+COPY ./rspamd-pipe /usr/local/bin/rspamd-pipe
+COPY ./docker-entrypoint.sh /
+COPY ./supervisord.conf /etc/supervisor/supervisord.conf
+
+RUN chmod +x /usr/local/bin/rspamd-pipe
+RUN chmod +x /usr/local/bin/imapsync_cron.pl
+
+RUN groupadd -g 5000 vmail
+RUN useradd -g vmail -u 5000 vmail -d /var/vmail
+
+EXPOSE 24 10001
+
+ENTRYPOINT ["/docker-entrypoint.sh"]
+CMD exec /usr/bin/supervisord -c /etc/supervisor/supervisord.conf
+
+RUN apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
diff --git a/data/Dockerfiles/dovecot/docker-entrypoint.sh b/data/Dockerfiles/dovecot/docker-entrypoint.sh
new file mode 100755
index 00000000..a015af2b
--- /dev/null
+++ b/data/Dockerfiles/dovecot/docker-entrypoint.sh
@@ -0,0 +1,28 @@
+#!/bin/bash
+set -e
+
+# 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')
+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 /etc/sogo ]] && mkdir -p /etc/sogo
+cat /etc/dovecot/sieve_after > /var/vmail/sieve/global.sieve
+sievec /var/vmail/sieve/global.sieve
+chown -R vmail:vmail /var/vmail/sieve
+
+# Do not do this every start-up, it may take a very long time. So we use a stat check here.
+if [[ $(stat -c %U /var/vmail/) != "vmail" ]] ; then chown -R vmail:vmail /var/vmail ; fi
+
+# Create random master for SOGo sieve features
+RAND_USER=$(cat /dev/urandom | tr -dc 'a-z0-9' | fold -w 16 | head -n 1)
+RAND_PASS=$(cat /dev/urandom | tr -dc 'a-z0-9' | fold -w 24 | head -n 1)
+echo ${RAND_USER}:$(doveadm pw -s SHA1 -p ${RAND_PASS}) > /etc/dovecot/dovecot-master.passwd
+echo ${RAND_USER}:${RAND_PASS} > /etc/sogo/sieve.creds
+
+exec "$@"
diff --git a/data/Dockerfiles/dovecot/imapsync b/data/Dockerfiles/dovecot/imapsync
new file mode 100755
index 00000000..8df547c6
--- /dev/null
+++ b/data/Dockerfiles/dovecot/imapsync
@@ -0,0 +1,9488 @@
+#!/usr/bin/perl
+
+# $Id: imapsync,v 1.727 2016/08/19 10:30:36 gilles Exp gilles $
+# structure
+# pod documentation
+# pragmas
+# main program
+# global variables initialisation
+# get_options(  ) ;
+# default values
+# folder loop
+# subroutines
+# sub usage {
+# IMAPClient 3.xx ads
+
+# pod documentation
+
+=pod
+
+=head1 NAME
+
+imapsync - Email IMAP tool for syncing, copying and migrating email mailboxes.
+
+The imapsync command synchronises mailboxes between two imap servers.
+More than 69 different IMAP server softwares supported with success, 
+few failures.
+
+$Revision: 1.727 $
+
+=head1 SYNOPSIS
+
+ To synchronize the source imap account
+   "test1" on server "test1.lamiral.info" with password "secret1"
+ to the destination imap account
+   "test2" on server "test2.lamiral.info" with password "secret2"
+ do:
+
+  imapsync \
+   --host1 test1.lamiral.info --user1 test1 --password1 secret1 \
+   --host2 test2.lamiral.info --user2 test2 --password2 secret2
+
+=head1 REQUIRED ARGUMENTS
+
+The required argmuments are the six values, three on each sides,
+needed to login into the IMAP servers, 
+a host, a username, and a password, two times.
+
+=head1 INSTALL
+
+ Imapsync works under any Unix with perl.
+ Imapsync works under Windows (2000, XP, Vista, Seven)
+ as a standalone binary software called imapsync.exe
+ Imapsync works under OS X as a standalone binary
+ software called imapsync_bin_Darwin.
+
+ Purchase latest imapsync at
+ http://imapsync.lamiral.info/
+
+ You'll receive a link to a compressed tarball called imapsync-x.xx.tgz
+ where x.xx is the version number. Untar the tarball where
+ you want (on Unix):
+
+ tar xzvf  imapsync-x.xx.tgz
+
+ Go into the directory imapsync-x.xx and read the INSTALL file.
+ As mentioned at http://imapsync.lamiral.info/#install
+ the INSTALL file can also be found at
+ http://imapsync.lamiral.info/INSTALL
+ It is now split in several files for each system
+ http://imapsync.lamiral.info/INSTALL.d/
+
+=head1 CONFIGURATION
+
+There is no specific configuration file for imapsync,
+everything is specified by the command line parameteres
+and the default behavior.
+
+=head1 USAGE
+
+To get a description of each option just run imapsync
+with no argument, like this:
+
+  imapsync
+
+This description of options is also available at
+http://imapsync.lamiral.info/OPTIONS and is
+reproduced here:
+
+ usage: ./imapsync [options]
+
+ Several options are mandatory.
+ str means string
+ int means integer
+ reg means regular expression
+ cmd means command
+
+ --dry               : Makes imapsync doing nothing, just print what would
+                       be done without --dry.
+
+ --host1        str  : Source or "from" imap server. Mandatory.
+ --port1        int  : Port to connect on host1. Default is 143, 993 if --ssl1
+ --user1        str  : User to login on host1. Mandatory.
+ --showpasswords     : Shows passwords on output instead of "MASKED".
+                       Useful to restart a complete run by just reading the log.
+ --password1    str  : Password for the user1.
+ --host2        str  : "destination" imap server. Mandatory.
+ --port2        int  : Port to connect on host2. Default is 143, 993 if --ssl2
+ --user2        str  : User to login on host2. Mandatory.
+ --password2    str  : Password for the user2.
+
+ --passfile1    str  : Password file for the user1. It must contain the
+                       password on the first line. This option avoids to show
+                       the password on the command line like --password1 does.
+ --passfile2    str  : Password file for the user2. Contains the password.
+
+ --ssl1              : Use a SSL connection on host1.
+ --ssl2              : Use a SSL connection on host2.
+ --tls1              : Use a TLS connection on host1.
+ --tls2              : Use a TLS connection on host2.
+ --debugssl     int  : SSL debug mode from 0 to 4.
+ --sslargs1     str  : Pass any ssl parameter for host1 ssl or tls connection. Example:
+                       --sslargs1 SSL_verify_mode=1 --sslargs1 SSL_version=SSLv3
+                       See all possibilities in the new() method of IO::Socket::SSL
+                       http://search.cpan.org/perldoc?IO::Socket::SSL#Description_Of_Methods
+ --sslargs2     str  : Pass any ssl parameter for host2 ssl or tls connection.
+                       See --sslargs1
+
+ --timeout1     int  : Connection timeout in seconds for host1.
+                       Default is 120 and 0 means no timeout at all.
+ --timeout2     int  : Connection timeout in seconds for host2.
+                       Default is 120 and 0 means no timeout at all.
+
+ --authmech1    str  : Auth mechanism to use with host1:
+                       PLAIN, LOGIN, CRAM-MD5 etc. Use UPPERCASE.
+ --authmech2    str  : Auth mechanism to use with host2. See --authmech1
+
+ --authuser1    str  : User to auth with on host1 (admin user).
+                       Avoid using --authmech1 SOMETHING with --authuser1.
+ --authuser2    str  : User to auth with on host2 (admin user).
+ --proxyauth1        : Use proxyauth on host1. Requires --authuser1.
+                       Required by Sun/iPlanet/Netscape IMAP servers to
+                       be able to use an administrative user.
+ --proxyauth2        : Use proxyauth on host2. Requires --authuser2.
+
+ --authmd51          : Use MD5 authentification for host1.
+ --authmd52          : Use MD5 authentification for host2.
+ --domain1      str  : Domain on host1 (NTLM authentication).
+ --domain2      str  : Domain on host2 (NTLM authentication).
+
+
+ --folder       str  : Sync this folder.
+ --folder       str  : and this one, etc.
+ --folderrec    str  : Sync this folder recursively.
+ --folderrec    str  : and this one, etc.
+
+ --folderfirst  str  : Sync this folder first. --folderfirst "Work"
+ --folderfirst  str  : then this one, etc.
+ --folderlast   str  : Sync this folder last. --folderlast "[Gmail]/All Mail"
+ --folderlast   str  : then this one, etc.
+
+ --nomixfolders      : Do not merge folders when host1 is case sensitive
+                       while host2 is not (like Exchange). Only the first
+                       similar folder is synced (ex: Sent SENT sent -> Sent).
+
+ --skipemptyfolders  : Empty host1 folders are not created on host2.
+
+ --f1f2    str1=str2 : Force folder str1 to be synced to str2.
+ --include      reg  : Sync folders matching this regular expression
+ --include      reg  : or this one, etc.
+                       in case both --include --exclude options are
+                       use, include is done before.
+ --exclude      reg  : Skips folders matching this regular expression
+                       Several folders to avoid:
+                        --exclude 'fold1|fold2|f3' skips fold1, fold2 and f3.
+ --exclude      reg  : or this one, etc.
+
+ --subfolder2   str  : Move whole host1 folders hierarchy under this
+                       host2 folder  str    .
+                       It does it by adding two --regextrans2 options before
+                       all others. Add --debug to see what's really going on.
+
+ --regextrans2  reg  : Apply the whole regex to each destination folders.
+ --regextrans2  reg  : and this one. etc.
+                       When you play with the --regextrans2 option, first
+                       add also the safe options --dry --justfolders
+                       Then, when happy, remove --dry, remove --justfolders.
+                       Have in mind that --regextrans2 is applied after prefix
+                       and separator inversion.
+
+ --tmpdir       str  : Where to store temporary files and subdirectories.
+                       Will be created if it doesn't exist.
+                       Default is system specific, Unix is /tmp but
+                       it's often small and deleted at reboot.
+                       --tmpdir /var/tmp should be better.
+ --pidfile      str  : The file where imapsync pid is written.
+ --pidfilelocking    : Abort if pidfile already exists. Usefull to avoid
+                       concurrent transfers on the same mailbox.
+
+ --nolog             : Turn off logging on file
+ --logfile      str  : Change the default log filename (can be dirname/filename).
+ --logdir       str  : Change the default log directory. Default is LOG_imapsync
+
+ --prefix1      str  : Remove prefix to all destination folders
+                       (usually INBOX. or INBOX/ or an empty string "")
+                       you have to use --prefix1 if host1 imap server
+                       does not have NAMESPACE capability, so imapsync
+                       suggests to use it. All other cases are bad.
+ --prefix2      str  : Add prefix to all host2 folders. See --prefix1
+ --sep1         str  : Host1 separator in case NAMESPACE is not supported.
+ --sep2         str  : Host2 separator in case NAMESPACE is not supported.
+
+ --skipmess     reg  : Skips messages maching the regex.
+                       Example: 'm/[\x80-ff]/' # to avoid 8bits messages.
+                       --skipmess is applied before --regexmess
+ --skipmess     reg  : or this one, etc.
+
+ --pipemess     cmd  : Apply this cmd command to each message content
+                       before the copy.
+ --pipemess     cmd  : and this one, etc.
+
+ --disarmreadreceipts : Disarms read receipts (host2 Exchange issue)
+
+ --regexmess    reg  : Apply the whole regex to each message before transfer.
+                       Example: 's/\000/ /g' # to replace null by space.
+ --regexmess    reg  : and this one, etc.
+
+ --regexflag    reg  : Apply the whole regex to each flags list.
+                       Example: 's/"Junk"//g' # to remove "Junk" flag.
+ --regexflag    reg  : and this one, etc.
+
+ --delete            : Deletes messages on host1 server after a successful
+                       transfer. Option --delete has the following behavior:
+                       it marks messages as deleted with the IMAP flag
+                       \Deleted, then messages are really deleted with an
+                       EXPUNGE IMAP command.
+
+ --delete2           : Delete messages in host2 that are not in
+                       host1 server. Useful for backup or pre-sync.
+ --delete2duplicates : Delete messages in host2 that are duplicates.
+                       Works only without --useuid since duplicates are
+                       detected with an header part of each message.
+
+ --delete2folders    : Delete folders in host2 that are not in host1 server.
+                       For safety, first try it like this (it is safe):
+                       --delete2folders --dry --justfolders --nofoldersizes
+ --delete2foldersonly   reg : Deleted only folders matching regex.
+                              Example: --delete2foldersonly "/^Junk$|^INBOX.Junk$/"
+ --delete2foldersbutnot reg : Do not delete folders matching regex.
+                              Example: --delete2foldersbutnot "/Tasks$|Contacts$|Foo$/"
+ --noexpunge         : Do not expunge messages on host1.
+                       Expunge really deletes messages marked deleted.
+                       Expunge is made at the beginning, on host1 only.
+                       Newly transferred messages are also expunged if
+                       option --delete is given.
+                       No expunge is done on host2 account (unless --expunge2)
+ --expunge1          : Expunge messages on host1 after messages transfer.
+ --expunge2          : Expunge messages on host2 after messages transfer.
+ --uidexpunge2       : uidexpunge messages on the host2 account
+                       that are not on the host1 account, requires --delete2
+ --nomixfolders      : Avoid merging folders that are considered different on
+                       host1 but the same on destination host2 because of
+                       case sensitivities and insensitivities.
+
+ --syncinternaldates : Sets the internal dates on host2 same as host1.
+                       Turned on by default. Internal date is the date
+                       a message arrived on a host (mtime).
+ --idatefromheader   : Sets the internal dates on host2 same as the
+                       "Date:" headers.
+
+ --maxsize      int  : Skip messages larger  (or equal) than  int  bytes
+ --minsize      int  : Skip messages smaller (or equal) than  int  bytes
+ --maxage       int  : Skip messages older than  int  days.
+                       final stats (skipped) don't count older messages
+                       see also --minage
+ --minage       int  : Skip messages newer than  int  days.
+                       final stats (skipped) don't count newer messages
+                       You can do (+ are the messages selected):
+                       past|----maxage+++++++++++++++>now
+                       past|+++++++++++++++minage---->now
+                       past|----maxage+++++minage---->now (intersection)
+                       past|++++minage-----maxage++++>now (union)
+
+ --search       str  : Selects only messages returned by this IMAP SEARCH
+                       command. Applied on both sides.
+ --search1      str  : Same as --search for selecting host1 messages only.
+ --search2      str  : Same as --search for selecting host2 messages only.
+                       --search CRIT equals --search1 CRIT --search2 CRIT
+
+ --exitwhenover int  : Stop syncing when total bytes transferred reached.
+                       Gmail per day allows
+                       2500000000 = 2.5 GB downloaded from Gmail as host2
+                        500000000 = 500 MB uploaded to Gmail as host1.
+
+ --maxlinelength int : skip messages with a line length longer than  int  bytes.
+                       RFC 2822 says it must be no more than 1000 bytes.
+
+ --useheader    str  : Use this header to compare messages on both sides.
+                       Ex: Message-ID or Subject or Date.
+ --useheader    str    and this one, etc.
+
+ --subscribed        : Transfers subscribed folders.
+ --subscribe         : Subscribe to the folders transferred on the
+                       host2 that are subscribed on host1. On by default.
+ --subscribeall      : Subscribe to the folders transferred on the
+                       host2 even if they are not subscribed on host1.
+
+ --nofoldersizes     : Do not calculate the size of each folder in bytes
+                       and message counts. Default is to calculate them.
+ --nofoldersizesatend: Do not calculate the size of each folder in bytes
+                       and message counts at the end. Default is on.
+ --justfoldersizes   : Exit after having printed the folder sizes.
+
+ --syncacls          : Synchronises acls (Access Control Lists).
+ --nosyncacls        : Does not synchronize acls. This is the default.
+                       Acls in IMAP are not standardized, be careful.
+
+ --usecache          : Use cache to speedup.
+ --nousecache        : Do not use cache. Caveat: --useuid --nousecache creates
+                       duplicates on multiple runs.
+ --useuid            : Use uid instead of header as a criterium to recognize
+                       messages. Option --usecache is then implied unless
+                       --nousecache is used.
+
+ --debug             : Debug mode.
+ --debugfolders      : Debug mode for the folders part only.
+ --debugcontent      : Debug content of the messages transfered. Huge ouput.
+ --debugflags        : Debug mode for flags.
+ --debugimap1        : IMAP debug mode for host1. Very verbose.
+ --debugimap2        : IMAP debug mode for host2. Very verbose.
+ --debugimap         : IMAP debug mode for host1 and host2.
+ --debugmemory       : Debug mode showing memory consumption after each copy.
+
+ --errorsmax     int : Exit when int number of errors is reached. Default is 50.
+
+ --tests             : Run local non-regression tests. Exit code 0 means all ok.
+ --testslive         : Run a live test with test1.lamiral.info imap server.
+                       Useful to check the basics. Needs internet connexion.
+
+ --version           : Print only software version.
+ --noreleasecheck    : Do not check for new imapsync release (a http request).
+ --releasecheck      : Check for new imapsync release (a http request).
+ --noid              : Do not send/receive ID command to imap servers.
+ --justconnect       : Just connect to both servers and print useful
+                       information. Need only --host1 and --host2 options.
+ --justlogin         : Just login to both host1 and host2 with users
+                       credentials, then exit.
+ --justfolders       : Do only things about folders (ignore messages).
+
+ --help              : print this help.
+
+ Example:
+ To synchronize the source imap account
+   "test1" on server "test1.lamiral.info" with password "secret1"
+ to the destination imap account
+   "test2" on server "test2.lamiral.info" with password "secret2"
+ do:
+
+ imapsync \
+    --host1 test1.lamiral.info --user1 test1 --password1 secret1 \
+    --host2 test2.lamiral.info --user2 test2 --password2 secret2
+
+=cut
+# comment
+
+=pod
+
+=head1 DESCRIPTION
+
+Imapsync command is a tool allowing incremental and
+recursive imap transfers from one mailbox to another.
+
+By default all folders are transferred, recursively, all
+possible flags (\Seen \Answered \Flagged etc.) are synced too.
+
+We sometimes need to transfer mailboxes from one imap server to
+another. This is called migration.
+
+Imapsync reduces the amount
+of data transferred by not transferring a given message
+if it resides already on both sides. Same specific headers
+and the transfer is done only once; taken into account are by default
+Message-Id and Received header lines.
+All flags are
+preserved, unread will stay unread, read will stay read,
+deleted will stay deleted. You can stop the transfer at any
+time and restart it later, imapsync works well with bad
+connections and interruptions.
+
+You can decide to delete the messages from the source mailbox
+after a successful transfer, it can be a good feature when migrating
+live mailboxes since messages will be only on one side.
+In that case, use the --delete option. Option --delete implies
+also option --expunge so all messages marked deleted on host1
+will be really deleted.
+(you can use --noexpunge to avoid this but I don't see any
+good real world scenario for the combination --delete --noexpunge).
+
+A different scenario is synchronizing a mailbox B from another mailbox A
+in case you just want to keep a "live" copy of A in B.
+In that case --delete2 has to be used, it deletes messages in host2
+folder B that are not in host1 folder A. If you also need to destroy
+host2 folders that are not in host1 then use --delete2folders (see also
+--delete2foldersonly and --delete2foldersbutnot).
+
+Imapsync is not adequate for maintaining two active imap accounts
+in synchronization when the user plays independently on both sides.
+Use offlineimap (written by John Goerzen) or mbsync (written by
+Michael R. Elkins) for 2 ways synchronizations.
+
+
+=head1 OPTIONS
+
+To get a description of each option just invoke:
+
+  imapsync
+
+or read the previous section named USAGE,
+
+or read http://imapsync.lamiral.info/OPTIONS
+
+=head1 HISTORY
+
+I wrote imapsync because an enterprise (basystemes) paid me to install
+a new imap server without losing huge old mailboxes located on a far
+away remote imap server accessible by a low bandwidth link. The tool
+imapcp (written in python) could not help me because I had to verify
+every mailbox was well transferred and delete it after a good
+transfer. imapsync started its life as a copy_folder.pl patch.
+The tool copy_folder.pl comes from the Mail-IMAPClient-2.1.3 perl
+module tarball source (in the examples/ directory of the tarball).
+
+=head1 EXAMPLE
+
+While working on imapsync parameters please run imapsync in
+dry mode (no modification induced) with the --dry
+option. Nothing bad can be done this way.
+
+To synchronize the imap account "buddy" (with password "secret1")
+on host "imap.src.fr" to the imap account "max" (with password "secret2")
+on host "imap.dest.fr":
+
+ imapsync --host1 imap.src.fr  --user1 buddy --password1 secret1 \
+          --host2 imap.dest.fr --user2 max   --password2 secret2
+
+Then you will have max's mailbox updated from buddy's
+mailbox.
+
+=head1 SECURITY
+
+You can use --passfile1  instead of --password1 to give the
+password since it is safer. With --password1 option any user
+on your host can see the password by using the 'ps auxwwww'
+command. Using a variable (like $PASSWORD1) is also
+dangerous because of the 'ps auxwwwwe' command. So, saving
+the password in a well protected file (600 or rw-------) is
+the best solution.
+
+imasync is not totally protected against sniffers on the
+network since passwords may be transferred in plain text
+if CRAM-MD5 is not supported by your imap servers.  Use
+--ssl1 (or --tls1) and --ssl2 (or --tls2) to enable
+encryption on host1 and host2.
+
+You may authenticate as one user (typically an admin user),
+but be authorized as someone else, which means you don't
+need to know every user's personal password.  Specify
+--authuser1 "adminuser" to enable this on host1.  In this
+case, --authmech1 PLAIN will be used by default since it
+is the only way to go for now. So don't use --authmech1 SOMETHING
+with --authuser1 "adminuser", it will not work.
+Same behavior with the --authuser2 option.
+Authenticate with an admin account must be supported by your
+imap server to work with imapsync.
+
+When working on Sun/iPlanet/Netscape IMAP servers you must use
+--proxyauth1 to enable administrative user to masquerade as another user.
+Can also be used on destination server with --proxyauth2
+
+You can authenticate with OAUTH when transfering from Google Apps.
+The consumer key will be the domain part of the --user, and the
+--password will be used as the consumer secret. It does not work
+with Google Apps free edition.
+
+=head1 EXIT STATUS
+
+imapsync will exit with a 0 status (return code) if everything went good.
+Otherwise, it exits with a non-zero status.
+
+So if you have an unreliable internet connection, you can use this loop
+in a Bourne shell:
+
+        while ! imapsync ...; do
+              echo imapsync not complete
+        done
+
+=head1 LICENSE AND COPYRIGHT
+
+imapsync is free, open, public but not always gratis software
+cover by the NOLIMIT Public License.
+See the LICENSE file included in the distribution or just read this
+simple sentence as it is the licence text:
+
+ "No limit to do anything with this work and this license."
+
+In case it is not long enough I repeat:
+
+ "No limit to do anything with this work and this license."
+
+=head1 MAILING-LIST
+
+The public mailing-list may be the best way to get free support.
+
+To write on the mailing-list, the address is:
+<[email protected]>
+
+To subscribe, send any message (even empty) to:
+<[email protected]>
+then just reply to the confirmation message.
+
+To unsubscribe, send a message to:
+<[email protected]>
+
+To contact the person in charge for the list:
+<[email protected]>
+
+The list archives are available at:
+http://www.linux-france.org/prj/imapsync_list/
+So consider that the list is public, anyone
+can see your post. Use a pseudonym or do not
+post to this list if you want to stay private.
+
+Thank you for your participation.
+
+=head1 AUTHOR
+
+Gilles LAMIRAL <[email protected]>
+
+Feedback good or bad is very often welcome.
+
+Gilles LAMIRAL earns his living by writing, installing,
+configuring and teaching free, open and often gratis
+softwares. It used to be "always gratis" but now it is
+"often" because imapsync is sold by its author, a good
+way to stay maintening and supporting free open public
+softwares (see the license) over decades.
+
+=head1 BUGS AND LIMITATIONS
+
+Help me to help you: follow the following guidelines.
+
+Report any bugs or feature requests to the public mailing-list
+or to the author.
+
+Before reporting bugs, read the FAQs, the README and the
+TODO files. http://imapsync.lamiral.info/
+
+Upgrade to last imapsync release, maybe the bug
+is already fixed.
+
+Upgrade to last Mail-IMAPClient Perl module.
+http://search.cpan.org/dist/Mail-IMAPClient/
+maybe the bug is already fixed there.
+
+Make a good title with word "imapsync" in it (my spam filters won't filter it),
+Try to write an email title with more words than just "imapsync" or "problem",
+a good title is made of keywords summary, but not too long (one visible line).
+
+Help us to help you: in your report, please include:
+
+ - imapsync version.
+
+ - output near the first failures, a few lines before is good to get the context
+   of the issue. First failures messages are often more significant than
+   the last ones.
+
+ - if the issue is always related to the same messages, include the output
+   with --debug --debugimap, near the failure point. For example,
+   Isolate a buggy message or two in a folder 'BUG' and use
+
+     imapsync ... --folder 'BUG' --debug --debugimap
+
+ - imap server softwares on both sides and their version number.
+
+ - imapsync with all the options you use,  the full command line
+   you use (except the passwords of course).
+
+ - IMAPClient.pm version.
+
+ - the run context. Do you run imapsync.exe, a unix binary
+   or the perl script imapsync.
+
+ - operating system running imapsync.
+
+ - virtual software context (vmware, xen etc.)
+
+ - operating systems on both sides and the third side in case
+   you run imapsync on a foreign host from the both.
+
+Most of those values can be found as a copy/paste at the begining of the output,
+so a carbon copy of the output is a very easy and very good debug report for me.
+
+One time in your life, read the paper
+"How To Ask Questions The Smart Way"
+http://www.catb.org/~esr/faqs/smart-questions.html
+and then forget it.
+
+=head1 IMAP SERVERS
+
+See http://imapsync.lamiral.info/S/imapservers.shtml
+
+=head1 HUGE MIGRATION
+
+Pay special attention to options
+--subscribed
+--subscribe
+--delete
+--delete2
+--delete2folders
+--maxage
+--minage
+--maxsize
+--useuid
+--usecache
+
+If you have many mailboxes to migrate think about a little
+shell program. Write a file called file.txt (for example)
+containing users and passwords.
+The separator used in this example is ';'
+
+The file.txt file contains:
+
+user001_1;password001_1;user001_2;password001_2
+user002_1;password002_1;user002_2;password002_2
+user003_1;password003_1;user003_2;password003_2
+user004_1;password004_1;user004_2;password004_2
+user005_1;password005_1;user005_2;password005_2
+...
+
+On Unix the shell program can be:
+
+ { while IFS=';' read  u1 p1 u2 p2; do
+	imapsync --host1 imap.side1.org --user1 "$u1" --password1 "$p1" \
+                 --host2 imap.side2.org --user2 "$u2" --password2 "$p2" ...
+ done ; } < file.txt
+
+On Windows the batch program can be:
+
+  FOR /F "tokens=1,2,3,4 delims=; eol=#" %%G IN (file.txt) DO imapsync ^
+  --host1 imap.side1.org --user1 %%G --password1 %%H ^
+  --host2 imap.side2.org --user2 %%I --password2 %%J ...
+
+The ... have to be replaced by nothing or any imapsync option.
+Welcome in shell programming !
+
+You will find already written scripts at
+http://imapsync.lamiral.info/examples/
+
+
+=head1 HACKING
+
+Feel free to hack imapsync as the NOLIMIT license permits it.
+
+=head1 LINKS
+
+Entries for imapsync:
+https://web.archive.org/web/20070202005121/http://www.imap.org/products/showall.php
+
+=head1 SIMILAR SOFTWARES
+
+  imap_tools    : http://www.athensfbc.com/imap_tools
+  offlineimap   : https://github.com/nicolas33/offlineimap
+  mbsync        : http://isync.sourceforge.net/
+  mailsync      : http://mailsync.sourceforge.net/
+  mailutil      : http://www.washington.edu/imap/
+                  part of the UW IMAP tookit.
+  imaprepl      : http://www.bl0rg.net/software/
+                  http://freecode.com/projects/imap-repl/
+  imapcopy      : http://home.arcor.de/armin.diehl/imapcopy/imapcopy.html
+  migrationtool : http://sourceforge.net/projects/migrationtool/
+  imapmigrate   : http://sourceforge.net/projects/cyrus-utils/
+  wonko_imapsync: http://wonko.com/article/554
+                  see also file W/tools/wonko_ruby_imapsync
+  exchange-away : http://exchange-away.sourceforge.net/
+  pop2imap      : http://www.linux-france.org/prj/pop2imap/
+
+
+Feedback (good or bad) will often be welcome.
+
+$Id: imapsync,v 1.727 2016/08/19 10:30:36 gilles Exp gilles $
+
+=cut
+
+
+# pragmas
+
+use strict ;
+use warnings ;
+++$| ;
+
+use Carp ;
+use Data::Dumper ;
+use Digest::HMAC_SHA1 qw( hmac_sha1 ) ;
+use Digest::MD5  qw( md5 md5_hex md5_base64 ) ;
+use English qw( -no_match_vars ) ;
+use Errno qw(EAGAIN EPIPE ECONNRESET) ;
+use Fcntl ;
+use File::Basename ;
+use File::Copy::Recursive ;
+use File::Glob qw( :glob ) ;
+use File::Path qw( mkpath rmtree ) ;
+use File::Spec ;
+use File::stat ;
+#use Imapsync::Getopt::Long ;
+use IO::File ;
+use IO::Socket qw(:crlf SOL_SOCKET SO_KEEPALIVE) ;
+#use IO::Socket::SSL ;
+use IO::Tee ;
+use IPC::Open3 'open3' ;
+use Mail::IMAPClient 3.30 ;
+use MIME::Base64 ;
+use POSIX qw(uname SIGALRM) ;
+use Term::ReadKey ;
+use Test::More ;
+use Time::HiRes qw( time sleep ) ;
+use Time::Local ;
+use Unicode::String ;
+use Cwd ;
+use Readonly ;
+
+# constants
+
+# Let us do like sysexits.h
+# /usr/include/sysexits.h
+
+Readonly my $EX_OK          => 0  ; #/* successful termination */
+Readonly my $EX_USAGE       => 64 ; #/* command line usage error */
+#Readonly my $EX_DATAERR     => 65 ; #/* data format error */
+#Readonly my $EX_NOINPUT     => 66 ; #/* cannot open input */
+#Readonly my $EX_NOUSER      => 67 ; #/* addressee unknown */
+#Readonly my $EX_NOHOST      => 68 ; #/* host name unknown */
+#Readonly my $EX_UNAVAILABLE => 69 ; #/* service unavailable */
+Readonly my $EX_SOFTWARE    => 70 ; #/* internal software error */
+#Readonly my $EX_OSERR       => 71 ; #/* system error (e.g., can't fork) */
+#Readonly my $EX_OSFILE      => 72 ; #/* critical OS file missing */
+#Readonly my $EX_CANTCREAT   => 73 ; #/* can't create (user) output file */
+#Readonly my $EX_IOERR       => 74 ; #/* input/output error */
+#Readonly my $EX_TEMPFAIL    => 75 ; #/* temp failure; user is invited to retry */
+#Readonly my $EX_PROTOCOL    => 76 ; #/* remote error in protocol */
+#Readonly my $EX_NOPERM      => 77 ; #/* permission denied */
+#Readonly my $EX_CONFIG      => 78 ; #/* configuration error */
+
+# Mine
+Readonly my $EXIT_BY_SIGNAL              =>   6 ;
+Readonly my $EXIT_PID_FILE_ALREADY_EXIST =>   8 ;
+Readonly my $EXIT_WITH_ERRORS            => 111 ;
+Readonly my $EXIT_WITH_ERRORS_MAX        => 112 ;
+Readonly my $EXIT_UNKNOWN                => 126 ;
+
+Readonly my $ERRORS_MAX =>  50 ; # exit after 50 errors.
+
+
+Readonly my $INTERVAL_TO_EXIT => 2 ; # interval max to exit instead of reconnect
+
+Readonly my $SPLIT        => 100 ; # By default, 100 at a time, not more.
+Readonly my $SPLIT_FACTOR =>  10 ; # init_imap() calls Maxcommandlength( $SPLIT_FACTOR * $split )
+                                   # which means default Maxcommandlength is 10*100 = 1000 characters ;
+
+Readonly my $IMAP_PORT     => 143 ; # Well know port for IMAP
+Readonly my $IMAP_SSL_PORT => 993 ; # Well know port for IMAP over SSL
+
+Readonly my $LAST => -1 ; 
+Readonly my $MINUS_ONE => -1 ; 
+
+Readonly my $RELEASE_NUMBER_EXAMPLE_1 => '1.351' ; 
+Readonly my $RELEASE_NUMBER_EXAMPLE_2 => 42.4242 ;
+
+
+Readonly my $DEFAULT_TIMEOUT => 120 ;
+Readonly my $DEFAULT_NB_RECONNECT_PER_IMAP_COMMAND => 3 ;
+Readonly my $DEFAULT_UIDNEXT => 999999 ;
+Readonly my $DEFAULT_BUFFER_SIZE => 4096 ;
+
+Readonly my $DEFAULT_EXPIRATION_TIME_OAUTH2_PK12 => 3600 ;
+
+Readonly my $PERMISSION_FILTER => 7777 ;
+
+Readonly my $KIBI => 1024 ;
+
+Readonly my $NUMBER_10 => 10 ;
+Readonly my $NUMBER_42 => 42 ;
+Readonly my $NUMBER_100 => 100 ;
+Readonly my $NUMBER_200 => 200 ;
+Readonly my $NUMBER_300 => 300 ;
+
+Readonly my $NUMBER_20_000 => 20_000 ;
+
+Readonly my $QUOTA_PERCENT_LIMIT => 90 ;
+
+Readonly my $NUMBER_104857600 => 104857600 ;
+
+Readonly my $SIZE_MAX_STR => 64 ;
+
+Readonly my $NB_SECONDS_IN_A_DAY => 86400 ;
+
+Readonly my $STD_CHAR_PER_LINE => 80 ;
+
+Readonly my $TRUE  => 1 ;
+Readonly my $FALSE => 0 ;
+
+Readonly my $LAST_RESSORT_SEPARATOR => q{/} ;
+
+# global variables
+
+my(
+	$sync,
+	$rcs,
+        $debug, $debugimap, $debugimap1, $debugimap2, $debugcontent, $debugflags,
+        $debuglist, $debugdev, $debugmaxlinelength, @debugbasket, $debugcgi,
+        $host1, $host2, $port1, $port2,
+        $user1, $user2, $domain1, $domain2,
+        $password1, $password2, $passfile1, $passfile2,
+        @folder, @include, @exclude, @folderrec,
+        @folderfirst, @folderlast,
+        $prefix1, $prefix2,
+	$subfolder2,
+        @regextrans2, @regexmess, @regexflag, @skipmess, @pipemess, $pipemesscheck,
+        $flagscase, $filterflags, $syncflagsaftercopy,
+        $sep1, $sep2,
+        $syncinternaldates,
+        $idatefromheader,
+        $syncacls,
+        $fastio1, $fastio2,
+        $maxsize, $minsize, $maxage, $minage,
+        $exitwhenover,
+        $search, $search1, $search2,
+        $skipheader, @useheader,
+        $skipsize, $allowsizemismatch, $foldersizes, $foldersizesatend, $buffersize,
+        $delete, $delete2, $delete2duplicates,
+        $expunge, $expunge1, $expunge2, $uidexpunge2, $dry,
+        $justfoldersizes,
+        $authmd5, $authmd51, $authmd52,
+        $subscribed, $subscribe, $subscribeall,
+        $version, $help,
+        $justconnect, $justfolders, $justbanner,
+        $fast,
+
+        $total_bytes_transferred,
+        $total_bytes_skipped,
+        $total_bytes_error,
+        $nb_msg_transferred,
+        $nb_msg_skipped,
+        $nb_msg_skipped_dry_mode,
+        $h1_nb_msg_duplicate,
+        $h2_nb_msg_duplicate,
+        $h1_nb_msg_noheader,
+        $h2_nb_msg_noheader,
+        $h1_total_bytes_duplicate,
+        $h2_total_bytes_duplicate,
+        $h1_nb_msg_deleted,
+        $h2_nb_msg_deleted,
+
+        $h1_bytes_processed,
+        $h1_nb_msg_processed,
+        $h1_nb_msg_start, $h1_bytes_start,
+        $h2_nb_msg_start, $h2_bytes_start,
+        $h1_nb_msg_end, $h1_bytes_end,
+        $h2_nb_msg_end, $h2_bytes_end,
+
+        $timeout,
+        $timestart_int, $timeend,
+        $timebefore,
+        $ssl1, $ssl2,
+        $ssl1_ssl_version, $ssl2_ssl_version,
+        $tls1, $tls2,
+        $uid1, $uid2,
+        $authuser1, $authuser2,
+        $proxyauth1, $proxyauth2,
+        $authmech1, $authmech2,
+        $split1, $split2,
+        $reconnectretry1, $reconnectretry2,
+        $tests, $test_builder, $testsdebug, $testslive,
+        $justlogin,
+        $tmpdir,
+        $releasecheck,
+        $max_msg_size_in_bytes,
+        $modulesversion,
+        $delete2folders, $delete2foldersonly, $delete2foldersbutnot,
+        $usecache, $debugcache, $cacheaftercopy,
+        $wholeheaderifneeded, %h1_msgs_copy_by_uid, $useuid, $h2_uidguess,
+        $addheader,
+        %h1, %h2,
+        $checkselectable, $checkmessageexists,
+        $expungeaftereach,
+        $abletosearch,
+        $showpasswords,
+        $fixslash2,
+        $messageidnodomain,
+        $fixInboxINBOX,
+        $maxlinelength, $maxlinelengthcmd,
+        $minmaxlinelength,
+        $uidnext_default,
+        $fixcolonbug,
+        $create_folder_old,
+        $maxmessagespersecond,
+        $maxbytespersecond,
+        $skipcrossduplicates, $debugcrossduplicates,
+        $disarmreadreceipts,
+        $mixfolders, $skipemptyfolders,
+	$fetch_hash_set,
+);
+
+# main program
+
+# global variables initialisation
+
+$rcs = q{$Id: imapsync,v 1.727 2016/08/19 10:30:36 gilles Exp gilles $} ;
+
+$total_bytes_transferred   = 0;
+$total_bytes_skipped = 0;
+$total_bytes_error   = 0;
+$nb_msg_transferred = 0;
+$nb_msg_skipped = $nb_msg_skipped_dry_mode = 0;
+$h1_nb_msg_deleted = $h2_nb_msg_deleted = 0;
+$h1_nb_msg_duplicate = $h2_nb_msg_duplicate = 0;
+$h1_nb_msg_noheader = $h2_nb_msg_noheader = 0;
+$h1_total_bytes_duplicate = $h2_total_bytes_duplicate = 0;
+
+
+$h1_nb_msg_start  = $h1_bytes_start = 0 ;
+$h2_nb_msg_start     = $h2_bytes_start = 0 ;
+$h1_nb_msg_processed = $h1_bytes_processed = 0 ;
+
+#$h1_nb_msg_end = $h1_bytes_end = 0 ;
+#$h2_nb_msg_end = $h2_bytes_end = 0 ;
+
+$sync->{nb_errors} = 0;
+$max_msg_size_in_bytes = 0;
+
+my %month_abrev = (
+   Jan => '00',
+   Feb => '01',
+   Mar => '02',
+   Apr => '03',
+   May => '04',
+   Jun => '05',
+   Jul => '06',
+   Aug => '07',
+   Sep => '08',
+   Oct => '09',
+   Nov => '10',
+   Dec => '11',
+);
+
+
+
+
+# @ARGV will be eat by get_options()
+my @argv_copy = @ARGV;
+
+my $cgi_dir = '/var/tmp/imapsync_cgi' ;
+
+# Under CGI environment
+if ( $ENV{SERVER_SOFTWARE} ) {
+        myprint( "\n" ) ;
+        myprint( "<pre>\n" ) ;
+        -d $cgi_dir or mkpath $cgi_dir or die "Can not create $cgi_dir: $!\n" ;
+        chdir  $cgi_dir or die "Can not cd to $cgi_dir: $!\n" ;
+}
+
+get_options(  ) ;
+unsetunsafe(  ) if ( $ENV{SERVER_SOFTWARE} ) ;
+
+# Under CGI environment
+if ( $ENV{SERVER_SOFTWARE} ) {
+        myprint( 'Current directory is ' . getcwd(  ) . "\n" ) ;
+        myprint( 'Real user id is ' . getpwuid_any_os( $REAL_USER_ID ) . " (uid $REAL_USER_ID)\n" ) ;
+        myprint( 'Effective user id is ' . getpwuid_any_os( $EFFECTIVE_USER_ID ). " (euid $EFFECTIVE_USER_ID)\n" ) ;
+}
+
+local $SIG{ INT } = sub {
+        my $signame = shift ;
+        catch_reconnect( $sync, $signame ) ;
+} ;
+
+local $SIG{ QUIT } = local $SIG{ TERM } = sub {
+	my $signame = shift ;
+        catch_exit( $sync, $signame ) ;
+} ;
+
+
+$sync->{timestart} = $BASETIME ; # Never too let reading books and perlvar
+
+$sync->{log}        = defined $sync->{log}        ? $sync->{log}        :  1 ;
+$sync->{errorsdump} = defined $sync->{errorsdump} ? $sync->{errorsdump} :  1 ;
+$sync->{errorsmax}  = defined $sync->{errorsmax}  ? $sync->{errorsmax}  : $ERRORS_MAX ;
+
+$sync->{user2} = $user2 ;
+
+if ( $sync->{log} ) {
+        setlogfile( $sync ) ;
+        teelaunch( $sync ) ;
+}
+
+$timestart_int = int( $sync->{timestart} ) ;
+$timebefore =    $sync->{timestart} ;
+
+my $timestart_str = localtime( $sync->{timestart} ) ;
+myprint( "Transfer started at $timestart_str\n" ) ;
+myprint( "PID is $PROCESS_ID\n" ) ;
+myprint( "Log file is $sync->{logfile} ( to change it, use --logfile path ; or use --nolog to turn off logging )\n" ) if ( $sync->{log} ) ;
+$modulesversion = defined  $modulesversion  ? $modulesversion : 1 ;
+
+# If you want releasecheck not to be done by default (like the github maintainer),
+# then uncomment the first "$releasecheck =" line, the line ending with "0 ;".
+# The second line (ending with "1 ;") can stay active or be commented,
+# the result will be the same: no releasecheck by default.
+
+$releasecheck = defined  $releasecheck  ? $releasecheck : 0 ;
+#$releasecheck = defined  $releasecheck  ? $releasecheck : 1 ;
+
+my $warn_release = ( $releasecheck ) ? check_last_release(  ) : q{} ;
+
+# default values
+
+$sync->{pidfile} =  defined  $sync->{pidfile}  ? $sync->{pidfile} : $tmpdir . '/imapsync.pid' ;
+
+$sync->{pidfilelocking} = defined  $sync->{pidfilelocking}  ? $sync->{pidfilelocking} : 0 ;
+
+$wholeheaderifneeded  = defined  $wholeheaderifneeded   ? $wholeheaderifneeded  : 1;
+
+# turn on RFC standard flags correction like \SEEN -> \Seen
+$flagscase = defined  $flagscase  ? $flagscase : 1 ;
+
+# Use PERMANENTFLAGS if available
+$filterflags = defined  $filterflags  ? $filterflags : 1 ;
+
+# sync flags just after an APPEND, some servers ignore the flags given in the APPEND
+# like MailEnable IMAP server.
+# Off by default since it takes time.
+$syncflagsaftercopy = defined  $syncflagsaftercopy   ? $syncflagsaftercopy : 0 ;
+
+
+# Activate --usecache if --useuid is set and no --nousecache
+$usecache = 1 if ( $useuid and ( ! defined  $usecache   ) ) ;
+$cacheaftercopy = 1 if ( $usecache and ( ! defined  $cacheaftercopy  ) ) ;
+
+$checkselectable    = defined  $checkselectable  ? $checkselectable : 1 ;
+$checkmessageexists = defined  $checkmessageexists  ? $checkmessageexists : 0 ;
+$expungeaftereach   = defined  $expungeaftereach  ? $expungeaftereach : 1 ;
+$abletosearch       = defined  $abletosearch  ? $abletosearch : 1 ;
+$checkmessageexists = 0 if ( not $abletosearch ) ;
+$showpasswords      = defined  $showpasswords  ? $showpasswords : 0 ;
+$fixslash2          = defined  $fixslash2  ? $fixslash2 : 1 ;
+$fixInboxINBOX      = defined  $fixInboxINBOX  ? $fixInboxINBOX : 1 ;
+$create_folder_old  = defined  $create_folder_old  ? $create_folder_old : 0 ;
+$mixfolders         = defined  $mixfolders  ? $mixfolders : 1 ;
+$sync->{automap}    = defined  $sync->{automap}  ? $sync->{automap} : 0 ;
+
+$delete2duplicates = 1 if ( $delete2 and ( ! defined  $delete2duplicates  ) ) ;
+
+$maxmessagespersecond = defined  $maxmessagespersecond  ? $maxmessagespersecond : 0 ;
+$maxbytespersecond    = defined  $maxbytespersecond     ? $maxbytespersecond    : 0 ;
+
+myprint( banner_imapsync( @argv_copy ) ) ;
+
+myprint( "Temp directory is $tmpdir  ( to change it use --tmpdir dirpath )\n") ;
+
+is_valid_directory( $tmpdir ) || croak "Error creating tmpdir $tmpdir : $!" ;
+
+if ( $sync->{pidfile} ) {
+        write_pidfile( $sync->{pidfile}, $sync->{pidfilelocking} ) ;
+}
+
+$fixcolonbug = defined  $fixcolonbug  ? $fixcolonbug : 1 ;
+
+if ( $usecache and $fixcolonbug ) { tmpdir_fix_colon_bug(  ) } ;
+
+$modulesversion and myprint( "Modules version list:\n", modulesversion(), "( use --no-modulesversion to turn off printing this Perl modules list )\n" ) ;
+
+my $DEFAULT_SSL_VERIFY ;
+my %SSL_VERIFY_STR ;
+
+if ( $ssl1 or $ssl2 or $tls1 or $tls2) {
+        Readonly $DEFAULT_SSL_VERIFY => IO::Socket::SSL::SSL_VERIFY_NONE(  ) ;
+        Readonly %SSL_VERIFY_STR => (
+                IO::Socket::SSL::SSL_VERIFY_NONE(  ) => 'SSL_VERIFY_NONE' ,
+                IO::Socket::SSL::SSL_VERIFY_PEER(  ) => 'SSL_VERIFY_PEER' ,
+        ) ;
+        $IO::Socket::SSL::DEBUG = $sync->{debugssl} || 1 ;
+        myprint( "SSL debug mode level is --debugssl $IO::Socket::SSL::DEBUG (can be set from 0 meaning no debug to 4 meaning max debug)\n" ) ;
+}
+
+if ( $ssl1 ) {
+        myprint( 'Host1: SSL default mode is like --sslargs1 SSL_verify_mode=' . $DEFAULT_SSL_VERIFY . " meaning $SSL_VERIFY_STR{$DEFAULT_SSL_VERIFY} on host1 (do not check the certificate server)\n" ) ;
+        myprint( 'Host1: Use --sslargs1 SSL_verify_mode=' . IO::Socket::SSL::SSL_VERIFY_PEER(  ) . " for $SSL_VERIFY_STR{IO::Socket::SSL::SSL_VERIFY_PEER(  )} on host1\n" ) ;
+}
+if ( $ssl2 ) {
+        myprint( 'Host2: SSL default mode is like --sslargs2 SSL_verify_mode=' . $DEFAULT_SSL_VERIFY . " meaning $SSL_VERIFY_STR{$DEFAULT_SSL_VERIFY} on host2 (do not check the certificate server)\n" ) ;
+        myprint( 'Host2: Use --sslargs2 SSL_verify_mode=' . IO::Socket::SSL::SSL_VERIFY_PEER(  ) . " for $SSL_VERIFY_STR{IO::Socket::SSL::SSL_VERIFY_PEER(  )} on host2\n" ) ;
+}
+
+
+check_lib_version(  ) or
+  croak "imapsync needs perl lib Mail::IMAPClient release 3.30 or superior.\n";
+
+exit_clean( $sync, $EX_OK ) if ( $justbanner ) ;
+
+
+$split1 ||= $SPLIT ;
+$split2 ||= $SPLIT ;
+
+$host1 || missing_option( '--host1' ) ;
+$port1 ||= ( $ssl1 ) ? $IMAP_SSL_PORT : $IMAP_PORT ;
+
+$host2 || missing_option( '--host2' ) ;
+$port2 ||= ( $ssl2 ) ? $IMAP_SSL_PORT : $IMAP_PORT ;
+
+$debugimap1 = $debugimap2 = 1 if ( $debugimap ) ;
+$debug = 1 if ( $debugimap1 or $debugimap2 ) ;
+
+# By default, don't take size to compare
+$skipsize = (defined $skipsize) ? $skipsize : 1;
+
+$uid1 = defined $uid1 ? $uid1 : 1;
+$uid2 = defined $uid2 ? $uid2 : 1;
+
+$subscribe = defined $subscribe ? $subscribe : 1;
+
+# Allow size mismatch by default
+$allowsizemismatch = defined $allowsizemismatch ? $allowsizemismatch : 1;
+
+$delete2folders = 1
+    if ( defined  $delete2foldersbutnot  or defined  $delete2foldersonly  ) ;
+
+if ( $justconnect ) {
+	justconnect(  ) ;
+	exit_clean( $sync, $EX_OK ) ;
+}
+
+$user1 || missing_option( '--user1' ) ;
+$user2 || missing_option( '--user2' ) ;
+
+$syncinternaldates = defined $syncinternaldates ? $syncinternaldates : 1;
+
+# Turn on expunge if there is not explicit option --noexpunge and option
+# --delete is given.
+# Done because --delete --noexpunge is very dangerous on the second run:
+# the Deleted flag is then synced to all previously transfered messages.
+# So --delete implies --expunge is a better usability default behaviour.
+if ( $delete ) {
+	if ( ! defined  $expunge  ) {
+		myprint( "Info: turning on --expunge1 because --delete --noexpunge1 is very dangerous on the second run.\n" ) ;
+		$expunge = 1 ;
+	}
+		myprint( "Info: if expunging after each message slows down too much the sync then use --noexpungeaftereach to speed up\n" ) ;
+}
+
+if ( $uidexpunge2 and not Mail::IMAPClient->can( 'uidexpunge' ) ) {
+        myprint( "Failure: uidexpunge not supported (IMAPClient release < 3.17), use --expunge2 instead\n" ) ;
+        exit_clean( $sync, $EX_SOFTWARE ) ;
+}
+
+if ( ( $delete2 or $delete2duplicates ) and not defined  $uidexpunge2  ) {
+        if ( Mail::IMAPClient->can( 'uidexpunge' ) ) {
+                myprint( "Info: will act as --uidexpunge2\n" ) ;
+		$uidexpunge2 = 1 ;
+        }elsif ( not defined  $expunge2  ) {
+                 myprint( "Info: will act as --expunge2 (no uidexpunge support)\n" ) ;
+                $expunge2 = 1 ;
+        }
+}
+
+if ( $delete and $delete2 ) {
+	myprint( "Warning: using --delete and --delete2 together is almost always a bad idea, exiting imapsync\n" ) ;
+	exit_clean( $sync, $EX_USAGE ) ;
+}
+
+if ( $idatefromheader ) {
+	myprint( 'Turned ON idatefromheader, ',
+	      "will set the internal dates on host2 from the 'Date:' header line.\n" ) ;
+	$syncinternaldates = 0 ;
+}
+
+if ( $syncinternaldates ) {
+	myprint( 'Info: turned ON syncinternaldates, ',
+	      "will set the internal dates (arrival dates) on host2 same as host1.\n" ) ;
+}else{
+        myprint( "Info: turned OFF syncinternaldates\n" ) ;
+}
+
+if ( defined $authmd5 and $authmd5 ) {
+	$authmd51 = 1 ;
+	$authmd52 = 1 ;
+}
+
+if ( defined $authmd51 and $authmd51 ) {
+	$authmech1 ||= 'CRAM-MD5';
+}
+else{
+	$authmech1 ||= $authuser1 ? 'PLAIN' : 'LOGIN';
+}
+
+if ( defined $authmd52 and $authmd52 ) {
+	$authmech2 ||= 'CRAM-MD5';
+}
+else{
+	$authmech2 ||= $authuser2 ? 'PLAIN' : 'LOGIN';
+}
+
+$authmech1 = uc $authmech1;
+$authmech2 = uc $authmech2;
+
+if (defined $proxyauth1 && !$authuser1) {
+        missing_option( 'With --proxyauth1, --authuser1' ) ;
+}
+
+if (defined $proxyauth2 && !$authuser2) {
+        missing_option( 'With --proxyauth2, --authuser2' ) ;
+}
+
+$authuser1 ||= $user1;
+$authuser2 ||= $user2;
+
+myprint( "Host1: will try to use $authmech1 authentication on host1\n") ;
+myprint( "Host2: will try to use $authmech2 authentication on host2\n") ;
+
+$timeout = defined  $timeout  ? $timeout : $DEFAULT_TIMEOUT ;
+
+$sync->{h1}->{timeout} = defined  $sync->{h1}->{timeout}  ? $sync->{h1}->{timeout} : $timeout ;
+myprint( "Host1: imap connexion timeout is $sync->{h1}->{timeout} seconds\n") ;
+$sync->{h2}->{timeout} = defined  $sync->{h2}->{timeout}  ? $sync->{h2}->{timeout} : $timeout ;
+myprint( "Host2: imap connexion timeout is $sync->{h2}->{timeout} seconds\n" ) ;
+
+$syncacls = defined  $syncacls  ? $syncacls : 0 ;
+
+# No folders sizes if --justfolders, unless really wanted.
+if ( $justfolders and not defined  $foldersizes  ) { $foldersizes = 0 ; }
+
+$foldersizes      = ( defined  $foldersizes       ) ? $foldersizes      : 1 ;
+$foldersizesatend = ( defined  $foldersizesatend  ) ? $foldersizesatend : $foldersizes ;
+
+$fastio1 = defined  $fastio1  ? $fastio1 : 0 ;
+$fastio2 = defined  $fastio2  ? $fastio2 : 0 ;
+
+$reconnectretry1 = defined  $reconnectretry1  ? $reconnectretry1 : $DEFAULT_NB_RECONNECT_PER_IMAP_COMMAND ;
+$reconnectretry2 = defined  $reconnectretry2  ? $reconnectretry2 : $DEFAULT_NB_RECONNECT_PER_IMAP_COMMAND ;
+
+# Since select_msgs() returns no messages when uidnext does not return something
+# then $uidnext_default is never used. So I have to remove it.
+$uidnext_default = $DEFAULT_UIDNEXT ;
+
+@useheader = qw( Message-Id Received ) unless ( @useheader ) ;
+
+my %useheader ;
+
+# Make a hash %useheader of each --useheader 'key' in uppercase
+for ( @useheader ) { $useheader{ uc  $_  } = undef } ;
+
+#myprint( Data::Dumper->Dump( [ \%useheader ] )  ) ;
+#exit ;
+
+myprint( "Host1: IMAP server [$host1] port [$port1] user [$user1]\n" ) ;
+myprint( "Host2: IMAP server [$host2] port [$port2] user [$user2]\n" ) ;
+
+$password1 || $passfile1 || 'PREAUTH' eq $authmech1 || 'EXTERNAL' eq $authmech1 || do {
+	myprint( << 'FIN_PASSFILE'  ) ;
+
+If you are afraid of giving password on the command line arguments, you can put the
+password of user1 in a file named file1 and use "--passfile1 file1" instead of typing it.
+Then give this file restrictive permissions with the command "chmod 600 file1".
+FIN_PASSFILE
+
+	$password1 = ask_for_password( $authuser1 || $user1, $host1 ) ;
+} ;
+
+$password1 = ( defined  $passfile1  ) ? firstline ( $passfile1 ) : $password1 ;
+
+
+$password2 || $passfile2 || 'PREAUTH' eq $authmech2 || 'EXTERNAL' eq $authmech2 || do {
+	myprint( << 'FIN_PASSFILE'  ) ;
+
+If you are afraid of giving password on the command line arguments, you can put the
+password of user2 in a file named file2 and use "--passfile2 file2" instead of typing it.
+Then give this file restrictive permissions with the command "chmod 600 file2".
+FIN_PASSFILE
+
+	$password2 = ask_for_password( $authuser2 || $user2, $host2 ) ;
+} ;
+
+$password2 = ( defined  $passfile2  ) ? firstline ( $passfile2 ) : $password2 ;
+
+
+# need clean up => write methods dry() and dry_message()
+$sync->{dry} = $dry ;
+my $dry_message = q{} ;
+if( $sync->{dry} ) {
+        $dry_message = "\t(not really since --dry mode)" ;
+}
+$sync->{dry_message} = $dry_message ;
+
+
+$search1 ||= $search if ( $search ) ;
+$search2 ||= $search if ( $search ) ;
+
+
+
+if ( $disarmreadreceipts ) {
+	push @regexmess, q{s{\A((?:[^\n]+\r\n)+|)(^Disposition-Notification-To:[^\n]*\n)(\r?\n|.*\n\r?\n)}{$1X-$2$3}ims} ;
+}
+
+$pipemesscheck = ( defined  $pipemesscheck  ) ? $pipemesscheck : 1 ;
+
+if ( @pipemess and $pipemesscheck ) {
+	myprint( 'Checking each --pipemess command, ' 
+                . join( q{, }, @pipemess ) 
+                . ", with an space string. ( Can avoid this check with --nopipemesscheck )\n" ) ;
+	my $string = pipemess( q{ }, @pipemess ) ;
+        # string undef means something was bad.
+        if ( not ( defined  $string  ) ) {
+        	die_clean( "Error: one of --pipemess command is bad, check it\n" ) ;
+        }
+	myprint( "Ok with each --pipemess @pipemess\n"  ) ;
+}
+
+if ( $maxlinelengthcmd ) {
+	myprint( "Checking  --maxlinelengthcmd command,  $maxlinelengthcmd, with an space string.\n"  ) ;
+	my $string = pipemess( q{ }, $maxlinelengthcmd ) ;
+        # string undef means something was bad.
+        if ( not ( defined  $string  ) ) {
+        	die_clean( "Error: --maxlinelengthcmd command is bad, check it\n" ) ;
+        }
+	myprint( "Ok with --maxlinelengthcmd $maxlinelengthcmd\n"  ) ;
+}
+
+if ( @regexmess ) {
+	my $string = regexmess( q{ } ) ;
+	myprint( "Checking each --regexmess command with an space string.\n"  ) ;
+        # string undef means one of the eval regex was bad.
+        if ( not ( defined  $string  ) ) {
+        	die_clean( 'Error: one of --regexmess option is bad, check it' ) ;
+        }
+	myprint( "Ok with each --regexmess\n"  ) ;
+}
+
+if ( @skipmess ) {
+	myprint( "Checking each --skipmess command with an space string.\n"  ) ;
+	my $match = skipmess( q{ } ) ;
+        # match undef means one of the eval regex was bad.
+        if ( not ( defined  $match  ) ) {
+        	die_clean( 'Error: one of --skipmess option is bad, check it' ) ;
+        }
+	myprint( "Ok with each --skipmess\n"  ) ;
+}
+
+if ( @regexflag ) {
+	myprint( "Checking each --regexflag command with an space string.\n"  ) ;
+	my $string = flags_regex( q{ } ) ;
+	# string undef means one of the eval regex was bad.
+	if ( not ( defined  $string  ) ) {
+		die_clean( 'Error: one of --regexflag option is bad, check it' ) ;
+	}
+	myprint( "Ok with each --regexflag\n"  ) ;
+}
+
+$sync->{imap1} = my $imap1 = login_imap($host1, $port1, $user1, $domain1, $password1,
+		   $debugimap1, $sync->{h1}->{timeout}, $fastio1, $ssl1, $tls1,
+		   $authmech1, $authuser1, $reconnectretry1,
+		   $proxyauth1, $uid1, $split1, 'Host1', $sync->{h1} ) ;
+
+$sync->{imap2} = my $imap2 = login_imap($host2, $port2, $user2, $domain2, $password2,
+		 $debugimap2, $sync->{h2}->{timeout}, $fastio2, $ssl2, $tls2,
+		 $authmech2, $authuser2, $reconnectretry2,
+		 $proxyauth2, $uid2, $split2, 'Host2', $sync->{h2} ) ;
+
+
+$debug and myprint( 'Host1 Buffer I/O: ', $imap1->Buffer(), "\n" ) ;
+$debug and myprint( 'Host2 Buffer I/O: ', $imap2->Buffer(), "\n" ) ;
+
+
+die_clean( 'Not authenticated on host1' ) unless $imap1->IsAuthenticated( ) ;
+myprint( "Host1: state Authenticated\n" ) ;
+die_clean( 'Not authenticated on host2' ) unless   $imap2->IsAuthenticated( ) ;
+myprint( "Host2: state Authenticated\n" ) ;
+
+myprint( 'Host1 capability: ', join(q{ }, @{ $imap1->capability_update() || [] }), "\n" ) ;
+myprint( 'Host2 capability: ', join(q{ }, @{ $imap2->capability_update() || [] }), "\n" ) ;
+
+imap_id_stuff( $sync ) ;
+
+#quota( $imap1, 'host1' ) ; # quota on host1 is useless and pollute host2 output.
+quota( $imap2, 'host2', $sync ) ;
+
+if ( $justlogin ) {
+	$imap1->logout(  ) ;
+	$imap2->logout(  ) ;
+	exit_clean( $sync, $EX_OK ) ;
+}
+
+
+#
+# Folder stuff
+#
+
+my (
+        @h1_folders_all , %h1_folders_all , @h1_folders_wanted , %requested_folder ,
+        %h1_subscribed_folder , %h2_subscribed_folder ,
+        @h2_folders_all , %h2_folders_all , %h2_folders_all_UPPER ,
+        @h2_folders_from_1_wanted , %h2_folders_from_1_wanted ,
+        %h2_folders_from_1_several ,
+        %h2_folders_from_1_all ,
+) ;
+
+my $h1_folders_wanted_nb = 0 ; 
+my $h1_folders_wanted_ct = 0 ; # counter of folders done.
+
+# All folders on host1 and host2
+
+@h1_folders_all = sort $imap1->folders(  ) ;
+@h2_folders_all = sort $imap2->folders(  ) ;
+
+myprint( 'Host1: found ', scalar  @h1_folders_all , " folders.\n"  ) ;
+myprint( 'Host2: found ', scalar  @h2_folders_all , " folders.\n"  ) ;
+
+for ( @h1_folders_all ) { $h1_folders_all{ $_ } = 1 } ;
+for ( @h2_folders_all ) {
+	$h2_folders_all{ $_ } = 1 ;
+	$h2_folders_all_UPPER{ uc  $_  } = 1 ;
+} ;
+
+$sync->{h1_folders_all} = \%h1_folders_all ;
+$sync->{h2_folders_all} = \%h2_folders_all ;
+$sync->{h2_folders_all_UPPER} = \%h2_folders_all_UPPER ;
+
+# Make a hash of subscribed folders in both servers.
+
+for ( $imap1->subscribed(  ) ) { $h1_subscribed_folder{ $_ } = 1 } ;
+for ( $imap2->subscribed(  ) ) { $h2_subscribed_folder{ $_ } = 1 } ;
+
+
+if ( defined  $subfolder2  ) {
+	unshift @regextrans2,
+		q's,^${h2_prefix}(.*),${h2_prefix}${subfolder2}${h2_sep}$1,',
+		q's,^INBOX$,${h2_prefix}${subfolder2}${h2_sep}INBOX,' ;
+
+}
+
+if ( $fixInboxINBOX and ( my $reg = fix_Inbox_INBOX_mapping( \%h1_folders_all, \%h2_folders_all ) ) ) {
+	push @regextrans2, $reg ;
+}
+
+if (scalar @folder or $subscribed or scalar @folderrec) {
+	# folders given by option --folder
+	if (scalar @folder) {
+		add_to_requested_folders(@folder);
+	}
+
+	# option --subscribed
+	if ( $subscribed ) {
+		add_to_requested_folders( keys  %h1_subscribed_folder  ) ;
+	}
+
+	# option --folderrec
+	if (scalar @folderrec) {
+		foreach my $folderrec (@folderrec) {
+			add_to_requested_folders($imap1->folders($folderrec));
+		}
+	}
+}
+else {
+	# no include, no folder/subscribed/folderrec options => all folders
+	if (not scalar @include) {
+		myprint( "Including all folders found by default. Use --subscribed or --folder or --folderrec or --include to select specific folders. Use --exclude to unselect specific folders.\n"  ) ;
+		add_to_requested_folders(@h1_folders_all);
+	}
+}
+
+
+# consider (optional) includes and excludes
+if ( scalar  @include  ) {
+	foreach my $include ( @include ) {
+		my @included_folders = grep { /$include/ } @h1_folders_all ;
+		add_to_requested_folders( @included_folders ) ;
+		myprint( "Including folders matching pattern $include\n" . jux_utf8_list( @included_folders )  . "\n"  ) ;
+	}
+}
+
+if ( scalar  @exclude  ) {
+	foreach my $exclude ( @exclude ) {
+		my @requested_folder = sort keys %requested_folder ;
+		my @excluded_folders = grep { /$exclude/ } @requested_folder ;
+		remove_from_requested_folders( @excluded_folders ) ;
+		myprint( "Excluding folders matching pattern $exclude\n" . jux_utf8_list( @excluded_folders ) . "\n"  ) ;
+	}
+}
+
+
+# sort before is not very powerful
+# it adds --folderfirst and --folderlast even if they don't exist on host1
+@h1_folders_wanted = sort_requested_folders(  ) ;
+
+# Remove no selectable folders
+
+
+my @h1_folders_wanted_exist ;
+myprint( "Host1: checking all wanted folders exist.\n"  ) ;
+foreach my $folder ( @h1_folders_wanted ) {
+	( $debug or $sync->{debugfolders} ) and myprint( "Checking $folder exists on host1\n"  ) ;
+	if ( ! exists  $h1_folders_all{ $folder }  ) {
+                myprint( "Host1: warning! ignoring folder $folder because it is not in host1 whole folders list.\n" ) ;
+		next ;
+	}else{
+		push  @h1_folders_wanted_exist, $folder  ;
+	}
+}
+
+@h1_folders_wanted = @h1_folders_wanted_exist ;
+
+
+
+$checkselectable and do {
+	my @h1_folders_wanted_selectable ;
+        myprint( "Host1: checking all wanted folders are selectable. Use --nocheckselectable to avoid this check.\n"  ) ;
+	foreach my $folder ( @h1_folders_wanted ) {
+        	( $debug or $sync->{debugfolders} ) and myprint( "Checking $folder is selectable on host1\n"  ) ;
+        	if ( ! $imap1->selectable( $folder ) ) {
+                                myprint( "Host1: warning! ignoring folder $folder because it is not selectable\n" ) ;
+        	}else{
+			push  @h1_folders_wanted_selectable, $folder  ;
+		}
+	}
+	@h1_folders_wanted = @h1_folders_wanted_selectable ;
+        ( $debug or $sync->{debugfolders} ) and myprint( 'Host1: checking folders took ', timenext(  ), " s\n"  ) ;
+} ;
+
+$sync->{h1_folders_wanted} = \@h1_folders_wanted ;
+
+
+my( $h1_sep, $h2_sep ) ;
+# what are the private folders separators for each server ?
+
+( $debug or $sync->{debugfolders} ) and myprint( "Getting separators\n"  ) ;
+$h1_sep = get_separator( $imap1, $sep1, '--sep1', 'Host1', \@h1_folders_all ) ;
+$h2_sep = get_separator( $imap2, $sep2, '--sep2', 'Host2', \@h2_folders_all ) ;
+
+my( $h1_prefix, $h2_prefix ) ;
+$sync->{ h1_prefix } = $h1_prefix = get_prefix( $imap1, $prefix1, '--prefix1', 'Host1', \@h1_folders_all ) ;
+$sync->{ h2_prefix } = $h2_prefix = get_prefix( $imap2, $prefix2, '--prefix2', 'Host2', \@h2_folders_all ) ;
+
+
+myprint( "Host1 separator and prefix: [$h1_sep][$h1_prefix]\n"  ) ;
+myprint( "Host2 separator and prefix: [$h2_sep][$h2_prefix]\n"  ) ;
+
+automap( $sync ) ;
+
+
+foreach my $h1_fold ( @h1_folders_wanted ) {
+	my $h2_fold ;
+	$h2_fold = imap2_folder_name( $h1_fold ) ;
+	$h2_folders_from_1_wanted{ $h2_fold }++ ;
+        if ( 1 < $h2_folders_from_1_wanted{ $h2_fold } ) {
+        	$h2_folders_from_1_several{ $h2_fold }++ ;
+        }
+}
+@h2_folders_from_1_wanted = sort keys %h2_folders_from_1_wanted;
+
+foreach my $h1_fold ( @h1_folders_all ) {
+	my $h2_fold ;
+	$h2_fold = imap2_folder_name( $h1_fold ) ;
+	$h2_folders_from_1_all{ $h2_fold }++ ;
+}
+
+
+
+myprint( << 'END_LISTING'  ) ;
+
+++++ Listing folders
+All foldernames are presented between brackets like [X] where X is the foldername.
+When a foldername contains non-ASCII characters it is presented in the form
+[X] = [Y] where
+X is the imap foldername you have to use in command line options and
+Y is the uft8 output just printed for convenience, to recognize it.
+
+END_LISTING
+
+print
+  "Host1 folders list:\n",
+  jux_utf8_list( @h1_folders_all ),
+  "\n",
+  "Host2 folders list:\n",
+  jux_utf8_list( @h2_folders_all ),
+  "\n" ;
+
+print
+  'Host1 subscribed folders list: ',
+  jux_utf8_list( sort keys  %h1_subscribed_folder  ), "\n"
+  if ( $subscribed ) ;
+
+my @h2_folders_not_in_1;
+@h2_folders_not_in_1 = list_folders_in_2_not_in_1(  ) ;
+
+if ( @h2_folders_not_in_1 ) {
+	myprint( "Folders in host2 not in host1:\n",
+	jux_utf8_list( @h2_folders_not_in_1 ), "\n" ) ;
+}
+
+
+if ( defined  $sync->{f1f2auto}  ) {
+	myprint( "Folders mapping from --automap feature (use --f1f2 to override any mapping):\n"  ) ;
+	foreach my $h1_fold ( keys %{$sync->{f1f2auto}} ) {
+        	my $h2_fold = $sync->{f1f2auto}{$h1_fold} ;
+		myprintf( "%-40s -> %-40s\n",
+		       jux_utf8( $h1_fold ), jux_utf8( $h2_fold ) ) ;
+        }
+        myprint( "\n"  ) ;
+}
+
+if ( defined  $sync->{f1f2}  ) {
+	myprint( "Folders mapping from --f1f2 options, it overrides --automap:\n"  ) ;
+	foreach my $h1_fold ( keys %{$sync->{f1f2}} ) {
+        	my $h2_fold = $sync->{f1f2}{$h1_fold} ;
+                my $warn = q{} ;
+                if ( not exists  $h1_folders_all{ $h1_fold }  ) {
+                        $warn = "BUT $h1_fold does NOT exist on host1!" ;
+                }
+		myprintf( "%-40s -> %-40s %s\n",
+		       jux_utf8( $h1_fold ), jux_utf8( $h2_fold ), $warn ) ;
+        }
+        myprint( "\n"  ) ;
+}
+
+exit_clean( $sync, $EX_OK ) if ( $sync->{justfolderlists} ) ;
+exit_clean( $sync, $EX_OK ) if ( $sync->{justautomap} ) ;
+
+debugsleep( $sync ) ;
+
+if ( $foldersizes ) {
+        foldersizes_on_h1h2(  ) ;
+}
+
+
+exit_clean( $sync, $EX_OK ) if ( $justfoldersizes ) ;
+
+$sync->{stats} = 1 ;
+
+if ( $sync->{'delete1emptyfolders'} ) {
+        delete1emptyfolders( $sync ) ;
+}
+
+delete_folders_in_2_not_in_1(  ) if $delete2folders ;
+
+# folder loop
+$h1_folders_wanted_nb = scalar  @h1_folders_wanted  ;
+
+myprint( "++++ Looping on each one of $h1_folders_wanted_nb folders to sync\n" ) ;
+
+my $begin_transfer_time = time ;
+
+my %uid_candidate_for_deletion ;
+my %uid_candidate_no_deletion ;
+
+my %h2_folders_of_md5 = (  ) ;
+
+FOLDER: foreach my $h1_fold ( @h1_folders_wanted ) {
+
+        last FOLDER if $imap1->IsUnconnected(  ) ;
+        last FOLDER if $imap2->IsUnconnected(  ) ;
+
+	my $h2_fold = imap2_folder_name( $h1_fold ) ;
+
+	$h1_folders_wanted_ct++ ;
+	myprintf( "Folder %7s %-35s -> %-35s\n", "$h1_folders_wanted_ct/$h1_folders_wanted_nb",
+		jux_utf8( $h1_fold ), jux_utf8( $h2_fold ) ) ;
+        if ( $sync->{debugmemory} ) {
+                myprintf("FL: Memory consumption: %.1f MiB\n", memory_consumption(  ) / $KIBI / $KIBI) ;
+        }
+	# host1 can not be fetched read only, select is needed because of expunge.
+	select_folder( $imap1, $h1_fold, 'Host1' ) or next FOLDER ;
+
+        debugsleep( $sync ) ;
+
+	my $h1_fold_nb_messages = count_from_select( $imap1->History ) ;
+        myprint( "Host1 folder [$h1_fold] has $h1_fold_nb_messages messages in total (mentioned by SELECT)\n" ) ;
+
+        if ( $skipemptyfolders and 0 == $h1_fold_nb_messages ) {
+        	myprint( "Skipping empty host1 folder [$h1_fold]\n"  ) ;
+                next FOLDER ;
+        }
+
+	if ( ! exists  $h2_folders_all{ $h2_fold }  ) {
+		create_folder( $imap2, $h2_fold, $h1_fold ) or next FOLDER ;
+	}
+
+	acls_sync( $h1_fold, $h2_fold ) ;
+
+        # Sometimes the folder on host2 is listed (it exists) but is
+        # not selectable but becomes selectable by a create (Gmail)
+	select_folder( $imap2, $h2_fold, 'Host2' )
+        or ( create_folder( $imap2, $h2_fold, $h1_fold )
+             and select_folder( $imap2, $h2_fold, 'Host2' ) )
+        or next FOLDER ;
+	my @select_results = $imap2->Results(  ) ;
+
+	my $h2_fold_nb_messages = count_from_select( @select_results ) ;
+        myprint( "Host2 folder [$h2_fold] has $h2_fold_nb_messages messages in total (mentioned by SELECT)\n" ) ;
+
+	my $permanentflags2 = permanentflags( @select_results ) ;
+	( $debug or $debugflags ) and myprint( "Host2 folder [$h2_fold] permanentflags: $permanentflags2\n"  ) ;
+
+	if ( $expunge or $expunge1 ){
+		myprint( "Host1: Expunging $h1_fold $dry_message\n"  ) ;
+		unless( $dry ) { $imap1->expunge(  ) } ;
+	}
+
+	if ( ( ( $subscribe and exists $h1_subscribed_folder{ $h1_fold } ) or $subscribeall )
+             and not exists  $h2_subscribed_folder{ $h2_fold }  ) {
+		myprint( "Host2: Subscribing to folder $h2_fold\n"  ) ;
+		unless( $dry ) { $imap2->subscribe( $h2_fold ) } ;
+	}
+
+	next FOLDER if ( $justfolders ) ;
+
+        last FOLDER if $imap1->IsUnconnected(  ) ;
+        last FOLDER if $imap2->IsUnconnected(  ) ;
+
+        my $h1_msgs_all_hash_ref = {  } ;
+	my @h1_msgs = select_msgs( $imap1, $h1_msgs_all_hash_ref, $search1, $h1_fold );
+	last FOLDER if $imap1->IsUnconnected(  ) ;
+
+        my $h1_msgs_nb = scalar  @h1_msgs  ;
+        $h1{ $h1_fold }{ 'messages_nb' } = $h1_msgs_nb ;
+
+	myprint( "Host1 folder [$h1_fold] considering $h1_msgs_nb messages\n"  ) ;
+	( $debug or $debuglist ) and myprint( "Host1 folder [$h1_fold] considering $h1_msgs_nb messages, LIST gives: @h1_msgs\n" ) ;
+        $debug and myprint( "Host1 selecting messages of folder [$h1_fold] took ", timenext(), " s\n" ) ;
+
+        my $h2_msgs_all_hash_ref = {  } ;
+	my @h2_msgs = select_msgs( $imap2, $h2_msgs_all_hash_ref, $search2, $h2_fold ) ;
+	last FOLDER if $imap2->IsUnconnected(  ) ;
+
+        my $h2_msgs_nb = scalar  @h2_msgs  ;
+        $h2{ $h2_fold }{ 'messages_nb' } = $h2_msgs_nb ;
+
+	myprint( "Host2 folder [$h2_fold] considering $h2_msgs_nb messages\n" ) ;
+	( $debug or $debuglist ) and myprint( "Host2 folder [$h2_fold] considering $h2_msgs_nb messages, LIST gives: @h2_msgs\n" ) ;
+        $debug and myprint( "Host2 selecting messages of folder [$h2_fold] took ", timenext(), " s\n" ) ;
+
+	my $cache_base = "$tmpdir/imapsync_cache/" ;
+	my $cache_dir = cache_folder( $cache_base, "$host1/$user1/$host2/$user2", $h1_fold, $h2_fold ) ;
+	my ( $cache_1_2_ref, $cache_2_1_ref ) = ( {}, {} ) ;
+
+	my $h1_uidvalidity = $imap1->uidvalidity(  ) || q{} ;
+	my $h2_uidvalidity = $imap2->uidvalidity(  ) || q{} ;
+
+        last FOLDER if $imap1->IsUnconnected(  ) ;
+        last FOLDER if $imap2->IsUnconnected(  ) ;
+
+	if ( $usecache ) {
+		myprint( "cache directory: $cache_dir\n"  ) ;
+		mkpath( "$cache_dir" ) ;
+		( $cache_1_2_ref, $cache_2_1_ref )
+                = get_cache( $cache_dir, \@h1_msgs, \@h2_msgs, $h1_msgs_all_hash_ref, $h2_msgs_all_hash_ref ) ;
+		myprint( 'CACHE h1 h2: ', scalar  keys %{ $cache_1_2_ref } , " files\n"  ) ;
+		$debug and myprint( '[',
+		    map ( { "$_->$cache_1_2_ref->{$_} " } keys %{ $cache_1_2_ref } ), " ]\n" ) ;
+	}
+
+	my %h1_hash = () ;
+	my %h2_hash = () ;
+
+	my ( %h1_msgs, %h2_msgs ) ;
+	@h1_msgs{ @h1_msgs } = ();
+	@h2_msgs{ @h2_msgs } = ();
+
+	my @h1_msgs_in_cache = sort { $a <=> $b } keys %{ $cache_1_2_ref } ;
+	my @h2_msgs_in_cache = keys %{ $cache_2_1_ref } ;
+
+	my ( %h1_msgs_not_in_cache, %h2_msgs_not_in_cache ) ;
+	%h1_msgs_not_in_cache = %h1_msgs ;
+	%h2_msgs_not_in_cache = %h2_msgs ;
+	delete @h1_msgs_not_in_cache{ @h1_msgs_in_cache } ;
+	delete @h2_msgs_not_in_cache{ @h2_msgs_in_cache } ;
+
+	my @h1_msgs_not_in_cache = keys %h1_msgs_not_in_cache ;
+	#myprint( "h1_msgs_not_in_cache: [@h1_msgs_not_in_cache]\n"  ) ;
+	my @h2_msgs_not_in_cache = keys %h2_msgs_not_in_cache ;
+
+	my @h2_msgs_delete2_not_in_cache = () ;
+	%h1_msgs_copy_by_uid = (  ) ;
+
+	if ( $useuid ) {
+		# use uid so we have to avoid getting header
+		@h1_msgs_copy_by_uid{ @h1_msgs_not_in_cache } = (  ) ;
+		@h2_msgs_delete2_not_in_cache = @h2_msgs_not_in_cache if $usecache ;
+		@h1_msgs_not_in_cache = (  ) ;
+		@h2_msgs_not_in_cache = (  ) ;
+
+		#myprint( "delete2: @h2_msgs_delete2_not_in_cache\n" ) ;
+	}
+
+	$debug and myprint( "Host1 parsing headers of folder [$h1_fold]\n" ) ;
+
+	my ($h1_heads_ref, $h1_fir_ref) = ({}, {});
+	$h1_heads_ref = $imap1->parse_headers([@h1_msgs_not_in_cache], @useheader) if (@h1_msgs_not_in_cache);
+	$debug and myprint( "Host1 parsing headers of folder [$h1_fold] took ", timenext(), " s\n" ) ;
+
+	@{ $h1_fir_ref }{@h1_msgs} = ( undef ) ;
+
+	$debug and myprint( "Host1 getting flags idate and sizes of folder [$h1_fold]\n"  ) ;
+        if ( $abletosearch ) {
+		$h1_fir_ref = $imap1->fetch_hash( \@h1_msgs, 'FLAGS', 'INTERNALDATE', 'RFC822.SIZE', $h1_fir_ref )
+	  	if ( @h1_msgs ) ;
+        }else{
+		my $uidnext = $imap1->uidnext( $h1_fold ) || $uidnext_default ;
+		my $fetch_hash_uids = $fetch_hash_set || "1:$uidnext" ;
+		$h1_fir_ref = $imap1->fetch_hash( $fetch_hash_uids, 'FLAGS', 'INTERNALDATE', 'RFC822.SIZE', $h1_fir_ref )
+		if ( @h1_msgs ) ;
+        }
+	$debug and myprint( "Host1 getting flags idate and sizes of folder [$h1_fold] took ", timenext(), " s\n"  ) ;
+	unless ($h1_fir_ref) {
+		my $error = join( q{}, "Host1 folder $h1_fold: Could not fetch_hash ",
+			scalar @h1_msgs, ' msgs: ', $imap1->LastError || q{}, "\n" ) ;
+		errors_incr( $sync, $error ) ;
+		next FOLDER ;
+	}
+
+	my @h1_msgs_duplicate;
+	foreach my $m (@h1_msgs_not_in_cache) {
+		my $rc = parse_header_msg($imap1, $m, $h1_heads_ref, $h1_fir_ref, 'Host1', \%h1_hash);
+		if (! defined $rc) {
+			my $h1_size = $h1_fir_ref->{$m}->{'RFC822.SIZE'} || 0;
+			myprint( "Host1 $h1_fold/$m size $h1_size ignored (no wanted headers so we ignore this message. To solve this: use --addheader)\n"  ) ;
+			$total_bytes_skipped += $h1_size;
+			$nb_msg_skipped += 1;
+			$h1_nb_msg_noheader +=1;
+                        $h1_nb_msg_processed +=1 ;
+		} elsif(0 == $rc) {
+			# duplicate
+			push @h1_msgs_duplicate, $m;
+			# duplicate, same id same size?
+			my $h1_size = $h1_fir_ref->{$m}->{'RFC822.SIZE'} || 0;
+			$nb_msg_skipped += 1;
+			$h1_total_bytes_duplicate += $h1_size;
+			$h1_nb_msg_duplicate += 1;
+                        $h1_nb_msg_processed +=1 ;
+		}
+	}
+        my $h1_msgs_duplicate_nb = scalar  @h1_msgs_duplicate  ;
+        $h1{ $h1_fold }{ 'duplicates_nb' } = $h1_msgs_duplicate_nb ;
+
+        $debug and myprint( "Host1 selected: $h1_msgs_nb  duplicates: $h1_msgs_duplicate_nb\n"  ) ;
+	$debug and myprint( 'Host1 whole time parsing headers took ', timenext(), " s\n"  ) ;
+
+	$debug and myprint( "Host2 parsing headers of folder [$h2_fold]\n" ) ;
+
+	my ($h2_heads_ref, $h2_fir_ref) = ( {}, {} );
+	$h2_heads_ref =   $imap2->parse_headers([@h2_msgs_not_in_cache], @useheader) if (@h2_msgs_not_in_cache);
+	$debug and myprint( "Host2 parsing headers of folder [$h2_fold] took ", timenext(), " s\n"  ) ;
+
+	$debug and myprint( "Host2 getting flags idate and sizes of folder [$h2_fold]\n"  ) ;
+	@{ $h2_fir_ref }{@h2_msgs} = (  ); # fetch_hash can select by uid with last arg as ref
+
+
+        if ( $abletosearch ) {
+		$h2_fir_ref = $imap2->fetch_hash( \@h2_msgs, 'FLAGS', 'INTERNALDATE', 'RFC822.SIZE', $h2_fir_ref)
+		if (@h2_msgs) ;
+        }else{
+		my $uidnext = $imap2->uidnext( $h2_fold ) || $uidnext_default ;
+		my $fetch_hash_uids = $fetch_hash_set || "1:$uidnext" ;
+		$h2_fir_ref = $imap2->fetch_hash( $fetch_hash_uids, 'FLAGS', 'INTERNALDATE', 'RFC822.SIZE', $h2_fir_ref )
+		if ( @h2_msgs ) ;
+        }
+
+	$debug and myprint( "Host2 getting flags idate and sizes of folder [$h2_fold] took ", timenext(), " s\n"  ) ;
+
+	my @h2_msgs_duplicate;
+	foreach my $m (@h2_msgs_not_in_cache) {
+		my $rc = parse_header_msg($imap2, $m, $h2_heads_ref, $h2_fir_ref, 'Host2', \%h2_hash) ;
+		my $h2_size = $h2_fir_ref->{$m}->{'RFC822.SIZE'} || 0 ;
+		if (! defined  $rc  ) {
+                        myprint( "Host2 $h2_fold/$m size $h2_size ignored (no wanted headers so we ignore this message)\n"  ) ;
+			$h2_nb_msg_noheader += 1 ;
+		} elsif( 0 == $rc ) {
+			# duplicate
+			$h2_nb_msg_duplicate += 1 ;
+			$h2_total_bytes_duplicate += $h2_size ;
+			push  @h2_msgs_duplicate, $m  ;
+		}
+	}
+
+        # %h2_folders_of_md5
+        foreach my $md5 (  keys  %h2_hash  ) {
+        	$h2_folders_of_md5{ $md5 }->{ $h2_fold } ++ ;
+        }
+
+
+        my $h2_msgs_duplicate_nb = scalar  @h2_msgs_duplicate  ;
+        $h2{ $h2_fold }{ 'duplicates_nb' } = $h2_msgs_duplicate_nb ;
+
+        myprint( "Host2 folder $h2_fold selected: $h2_msgs_nb messages,  duplicates: $h2_msgs_duplicate_nb\n" )
+        	if ( $debug or $delete2duplicates or $h2_msgs_duplicate_nb ) ;
+	$debug and myprint( 'Host2 whole time parsing headers took ', timenext(  ), " s\n"  ) ;
+
+	$debug and myprint( "++++ Verifying [$h1_fold] -> [$h2_fold]\n" ) ;
+	# messages in host1 that are not in host2
+
+	my @h1_hash_keys_sorted_by_uid
+	  = sort {$h1_hash{$a}{'m'} <=> $h1_hash{$b}{'m'}} keys %h1_hash;
+
+	#myprint( map { $h1_hash{$_}{'m'} . q{ }} @h1_hash_keys_sorted_by_uid ) ;
+
+	my @h2_hash_keys_sorted_by_uid
+	  = sort {$h2_hash{$a}{'m'} <=> $h2_hash{$b}{'m'}} keys %h2_hash;
+
+
+	if( $delete2duplicates and not exists  $h2_folders_from_1_several{ $h2_fold }  ) {
+		my @h2_expunge ;
+
+		foreach my $h2_msg ( @h2_msgs_duplicate ) {
+			myprint( "msg $h2_fold/$h2_msg marked \\Deleted [duplicate] on host2 $dry_message\n"  ) ;
+			push  @h2_expunge, $h2_msg  if $uidexpunge2 ;
+			unless ( $dry ) {
+				$imap2->delete_message( $h2_msg ) ;
+				$h2_nb_msg_deleted += 1 ;
+			}
+		}
+		my $cnt = scalar @h2_expunge ;
+		if( @h2_expunge and not $expunge2 ) {
+			myprint( "Host2: UidExpunging $cnt message(s) in folder $h2_fold $dry_message\n"  ) ;
+			$imap2->uidexpunge( \@h2_expunge ) if ! $dry ;
+		}
+        	if ( $expunge2 ){
+                	myprint( "Host2: Expunging folder $h2_fold $dry_message\n"  ) ;
+                	$imap2->expunge(  ) if ! $dry ;
+        	}
+	}
+
+	if( $delete2 and not exists  $h2_folders_from_1_several{ $h2_fold }  ) {
+        	# No host1 folders f1a f1b ... going all to same f2 (via --regextrans2)
+		my @h2_expunge;
+		foreach my $m_id (@h2_hash_keys_sorted_by_uid) {
+			#myprint( "$m_id " ) ;
+			unless (exists $h1_hash{$m_id}) {
+				my $h2_msg  = $h2_hash{$m_id}{'m'};
+				my $h2_flags  = $h2_hash{$m_id}{'F'} || q{};
+				my $isdel  = $h2_flags =~ /\B\\Deleted\b/x ? 1 : 0;
+				myprint( "Host2: msg $h2_fold/$h2_msg marked \\Deleted on host2 [$m_id] $dry_message\n" )
+				  if ! $isdel;
+				push @h2_expunge, $h2_msg if $uidexpunge2;
+				unless ($dry or $isdel) {
+					$imap2->delete_message($h2_msg);
+					$h2_nb_msg_deleted += 1;
+				}
+			}
+		}
+		foreach my $h2_msg ( @h2_msgs_delete2_not_in_cache ) {
+			myprint( "Host2: msg $h2_fold/$h2_msg marked \\Deleted [not in cache] on host2 $dry_message\n" ) ;
+                        push @h2_expunge, $h2_msg if $uidexpunge2;
+			unless ($dry) {
+				$imap2->delete_message($h2_msg);
+				$h2_nb_msg_deleted += 1;
+			}
+		}
+		my $cnt = scalar @h2_expunge ;
+
+		if( @h2_expunge and not $expunge2 ) {
+			myprint( "Host2: UidExpunging $cnt message(s) in folder $h2_fold $dry_message\n"  ) ;
+			$imap2->uidexpunge( \@h2_expunge ) if ! $dry ;
+		}
+        	if ( $expunge2 ) {
+                	myprint( "Host2: Expunging folder $h2_fold $dry_message\n"  ) ;
+                	$imap2->expunge(  ) if ! $dry ;
+        	}
+	}
+
+	if( $delete2 and exists  $h2_folders_from_1_several{ $h2_fold }  ) {
+        	myprint( "Host2 folder $h2_fold $h2_folders_from_1_several{ $h2_fold } folders left to sync there\n"  ) ;
+		my @h2_expunge;
+		foreach my $m_id ( @h2_hash_keys_sorted_by_uid ) {
+                	my $h2_msg  = $h2_hash{ $m_id }{ 'm' } ;
+			unless ( exists  $h1_hash{ $m_id }  ) {
+				my $h2_flags  = $h2_hash{ $m_id }{ 'F' } || q{} ;
+				my $isdel  = $h2_flags =~ /\B\\Deleted\b/x ? 1 : 0 ;
+				unless ( $isdel ) {
+                                	$debug and myprint( "Host2: msg $h2_fold/$h2_msg candidate for deletion [$m_id]\n"  ) ;
+					$uid_candidate_for_deletion{ $h2_fold }{ $h2_msg }++ ;
+				}
+			}else{
+                        	$debug and myprint( "Host2: msg $h2_fold/$h2_msg will cancel deletion [$m_id]\n"  ) ;
+                        	$uid_candidate_no_deletion{ $h2_fold }{ $h2_msg }++ ;
+                        }
+		}
+		foreach my $h2_msg ( @h2_msgs_delete2_not_in_cache ) {
+			myprint( "Host2: msg $h2_fold/$h2_msg candidate for deletion [not in cache]\n" ) ;
+                        $uid_candidate_for_deletion{ $h2_fold }{ $h2_msg }++ ;
+		}
+
+		foreach my $h2_msg ( @h2_msgs_in_cache ) {
+			myprint( "Host2: msg $h2_fold/$h2_msg will cancel deletion [in cache]\n" ) ;
+                        $uid_candidate_no_deletion{ $h2_fold }{ $h2_msg }++ ;
+		}
+
+
+                if ( 0 == $h2_folders_from_1_several{ $h2_fold } ) {
+                	# last host1 folder going to $h2_fold
+                        myprint( "Last host1 folder going to $h2_fold\n"  ) ;
+                        foreach my $h2_msg ( keys %{ $uid_candidate_for_deletion{ $h2_fold } } ) {
+                        	$debug and myprint( "Host2: msg $h2_fold/$h2_msg candidate for deletion\n"  ) ;
+                                if ( exists  $uid_candidate_no_deletion{ $h2_fold }{ $h2_msg }  ) {
+                                	$debug and myprint( "Host2: msg $h2_fold/$h2_msg canceled deletion\n"  ) ;
+                                }else{
+                                	myprint( "Host2: msg $h2_fold/$h2_msg marked \\Deleted $dry_message\n" ) ;
+                                        push  @h2_expunge, $h2_msg  if $uidexpunge2 ;
+                                        unless ( $dry ) {
+                                        	$imap2->delete_message( $h2_msg ) ;
+                                        	$h2_nb_msg_deleted += 1 ;
+                                        }
+                                }
+                        }
+                }
+
+		my $cnt = scalar @h2_expunge ;
+		if( @h2_expunge and not $expunge2 ) {
+			myprint( "Host2: UidExpunging $cnt message(s) in folder $h2_fold $dry_message\n"  ) ;
+			$imap2->uidexpunge( \@h2_expunge ) if ! $dry ;
+		}
+        	if ( $expunge2 ) {
+                	myprint( "Host2: Expunging host2 folder $h2_fold $dry_message\n"  ) ;
+                	$imap2->expunge(  ) if ! $dry ;
+        	}
+
+                $h2_folders_from_1_several{ $h2_fold }-- ;
+	}
+
+
+	my $h2_uidnext = $imap2->uidnext( $h2_fold ) ;
+        $debug and myprint( "Host2 uidnext: $h2_uidnext\n"  ) ;
+	$h2_uidguess = $h2_uidnext ;
+	MESS: foreach my $m_id (@h1_hash_keys_sorted_by_uid) {
+        	last FOLDER if $imap1->IsUnconnected(  ) ;
+                last FOLDER if $imap2->IsUnconnected(  ) ;
+
+		#myprint( "h1_nb_msg_processed: $h1_nb_msg_processed\n"  ) ;
+		my $h1_size  = $h1_hash{$m_id}{'s'};
+		my $h1_msg   = $h1_hash{$m_id}{'m'};
+		my $h1_idate = $h1_hash{$m_id}{'D'};
+
+		if ( ( not exists  $h2_hash{ $m_id }  )
+                	and ( not ( exists $h2_folders_of_md5{ $m_id } )
+                              or not $skipcrossduplicates ) ) {
+			# copy
+			my $h2_msg = copy_message( $sync, $h1_msg, $h1_fold, $h2_fold, $h1_fir_ref, $permanentflags2, $cache_dir ) ;
+                        $h2_folders_of_md5{ $m_id }->{ $h2_fold } ++ ;
+                        if( $delete2 and ( exists $h2_folders_from_1_several{ $h2_fold } ) and $h2_msg ) {
+                        	myprint( "Host2: msg $h2_fold/$h2_msg will cancel deletion [fresh copy] on host2\n"  ) ;
+	                        $uid_candidate_no_deletion{ $h2_fold }{ $h2_msg }++ ;
+                        }
+                        last FOLDER if total_bytes_max_reached(  ) ;
+			next MESS;
+		}
+		else{
+		        # already on host2
+                        if ( exists  $h2_hash{ $m_id }  ) {
+				my $h2_msg   = $h2_hash{$m_id}{'m'} ;
+				$debug and myprint( "Host1 found msg $h1_fold/$h1_msg equals Host2 $h2_fold/$h2_msg\n"  ) ;
+                                if ( $usecache ) {
+                                	$debugcache and myprint( "touch $cache_dir/${h1_msg}_$h2_msg\n"  ) ;
+                                	touch( "$cache_dir/${h1_msg}_$h2_msg" )
+                                        or croak( "Couldn't touch $cache_dir/${h1_msg}_$h2_msg" ) ;
+                                }
+                        }elsif( exists  $h2_folders_of_md5{ $m_id }  ) {
+                        	my @folders_dup = keys  %{ $h2_folders_of_md5{ $m_id } }  ;
+                        	( $debug or $debugcrossduplicates ) and myprint( "Host1 found msg $h1_fold/$h1_msg is also in Host2 folders @folders_dup\n"  ) ;
+                        }
+			$total_bytes_skipped += $h1_size ;
+			$nb_msg_skipped += 1 ;
+                        $h1_nb_msg_processed +=1 ;
+                }
+
+                if ( exists  $h2_hash{ $m_id }  ) {
+			#$debug and myprint( "MESSAGE $m_id\n" ) ;
+			my $h2_msg  = $h2_hash{$m_id}{'m'};
+
+                	sync_flags_fir( $h1_fold, $h1_msg, $h2_fold, $h2_msg, $permanentflags2, $h1_fir_ref, $h2_fir_ref ) ;
+	    		# Good
+			my $h2_size = $h2_hash{$m_id}{'s'};
+			$debug and myprint(
+			"Host1 size  msg $h1_fold/$h1_msg = $h1_size <> $h2_size = Host2 $h2_fold/$h2_msg\n" ) ;
+		}
+                last FOLDER if $imap2->IsUnconnected(  ) ;
+
+		if ( $delete ) {
+			delete_message_on_host1( $h1_msg, $h1_fold ) ;
+		}
+	}
+	# END MESS: loop
+        last FOLDER if $imap1->IsUnconnected(  ) ;
+        last FOLDER if $imap2->IsUnconnected(  ) ;
+	MESS_IN_CACHE: foreach my $h1_msg ( @h1_msgs_in_cache ) {
+		my $h2_msg = $cache_1_2_ref->{ $h1_msg } ;
+		$debugcache and myprint( "cache messages update flags $h1_msg->$h2_msg\n" ) ;
+		sync_flags_fir( $h1_fold, $h1_msg, $h2_fold, $h2_msg, $permanentflags2, $h1_fir_ref, $h2_fir_ref ) ;
+		my $h1_size = $h1_fir_ref->{ $h1_msg }->{ 'RFC822.SIZE' } || 0 ;
+		$total_bytes_skipped += $h1_size;
+		$nb_msg_skipped += 1;
+                $h1_nb_msg_processed +=1 ;
+                last FOLDER if $imap2->IsUnconnected(  ) ;
+	}
+
+	#myprint( "Messages by uid: ", map { "$_ " } keys %h1_msgs_copy_by_uid, "\n"  ) ;
+	MESS_BY_UID: foreach my $h1_msg ( sort { $a <=> $b } keys %h1_msgs_copy_by_uid ) {
+		#
+		$debug and myprint( "Copy by uid $h1_fold/$h1_msg\n"  ) ;
+                last FOLDER if $imap1->IsUnconnected(  ) ;
+                last FOLDER if $imap2->IsUnconnected(  ) ;
+		my $h2_msg = copy_message( $sync, $h1_msg, $h1_fold, $h2_fold, $h1_fir_ref, $permanentflags2, $cache_dir ) ;
+                if( $delete2 and exists  $h2_folders_from_1_several{ $h2_fold }  and $h2_msg ) {
+                	myprint( "Host2: msg $h2_fold/$h2_msg will cancel deletion [fresh copy] on host2\n"  ) ;
+	                $uid_candidate_no_deletion{ $h2_fold }{ $h2_msg }++ ;
+                }
+		last FOLDER if total_bytes_max_reached(  ) ;
+	}
+
+	if ( $expunge or $expunge1 ){
+		myprint( "Host1: Expunging folder $h1_fold $dry_message\n"  ) ;
+		unless( $dry ) { $imap1->expunge(  ) } ;
+	}
+	if ( $expunge2 ){
+		myprint( "Host2: Expunging folder $h2_fold $dry_message\n"  ) ;
+		unless( $dry ) { $imap2->expunge(  ) } ;
+	}
+	$debug and myprint( 'Time: ', timenext(  ), " s\n"  ) ;
+}
+
+
+sub total_bytes_max_reached {
+
+	return( 0 ) if not $exitwhenover ;
+	if ( $total_bytes_transferred >= $exitwhenover ) {
+        	myprint( "Maximum bytes transferred reached, $total_bytes_transferred >= $exitwhenover, ending sync\n"  ) ;
+        	return( 1 ) ;
+        }
+
+}
+
+myprint( "++++ End looping on each folder\n"  ) ;
+( $debug or $sync->{debugfolders} ) and myprint( 'Time: ', timenext(  ), " s\n"  ) ;
+
+
+if ( $foldersizesatend ) {
+	myprint( << 'END_SIZE'  ) ;
+
+Folders sizes after the synchronization.
+You can remove this foldersizes listing by using  "--nofoldersizesatend"
+END_SIZE
+
+	foldersizesatend(  ) ;
+}
+
+$imap1->logout(  ) unless lost_connection( $imap1, "for host1 [$host1]" ) ;
+$imap2->logout(  ) unless lost_connection( $imap2, "for host2 [$host2]" ) ;
+
+stats( $sync ) ;
+myprint( errorsdump( $sync->{nb_errors}, errors_log( $sync ) ) ) if ( $sync->{errorsdump} ) ;
+tests_live_result( $sync->{nb_errors} ) if ( $testslive ) ;
+exit_clean( $sync, $EXIT_WITH_ERRORS ) if ( $sync->{nb_errors} ) ;
+exit_clean( $sync, $EX_OK ) ;
+
+# END of main program
+
+
+# subroutines
+sub  myprint  { return print  @ARG ; } 
+sub  myprintf { return printf @ARG ; } 
+
+sub mysprintf {
+        my( $format, @list ) = @ARG ;
+        return sprintf $format, @list ; 
+}
+
+sub unsetunsafe {
+        # Remove all content in unsafe evalued options
+        @regextrans2 = (  ) ;
+        @regexflag = (  ) ;
+        @regexmess = (  ) ;
+        @skipmess = (  ) ;
+        @pipemess = (  ) ;
+        $delete2foldersonly = undef ;
+        $delete2foldersbutnot = undef ;
+        return ;
+}
+
+sub debugsleep {
+        my $mysync = shift ;
+        if ( defined $mysync->{debugsleep} ) {
+                myprint( "Info: sleeping $mysync->{debugsleep}s\n" ) ;
+                sleep $mysync->{debugsleep} ;
+        }
+        return ;
+}
+
+sub foldersizes_on_h1h2 {
+	myprint( << 'END_SIZE'  ) ;
+
+Folders sizes before the synchronization.
+You can remove foldersizes listings by using "--nofoldersizes" and  "--nofoldersizesatend"
+but then you will also loose the ETA (Estimation Time of Arrival) given after each message copy.
+END_SIZE
+
+	( $h1_nb_msg_start, $h1_bytes_start ) = foldersizes( 'Host1', $imap1, $search1, @h1_folders_wanted        ) ;
+	( $h2_nb_msg_start, $h2_bytes_start ) = foldersizes( 'Host2', $imap2, $search2, @h2_folders_from_1_wanted ) ;
+
+        if ( not all_defined( $h1_nb_msg_start, $h1_bytes_start, $h2_nb_msg_start, $h2_bytes_start ) ) {
+                my $error = "Failure getting foldersizes, ETA and final diff will not be displayed\n" ;
+                errors_incr( $sync, $error ) ;
+                $foldersizes = 0 ;
+                $foldersizesatend = 0 ;
+                return ;
+        }
+        
+        my $h2_bytes_limit = $sync->{host2}->{quota_limit_bytes} || 0 ;
+        if ( $h2_bytes_limit and ( $h2_bytes_limit < $h1_bytes_start ) ) {
+        	my $quota_percent = mysprintf( '%.0f', $h1_bytes_start/$h2_bytes_limit ) ;
+                my $error = "Host2: Quota limit will be exceeded! Over $quota_percent % ( $h1_bytes_start bytes / $h2_bytes_limit bytes )\n" ;
+                errors_incr( $sync, $error ) ;
+        }
+        return ;
+}
+
+sub all_defined {
+        if ( not @ARG ) {
+                return 0 ;
+        }
+        foreach my $elem ( @ARG ) {
+                if ( not defined $elem ) {
+                        return 0 ;
+                }
+        }
+        return 1 ;
+}
+
+sub tests_all_defined {
+        is( 0, all_defined(  ),             'all_defined: no param  => 0' ) ;
+        is( 0, all_defined( () ),           'all_defined: void list => 0' ) ;
+        is( 0, all_defined( undef ),        'all_defined: undef     => 0' ) ;
+        is( 0, all_defined( undef, undef ), 'all_defined: undef     => 0' ) ;
+        is( 0, all_defined( 1, undef ),     'all_defined: 1 undef   => 0' ) ;
+        is( 0, all_defined( undef, 1 ),     'all_defined: undef 1   => 0' ) ;
+        is( 1, all_defined( 1, 1 ),         'all_defined: 1 1   => 1' ) ;
+        is( 1, all_defined( (1, 1) ),       'all_defined: (1 1) => 1' ) ;
+        return ;
+}
+
+
+sub imap_id_stuff {
+	my $sync = shift ;
+
+	if ( not $sync->{id} ) { return ; } ;
+
+	$sync->{h1_imap_id} = imap_id( $sync->{imap1}, 'Host1' ) ;
+	#myprint( 'Host1: ' . $sync->{h1_imap_id}  ) ;
+	$sync->{h2_imap_id} = imap_id( $sync->{imap2}, 'Host2' ) ;
+	#myprint( 'Host2: ' . $sync->{h2_imap_id}  ) ;
+
+	return ;
+}
+
+sub imap_id {
+	my ( $imap, $Side ) = @_ ;
+
+	$Side ||= q{} ;
+	my $imap_id_response = q{} ;
+
+	if ( not $imap->has_capability( 'ID' ) ) {
+		 $imap_id_response = 'No ID capability' ;
+                 myprint( "$Side: No ID capability\n"  ) ;
+	}else{
+		my $id_inp = imapsync_id( { side => lc $Side } ) ;
+		myprint( "\n$Side: found ID capability. Sending/receiving ID, presented in raw IMAP for now.\n"
+                . "In order to avoid sending/receiving ID, use option --noid\n" ) ;
+		my $debug_before = $imap->Debug(  ) ;
+		$imap->Debug( 1 ) ;
+		my $id_out = $imap->tag_and_run( 'ID ' . $id_inp ) ;
+		#my $id_out = $imap->tag_and_run( 'ID NIL' ) ;
+                myprint( "\n"  ) ;
+		$imap->Debug( $debug_before ) ;
+		#$imap_id_response = Data::Dumper->Dump( [ $id_out ], [ 'IMAP_ID' ] ) ;
+	}
+	return( $imap_id_response ) ;
+}
+
+sub imapsync_id {
+	my $overhashref = shift ;
+	# See http://tools.ietf.org/html/rfc2971.html
+
+	my $imapsync_id = { } ;
+
+	my $imapsync_id_lamiral = {
+		name          => 'imapsync',
+		version       => imapsync_version(  ),
+		os            => $OSNAME,
+		vendor        => 'Gilles LAMIRAL',
+		'support-url' => 'http://imapsync.lamiral.info/',
+		# Example of date-time:  19-Sep-2015 08:56:07
+		date          => date_from_rcs( q{$Date: 2016/08/19 10:30:36 $ } ),
+	} ;
+
+	my $imapsync_id_github  = {
+		name          => 'imapsync',
+		version       => imapsync_version(  ),
+		os            => $OSNAME,
+		vendor        => 'github',
+		'support-url' => 'https://github.com/imapsync/imapsync',
+		date          => date_from_rcs( q{$Date: 2016/08/19 10:30:36 $ } ),
+	} ;
+
+	$imapsync_id = $imapsync_id_lamiral ;
+	#$imapsync_id = $imapsync_id_github ;
+	my %mix = ( %{ $imapsync_id }, %{ $overhashref } ) ;
+	my $imapsync_id_str = format_for_imap_arg( \%mix ) ;
+	#myprint( "$imapsync_id_str\n"  ) ;
+	return( $imapsync_id_str ) ;
+}
+
+sub tests_imapsync_id {
+	ok( '("name" "imapsync" "version" "111" "os" "beurk" "vendor" "Gilles LAMIRAL" "support-url" "http://imapsync.lamiral.info/" "date" "22-12-1968" "side" "host1")'
+	eq imapsync_id( {
+		version => 111,
+		os => 'beurk',
+		date => '22-12-1968',
+		side => 'host1' } ),
+	'tests_imapsync_id override' ) ;
+
+	return ;
+}
+
+sub format_for_imap_arg {
+	my $ref = shift ;
+
+	my $string = q{} ;
+	my %terms = %{ $ref } ;
+	my @terms = (  ) ;
+	if ( not ( %terms ) ) { return( 'NIL' ) } ;
+	# sort like in RFC then add extra key/values
+	foreach my $key ( qw( name version os os-version vendor support-url address date command arguments environment) ) {
+		if ( $terms{ $key } ) {
+			push  @terms, $key, $terms{ $key }  ;
+			delete $terms{ $key } ;
+		}
+	}
+	push  @terms, %terms  ;
+	$string = '(' . ( join q{ }, map { '"' . $_ . '"' } @terms )  . ')' ;
+	return( $string ) ;
+}
+
+
+
+sub tests_format_for_imap_arg {
+	ok( 'NIL' eq format_for_imap_arg( { } ), 'format_for_imap_arg empty hash ref' ) ;
+	ok( '("name" "toto")' eq format_for_imap_arg( { name => 'toto' } ), 'format_for_imap_arg { name => toto }' ) ;
+	ok( '("name" "toto" "key" "val")' eq format_for_imap_arg( { name => 'toto', key => 'val' } ), 'format_for_imap_arg 2 x key val' ) ;
+	return ;
+}
+
+sub quota {
+	my ( $imap, $side, $sync ) = @_ ;
+
+        my $Side = ucfirst $side ;
+	my $debug_before = $imap->Debug(  ) ;
+	$imap->Debug( 1 ) ;
+	if ( not $imap->has_capability( 'QUOTA' ) ) {
+        	$imap->Debug( $debug_before ) ;
+        	return ;
+        } ;
+	myprint( "\n$Side: found quota, presented in raw IMAP\n"  ) ;
+	my $getquotaroot = $imap->getquotaroot( 'INBOX' ) ;
+        # Gmail INBOX quotaroot is "" but with it Mail::IMAPClient does a literal GETQUOTA {2} \n ""
+        #$imap->quota( 'ROOT' ) ;
+        #$imap->quota( '""' ) ;
+	myprint( "\n"  ) ;
+	$imap->Debug( $debug_before ) ;
+        my $quota_limit_bytes   = quota_extract_storage_limit_in_bytes( $getquotaroot ) ;
+        my $quota_current_bytes = quota_extract_storage_current_in_bytes( $getquotaroot ) ;
+        $sync->{$side}->{quota_limit_bytes}   = $quota_limit_bytes ;
+        $sync->{$side}->{quota_current_bytes} = $quota_current_bytes ;
+        my $quota_percent ;
+        if ( $quota_limit_bytes > 0 ) {
+        	$quota_percent = mysprintf( '%.2f', $NUMBER_100 * $quota_current_bytes / $quota_limit_bytes ) ;
+        }else{
+        	$quota_percent = 0 ;
+        }
+        myprint( "$Side: Quota current storage is $quota_current_bytes bytes. Limit is $quota_limit_bytes bytes. So $quota_percent % full\n"  ) ;
+        if ( $QUOTA_PERCENT_LIMIT < $quota_percent ) {
+        	my $error = "$Side: $quota_percent % full: it is time to find a bigger place! ( $quota_current_bytes bytes / $quota_limit_bytes bytes )\n" ;
+                errors_incr( $sync, $error ) ;
+        }
+	return ;
+}
+
+sub tests_quota_extract_storage_limit_in_bytes {
+	my $imap_output = [
+	'* QUOTAROOT "INBOX" "Storage quota" "Messages quota"',
+        '* QUOTA "Storage quota" (STORAGE 1 104857600)',
+        '* QUOTA "Messages quota" (MESSAGE 2 100000)',
+        '5 OK Getquotaroot completed.'
+	] ;
+        ok( $NUMBER_104857600 * $KIBI == quota_extract_storage_limit_in_bytes( $imap_output ), 'quota_extract_storage_limit_in_bytes ') ;
+        return ;
+}
+
+sub quota_extract_storage_limit_in_bytes {
+	my $imap_output = shift ;
+
+        my $limit_kb ;
+        $limit_kb = ( map { /.*\(\s*STORAGE\s+\d+\s+(\d+)\s*\)/ ? $1 : () } @{ $imap_output } )[0] ;
+        $limit_kb ||= 0 ;
+        $debug and myprint( "storage_limit_kb = $limit_kb\n"  ) ;
+        return( $KIBI * $limit_kb ) ;
+}
+
+
+sub tests_quota_extract_storage_current_in_bytes {
+	my $imap_output = [
+	'* QUOTAROOT "INBOX" "Storage quota" "Messages quota"',
+        '* QUOTA "Storage quota" (STORAGE 1 104857600)',
+        '* QUOTA "Messages quota" (MESSAGE 2 100000)',
+        '5 OK Getquotaroot completed.'
+	] ;
+        ok( 1*$KIBI == quota_extract_storage_current_in_bytes( $imap_output ), 'quota_extract_storage_current_in_bytes: 1 => 1024 ') ;
+        return ;
+}
+
+sub quota_extract_storage_current_in_bytes {
+	my $imap_output = shift ;
+
+        my $current_kb ;
+        $current_kb = ( map { /.*\(\s*STORAGE\s+(\d+)\s+\d+\s*\)/ ? $1 : () } @{ $imap_output } )[0] ;
+        $current_kb ||= 0 ;
+        $debug and myprint( "storage_current_kb = $current_kb\n"  ) ;
+        return( $KIBI * $current_kb ) ;
+
+}
+
+
+sub automap {
+	my ( $sync ) = @_ ;
+
+	if ( $sync->{automap} ) {
+		myprint( "Turned on automapping folders ( use --noautomap to turn off automapping )\n"  ) ;
+	}else{
+		myprint( "Turned off automapping folders ( use --automap to turn on automapping )\n"  ) ;
+		return ;
+	}
+
+        $sync->{h1_special} = special_from_folders_hash( $sync->{imap1}, 'Host1' ) ;
+        $sync->{h2_special} = special_from_folders_hash( $sync->{imap2}, 'Host2' ) ;
+
+	build_possible_special( $sync ) ;
+        build_guess_special(  $sync ) ;
+	build_automap( $sync ) ;
+
+	return ;
+}
+
+
+
+
+sub build_guess_special {
+	my ( $sync ) = shift ;
+
+        foreach my $h1_fold ( sort keys  %{ $sync->{h1_folders_all} }  ) {
+        	my $special = guess_special( $h1_fold, $sync->{possible_special}, $sync->{h1_prefix} ) ;
+        	if ( $special ) {
+                	$sync->{h1_special_guessed}{$h1_fold} = $special ;
+                        my $already_guessed = $sync->{h1_special_guessed}{$special} ;
+                        if ( $already_guessed ) {
+                        	myprint( "Host1: $h1_fold not $special because set to $already_guessed\n"  ) ;
+                        }else{
+	                        $sync->{h1_special_guessed}{$special} = $h1_fold ;
+                        }
+                }
+        }
+        foreach my $h2_fold ( sort keys  %{ $sync->{h2_folders_all} }  ) {
+        	my $special = guess_special( $h2_fold, $sync->{possible_special}, $sync->{h2_prefix} ) ;
+        	if ( $special ) {
+                	$sync->{h2_special_guessed}{$h2_fold} = $special ;
+                        my $already_guessed = $sync->{h2_special_guessed}{$special} ;
+                        if ( $already_guessed ) {
+                        	myprint( "Host2: $h2_fold not $special because set to $already_guessed\n"  ) ;
+                        }else{
+	                        $sync->{h2_special_guessed}{$special} = $h2_fold ;
+                        }
+                }
+        }
+        return ;
+}
+
+sub guess_special {
+	my( $folder, $possible_special_ref, $prefix ) = @_ ;
+
+        my $folder_no_prefix = $folder ;
+        $folder_no_prefix =~ s/${prefix}// ;
+        #$debug and myprint( "folder_no_prefix: $folder_no_prefix\n"  ) ;
+
+        my $guess_special = $possible_special_ref->{ $folder }
+        	|| $possible_special_ref->{ $folder_no_prefix }
+        	|| q{} ;
+
+        return( $guess_special ) ;
+}
+
+sub tests_guess_special {
+	my $possible_special_ref = build_possible_special( my $sync ) ;
+        ok( '\Sent' eq guess_special( 'Sent', $possible_special_ref, q{} ) ,'guess_special: Sent => \Sent' ) ;
+        ok( q{} eq guess_special( 'Blabla', $possible_special_ref, q{} ) ,'guess_special: Blabla => q{}' ) ;
+        ok( '\Sent' eq guess_special( 'INBOX.Sent', $possible_special_ref, 'INBOX.' ) ,'guess_special: INBOX.Sent => \Sent' ) ;
+	return ;
+}
+
+sub build_automap {
+	my ( $sync ) = @_ ;
+
+	foreach my $h1_fold ( @{ $sync->{h1_folders_wanted} } ) {
+		my $h2_fold ;
+		my $h1_special = $sync->{h1_special}{$h1_fold} ;
+                my $h1_special_guessed = $sync->{h1_special_guessed}{$h1_fold} ;
+
+		# Case 1: special on both sides.
+		if ( $h1_special
+                     and exists  $sync->{h2_special}{$h1_special}  ) {
+			$h2_fold = $sync->{h2_special}{$h1_special} ;
+			$sync->{f1f2auto}{ $h1_fold } = $h2_fold ;
+			next ;
+		}
+		# Case 2: special on host1, not on host2
+		if ( $h1_special
+                     and ( not exists  $sync->{h2_special}{$h1_special}  )
+                     and ( exists  $sync->{h2_special_guessed}{$h1_special}  )
+                   ) {
+			# special_guessed on host2
+                        $h2_fold = $sync->{h2_special_guessed}{$h1_special} ;
+                        $sync->{f1f2auto}{ $h1_fold } = $h2_fold ;
+			next ;
+		}
+		# Case 3: no special on host1, special on host2
+                if ( ( not $h1_special )
+                     and ( $h1_special_guessed )
+                     and ( exists  $sync->{h2_special}{$h1_special_guessed}  )
+                ) {
+                	$h2_fold = $sync->{h2_special}{$h1_special_guessed} ;
+                        $sync->{f1f2auto}{ $h1_fold } = $h2_fold ;
+			next ;
+                }
+                # Case 4: no special on both sides.
+                if ( ( not $h1_special )
+                     and ( $h1_special_guessed )
+                     and ( not exists  $sync->{h2_special}{$h1_special_guessed}  )
+                     and ( exists  $sync->{h2_special_guessed}{$h1_special_guessed}  )
+                ) {
+                	$h2_fold = $sync->{h2_special_guessed}{$h1_special_guessed} ;
+                        $sync->{f1f2auto}{ $h1_fold } = $h2_fold ;
+			next ;
+                }
+	}
+	return( $sync->{f1f2auto} ) ;
+}
+
+# I willll probably add what there is at:
+# http://stackoverflow.com/questions/2185391/localized-gmail-imap-folders/2185548#2185548
+sub build_possible_special {
+	my $sync = shift ;
+	my $possible_special = { } ;
+	# All|Archive|Drafts|Flagged|Junk|Sent|Trash
+
+	$possible_special->{'\All'}     = [ 'All', 'All Messages', '&BBIEQQQ1-' ] ;
+	$possible_special->{'\Archive'} = [ 'Archive', 'Archives', '&BBAEQARFBDgEMg-' ] ;
+	$possible_special->{'\Drafts'}  = [ 'Drafts', '&BCcENQRABD0EPgQyBDgEOgQ4-' ] ;
+	$possible_special->{'\Flagged'} = [ 'Flagged', 'Starred', '&BB8EPgQ8BDUERwQ1BD0EPQRLBDU-' ] ;
+	$possible_special->{'\Junk'}    = [ 'Junk', 'Spam', '&BCEEPwQwBDw-' ] ;
+	$possible_special->{'\Sent'}    = [ 'Sent', 'Sent Messages', 'Sent Items',
+                                            'Gesendete Elemente', 'Gesendete Objekte',
+                                            '&AMk-l&AOk-ments envoy&AOk-s', 'Envoy&AOk-',
+                                            'Elementos enviados',
+                                            '&kAFP4W4IMH8wojCkMMYw4A-',
+                                            '&BB4EQgQ,BEAEMAQyBDsENQQ9BD0ESwQ1-'] ;
+	$possible_special->{'\Trash'}   = [ 'Trash', '&BCMENAQwBDsENQQ9BD0ESwQ1-', '&BBoEPgRABDcEOAQ9BDA-' ] ;
+
+	foreach my $special ( qw( \All \Archive \Drafts \Flagged \Junk \Sent \Trash ) ){
+		foreach my $possible_folder ( @{ $possible_special->{$special} } ) {
+			$possible_special->{ $possible_folder } = $special ;
+		} ;
+	}
+        $sync->{possible_special} = $possible_special ;
+	$debug and myprint( Data::Dumper->Dump( [ $possible_special ], [ 'possible_special' ] )  ) ;
+        return( $possible_special ) ;
+}
+
+sub special_from_folders_hash {
+	my ( $imap, $side ) = @_ ;
+	my %special = (  ) ;
+        if ( not( Mail::IMAPClient->can( 'folders_hash' ) ) ) {
+        	my $error =  "$side: To have automagic rfc6154 folder mapping, upgrade Mail::IMAPClient >= 3.34\n" ;
+                errors_incr( $sync, $error ) ;
+                return( \%special ) ; # empty hash ref
+        }
+	my $folders_hash = $imap->folders_hash(  ) ;
+	foreach my $fhash (@{ $folders_hash } ) {
+			my @special =  grep { /\\(?:All|Archive|Drafts|Flagged|Junk|Sent|Trash)/ } @{ $fhash->{attrs} }  ;
+			if ( @special ) {
+				my $special = $special[0] ; # keep first one. Could be not very good.
+				if ( exists  $special{ $special }  ) {
+					myprintf( "%s: special %-20s = %s already asigned to %s\n",
+					        $side, $fhash->{name}, join( q{ }, @special ), $special{ $special } ) ;
+				}else{
+					myprintf( "%s: special %-20s = %s\n",
+					        $side, $fhash->{name}, join( q{ }, @special ) ) ;
+					$special{ $special } = $fhash->{name} ;
+					$special{ $fhash->{name} } = $special ; # double entry value => key
+				}
+			}
+		}
+        myprint( "\n" ) if ( %special ) ;
+	return( \%special ) ;
+}
+
+sub errors_incr {
+	my ( $mysync, @error ) = @ARG ;
+	$sync->{nb_errors}++ ;
+        
+        if ( @error ) {
+		errors_log( $mysync, @error ) ;
+                myprint( @error ) ;
+        }
+        
+        $mysync->{errorsmax} ||= $ERRORS_MAX ;
+	if ( $sync->{nb_errors} >= $mysync->{errorsmax} ) {
+		myprint( "Maximum number of errors $mysync->{errorsmax} reached ( you can change $mysync->{errorsmax} to 100 with --errorsmax 100 ). Exiting.\n"  ) ;
+                if ( $mysync->{errorsdump} ) {
+                        myprint( errorsdump( $sync->{nb_errors}, errors_log( $mysync ) ) ) ;
+                        # again since errorsdump(  ) can be very verbose and masq previous warning
+		        myprint( "Maximum number of errors $mysync->{errorsmax} reached ( you can change $mysync->{errorsmax} to 100 with --errorsmax 100 ). Exiting.\n"  ) ;
+		}
+                exit_clean( $mysync, $EXIT_WITH_ERRORS_MAX ) ;
+	}
+	return ;
+}
+
+sub errors_log {
+        my ( $mysync, @error ) = @ARG ;
+
+        if ( ! $mysync->{errors_log} ) {
+                $mysync->{errors_log} = [] ;
+        }
+
+        if ( @error ) {
+		push  @{ $mysync->{errors_log} }, join( q{}, @error  ) ;
+        }
+        if ( @{ $mysync->{errors_log} } ) {
+                return @{ $mysync->{errors_log} } ;
+        }
+        else {
+                return ;
+        }
+}
+
+sub tests_errors_log {
+
+
+}
+
+
+sub errorsdump {
+        my( $nb_errors, @errors_log ) = @ARG ;
+	my $error_num = 0 ;
+	my $errors_list = q{} ;
+	if ( @errors_log ) {
+		$errors_list = "++++ Listing $nb_errors errors encountered during the sync ( avoid this listing with --noerrorsdump ).\n" ;
+		foreach my $error ( @errors_log ) {
+			$error_num++ ;
+			$errors_list .= "Err $error_num/$nb_errors: $error" ;
+		}
+	}
+	return( $errors_list ) ;
+}
+
+
+sub tests_live_result {
+	my $nb_errors = shift ;
+	if ( $nb_errors  ) {
+		myprint( "Live tests failed with $nb_errors errors\n"  ) ;
+	} else {
+		myprint( "Live tests ended successfully\n"  ) ;
+	}
+	return ;
+}
+
+sub foldersizesatend {
+	timenext(  ) ;
+	return if ( $imap1->IsUnconnected(  ) ) ;
+	return if ( $imap2->IsUnconnected(  ) ) ;
+	# Get all folders on host2 again since new were created
+	@h2_folders_all = sort $imap2->folders();
+	for ( @h2_folders_all ) {
+        	$h2_folders_all{ $_ } = 1 ;
+        	$h2_folders_all_UPPER{ uc  $_  } = 1 ;
+        } ;
+	( $h1_nb_msg_end, $h1_bytes_end ) = foldersizes( 'Host1', $imap1, $search1, @h1_folders_wanted ) ;
+	( $h2_nb_msg_end, $h2_bytes_end ) = foldersizes( 'Host2', $imap2, $search2, @h2_folders_from_1_wanted ) ;
+        if ( not all_defined( $h1_nb_msg_end, $h1_bytes_end, $h2_nb_msg_end, $h2_bytes_end ) ) {
+                my $error = "Failure getting foldersizes, final differences will not be calculated\n" ;
+                errors_incr( $sync, $error ) ;
+        }
+	return ;
+}
+
+sub size_filtered_flag {
+	my $h1_size = shift ;
+
+	if (defined $maxsize and $h1_size >= $maxsize) {
+		return( 1 ) ;
+	}
+	if (defined $minsize and $h1_size <= $minsize) {
+		return( 1 ) ;
+	}
+	return( 0 ) ;
+}
+
+sub sync_flags_fir {
+	my ( $h1_fold, $h1_msg, $h2_fold, $h2_msg, $permanentflags2, $h1_fir_ref, $h2_fir_ref ) = @_ ;
+
+	if ( not defined  $h1_msg  ) { return } ;
+	if ( not defined  $h2_msg  ) { return } ;
+
+	my $h1_size = $h1_fir_ref->{$h1_msg}->{'RFC822.SIZE'} ;
+	return if size_filtered_flag( $h1_size ) ;
+
+	# used cached flag values for efficiency
+	my $h1_flags = $h1_fir_ref->{ $h1_msg }->{ 'FLAGS' } || q{} ;
+	my $h2_flags = $h2_fir_ref->{ $h2_msg }->{ 'FLAGS' } || q{} ;
+
+	sync_flags( $h1_fold, $h1_msg, $h1_flags, $h2_fold, $h2_msg, $h2_flags, $permanentflags2 ) ;
+
+        return ;
+}
+
+sub sync_flags_after_copy {
+	my( $h1_fold, $h1_msg, $h1_flags, $h2_fold, $h2_msg, $permanentflags2 ) = @_ ;
+
+        my @h2_flags = $imap2->flags( $h2_msg ) ;
+        my $h2_flags = "@h2_flags" ;
+        ( $debug or $debugflags ) and myprint( "Host2 flags before resync by STORE on msg $h2_msg: $h2_flags\n"  ) ;
+	sync_flags( $h1_fold, $h1_msg, $h1_flags, $h2_fold, $h2_msg, $h2_flags, $permanentflags2 ) ;
+        return ;
+}
+
+sub sync_flags {
+	my( $h1_fold, $h1_msg, $h1_flags, $h2_fold, $h2_msg, $h2_flags, $permanentflags2 ) = @_ ;
+
+	( $debug or $debugflags ) and
+        myprint( "Host1: flags init msg $h1_fold/$h1_msg flags( $h1_flags ) Host2 $h2_fold/$h2_msg flags( $h2_flags )\n"  ) ;
+
+	$h1_flags = flags_for_host2( $h1_flags, $permanentflags2 ) ;
+
+	$h2_flags = flagscase( $h2_flags ) ;
+
+	( $debug or $debugflags ) and
+        myprint( "Host1 flags filt msg $h1_fold/$h1_msg flags( $h1_flags ) Host2 $h2_fold/$h2_msg flags( $h2_flags )\n"  ) ;
+
+
+	# compare flags - set flags if there a difference
+	my @h1_flags = sort split(q{ }, $h1_flags );
+	my @h2_flags = sort split(q{ }, $h2_flags );
+	my $diff = compare_lists( \@h1_flags, \@h2_flags );
+
+	$diff and ( $debug or $debugflags )
+		and     myprint( "Host2 flags msg $h2_fold/$h2_msg replacing h2 flags( $h2_flags ) with h1 flags( $h1_flags )\n" ) ;
+	# This sets flags so flags can be removed with this
+	# When you remove a \Seen flag on host1 you want to it
+	# to be removed on host2. Just add flags is not what
+	# we need most of the time.
+
+	if ( not $dry and $diff and not $imap2->store( $h2_msg, "FLAGS.SILENT (@h1_flags)" ) ) {
+		my $error_msg = join q{}, "Host2 flags msg $h2_fold/$h2_msg could not add flags [@h1_flags]: ",
+		  $imap2->LastError || q{}, "\n" ;
+		errors_incr( $sync, $error_msg ) ;
+	}
+
+        return ;
+}
+
+
+
+sub _filter {
+	my $str = shift or return q{} ;
+        my $sz  = $SIZE_MAX_STR ;
+        my $len = length $str ;
+        if ( not $debug and $len > $sz*2 ) {
+                my $beg = substr $str, 0, $sz ;
+                my $end = substr $str, -$sz, $sz ;
+                $str = $beg . '...' . $end ;
+        }
+        $str =~ s/\012?\015$//x ;
+        return "(len=$len) " . $str ;
+}
+
+
+
+sub lost_connection {
+	my( $imap, $error_message ) = @_;
+        if ( $imap->IsUnconnected(  ) ) {
+                $sync->{nb_errors}++ ;
+                my $lcomm = $imap->LastIMAPCommand || q{} ;
+                my $einfo = $imap->LastError || @{$imap->History}[$LAST] || q{} ;
+
+                # if string is long try reduce to a more reasonable size
+                $lcomm = _filter( $lcomm ) ;
+                $einfo = _filter( $einfo ) ;
+                myprint( "Failure: last command: $lcomm\n") if ($debug && $lcomm) ;
+                myprint( "Failure: lost connection $error_message: ", $einfo, "\n") ;
+                return( 1 ) ;
+        }
+        else{
+        	return( 0 ) ;
+        }
+}
+
+sub max {
+	my @list = @_ ;
+	return( undef ) if ( 0 == scalar  @list  ) ;
+	my @sorted = sort { $a <=> $b } @list ;
+	return( pop @sorted ) ;
+}
+
+sub tests_max {
+	ok( 0  == max( 0 ),  'max 0' ) ;
+	ok( 1  == max( 1 ),  'max 1' ) ;
+	ok( $MINUS_ONE == max( $MINUS_ONE ), 'max -1') ;
+	ok( not ( defined max(  ) ), 'max no arg' ) ;
+	ok( $NUMBER_100 == max( 1, $NUMBER_100 ), 'max 1 100' ) ;
+	ok( $NUMBER_100 == max( $NUMBER_100, 1 ), 'max 100 1' ) ;
+	ok( $NUMBER_100 == max( $NUMBER_100, $NUMBER_42, 1 ), 'max 100 42 1' ) ;
+	ok( $NUMBER_100 == max( $NUMBER_100, '42', 1 ), 'max 100 42 1' ) ;
+	ok( $NUMBER_100 == max( '100', '42', 1 ), 'max 100 42 1' ) ;
+	#ok( 100 == max( 100, 'haha', 1 ), 'max 100 42 1') ;
+        return ;
+}
+
+
+sub check_lib_version {
+	$debug and myprint( "IMAPClient $Mail::IMAPClient::VERSION\n"  ) ;
+	if ( '2.2.9' eq $Mail::IMAPClient::VERSION ) {
+		myprint( "imapsync no longer supports Mail::IMAPClient 2.2.9, upgrade it\n"  ) ;
+		return 0 ;
+	}
+	else{
+		# 3.x.x is no longer buggy with imapsync.
+                # 3.30 or currently superior is imposed in the Perl "use Mail::IMAPClient line".
+		return 1 ;
+	}
+        return ;
+}
+
+sub module_version_str {
+	my( $module_name, $module_version ) = @_ ;
+	my $str = mysprintf( "%-20s %s\n", $module_name, $module_version ) ;
+        return( $str ) ;
+}
+
+sub modulesversion {
+
+	my @list_version;
+
+	my $v ;
+	eval { require Mail::IMAPClient; $v = $Mail::IMAPClient::VERSION } or $v = q{?} ;
+	push  @list_version, module_version_str( 'Mail::IMAPClient', $v )  ;
+
+	eval { require IO::Socket; $v = $IO::Socket::VERSION } or $v = q{?} ;
+	push  @list_version, module_version_str( 'IO::Socket', $v )  ;
+
+	eval { require IO::Socket::INET; $v = $IO::Socket::INET::VERSION } or $v = q{?} ;
+	push  @list_version, module_version_str( 'IO::Socket::INET', $v )  ;
+
+	eval { require IO::Socket::INET6; $v = $IO::Socket::INET6::VERSION } or $v = q{?} ;
+	push  @list_version, module_version_str( 'IO::Socket::INET6', $v )  ;
+
+	eval { require IO::Socket::SSL ; $v = $IO::Socket::SSL::VERSION } or $v = q{?} ;
+	push  @list_version, module_version_str( 'IO::Socket::SSL ', $v )  ;
+
+	eval { require Net::SSLeay ; $v = $Net::SSLeay::VERSION } or $v = q{?} ;
+	push  @list_version, module_version_str( 'Net::SSLeay ', $v )  ;
+
+	eval { require Compress::Zlib; $v = $Compress::Zlib::VERSION } or $v = q{?} ;
+	push  @list_version, module_version_str( 'Compress::Zlib', $v )  ;
+
+	eval { require Digest::MD5; $v = $Digest::MD5::VERSION } or $v = q{?} ;
+	push  @list_version, module_version_str( 'Digest::MD5', $v )  ;
+
+	eval { require Digest::HMAC_MD5; $v = $Digest::HMAC_MD5::VERSION } or $v = q{?} ;
+	push  @list_version, module_version_str( 'Digest::HMAC_MD5', $v )  ;
+
+	eval { require Digest::HMAC_SHA1; $v = $Digest::HMAC_SHA1::VERSION } or $v = q{?} ;
+	push  @list_version, module_version_str( 'Digest::HMAC_SHA1', $v )  ;
+
+	eval { require Term::ReadKey; $v = $Term::ReadKey::VERSION } or $v = q{?} ;
+	push  @list_version, module_version_str( 'Term::ReadKey', $v )  ;
+
+	eval { require File::Spec; $v = $File::Spec::VERSION } or $v = q{?} ;
+	push  @list_version, module_version_str( 'File::Spec', $v )  ;
+
+	eval { require Time::HiRes; $v = $Time::HiRes::VERSION } or $v = q{?} ;
+	push  @list_version, module_version_str( 'Time::HiRes', $v )  ;
+
+	eval { require Unicode::String; $v = $Unicode::String::VERSION } or $v = q{?} ;
+	push  @list_version, module_version_str( 'Unicode::String', $v )  ;
+
+	eval { require IO::Tee; $v = $IO::Tee::VERSION } or $v = q{?} ;
+	push  @list_version, module_version_str( 'IO::Tee', $v )  ;
+
+	eval { require File::Copy::Recursive; $v = $File::Copy::Recursive::VERSION } or $v = q{?} ;
+	push  @list_version, module_version_str( 'File::Copy::Recursive', $v )  ;
+
+	eval { require Authen::NTLM; $v = $Authen::NTLM::VERSION } or $v = q{?} ;
+	push  @list_version, module_version_str( 'Authen::NTLM', $v )  ;
+
+	eval { require URI::Escape; $v = $URI::Escape::VERSION } or $v = q{?} ;
+	push  @list_version, module_version_str( 'URI::Escape', $v )  ;
+
+	eval { require Data::Uniqid; $v = $Data::Uniqid::VERSION } or $v = q{?} ;
+	push  @list_version, module_version_str( 'Data::Uniqid', $v )  ;
+
+	eval { require JSON; $v = $JSON::VERSION } or $v = q{?} ;
+	push  @list_version, module_version_str( 'JSON', $v )  ;
+
+	eval { require JSON::WebToken; $v = $JSON::WebToken::VERSION } or $v = q{?} ;
+	push  @list_version, module_version_str( 'JSON::WebToken', $v )  ;
+
+	eval { require Crypt::OpenSSL::RSA; $v = $Crypt::OpenSSL::RSA::VERSION } or $v = q{?} ;
+	push  @list_version, module_version_str( 'Crypt::OpenSSL::RSA', $v )  ;
+
+	eval { require LWP; $v = $LWP::VERSION } or $v = q{?} ;
+	push  @list_version, module_version_str( 'LWP', $v )  ;
+
+	eval { require HTML::Entities; $v = $HTML::Entities::VERSION } or $v = q{?} ;
+	push  @list_version, module_version_str( 'HTML::Entities', $v )  ;
+
+	#eval { require Filesys::DfPortable; $v = $Filesys::DfPortable::VERSION } or $v = q{?} ;
+	#push  @list_version, module_version_str( 'Filesys::DfPortable', $v )  ;
+
+	eval { require Getopt::Long; $v = $Getopt::Long::VERSION } or $v = q{?} ;
+	push  @list_version, module_version_str( 'Getopt::Long', $v )  ;
+
+	eval { require Test::MockObject; $v = $Test::MockObject::VERSION } or $v = q{?} ;
+	push  @list_version, module_version_str( 'Test::MockObject', $v )  ;
+
+	return( @list_version ) ;
+}
+
+
+# Construct a command line copy with passwords replaced by MASKED.
+sub command_line_nopassword {
+	my @argv = @_ ;
+	my @argv_nopassword ;
+
+        return( "@argv" ) if $showpasswords ;
+	while ( @argv ) {
+		my $arg = shift @argv ; # option name or value
+		if ( $arg =~ m/-password[12]/x ) {
+			shift @argv ; # password value
+			push  @argv_nopassword, $arg, 'MASKED'  ; # option name and fake value
+		}else{
+			push  @argv_nopassword, $arg ; # same option or value
+		}
+	}
+	return("@argv_nopassword") ;
+}
+
+sub tests_command_line_nopassword {
+
+	ok(q{} eq command_line_nopassword(), 'command_line_nopassword void');
+	ok('--blabla' eq command_line_nopassword('--blabla'), 'command_line_nopassword --blabla');
+	#myprint( command_line_nopassword((qw{ --password1 secret1 })), "\n" ) ;
+	ok('--password1 MASKED' eq command_line_nopassword(qw{ --password1 secret1}), 'command_line_nopassword --password1');
+	ok('--blabla --password1 MASKED --blibli'
+	eq command_line_nopassword(qw{ --blabla --password1 secret1 --blibli }), 'command_line_nopassword --password1 --blibli');
+	$showpasswords = 1 ;
+	ok(q{} eq command_line_nopassword(), 'command_line_nopassword void');
+	ok('--blabla' eq command_line_nopassword('--blabla'), 'command_line_nopassword --blabla');
+	#myprint( command_line_nopassword((qw{ --password1 secret1 })), "\n" ) ;
+	ok('--password1 secret1' eq command_line_nopassword(qw{ --password1 secret1}), 'command_line_nopassword --password1');
+	ok('--blabla --password1 secret1 --blibli'
+	eq command_line_nopassword(qw{ --blabla --password1 secret1 --blibli }), 'command_line_nopassword --password1 --blibli');
+        return ;
+}
+
+sub ask_for_password {
+	my ( $user, $host ) = @_ ;
+	myprint( "What's the password for $user" . '@' . "$host? (not visible while you type, then enter RETURN) "  ) ;
+	Term::ReadKey::ReadMode( 2 ) ;
+	my $password = <> ;
+	chomp $password ;
+	myprint( "\nGot it\n" ) ;
+	Term::ReadKey::ReadMode( 0 ) ;
+	return $password ;
+}
+
+sub catch_exit {
+        my $mysync = shift ;
+        my $signame = shift ;
+        if ( $signame ) {
+                myprint( "\nGot a signal $signame\n" ) ;
+        }
+	stats( $mysync ) ;
+        myprint( "Ended by a signal\n" ) ;
+	exit_clean( $mysync, $EXIT_BY_SIGNAL ) ;
+        return ;
+}
+
+sub catch_reconnect {
+	my $mysync = shift ;
+        my $signame = shift ;
+        myprint( "\nGot a signal $signame\n",
+                "Hit 2 ctr-c within 2 seconds to exit the program\n",
+                "Hit only 1 ctr-c to reconnect to both imap servers\n",
+        ) ;
+        if ( here_twice( $mysync ) ) {
+                myprint( "Got two signals $signame within $INTERVAL_TO_EXIT seconds. Exiting...\n" ) ;
+                catch_exit( $mysync ) ;
+        }
+        else{
+                myprint( "For now only one signal $signame within $INTERVAL_TO_EXIT seconds.\n" ) ;
+        }
+
+        if ( ! defined $mysync->{imap1} ) { return ; }
+        if ( ! defined $mysync->{imap2} ) { return ; }
+        
+
+        myprint( "Info: reconnecting to host1 imap server\n" ) ;
+        $mysync->{imap1}->State( Mail::IMAPClient::Unconnected ) ;
+        $mysync->{imap1}->reconnect(  ) ;
+        myprint( "Info: reconnecting to host2 imap server\n" ) ;
+        $mysync->{imap2}->State( Mail::IMAPClient::Unconnected ) ;
+        $mysync->{imap2}->reconnect(  ) ;
+        myprint( "Info: reconnected to both imap servers\n" ) ;
+        return ;
+}
+
+sub here_twice {
+        my $mysync = shift ;
+        my $now = time ;
+        my $previous = $mysync->{lastcatch} || 0 ;
+        $mysync->{lastcatch} = $now ;
+        
+        if ( $INTERVAL_TO_EXIT >= $now - $previous ) {
+                return $TRUE ;
+        }else{
+                return $FALSE ;
+        }
+}
+
+
+
+
+sub justconnect {
+
+	$imap1 = connect_imap( $host1, $port1, $debugimap1, $ssl1, $tls1, 'Host1', $sync->{h1}->{timeout}, $sync->{h1} ) ;
+	myprint( 'Host1 banner: ', $imap1->Banner(  )  ) ;
+	myprint( 'Host1 capability: ', join(q{ }, $imap1->capability(  ) ), "\n"  ) ;
+	$imap2 = connect_imap( $host2, $port2, $debugimap2, $ssl2, $tls2, 'Host2', $sync->{h2}->{timeout}, $sync->{h2} ) ;
+	myprint( 'Host2 banner: ', $imap2->Banner(  )  ) ;
+	myprint( 'Host2 capability: ', join(q{ }, $imap2->capability(  ) ), "\n"  ) ;
+	$imap1->logout(  ) ;
+	$imap2->logout(  ) ;
+        return ;
+}
+
+sub connect_imap {
+	my( $host, $port, $mydebugimap, $ssl, $tls, $Side, $mytimeout, $h ) = @_ ;
+	my $imap = Mail::IMAPClient->new() ;
+	if ( $ssl ) { set_ssl( $imap, $h ) }
+	if ( $tls ) { $imap->Tls( 1 ) }
+	$imap->Server( $host ) ;
+	$imap->Port( $port ) ;
+	$imap->Debug( $mydebugimap ) ;
+        $imap->Timeout( $mytimeout ) ;
+	$imap->connect(  )
+	  or die_clean( "$Side: Can not open imap connection on [$host]: $@\n" ) ;
+
+        my $banner = $imap->Results()->[0] ;
+        $imap->Banner( $banner ) ;
+
+        if ( $imap->Tls(  ) ) {
+        	set_tls( $imap, $h ) ;
+        	$imap->starttls(  )
+                or die_clean("$Side: Can not go to tls encryption on [$host]:", $imap->LastError, "\n" ) ;
+                myprint( "$Side: Socket successfuly converted to SSL\n"  ) ;
+        }
+        return( $imap ) ;
+}
+
+
+sub login_imap {
+
+	my @allargs = @_ ;
+	my(
+		$host, $port, $user, $domain, $password,
+		$mydebugimap, $mytimeout, $fastio,
+		$ssl, $tls, $authmech, $authuser, $reconnectretry,
+		$proxyauth, $uid, $split, $Side, $h ) = @allargs ;
+
+	my $side = lc $Side ;
+	myprint( "$Side: connecting and login on $side [$host] port [$port] with user [$user]\n"  ) ;
+
+	my $imap = init_imap( @allargs ) ;
+
+	$imap->connect()
+	  or die_clean("$Side failure: can not open imap connection on $side [$host] with user [$user]: $@\n") ;
+
+        my $banner = $imap->Results()->[0] ;
+        $imap->Banner( $banner ) ;
+	myprint( "$Side banner: $banner"  ) ;
+
+        if ( $authmech eq 'PREAUTH' ) {
+        	if ( $imap->IsAuthenticated( ) ) {
+        		$imap->Socket ;
+			myprintf("%s: Assuming PREAUTH for %s\n", $Side, $imap->Server ) ;
+        	}else{
+                	die_clean( "$Side failure: error login on $side [$host] with user [$user] auth [PREAUTH]" ) ;
+                }
+        }
+
+        if ( $imap->Tls(  ) ) {
+		set_tls( $imap, $h ) ;
+        	$imap->starttls(  )
+                or die_clean("$Side failure: Can not go to tls encryption on $side [$host]:", $imap->LastError, "\n" ) ;
+                myprint( "$Side: Socket successfuly converted to SSL\n"  ) ;
+        }
+
+        authenticate_imap( $imap, @allargs ) ;
+
+	myprint( "$Side: success login on [$host] with user [$user] auth [$authmech]\n"  ) ;
+	return( $imap ) ;
+}
+
+
+sub authenticate_imap {
+
+	my($imap,
+           $host, $port, $user, $domain, $password,
+	   $mydebugimap, $mytimeout, $fastio,
+	   $ssl, $tls, $authmech, $authuser, $reconnectretry,
+	   $proxyauth, $uid, $split, $Side, $h ) = @_ ;
+
+	check_capability( $imap, $authmech, $Side ) ;
+
+        if ( $proxyauth ) {
+                $imap->Authmechanism(q{}) ;
+                $imap->User($authuser) ;
+        } else {
+                $imap->Authmechanism( $authmech ) unless ( $authmech eq 'LOGIN'  or $authmech eq 'PREAUTH' ) ;
+                $imap->User($user) ;
+        }
+
+	$imap->Authcallback(\&xoauth)  if ( 'XOAUTH'  eq $authmech ) ;
+	$imap->Authcallback(\&xoauth2) if ( 'XOAUTH2' eq $authmech ) ;
+	$imap->Authcallback(\&plainauth) if ( ( 'PLAIN' eq $authmech ) or ( 'EXTERNAL' eq $authmech )  ) ;
+
+        $imap->Domain($domain) if (defined $domain) ;
+        $imap->Authuser($authuser) ;
+        $imap->Password($password) ;
+
+	unless ( $authmech eq 'PREAUTH' or $imap->login( ) ) {
+		my $info  = "$Side failure: Error login on [$host] with user [$user] auth" ;
+		my $einfo = $imap->LastError || @{$imap->History}[$LAST] ;
+		chomp $einfo ;
+		my $error = "$info [$authmech]: $einfo\n" ;
+                if ( $authmech eq 'LOGIN' or $imap->IsUnconnected(  ) or $authuser ) {
+                	die_clean( $error ) ;
+                }else{
+			myprint( $error  ) ;
+                }
+		myprint( "$Side info: trying LOGIN Auth mechanism on [$host] with user [$user]\n"  ) ;
+		$imap->Authmechanism(q{}) ;
+		$imap->login() or
+		  die_clean("$info [LOGIN]: ", $imap->LastError, "\n") ;
+	}
+
+        if ( $proxyauth ) {
+                if ( ! $imap->proxyauth( $user ) ) {
+                        my $info  = "$Side failure: Error doing proxyauth as user [$user] on [$host] using proxy-login as [$authuser]" ;
+                        my $einfo = $imap->LastError || @{$imap->History}[$LAST] ;
+                        chomp $einfo ;
+                        die_clean( "$info: $einfo\n" ) ;
+                }
+        }
+
+	return ;
+}
+
+sub check_capability {
+
+	my( $imap, $authmech, $Side ) = @_ ;
+
+	if ($imap->has_capability("AUTH=$authmech")
+	    or $imap->has_capability($authmech)
+	   ) {
+		myprintf("%s: %s says it has CAPABILITY for AUTHENTICATE %s\n",
+		       $Side, $imap->Server, $authmech);
+	}
+	else {
+		myprintf("%s: %s says it has NO CAPABILITY for AUTHENTICATE %s\n",
+		       $Side, $imap->Server, $authmech);
+		if ($authmech eq 'PLAIN') {
+			myprint( "$Side: frequently PLAIN is only supported with SSL, ",
+			  "try --ssl or --tls options\n" ) ;
+		}
+	}
+	return ;
+}
+
+sub set_ssl {
+	my ( $imap, $h ) = @_ ;
+        # SSL_version can be
+        #    SSLv3 SSLv2 SSLv23 SSLv23:!SSLv2 (last one is the default in IO-Socket-SSL-1.953)
+        #
+
+        my $sslargs_hash = $h->{sslargs} ;
+
+	my $sslargs_default = {
+		SSL_verify_mode => $DEFAULT_SSL_VERIFY,
+        	SSL_verifycn_scheme => 'imap',
+        } ;
+
+        # initiate with default values
+        my %sslargs_mix = %{ $sslargs_default } ;
+        # now override with passed values
+        @sslargs_mix{ keys %{ $sslargs_hash } } = values %{ $sslargs_hash } ;
+        # remove keys with undef values
+        foreach my $key ( keys %sslargs_mix ) {
+                delete $sslargs_mix{ $key } if ( not defined  $sslargs_mix{ $key }  ) ;
+        }
+        # back to an ARRAY
+        my @sslargs_mix = %sslargs_mix ;
+        #myprint( Data::Dumper->Dump( [ $sslargs_hash, $sslargs_default, \%sslargs_mix, \@sslargs_mix ] )  ) ;
+        $imap->Ssl( \@sslargs_mix ) ;
+	return ;
+}
+
+sub set_tls {
+	my ( $imap, $h ) = @_ ;
+
+        my $sslargs_hash = $h->{sslargs} ;
+
+	my $sslargs_default = {
+		SSL_verify_mode => $DEFAULT_SSL_VERIFY,
+        } ;
+
+        # initiate with default values
+        my %sslargs_mix = %{ $sslargs_default } ;
+        # now override with passed values
+        @sslargs_mix{ keys %{ $sslargs_hash } } = values %{ $sslargs_hash } ;
+        # remove keys with undef values
+        foreach my $key ( keys %sslargs_mix ) {
+                delete $sslargs_mix{ $key } if ( not defined  $sslargs_mix{ $key } ) ;
+        }
+        # back to an ARRAY
+        my @sslargs_mix = %sslargs_mix ;
+
+        $imap->Starttls( \@sslargs_mix ) ;
+	return ;
+}
+
+
+
+
+sub init_imap {
+	my(
+	   $host, $port, $user, $domain, $password,
+	   $mydebugimap, $mytimeout, $fastio,
+	   $ssl, $tls, $authmech, $authuser, $reconnectretry,
+	   $proxyauth, $uid, $split, $Side, $h ) = @_ ;
+
+	my ( $imap ) ;
+
+	$imap = Mail::IMAPClient->new() ;
+
+	if ( $ssl ) { set_ssl( $imap, $h ) }
+	if ( $tls ) { $imap->Tls( 1 ) } # can not do set_tls() here because connect() will directly do a STARTTLS
+	$imap->Clear(1);
+	$imap->Server($host);
+	$imap->Port($port);
+	$imap->Fast_io($fastio);
+	$imap->Buffer($buffersize || $DEFAULT_BUFFER_SIZE);
+	$imap->Uid($uid);
+
+	$imap->Peek(1);
+	$imap->Debug($mydebugimap);
+	defined  $mytimeout  and $imap->Timeout( $mytimeout ) ;
+
+	$imap->Reconnectretry( $reconnectretry ) if ( $reconnectretry ) ;
+	$imap->Ignoresizeerrors( $allowsizemismatch ) ;
+	$split and $imap->Maxcommandlength( $SPLIT_FACTOR * $split ) ;
+
+
+	return( $imap ) ;
+
+}
+
+sub plainauth {
+        my $code = shift;
+        my $imap = shift;
+
+        my $string = mysprintf("%s\x00%s\x00%s", $imap->User,
+                            $imap->Authuser, $imap->Password);
+        return encode_base64("$string", q{});
+}
+
+# Copy from https://github.com/imapsync/imapsync/pull/25/files
+# Changes "use" pragmas to "require".
+# The openssl system call shall be replaced by pure Perl and
+# https://metacpan.org/pod/Crypt::OpenSSL::PKCS12
+
+# Now the Joaquin Lopez code:
+#
+# Used this as an example: https://gist.github.com/gsainio/6322375
+#
+# And this as a reference: https://developers.google.com/accounts/docs/OAuth2ServiceAccount
+# (note there is an http/rest tab, where the real info is hidden away... went on a witch hunt
+# until I noticed that...)
+#
+# This is targeted at gmail to maintain compatibility after google's oauth1 service is deactivated
+# on May 5th, 2015: https://developers.google.com/gmail/oauth_protocol
+# If there are other oauth2 implementations out there, this would need to be modified to be
+# compatible
+#
+# This is a good guide on setting up the google api/apps side of the equation:
+# http://www.limilabs.com/blog/oauth2-gmail-imap-service-account
+#
+# 2016/05/27: Updated to support oauth/key data in the .json files Google now defaults to
+# when creating gmail service accounts. They're easier to work with since they neither
+# requiring decrypting nor specifying the oauth2 client id separately.
+#
+# If the password arg ends in .json, it will assume this new json method, otherwise it
+# will fallback to the "oauth client id;.p12" format it was previously using.
+sub xoauth2 {
+	require JSON::WebToken ;
+	require LWP::UserAgent ;
+	require HTML::Entities ;
+	require JSON ;
+	require JSON::WebToken::Crypt::RSA ;
+	require Crypt::OpenSSL::RSA ;
+        require Encode::Byte ;
+        require IO::Socket::SSL ;
+
+        my $code = shift;
+        my $imap = shift;
+
+        my ($iss,$key);
+
+        if( $imap->Password =~ /^(.*\.json)$/ ) {
+            my $json = JSON->new( ) ;
+            my $filename = $1;
+            $debug and myprint( "XOAUTH2 json file: $filename\n" ) ;
+            open( my $FILE, '<', $filename ) or die_clean( "error [$filename]: $! " ) ;
+            my $jsonfile = $json->decode( join q{}, <$FILE> ) ;
+            close $FILE ;
+
+            $iss = $jsonfile->{client_id};
+            $key = $jsonfile->{private_key};
+            $debug and myprint( "Service account: $iss\n");
+            $debug and myprint( "Private key:\n$key\n");
+        }
+        else {
+            # Get iss (service account address), keyfile name, and keypassword if necessary
+            ( $iss, my $keyfile, my $keypass ) = $imap->Password =~ /([\-\d\w\@\.]+);([a-zA-Z0-9 \_\-\.\/]+);?(.*)?/ ;
+
+            # Assume key password is google default if not provided
+            $keypass = 'notasecret' if not $keypass;
+
+            $debug and myprint( "Service account: $iss\nKey file: $keyfile\nKey password: $keypass\n");
+
+            # Get private key from p12 file (would be better in perl...)
+            $key = `openssl pkcs12 -in "$keyfile" -nodes -nocerts -passin pass:$keypass -nomacver`;
+
+            $debug and myprint( "Private key:\n$key\n");
+        }
+
+        # Create jwt of oauth2 request
+        my $time = time ;
+        my $jwt = JSON::WebToken->encode( {
+        'iss' => $iss, # service account
+        'scope' => 'https://mail.google.com/',
+        'aud' => 'https://www.googleapis.com/oauth2/v3/token',
+        'exp' => $time + $DEFAULT_EXPIRATION_TIME_OAUTH2_PK12,
+        'iat' => $time,
+        'prn' => $imap->User # user to auth as
+        },
+        $key, 'RS256', {'typ' => 'JWT'} ); # Crypt::OpenSSL::RSA needed here.
+
+        # Post oauth2 request
+        my $ua = LWP::UserAgent->new(  ) ;
+        $ua->env_proxy(  ) ;
+
+        my $response = $ua->post('https://www.googleapis.com/oauth2/v3/token',
+        { grant_type => HTML::Entities::encode_entities('urn:ietf:params:oauth:grant-type:jwt-bearer'),
+        assertion => $jwt } ) ;
+
+        unless( $response->is_success(  ) ) {
+                die_clean( $response->code, "\n", $response->content, "\n" ) ;
+        }else{
+                $debug and myprint( $response->content  ) ;
+        }
+
+        # access_token in response is what we need
+        my $data = JSON::decode_json( $response->content ) ;
+
+        # format as oauth2 auth data
+        my $xoauth2_string = encode_base64( 'user=' . $imap->User . "\1auth=Bearer " . $data->{access_token} . "\1\1", q{} ) ;
+
+        $debug and myprint( "XOAUTH2 String: $xoauth2_string\n");
+        return($xoauth2_string);
+}
+
+
+
+
+# xoauth() thanks to Eduardo Bortoluzzi Junior
+sub xoauth {
+        require URI::Escape  ;
+        require Data::Uniqid ;
+
+        my $code = shift;
+        my $imap = shift;
+
+        # The base information needed to construct the OAUTH authentication
+        my $method = 'GET' ;
+        my $url = mysprintf( 'https://mail.google.com/mail/b/%s/imap/', $imap->User ) ;
+        my $urlparm = mysprintf( 'xoauth_requestor_id=%s', URI::Escape::uri_escape( $imap->User ) ) ;
+
+        # For Google Apps, the consumer key is the primary domain
+        # TODO: create a command line argument to define the consumer key
+        my @user_parts = split /@/x, $imap->User ;
+        $debug and myprint( "XOAUTH: consumer key: $user_parts[1]\n" ) ;
+
+        # All the parameters needed to be signed on the XOAUTH
+        my %hash = ();
+        $hash { 'xoauth_requestor_id' } = URI::Escape::uri_escape($imap->User);
+        $hash { 'oauth_consumer_key' } = $user_parts[1];
+        $hash { 'oauth_nonce' } = md5_hex(Data::Uniqid::uniqid(rand(), 1==1));
+        $hash { 'oauth_signature_method' } = 'HMAC-SHA1';
+        $hash { 'oauth_timestamp' } = time ;
+        $hash { 'oauth_version' } = '1.0';
+
+        # Base will hold the string to be signed
+        my $base = "$method&" . URI::Escape::uri_escape( $url ) . q{&} ;
+
+        # The parameters must be in dictionary order before signing
+        my $baseparms = q{} ;
+        foreach my $key ( sort keys %hash ) {
+                if ( length( $baseparms ) > 0 ) {
+                        $baseparms .= q{&} ;
+                }
+
+                $baseparms .= "$key=$hash{$key}" ;
+        }
+
+        $base .= URI::Escape::uri_escape($baseparms);
+        $debug and myprint( "XOAUTH: base request to sign: $base\n" ) ;
+        # Sign it with the consumer secret, informed on the command line (password)
+        my $digest = hmac_sha1( $base, URI::Escape::uri_escape( $imap->Password ) . q{&} ) ;
+
+        # The parameters signed become a parameter and...
+        $hash { 'oauth_signature' } = URI::Escape::uri_escape( substr encode_base64( $digest ), 0, $MINUS_ONE ) ;
+
+        # ... we don't need the requestor_id anymore.
+        delete $hash{'xoauth_requestor_id'} ;
+
+        # Create the final authentication string
+        my $string = $method . q{ } . $url . q{?} . $urlparm .q{ } ;
+
+        # All the parameters must be sorted
+        $baseparms = q{};
+        foreach my $key (sort keys %hash) {
+                if(length($baseparms)>0) {
+                        $baseparms .= q{,} ;
+                }
+
+                $baseparms .= "$key=\"$hash{$key}\"";
+        }
+
+        $string .= $baseparms;
+
+        $debug and myprint( "XOAUTH: authentication string: $string\n" ) ;
+
+       # It must be base64 encoded
+        return encode_base64("$string", q{});
+}
+
+sub server_banner {
+	my $imap = shift;
+	my $banner = $imap->Banner() ||  "No banner\n";
+	return $banner;
+ }
+
+
+sub banner_imapsync {
+
+	my @argv = @_ ;
+
+	my $banner_imapsync = join q{},
+		q{$RCSfile: imapsync,v $ },
+		q{$Revision: 1.727 $ },
+		q{$Date: 2016/08/19 10:30:36 $ },
+		"\n", localhost_info(), "\n",
+		"Command line used:\n",
+		"$0 ", command_line_nopassword( @argv ), "\n" ;
+
+        return( $banner_imapsync ) ;
+}
+
+sub is_valid_directory {
+	my $dir = shift;
+
+	# all good => return ok.
+	return( 1 ) if ( -d $dir and -r _ and -w _ ) ;
+
+	# exist but bad
+	if ( -e $dir and not -d _ ) {
+		myprint( "Error: $dir exists but is not a directory\n"  ) ;
+		return( 0 ) ;
+	}
+	if ( -e $dir and not -w _ ) {
+		my $sb = stat $dir ;
+		myprintf( "Error: directory %s is not writable for user %s, permissions are %04o and owner is %s ( uid %s )\n",
+		         $dir, getpwuid_any_os( $EFFECTIVE_USER_ID ), ($sb->mode & oct($PERMISSION_FILTER) ), getpwuid_any_os( $sb->uid ), $sb->uid(  ) ) ;
+		return( 0 ) ;
+	}
+	# Trying to create it
+	myprint( "Creating directory $dir\n"  ) ;
+	eval { mkpath( $dir ) } ;
+	myprint( "$@" ) if ( $@ )  ;
+	return( 1 ) if ( -d $dir and -r _ and -w _ ) ;
+	return( 0 ) ;
+}
+
+sub tests_is_valid_directory {
+        Readonly my $NB_UNIX_tests_is_valid_directory => 4 ;
+	SKIP: {
+		skip( 'Tests only for Unix', $NB_UNIX_tests_is_valid_directory ) if ( 'MSWin32' eq $OSNAME ) ;
+		ok( 1 == is_valid_directory( '.'), 'is_valid_directory: . good' ) ;
+		ok( 1 == is_valid_directory( './tmp/tests/valid/sub'), 'is_valid_directory: ./tmp/tests/valid/sub good' ) ;
+		diag( 'Error / not writable is on purpose' ) ;
+		ok( 0 == is_valid_directory( '/'), 'is_valid_directory: / bad' ) ;
+		diag( 'Error permission denied on /noway is on purpose' ) ;
+		ok( 0 == is_valid_directory( '/noway'), 'is_valid_directory: /noway bad' ) ;
+	}
+	return ;
+}
+
+sub write_pidfile {
+	my $pid_filename = shift ;
+        my $lock = shift ;
+        
+	myprint( "PID file is $pid_filename ( to change it use --pidfile filepath ; to avoid it use --pidfile \"\" )\n" ) ;
+	if ( -e $pid_filename and $lock ) {
+		myprint( "$pid_filename already exists, another imapsync may be curently running. Aborting imapsync.\n"  ) ;
+                exit $EXIT_PID_FILE_ALREADY_EXIST ;
+	}
+	if ( -e $pid_filename ) {
+		myprint( "$pid_filename already exists, overwriting it ( use --pidfilelocking to avoid concurrent runs )\n"  ) ;
+	}
+
+	open my $FILE_HANDLE, '>', $pid_filename
+        	or do {
+			myprint( "Could not open $pid_filename for writing. Check permissions or disk space."  ) ;
+		return ;
+	} ;
+        myprint( "Wrinting my PID $PROCESS_ID in $pid_filename\n"  ) ;
+	print $FILE_HANDLE $PROCESS_ID ;
+	close $FILE_HANDLE ;
+
+	return( $PROCESS_ID ) ;
+}
+
+sub remove_tmp_files {
+        my $mysync = shift ;
+	unlink $mysync->{pidfile} ;
+	return ;
+}
+
+
+sub exit_clean {
+        my $mysync = shift ;
+	my $status = shift ;
+	$status = defined  $status  ? $status : $EXIT_UNKNOWN ;
+        remove_tmp_files( $mysync ) ;
+        myprint( "Exiting with return value $status\n" ) ;
+        if ( $mysync->{log} ) {
+                myprint( "Log file is $mysync->{logfile} ( to change it, use --logfile filepath ; or use --nolog to turn off logging )\n" ) ;
+                close $mysync->{logfile_handle} ;
+        }
+	exit $status ;
+}
+
+sub die_clean {
+	my @messages = @_ ;
+        remove_tmp_files( $sync ) ;
+	die @messages ;
+}
+
+sub missing_option {
+	my ( $option ) = @_ ;
+	die_clean( "$option option is mandatory, for help run $0 --help\n" ) ;
+	return ;
+}
+
+
+sub fix_Inbox_INBOX_mapping {
+	my( $h1_all, $h2_all ) = @_ ;
+
+	my $regex = q{} ;
+	SWITCH: {
+		if ( exists  $h1_all->{INBOX}  and exists  $h2_all->{INBOX}  ) { $regex = q{} ; last SWITCH ; } ;
+		if ( exists  $h1_all->{Inbox}  and exists  $h2_all->{Inbox}  ) { $regex = q{} ; last SWITCH ; } ;
+		if ( exists  $h1_all->{INBOX}  and exists  $h2_all->{Inbox}  ) { $regex = q{s/^INBOX$/Inbox/x} ; last SWITCH ; } ;
+		if ( exists  $h1_all->{Inbox}  and exists  $h2_all->{INBOX}  ) { $regex = q{s/^Inbox$/INBOX/x} ; last SWITCH ; } ;
+	} ;
+        return( $regex ) ;
+}
+
+sub tests_fix_Inbox_INBOX_mapping {
+
+	my( $h1_all, $h2_all ) ;
+
+	$h1_all = { 'INBOX' => q{} } ;
+	$h2_all = { 'INBOX' => q{} } ;
+	ok( q{} eq fix_Inbox_INBOX_mapping( $h1_all, $h2_all ), 'fix_Inbox_INBOX_mapping: INBOX INBOX' ) ;
+
+	$h1_all = { 'Inbox' => q{} } ;
+	$h2_all = { 'Inbox' => q{} } ;
+	ok( q{} eq fix_Inbox_INBOX_mapping( $h1_all, $h2_all ), 'fix_Inbox_INBOX_mapping: Inbox Inbox' ) ;
+
+	$h1_all = { 'INBOX' => q{} } ;
+	$h2_all = { 'Inbox' => q{} } ;
+	ok( q{s/^INBOX$/Inbox/x} eq fix_Inbox_INBOX_mapping( $h1_all, $h2_all ), 'fix_Inbox_INBOX_mapping: INBOX Inbox' ) ;
+
+	$h1_all = { 'Inbox' => q{} } ;
+	$h2_all = { 'INBOX' => q{} } ;
+	ok( q{s/^Inbox$/INBOX/x} eq fix_Inbox_INBOX_mapping( $h1_all, $h2_all ), 'fix_Inbox_INBOX_mapping: Inbox INBOX' ) ;
+
+	$h1_all = { 'INBOX' => q{} } ;
+	$h2_all = { 'rrrrr' => q{} } ;
+	ok( q{} eq fix_Inbox_INBOX_mapping( $h1_all, $h2_all ), 'fix_Inbox_INBOX_mapping: INBOX rrrrrr' ) ;
+
+	$h1_all = { 'rrrrr' => q{} } ;
+	$h2_all = { 'Inbox' => q{} } ;
+	ok( q{} eq fix_Inbox_INBOX_mapping( $h1_all, $h2_all ), 'fix_Inbox_INBOX_mapping: rrrrr Inbox' ) ;
+
+	return ;
+}
+
+
+sub jux_utf8_list {
+	my @s_inp = @_ ;
+	my $s_out = q{} ;
+	foreach my $s ( @s_inp ) {
+		$s_out .= jux_utf8( $s ) . "\n" ;
+	}
+	return( $s_out ) ;
+}
+
+sub tests_jux_utf8_list {
+	ok( q{} eq jux_utf8_list(  ), 'jux_utf8_list: void' ) ;
+	ok( "[]\n" eq jux_utf8_list( q{} ), 'jux_utf8_list: empty string' ) ;
+	ok( "[INBOX]\n" eq jux_utf8_list( 'INBOX' ), 'jux_utf8_list: INBOX' ) ;
+	ok( "[&ANY-] = [Ö]\n" eq jux_utf8_list( '&ANY-' ), 'jux_utf8_list: &ANY-' ) ;
+	return( 0 ) ;
+}
+
+sub jux_utf8 {
+	# juxtapose utf8 at the right if different
+        my ( $s_utf7 ) =  shift ;
+        my ( $s_utf8 ) =  imap_utf7_decode( $s_utf7 ) ;
+
+        if ( $s_utf7 eq $s_utf8 ) {
+        	#myprint( "[$s_utf7]\n"  ) ;
+        	return( "[$s_utf7]" ) ;
+        }else{
+        	#myprint( "[$s_utf7] = [$s_utf8]\n"  ) ;
+        	return( "[$s_utf7] = [$s_utf8]" ) ;
+        }
+}
+
+# editing utf8 can be tricky without an utf8 editor
+sub tests_jux_utf8 {
+	ok( '[INBOX]' eq jux_utf8( 'INBOX'), 'jux_utf8: INBOX => [INBOX]' ) ;
+	ok( '[&ZTZO9nux-] = [收件箱]' eq jux_utf8( '&ZTZO9nux-'), 'jux_utf8: => [&ZTZO9nux-] = [收件箱]' ) ;
+	ok( '[&ANY-] = [Ö]' eq jux_utf8( '&ANY-'), 'jux_utf8: &ANY- => [&ANY-] = [Ö]' ) ;
+        ok( '[]' eq jux_utf8( q{} ), 'jux_utf8: void => []' ) ;
+        ok( '[+BD8EQAQ1BDQEOwQ+BDM-] = [предлог]' eq jux_utf8( '+BD8EQAQ1BDQEOwQ+BDM-' ), 'jux_utf8: => [+BD8EQAQ1BDQEOwQ+BDM-] = [предлог]' ) ;
+        ok( '[&BB8EQAQ+BDUEOgRC-] = [Проект]'      eq jux_utf8( '&BB8EQAQ+BDUEOgRC-' ),    'jux_utf8: => [&BB8EQAQ+BDUEOgRC-] = [Проект]' ) ;
+
+	return( 0 ) ;
+}
+
+# Copied from http://cpansearch.perl.org/src/FABPOT/Unicode-IMAPUtf7-2.01/lib/Unicode/IMAPUtf7.pm
+# and then fixed with
+# https://rt.cpan.org/Public/Bug/Display.html?id=11172
+sub imap_utf7_decode {
+        my ( $s ) = shift ;
+
+        # Algorithm
+        # On remplace , par / dans les BASE 64 (, entre & et -)
+        # On remplace les &, non suivi d'un - par +
+        # On remplace les &- par &
+        $s =~ s/&([^,&\-]*),([^,\-&]*)\-/&$1\/$2\-/g ;
+        $s =~ s/&(?!\-)/\+/g ;
+        $s =~ s/&\-/&/g ;
+        return( Unicode::String::utf7( $s )->utf8 ) ;
+}
+
+sub imap_utf7_encode {
+	my ( $s ) = @_ ;
+
+	$s = Unicode::String::utf8( $s )->utf7 ;
+
+	$s =~ s/\+([^\/&\-]*)\/([^\/\-&]*)\-/\+$1,$2\-/g ;
+	$s =~ s/&/&\-/g ;
+	$s =~ s/\+([^+\-]+)?\-/&$1\-/g ;
+	return( $s ) ;
+}
+
+
+
+
+sub select_folder {
+	my ( $imap, $folder, $hostside ) = @_ ;
+	if ( ! $imap->select( $folder ) ) {
+		my $error = join q{},
+			"$hostside folder $folder: Could not select: ",
+			$imap->LastError,  "\n" ;
+		errors_incr( $sync, $error ) ;
+		return( 0 ) ;
+	}else{
+		# ok select succeeded
+		return( 1 ) ;
+	}
+}
+
+sub examine_folder {
+	my ( $imap, $folder, $hostside ) = @_ ;
+	if ( ! $imap->examine( $folder ) ) {
+		my $error = join q{},
+			"$hostside folder $folder: Could not examine: ",
+			$imap->LastError,  "\n" ;
+		errors_incr( $sync, $error ) ;
+		return( 0 ) ;
+	}else{
+		# ok select succeeded
+		return( 1 ) ;
+	}
+}
+
+
+
+
+sub count_from_select {
+	my @lines = @_ ;
+        my $count ;
+        foreach my $line ( @lines ) {
+        	#myprint( "line = [$line]\n"  ) ;
+                if ( $line =~ m/^\*\s+(\d+)\s+EXISTS/ ) {
+                	$count = $1 ;
+                        return( $count ) ;
+                }
+        }
+        return( undef ) ;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+sub create_folder_old {
+	my( $imap, $h2_fold, $h1_fold ) = @_ ;
+
+	myprint( "Creating (old way) folder [$h2_fold] on host2\n" ) ;
+        if ( ( 'INBOX' eq uc  $h2_fold )
+         and ( $imap->exists( $h2_fold ) ) ) {
+                myprint( "Folder [$h2_fold] already exists\n"  ) ;
+                return( 1 ) ;
+        }
+	if ( ! $dry ){
+		if ( ! $imap->create( $h2_fold ) ) {
+			my $error = join q{},
+				"Could not create folder [$h2_fold] from [$h1_fold]: ",
+				$imap->LastError(  ), "\n" ;
+			errors_incr( $sync, $error ) ;
+                        # success if folder exists ("already exists" error)
+                        return( 1 ) if $imap->exists( $h2_fold ) ;
+                        # failure since create failed
+			return( 0 ) ;
+		}else{
+			#create succeeded
+                        myprint( "Created ( the old way ) folder [$h2_fold] on host2\n"  ) ;
+			return( 1 ) ;
+		}
+	}else{
+		# dry mode, no folder so many imap will fail, assuming failure
+                myprint( "Created ( the old way ) folder [$h2_fold] on host2 $dry_message\n"  ) ;
+		return( 0 ) ;
+	}
+}
+
+
+sub create_folder {
+        my( $imap2 , $h2_fold , $h1_fold ) = @_ ;
+        my( @parts , $parent ) ;
+
+        if ( $imap2->IsUnconnected(  ) ) {
+                myprint( "Host2: Unconnected state\n"  ) ;
+                return( 0 ) ;
+        }
+
+	if ( $create_folder_old ) {
+        	return( create_folder_old( $imap2 , $h2_fold , $h1_fold ) ) ;
+	}
+        myprint( "Creating folder [$h2_fold] on host2\n"  ) ;
+        if ( ( 'INBOX' eq uc  $h2_fold  )
+         and ( $imap2->exists( $h2_fold ) ) ) {
+                myprint( "Folder [$h2_fold] already exists\n"  ) ;
+                return( 1 ) ;
+        }
+
+        if ( $mixfolders and $imap2->exists( $h2_fold ) ) {
+                myprint( "Folder [$h2_fold] already exists  (--nomixfolders is not set)\n"  ) ;
+                return( 1 ) ;
+        }
+
+
+        if ( ( not $mixfolders ) and ( $imap2->exists( $h2_fold ) ) ) {
+                myprint( "Folder [$h2_fold] already exists and --nomixfolders is set\n"  ) ;
+                return( 0 ) ;
+        }
+
+        @parts = split /\Q$h2_sep\E/, $h2_fold ;
+        pop @parts ;
+        $parent = join $h2_sep, @parts ;
+        $parent =~ s/^\s+|\s+$//g ;
+        if ( ( $parent ne q{} ) and ( ! $imap2->exists( $parent ) ) ) {
+                create_folder( $imap2 , $parent , $h1_fold ) ;
+        }
+
+        if ( ! $dry ) {
+                if ( ! $imap2->create( $h2_fold ) ) {
+			my $error = join q{},
+				"Could not create folder [$h2_fold] from [$h1_fold]: " ,
+				$imap2->LastError(  ), "\n" ;
+			errors_incr( $sync, $error ) ;
+                        # success if folder exists ("already exists" error)
+                        return( 1 ) if $imap2->exists( $h2_fold ) ;
+                        # failure since create failed
+                        return( 0 ) ;
+                }else{
+                        #create succeeded
+                        myprint( "Created folder [$h2_fold] on host2\n"  ) ;
+                        return( 1 ) ;
+                }
+        }else{
+                # dry mode, no folder so many imap will fail, assuming failure
+                myprint( "Created  folder [$h2_fold] on host2 $dry_message\n"  ) ;
+                if ( ! $justfolders ) {
+			myprint( "Since --dry mode is on and folder [$h2_fold] on host2 does not exist yet, syncing messages will not be simulated.\n"
+			. "To simulate message syncing, use --justfolders without --dry to first create the missing folders then rerun the --dry sync.\n" ) ;
+                }
+		return( 0 ) ;
+        }
+}
+
+
+
+sub tests_folder_routines {
+	ok( !is_requested_folder('folder_foo'), 'is_requested_folder folder_foo 1'               );
+	ok(  add_to_requested_folders('folder_foo'), 'add_to_requested_folders folder_foo'       );
+	ok(  is_requested_folder('folder_foo'), 'is_requested_folder folder_foo 2'               );
+	ok( !is_requested_folder('folder_NO_EXIST'), 'is_requested_folder folder_NO_EXIST'       );
+	ok( !remove_from_requested_folders('folder_foo'), 'removed folder_foo'                   );
+	ok( !is_requested_folder('folder_foo'), 'is_requested_folder folder_foo 3'               );
+	my @f ;
+	ok(  @f = add_to_requested_folders('folder_bar', 'folder_toto'), "add result: @f"        );
+	ok(  is_requested_folder('folder_bar'), 'is_requested_folder 4'                          );
+	ok(  is_requested_folder('folder_toto'), 'is_requested_folder 5'                         );
+	ok(  remove_from_requested_folders('folder_toto'), 'remove_from_requested_folders: '       );
+	ok( !is_requested_folder('folder_toto'), 'is_requested_folder 6'                         );
+	ok( !remove_from_requested_folders('folder_bar'), 'remove_from_requested_folders: empty' ) ;
+
+        ok( 0 == compare_lists( [ sort_requested_folders(  ) ], [] ), 'sort_requested_folders: all empty' ) ;
+	ok(  add_to_requested_folders('M_55'), 'add_to_requested_folders M_55'       );
+        ok( 0 == compare_lists( [ sort_requested_folders(  ) ], [ 'M_55' ] ), 'sort_requested_folders: middle' ) ;
+	@folderfirst = ( 'Z_11' ) ;
+        ok( 0 == compare_lists( [ sort_requested_folders(  ) ], [ 'Z_11', 'M_55' ] ), 'sort_requested_folders: first+middle' ) ;
+	@folderlast = ( 'A_99' ) ;
+        ok( 0 == compare_lists( [ sort_requested_folders(  ) ], [ 'Z_11', 'M_55', 'A_99' ] ), 'sort_requested_folders: first+middle+last 1' ) ;
+
+	ok(  add_to_requested_folders('M_55', 'M_44',), 'add_to_requested_folders M_55 M_44'       );
+        ok( 0 == compare_lists( [ sort_requested_folders(  ) ], [ 'Z_11', 'M_44', 'M_55', 'A_99' ] ), 'sort_requested_folders: first+middle+last 2' ) ;
+	@folderfirst = qw( Z_22  Z_11 ) ;
+	@folderlast  = qw( A_99  A_88 ) ;
+        ok( 0 == compare_lists( [ sort_requested_folders(  ) ], [  'Z_22', 'Z_11', 'M_44', 'M_55', 'A_99', 'A_88' ] ), 'sort_requested_folders: first+middle+last 3' ) ;
+
+	return ;
+}
+
+
+sub sort_requested_folders {
+	my @requested_folders_sorted = () ;
+
+	foreach my $folder ( @folderfirst ) {
+        	remove_from_requested_folders( $folder ) ;
+        }
+
+	foreach my $folder ( @folderlast ) {
+        	remove_from_requested_folders( $folder ) ;
+        }
+
+	my @middle = sort keys %requested_folder ;
+
+        @requested_folders_sorted = ( @folderfirst, @middle, @folderlast ) ;
+
+	return( @requested_folders_sorted ) ;
+}
+
+sub is_requested_folder {
+	my ( $folder ) = @_;
+
+	return( defined  $requested_folder{ $folder }  ) ;
+}
+
+
+sub add_to_requested_folders {
+	my @wanted_folders = @_ ;
+
+	foreach my $folder ( @wanted_folders ) {
+	 	++$requested_folder{ $folder } ;
+	}
+	return( keys  %requested_folder  ) ;
+}
+
+sub remove_from_requested_folders {
+	my @wanted_folders = @_ ;
+
+	foreach my $folder ( @wanted_folders ) {
+	 	delete $requested_folder{ $folder } ;
+	}
+	return( keys %requested_folder ) ;
+}
+
+sub compare_lists {
+	my ($list_1_ref, $list_2_ref) = @_;
+
+	return($MINUS_ONE) if ((not defined $list_1_ref) and defined $list_2_ref);
+	return(0)  if ((not defined $list_1_ref) and not defined $list_2_ref); # end if no list
+	return(1)  if (not defined $list_2_ref); # end if only one list
+
+	if (not ref $list_1_ref ) {$list_1_ref = [$list_1_ref]};
+	if (not ref $list_2_ref ) {$list_2_ref = [$list_2_ref]};
+
+
+	my $last_used_indice = $MINUS_ONE;
+
+
+	ELEMENT:
+	foreach my $indice ( 0 .. $#{ $list_1_ref } ) {
+		$last_used_indice = $indice ;
+
+		# End of list_2
+		return 1 if ($indice > $#{ $list_2_ref } ) ;
+
+		my $element_list_1 = $list_1_ref->[$indice] ;
+		my $element_list_2 = $list_2_ref->[$indice] ;
+		my $balance = $element_list_1 cmp $element_list_2 ;
+		next ELEMENT if ($balance == 0) ;
+		return $balance ;
+	}
+	# each element equal until last indice of list_1
+	return $MINUS_ONE if ($last_used_indice < $#{ $list_2_ref } ) ;
+
+	# same size, each element equal
+	return 0 ;
+}
+
+sub tests_compare_lists {
+
+
+	my $empty_list_ref = [];
+
+	ok( 0 == compare_lists()               , 'compare_lists, no args');
+	ok( 0 == compare_lists(undef)          , 'compare_lists, undef = nothing');
+	ok( 0 == compare_lists(undef, undef)   , 'compare_lists, undef = undef');
+	ok($MINUS_ONE == compare_lists(undef , [])     , 'compare_lists, undef < []');
+	ok($MINUS_ONE == compare_lists(undef , [1])    , 'compare_lists, undef < [1]');
+	ok($MINUS_ONE == compare_lists(undef , [0])    , 'compare_lists, undef < [0]');
+      	ok(+1 == compare_lists([])             , 'compare_lists, [] > nothing');
+        ok(+1 == compare_lists([], undef)      , 'compare_lists, [] > undef');
+	ok( 0 == compare_lists([] , [])        , 'compare_lists, [] = []');
+
+	ok($MINUS_ONE == compare_lists([] , [1])        , 'compare_lists, [] < [1]');
+	ok(+1 == compare_lists([1] , [])        , 'compare_lists, [1] > []');
+
+
+	ok( 0 == compare_lists([1],  1 )          , 'compare_lists, [1] =  1 ') ;
+	ok( 0 == compare_lists( 1 , [1])          , 'compare_lists,  1  = [1]') ;
+	ok( 0 == compare_lists( 1 ,  1 )          , 'compare_lists,  1  =  1 ') ;
+	ok($MINUS_ONE == compare_lists( 0 ,  1 )          , 'compare_lists,  0  <  1 ') ;
+	ok($MINUS_ONE == compare_lists($MINUS_ONE ,  0 )          , 'compare_lists, -1  <  0 ') ;
+	ok($MINUS_ONE == compare_lists( 1 ,  2 )          , 'compare_lists,  1  <  2 ') ;
+	ok(+1 == compare_lists( 2 ,  1 )          , 'compare_lists,  2  >  1 ') ;
+
+
+	ok( 0 == compare_lists([1,2], [1,2])   , 'compare_lists,  [1,2] = [1,2]' ) ;
+	ok($MINUS_ONE == compare_lists([1], [1,2])     , 'compare_lists,    [1] < [1,2]' ) ;
+	ok(+1 == compare_lists([2], [1,2])     , 'compare_lists,    [2] > [1,2]' ) ;
+	ok($MINUS_ONE == compare_lists([1], [1,1])     , 'compare_lists,    [1] < [1,1]' ) ;
+	ok(+1 == compare_lists([1, 1], [1])    , 'compare_lists, [1, 1] >   [1]' ) ;
+	ok( 0 == compare_lists([1 .. $NUMBER_20_000] , [1 .. $NUMBER_20_000])
+                                               , 'compare_lists, [1..20_000] = [1..20_000]' ) ;
+	ok($MINUS_ONE == compare_lists([1], [2])       , 'compare_lists, [1] < [2]') ;
+	ok( 0 == compare_lists([2], [2])       , 'compare_lists, [0] = [2]') ;
+	ok(+1 == compare_lists([2], [1])       , 'compare_lists, [2] > [1]') ;
+
+	ok($MINUS_ONE == compare_lists(['a'],  ['b'])   , 'compare_lists, ["a"] < ["b"]') ;
+	ok( 0 == compare_lists(['a'],  ['a'])   , 'compare_lists, ["a"] = ["a"]') ;
+	ok( 0 == compare_lists(['ab'], ['ab']) , 'compare_lists, ["ab"] = ["ab"]') ;
+	ok(+1 == compare_lists(['b'],  ['a'])   , 'compare_lists, ["b"] > ["a"]') ;
+	ok($MINUS_ONE == compare_lists(['a'],  ['aa'])  , 'compare_lists, ["a"] < ["aa"]') ;
+	ok($MINUS_ONE == compare_lists(['a'],  ['a', 'a']), 'compare_lists, ["a"] < ["a", "a"]') ;
+	ok( 0 == compare_lists([split q{ }, 'a b' ], ['a', 'b']), 'compare_lists, split') ;
+	ok( 0 == compare_lists([sort split q{ }, 'b a' ], ['a', 'b']), 'compare_lists, sort split') ;
+        return ;
+}
+
+
+sub guess_prefix {
+	my @foldernames = @_ ;
+
+	return( undef ) unless ( @foldernames ) ;
+
+	my $prefix_guessed = q{} ;
+	foreach my $folder ( @foldernames ) {
+		next if ( $folder =~ m{^INBOX$}i ) ; # no guessing from INBOX
+		if ( $folder !~ m{^INBOX}i ) {
+			$prefix_guessed = q{} ; # prefix empty guessed
+			last ;
+		}
+		if ( $folder =~ m{^(INBOX(?:\.|\/))}i ) {
+			$prefix_guessed = $1 ;  # prefix Inbox/ or INBOX. guessed
+		}
+	}
+	return( $prefix_guessed ) ;
+}
+
+sub tests_guess_prefix {
+
+	ok( not( defined guess_prefix(  ) ), 'guess_prefix: no args' ) ;
+	ok( q{} eq guess_prefix( 'INBOX' ), 'guess_prefix: INBOX alone' ) ;
+	ok( q{} eq guess_prefix( 'Inbox' ), 'guess_prefix: Inbox alone' ) ;
+	ok( q{} eq guess_prefix( 'INBOX' ), 'guess_prefix: INBOX alone' ) ;
+	ok( 'INBOX/' eq guess_prefix( 'INBOX', 'INBOX/Junk' ), 'guess_prefix: INBOX INBOX/Junk' ) ;
+	ok( 'INBOX.' eq guess_prefix( 'INBOX', 'INBOX.Junk' ), 'guess_prefix: INBOX INBOX.Junk' ) ;
+	ok( 'Inbox/' eq guess_prefix( 'Inbox', 'Inbox/Junk' ), 'guess_prefix: Inbox Inbox/Junk' ) ;
+	ok( 'Inbox.' eq guess_prefix( 'Inbox', 'Inbox.Junk' ), 'guess_prefix: Inbox Inbox.Junk' ) ;
+	ok( 'INBOX/' eq guess_prefix( 'INBOX', 'INBOX/Junk', 'INBOX/rrr' ), 'guess_prefix: INBOX INBOX/Junk INBOX/rrr' ) ;
+	ok( q{} eq guess_prefix( 'INBOX', 'INBOX/Junk', 'INBOX/rrr', 'zzz' ), 'guess_prefix: INBOX INBOX/Junk INBOX/rrr zzz' ) ;
+	ok( q{} eq guess_prefix( 'INBOX', 'Junk' ), 'guess_prefix: INBOX Junk' ) ;
+	ok( q{} eq guess_prefix( 'INBOX', 'Junk' ), 'guess_prefix: INBOX Junk' ) ;
+
+	return ;
+}
+
+sub get_prefix {
+	my( $imap, $prefix_in, $prefix_opt, $Side, $folders_ref ) = @_ ;
+	my( $prefix_out, $prefix_guessed ) ;
+
+	( $debug or $sync->{debugfolders} ) and myprint( "$Side: Getting prefix\n"  ) ;
+	$prefix_guessed = guess_prefix( @{ $folders_ref } ) ;
+	myprint( "$Side: guessing prefix from folder listing: [$prefix_guessed]\n"  ) ;
+	( $debug or $sync->{debugfolders} ) and myprint( "$Side: Calling namespace capability\n"  ) ;
+	if ( $imap->has_capability( 'namespace' ) ) {
+		my $r_namespace = $imap->namespace(  ) ;
+		$prefix_out = $r_namespace->[0][0][0] ;
+                myprint( "$Side: prefix given by NAMESPACE: [$prefix_out]\n"  ) ;
+		if ( defined  $prefix_in  ) {
+                	myprint( "$Side: but using [$prefix_in] given by $prefix_opt\n"  ) ;
+                	$prefix_out = $prefix_in ;
+                	return( $prefix_out ) ;
+                }else{
+                	# all good
+	                return( $prefix_out ) ;
+                }
+	}
+	else{
+        	if ( defined  $prefix_in  ) {
+                	myprint( "$Side: using [$prefix_in] given by $prefix_opt\n"  ) ;
+                	$prefix_out = $prefix_in ;
+                	return( $prefix_out ) ;
+                }else{
+			myprint(
+			  "$Side: No NAMESPACE capability so using guessed prefix [$prefix_guessed]\n",
+			  help_to_guess_prefix( $imap, $prefix_opt ) ) ;
+			return( $prefix_guessed ) ;
+                }
+	}
+        return ;
+}
+
+
+sub guess_separator {
+	my @foldernames = @_ ;
+
+	#return( undef ) unless ( @foldernames ) ;
+
+	my $sep_guessed ;
+	my %counter ;
+	foreach my $folder ( @foldernames ) {
+		$counter{'/'}++  while ( $folder =~ m{/}g ) ;  # count /
+		$counter{'.'}++  while ( $folder =~ m{\.}g ) ; # count .
+		$counter{'\\\\'}++ while ( $folder =~ m{(\\){2}}g ) ; # count \\
+	}
+	my @race_sorted = sort { $counter{ $b } <=> $counter{ $a } } keys  %counter  ;
+	#myprint( "@race_sorted\n"  ) ;
+	$sep_guessed = shift @race_sorted || $LAST_RESSORT_SEPARATOR ; # / when nothing found.
+	return( $sep_guessed ) ;
+}
+
+sub tests_guess_separator {
+	ok( '/' eq  guess_separator(  ), 'guess_separator: no args' ) ;
+	ok( '/' eq guess_separator( 'abcd' ), 'guess_separator: abcd' ) ;
+	ok( '/' eq guess_separator( 'a/b/c.d' ), 'guess_separator: a/b/c.d' ) ;
+	ok( '.' eq guess_separator( 'a.b/c.d' ), 'guess_separator: a.b/c.d' ) ;
+	ok( '\\\\' eq guess_separator( 'a\\\\b\\\\c.c\\\\d/e/f' ), 'guess_separator: a\\\\b\\\\c.c\\\\d/e/f' ) ;
+	return ;
+}
+
+sub get_separator {
+	my( $imap, $sep_in, $sep_opt, $Side, $folders_ref ) = @_ ;
+	my( $sep_out, $sep_guessed ) ;
+
+	( $debug or $sync->{debugfolders} ) and myprint( "$Side: Getting separator\n"  ) ;
+	$sep_guessed = guess_separator( @{ $folders_ref } ) ;
+	myprint( "$Side: guessing separator from folder listing: [$sep_guessed]\n"  ) ;
+
+	( $debug or $sync->{debugfolders} ) and myprint( "$Side: calling namespace capability\n"  ) ;
+	if ( $imap->has_capability( 'namespace' ) ) {
+		$sep_out = $imap->separator(  ) ;
+		if ( defined  $sep_out  ) {
+                	myprint( "$Side: separator given by NAMESPACE: [$sep_out]\n"  ) ;
+                        if ( defined  $sep_in  ) {
+                		myprint( "$Side: but using [$sep_in] given by $sep_opt\n"  ) ;
+                        	$sep_out = $sep_in ;
+                        	return( $sep_out ) ;
+                        }else{
+                        	return( $sep_out ) ;
+                        }
+		}else{
+                	if ( defined  $sep_in  ) {
+                        	myprint( "$Side: NAMESPACE request failed but using [$sep_in] given by $sep_opt\n"  ) ;
+                        	$sep_out = $sep_in ;
+                        	return( $sep_out ) ;
+                        }else{
+				myprint(
+		  		"$Side: NAMESPACE request failed so using guessed separator [$sep_guessed]\n",
+                  		help_to_guess_sep( $imap, $sep_opt ) ) ;
+				return( $sep_guessed ) ;
+                        }
+                }
+	}
+	else{
+        	if ( defined  $sep_in  ) {
+                	myprint( "$Side: No NAMESPACE capability but using [$sep_in] given by $sep_opt\n"  ) ;
+                	$sep_out = $sep_in ;
+                	return( $sep_out ) ;
+                }else{
+			myprint(
+		  	"$Side: No NAMESPACE capability, so using guessed separator [$sep_guessed]\n",
+		      	help_to_guess_sep( $imap, $sep_opt ) ) ;
+			return( $sep_guessed ) ;
+                }
+	}
+        return ;
+}
+
+sub help_to_guess_sep {
+	my( $imap, $sep_opt ) = @_ ;
+
+	my $help_to_guess_sep = "You can set the separator character with the $sep_opt option,\n"
+	. "the complete listing of folders may help you to find it\n"
+	. folders_list_to_help( $imap ) ;
+
+	return( $help_to_guess_sep ) ;
+}
+
+sub help_to_guess_prefix {
+	my( $imap, $prefix_opt ) = @_ ;
+
+	my $help_to_guess_prefix = "You can set the prefix namespace with the $prefix_opt option,\n"
+	. "the folowing listing of folders may help you to find it:\n"
+	. folders_list_to_help( $imap ) ;
+
+	return( $help_to_guess_prefix ) ;
+}
+
+
+sub folders_list_to_help {
+	my($imap) = @_ ;
+
+	my @folders = $imap->folders ;
+	my $listing = join q{}, map { "[$_]\n" } @folders ;
+	return( $listing ) ;
+}
+
+
+sub tests_separator_invert {
+	$fixslash2 = 0 ;
+	ok( not( defined separator_invert(  )  ), 'separator_invert: no args' ) ;
+	ok( not( defined separator_invert( q{} ) ), 'separator_invert: not enough args' ) ;
+	ok( not( defined separator_invert( q{}, q{} ) ), 'separator_invert: not enough args' ) ;
+
+	ok( q{} eq separator_invert( q{}, q{}, q{} ), 'separator_invert: 3 empty strings' ) ;
+	ok( 'lalala' eq separator_invert( 'lalala', q{}, q{} ), 'separator_invert: empty separator' ) ;
+	ok( 'lalala' eq separator_invert( 'lalala', '/', '/' ), 'separator_invert: same separator /' ) ;
+	ok( 'lal/ala' eq separator_invert( 'lal/ala', '/', '/' ), 'separator_invert: same separator / 2' ) ;
+	ok( 'lal.ala' eq separator_invert( 'lal/ala', '/', '.' ), 'separator_invert: separators /.' ) ;
+	ok( 'lal/ala' eq separator_invert( 'lal.ala', '.', '/' ), 'separator_invert: separators ./' ) ;
+	ok( 'la.l/ala' eq separator_invert( 'la/l.ala', '.', '/' ), 'separator_invert: separators ./' ) ;
+
+	ok( 'l/al.ala' eq separator_invert( 'l.al/ala', '/', '.' ), 'separator_invert: separators /.' ) ;
+        $fixslash2 = 1 ;
+	ok( 'l_al.ala' eq separator_invert( 'l.al/ala', '/', '.' ), 'separator_invert: separators /.' ) ;
+
+	return ;
+}
+
+sub separator_invert {
+	my( $h1_fold, $h1_separator, $h2_separator ) = @_ ;
+
+	return( undef ) if ( not defined  $h1_fold  or not defined  $h1_separator  or not defined  $h2_separator  ) ;
+	# The separator we hope we'll never encounter: 00000000 == 0x00
+	my $o_sep = "\000" ;
+
+	my $h2_fold = $h1_fold ;
+	$h2_fold =~ s,\Q$h2_separator,$o_sep,xg ;
+	$h2_fold =~ s,\Q$h1_separator,$h2_separator,xg ;
+	$h2_fold =~ s,\Q$o_sep,$h1_separator,xg ;
+        $h2_fold =~ s,/,_,xg if( $fixslash2 and '/' ne $h2_separator and '/' eq $h1_separator ) ;
+	return( $h2_fold ) ;
+}
+
+
+sub tests_imap2_folder_name {
+
+$h1_prefix = $h2_prefix = q{};
+$h1_sep = '/';
+$h2_sep = '.';
+
+$debug and myprint( <<"EOS"
+prefix1: [$h1_prefix]
+prefix2: [$h2_prefix]
+sep1:[$h1_sep]
+sep2:[$h2_sep]
+EOS
+) ;
+
+$fixslash2 = 0 ;
+ok(q{} eq imap2_folder_name(q{}), 'imap2_folder_name: empty string');
+ok('blabla' eq imap2_folder_name('blabla'), 'imap2_folder_name: blabla');
+ok('spam.spam' eq imap2_folder_name('spam/spam'), 'imap2_folder_name: spam/spam');
+ok('spam/spam' eq imap2_folder_name('spam.spam'), 'imap2_folder_name: spam.spam');
+ok('spam.spam/spam' eq imap2_folder_name('spam/spam.spam'), 'imap2_folder_name: spam/spam.spam');
+ok('s pam.spam/sp  am' eq imap2_folder_name('s pam/spam.sp  am'), 'imap2_folder_name: s pam/spam.sp  am');
+
+$sync->{f1f2}{ 'auto' } = 'moto' ;
+ok( 'moto' eq imap2_folder_name( 'auto' ), 'imap2_folder_name: auto' ) ;
+$sync->{f1f2}{ 'auto/auto' } = 'moto x 2' ;
+ok( 'moto x 2' eq imap2_folder_name( 'auto/auto' ), 'imap2_folder_name: auto/auto' ) ;
+
+@regextrans2 = ('s,/,X,g');
+ok(q{} eq imap2_folder_name(q{}), 'imap2_folder_name: empty string [s,/,X,g]');
+ok('blabla' eq imap2_folder_name('blabla'), 'imap2_folder_name: blabla [s,/,X,g]');
+ok('spam.spam' eq imap2_folder_name('spam/spam'), 'imap2_folder_name: spam/spam [s,/,X,g]');
+ok('spamXspam' eq imap2_folder_name('spam.spam'), 'imap2_folder_name: spam.spam [s,/,X,g]');
+ok('spam.spamXspam' eq imap2_folder_name('spam/spam.spam'), 'imap2_folder_name: spam/spam.spam [s,/,X,g]');
+
+@regextrans2 = ( 's, ,_,g' ) ;
+ok('blabla' eq imap2_folder_name('blabla'), 'imap2_folder_name: blabla [s, ,_,g]');
+ok('bla_bla' eq imap2_folder_name('bla bla'), 'imap2_folder_name: blabla [s, ,_,g]');
+
+@regextrans2 = ( q{s,(.*),\U$1,} ) ;
+ok( 'BLABLA' eq imap2_folder_name( 'blabla' ), q{imap2_folder_name: blabla [s,\U(.*)\E,$1,]} ) ;
+
+$fixslash2 = 1 ;
+@regextrans2 = (  ) ;
+ok(q{} eq imap2_folder_name(q{}), 'imap2_folder_name: empty string');
+ok('blabla' eq imap2_folder_name('blabla'), 'imap2_folder_name: blabla');
+ok('spam.spam' eq imap2_folder_name('spam/spam'), 'imap2_folder_name: spam/spam -> spam.spam');
+ok('spam_spam' eq imap2_folder_name('spam.spam'), 'imap2_folder_name: spam.spam -> spam_spam');
+ok('spam.spam_spam' eq imap2_folder_name('spam/spam.spam'), 'imap2_folder_name: spam/spam.spam -> spam.spam_spam');
+ok('s pam.spam_spa  m' eq imap2_folder_name('s pam/spam.spa  m'), 'imap2_folder_name: s pam/spam.spa m -> s pam.spam_spa  m');
+
+$h1_sep = '.';
+$h2_sep = '/';
+ok(q{} eq imap2_folder_name(q{}), 'imap2_folder_name: empty string');
+ok('blabla' eq imap2_folder_name('blabla'), 'imap2_folder_name: blabla');
+ok('spam.spam' eq imap2_folder_name('spam/spam'), 'imap2_folder_name: spam/spam -> spam.spam');
+ok('spam/spam' eq imap2_folder_name('spam.spam'), 'imap2_folder_name: spam.spam -> spam/spam');
+ok('spam.spam/spam' eq imap2_folder_name('spam/spam.spam'), 'imap2_folder_name: spam/spam.spam -> spam.spam/spam');
+
+
+
+$fixslash2 = 0 ;
+$h1_prefix = q{ };
+
+ok('spam.spam/spam' eq imap2_folder_name('spam/spam.spam'), 'imap2_folder_name: spam/spam.spam -> spam.spam/spam');
+ok('spam.spam/spam' eq imap2_folder_name(' spam/spam.spam'), 'imap2_folder_name:  spam/spam.spam -> spam.spam/spam');
+
+$h1_sep = '.' ;
+$h2_sep = '/' ;
+$h1_prefix = 'INBOX.' ;
+$h2_prefix = q{} ;
+@regextrans2 = ( q{s,(.*),\U$1,} ) ;
+ok( 'BLABLA' eq imap2_folder_name( 'blabla' ), 'imap2_folder_name: blabla' ) ;
+ok( 'TEST/TEST/TEST/TEST' eq imap2_folder_name( 'INBOX.TEST.test.Test.tesT' ), 'imap2_folder_name: INBOX.TEST.test.Test.tesT' ) ;
+@regextrans2 = ( q{s,(.*),\L$1,} ) ;
+ok( 'test/test/test/test' eq imap2_folder_name( 'INBOX.TEST.test.Test.tesT' ), 'imap2_folder_name: INBOX.TEST.test.Test.tesT' ) ;
+
+
+return ;
+
+}
+
+sub imap2_folder_name {
+	my ( $h1_fold ) = @_ ;
+	my ( $h2_fold ) ;
+	if ( $sync->{f1f2}{ $h1_fold } ) {
+		$h2_fold = $sync->{f1f2}{ $h1_fold } ;
+		( $debug or $sync->{debugfolders} ) and myprint( "f1f2 [$h1_fold] -> [$h2_fold]\n"  ) ;
+		return( $h2_fold ) ;
+	}
+	if ( $sync->{f1f2auto}{ $h1_fold } ) {
+		$h2_fold = $sync->{f1f2auto}{ $h1_fold } ;
+		( $debug or $sync->{debugfolders} ) and myprint( "automap [$h1_fold] -> [$h2_fold]\n"  ) ;
+		return( $h2_fold ) ;
+	}
+
+	$h2_fold = prefix_seperator_invertion( $h1_fold ) ;
+	$h2_fold = regextrans2( $h2_fold ) ;
+	return( $h2_fold ) ;
+}
+
+sub prefix_seperator_invertion {
+	my ( $h1_fold ) = @_ ;
+	my ( $h2_fold ) ;
+
+	# first we remove the prefix
+	$h1_fold =~ s/^\Q$h1_prefix\E//x ;
+	( $debug or $sync->{debugfolders} ) and myprint( "removed host1 prefix: [$h1_fold]\n"  ) ;
+	$h2_fold = separator_invert( $h1_fold, $h1_sep, $h2_sep ) ;
+	( $debug or $sync->{debugfolders} ) and myprint( "inverted  separators: [$h2_fold]\n"  ) ;
+	# Adding the prefix supplied by namespace or the --prefix2 option
+	$h2_fold = $h2_prefix . $h2_fold
+	  unless( ( $h2_prefix eq 'INBOX' . $h2_sep ) and ( $h2_fold =~ m/^INBOX$/xi ) ) ;
+	( $debug or $sync->{debugfolders} ) and myprint( "added   host2 prefix: [$h2_fold]\n"  ) ;
+	return( $h2_fold ) ;
+}
+
+sub regextrans2 {
+	my( $h2_fold ) = @_ ;
+	# Transforming the folder name by the --regextrans2 option(s)
+	foreach my $regextrans2 ( @regextrans2 ) {
+	        my $h2_fold_before = $h2_fold ;
+		my $ret = eval "\$h2_fold =~ $regextrans2 ; 1 " ;
+		( $debug or $sync->{debugfolders} ) and myprint( "[$h2_fold_before] -> [$h2_fold] using regextrans2 [$regextrans2]\n"  ) ;
+                if ( not ( defined  $ret  ) or $@ ) {
+			die_clean( "error: eval regextrans2 '$regextrans2': $@\n" ) ;
+                }
+	}
+	return( $h2_fold ) ;
+}
+
+
+sub tests_decompose_regex {
+	ok( 1, 'decompose_regex 1' ) ;
+	ok( 0 == compare_lists( [ q{}, q{} ], [ decompose_regex( q{} ) ] ), 'decompose_regex empty string' ) ;
+	ok( 0 == compare_lists( [ '.*', 'lala' ], [ decompose_regex( 's/.*/lala/' ) ] ), 'decompose_regex s/.*/lala/' ) ;
+	return ;
+}
+
+sub decompose_regex {
+	my $regex = shift ;
+	my( $left_part, $right_part ) ;
+
+	( $left_part, $right_part ) = $regex =~ m{^s/((?:[^/]|\\/)+)/((?:[^/]|\\/)+)/}x;
+        return( q{}, q{} ) if not $left_part ;
+	return( $left_part, $right_part ) ;
+}
+
+
+sub foldersizes {
+
+	my ( $side, $imap, $search_cmd, @folders ) = @_ ;
+	my $total_size = 0 ;
+	my $total_nb = 0 ;
+	my $biggest_in_all = 0 ;
+
+	my $nb_folders = scalar  @folders  ;
+	my $ct_folders = 0 ; # folder counter.
+	myprint( "++++ Calculating sizes of $nb_folders folders on $side\n"  ) ;
+	foreach my $folder ( @folders )     {
+		my $stot = 0 ;
+		my $nb_msgs = 0 ;
+		$ct_folders++ ;
+		myprintf( "$side folder %7s %-35s", "$ct_folders/$nb_folders", jux_utf8( $folder ) ) ;
+                if ( 'Host2' eq $side and not exists  $h2_folders_all_UPPER{ uc  $folder  }  ) {
+		        myprint( " does not exist yet\n") ;
+			next ;
+		}
+                if ( 'Host1' eq $side and not exists  $h1_folders_all{ $folder }  ) {
+		        myprint( " does not exist\n" ) ;
+			next ;
+		}
+
+		last if $imap->IsUnconnected(  ) ;
+		# FTGate is RFC buggy with EXAMINE it does not act as SELECT
+		#unless ( $imap->examine( $folder ) ) {
+		unless ( $imap->select( $folder ) ) {
+			my $error = join q{},
+				"$side Folder $folder: Could not select: ",
+				$imap->LastError,  "\n"  ;
+			errors_incr( $sync, $error ) ;
+			next ;
+		}
+		last if $imap->IsUnconnected(  ) ;
+
+		my $hash_ref = { } ;
+		my @msgs = select_msgs( $imap, undef, $search_cmd, $folder ) ;
+		$nb_msgs = scalar  @msgs  ;
+		my $biggest_in_folder = 0 ;
+		@{ $hash_ref }{ @msgs } = ( undef ) if @msgs ;
+
+		last if $imap->IsUnconnected(  ) ;
+		if ( $nb_msgs > 0 and @msgs ) {
+                	if ( $abletosearch ) {
+				if ( ! $imap->fetch_hash( \@msgs, 'RFC822.SIZE', $hash_ref) ) {
+                                        my $error = "$side failure with fetch_hash: $@" ;
+                                        errors_incr( $sync, $error ) ;
+                                        return ;
+                                }
+                        }else{
+				my $uidnext = $imap->uidnext( $folder ) || $uidnext_default ;
+				my $fetch_hash_uids = $fetch_hash_set || "1:$uidnext" ;
+				if ( ! $imap->fetch_hash( $fetch_hash_uids, 'RFC822.SIZE', $hash_ref ) ) {
+                                        my $error = "$side failure with fetch_hash: $@" ;
+                                        errors_incr( $sync, $error ) ;
+                                        return ;
+                                }
+                        }
+			for ( keys %{ $hash_ref } ) {
+                        	my $size =  $hash_ref->{ $_ }->{ 'RFC822.SIZE' } ;
+                        	$stot    += $size ;
+                                $biggest_in_folder =  max( $biggest_in_folder, $size ) ;
+                        }
+		}
+
+		myprintf( ' Size: %9s', $stot ) ;
+		myprintf( ' Messages: %5s', $nb_msgs ) ;
+		myprintf( " Biggest: %9s\n", $biggest_in_folder ) ;
+		$total_size += $stot ;
+		$total_nb += $nb_msgs ;
+                $biggest_in_all =  max( $biggest_in_all, $biggest_in_folder ) ;
+	}
+	myprintf( "%s Nb folders:      %11s folders\n",    $side, $nb_folders ) ;
+	myprintf( "%s Nb messages:     %11s messages\n",   $side, $total_nb ) ;
+	myprintf( "%s Total size:      %11s bytes (%s)\n", $side, $total_size, bytes_display_string( $total_size ) ) ;
+	myprintf( "%s Biggest message: %11s bytes (%s)\n", $side, $biggest_in_all, bytes_display_string( $biggest_in_all ) ) ;
+	myprintf( "%s Time spent:      %11.1f seconds\n",  $side, timenext(  ) ) ;
+        return( $total_nb, $total_size ) ;
+}
+
+sub timenext {
+	my ( $timenow, $timediff ) ;
+	# $timebefore is global, beurk !
+	$timenow    = time ;
+	$timediff   = $timenow - $timebefore ;
+	$timebefore = $timenow ;
+	return( $timediff ) ;
+}
+
+sub timesince {
+	my $timeinit = shift ;
+	my ( $timenow, $timediff ) ;
+	$timenow    = time ;
+	$timediff   = $timenow - $timeinit ;
+	return( $timediff ) ;
+}
+
+
+
+
+sub tests_flags_regex {
+
+	ok( q{} eq flags_regex(q{} ), 'flags_regex, null string q{}' ) ;
+	ok( q'\Seen NonJunk $Spam' eq flags_regex( q'\Seen NonJunk $Spam' ), 'flags_regex, nothing to do');
+
+	@regexflag = ('I am BAD' ) ;
+        ok( not ( defined flags_regex( q{} ) ), 'flags_regex, bad regex' ) ;
+
+	@regexflag = ( 's/NonJunk//g' ) ;
+	ok( q'\Seen  $Spam' eq flags_regex( q'\Seen NonJunk $Spam' ), q{flags_regex, remove NonJunk: 's/NonJunk//g'} ) ;
+	@regexflag = ( q's/\$Spam//g' ) ;
+	ok( '\Seen NonJunk ' eq flags_regex( q'\Seen NonJunk $Spam' ), q{flags_regex, remove $Spam: 's/\$Spam//g'} ) ;
+
+	@regexflag = ( 's/\\\\Seen//g' ) ;
+
+	ok( q' NonJunk $Spam' eq flags_regex( q'\Seen NonJunk $Spam' ), q{flags_regex, remove \Seen: 's/\\\\\\\\Seen//g'} ) ;
+
+	@regexflag = ( 's/(\s|^)[^\\\\]\w+//g' ) ;
+	ok( '\Seen \Middle \End'   eq flags_regex( q'\Seen NonJunk \Middle $Spam \End' ), q{flags_regex: only \word among \Seen NonJunk \Middle $Spam \End} ) ;
+	ok( ' \Seen \Middle \End1' eq flags_regex( q'Begin \Seen NonJunk \Middle $Spam \End1 End' ), 
+                     q'flags_regex: only \word among Begin \Seen NonJunk \Middle $Spam \End1 End' ) ;
+
+	@regexflag = ( q's/.*?(Keep1|Keep2|Keep3)/$1 /g' ) ;
+	ok('Keep1 Keep2  ReB' eq flags_regex('ReA Keep1 REM Keep2 ReB'), 'Keep only regex' ) ;
+	
+	ok('Keep1 Keep2 ' eq flags_regex( 'REM REM Keep1 Keep2'), 'Keep only regex' ) ;
+	ok('Keep1 Keep2 ' eq flags_regex( 'Keep1 REM REM Keep2'), 'Keep only regex' ) ;
+	ok('Keep1 Keep2 ' eq flags_regex( 'REM Keep1 REM REM  Keep2'), 'Keep only regex' ) ;
+	ok('Keep1 Keep2 ' eq flags_regex( 'Keep1 Keep2'), 'Keep only regex' ) ;
+	ok('Keep1 ' eq flags_regex( 'REM Keep1'), 'Keep only regex' ) ;
+
+	@regexflag = ( q's/(Keep1|Keep2|Keep3) (?!(Keep1|Keep2|Keep3)).*/$1 /g' ) ;
+	ok('Keep1 Keep2 ' eq flags_regex( 'Keep1 Keep2 ReB'), 'Keep only regex' ) ;
+	ok('Keep1 Keep2 ' eq flags_regex( 'Keep1 Keep2 REM REM  REM'), 'Keep only regex' ) ;
+	ok('Keep2 ' eq flags_regex('Keep2 REM REM  REM'), 'Keep only regex' ) ;
+	
+
+	@regexflag = ( q's/.*?(Keep1|Keep2|Keep3)/$1 /g',
+	   's/(Keep1|Keep2|Keep3) (?!(Keep1|Keep2|Keep3)).*/$1 /g');
+	ok('Keep1 Keep2 ' eq flags_regex('REM Keep1 REM Keep2 REM'), 'Keep only regex');
+	ok('Keep1 Keep2 ' eq flags_regex('Keep1 REM Keep2 REM'), 'Keep only regex');
+	ok('Keep1 Keep2 ' eq flags_regex('REM Keep1 Keep2 REM'), 'Keep only regex');
+	ok('Keep1 Keep2 ' eq flags_regex('REM Keep1 REM Keep2'), 'Keep only regex');
+	ok('Keep1 Keep2 Keep3 ' eq flags_regex('REM Keep1 REM Keep2 REM REM Keep3 REM'), 'Keep only regex');
+	ok('Keep1 ' eq flags_regex('REM  REM Keep1 REM REM REM '), 'Keep only regex');
+	ok('Keep1 Keep3 ' eq flags_regex('RE1 Keep1 RE2 Keep3 RE3 RE4 RE5 '), 'Keep only regex');
+
+	@regexflag = ('s/(.*)/$1 jrdH8u/');
+	ok('REM  REM  REM REM REM jrdH8u' eq flags_regex('REM  REM  REM REM REM'), q{Keep only regex 's/(.*)/\$1 jrdH8u/'} ) ;
+	@regexflag = ('s/jrdH8u *//');
+	ok('REM  REM  REM REM REM ' eq flags_regex('REM  REM  REM REM REM jrdH8u'), q{Keep only regex s/jrdH8u *//} ) ;
+
+	@regexflag = (
+	's/(.*)/$1 jrdH8u/',
+	's/.*?(Keep1|Keep2|Keep3|jrdH8u)/$1 /g',
+	's/(Keep1|Keep2|Keep3|jrdH8u) (?!(Keep1|Keep2|Keep3|jrdH8u)).*/$1 /g',
+	's/jrdH8u *//'
+	);
+
+	ok('Keep1 Keep2 ' eq flags_regex('REM Keep1 REM Keep2 REM'), q{Keep only regex 'REM Keep1 REM Keep2 REM'} ) ;
+	ok('Keep1 Keep2 ' eq flags_regex('Keep1 REM Keep2 REM'), 'Keep only regex');
+	ok('Keep1 Keep2 ' eq flags_regex('REM Keep1 Keep2 REM'), 'Keep only regex');
+	ok('Keep1 Keep2 ' eq flags_regex('REM Keep1 REM Keep2'), 'Keep only regex');
+	ok('Keep1 Keep2 Keep3 ' eq flags_regex('REM Keep1 REM Keep2 REM REM Keep3 REM'), 'Keep only regex');
+	ok('Keep1 ' eq flags_regex('REM  REM Keep1 REM REM REM '), 'Keep only regex');
+	ok('Keep1 Keep3 ' eq flags_regex('RE1 Keep1 RE2 Keep3 RE3 RE4 RE5 '), 'Keep only regex');
+	ok(q{} eq flags_regex('REM  REM REM REM REM'), 'Keep only regex');
+
+	@regexflag = (
+	's/(.*)/$1 jrdH8u/',
+	's/.*?(\\\\Seen|\\\\Answered|\\\\Flagged|\\\\Deleted|\\\\Draft|jrdH8u)/$1 /g',
+	's/(\\\\Seen|\\\\Answered|\\\\Flagged|\\\\Deleted|\\\\Draft|jrdH8u) (?!(\\\\Seen|\\\\Answered|\\\\Flagged|\\\\Deleted|\\\\Draft|jrdH8u)).*/$1 /g',
+	's/jrdH8u *//'
+	);
+
+	ok('\\Deleted \\Answered '
+	    eq flags_regex('Blabla $Junk \\Deleted machin \\Answered truc'), 'Keep only regex: Exchange case' ) ;
+	ok( q{} eq flags_regex( q{} ), 'Keep only regex: Exchange case, null string' ) ;
+	ok( q{}
+	   eq flags_regex('Blabla $Junk  machin  truc'), 'Keep only regex: Exchange case, no accepted flags' ) ;
+	ok( '\\Deleted \\Answered \\Draft \\Flagged '
+	    eq flags_regex('\\Deleted    \\Answered  \\Draft \\Flagged '), 'Keep only regex: Exchange case' ) ;
+
+
+	@regexflag = (
+	's/.*?(?:(\\\\(?:Answered|Flagged|Deleted|Seen|Draft)\s?)|$)/defined($1)?$1:q()/eg'
+	);
+
+	ok( '\\Deleted \\Answered '
+	eq flags_regex('Blabla \$Junk \\Deleted machin \\Answered truc'),
+	'Keep only regex: Exchange case (Phil)' ) ;
+
+	ok( q{} eq flags_regex( q{} ), 'Keep only regex: Exchange case, null string (Phil)' ) ;
+
+	ok( q{}
+	eq flags_regex('Blabla $Junk  machin  truc'),
+	'Keep only regex: Exchange case, no accepted flags (Phil)' ) ;
+
+	ok('\\Deleted \\Answered \\Draft \\Flagged '
+	eq flags_regex('\\Deleted    \\Answered  \\Draft \\Flagged '),
+	'Keep only regex: Exchange case (Phil)' ) ;
+
+	return ;
+}
+
+sub flags_regex {
+	my ( $h1_flags ) = @_ ;
+	foreach my $regexflag ( @regexflag ) {
+		my $h1_flags_orig = $h1_flags ;
+		$debugflags and myprint( "eval \$h1_flags =~ $regexflag\n"  ) ;
+		my $ret = eval "\$h1_flags =~ $regexflag ; 1 " ;
+		$debugflags and myprint( "regexflag $regexflag [$h1_flags_orig] -> [$h1_flags]\n"  ) ;
+                if( not ( defined $ret ) or $@ ) {
+			myprint( "Error: eval regexflag '$regexflag': $@\n"  ) ;
+                        return( undef ) ;
+                }
+	}
+	return( $h1_flags ) ;
+}
+
+sub acls_sync {
+	my($h1_fold, $h2_fold) = @_ ;
+	if ( $syncacls ) {
+		my $h1_hash = $imap1->getacl($h1_fold)
+		  or myprint( "Could not getacl for $h1_fold: $@\n" ) ;
+		my $h2_hash = $imap2->getacl($h2_fold)
+		  or myprint( "Could not getacl for $h2_fold: $@\n" ) ;
+		my %users = map { ($_, 1) } ( keys  %{ $h1_hash} , keys %{ $h2_hash }  ) ;
+		foreach my $user (sort keys %users ) {
+			my $acl = $h1_hash->{$user} || 'none' ;
+			myprint( "acl $user: [$acl]\n" ) ;
+			next if ($h1_hash->{$user} && $h2_hash->{$user} &&
+				 $h1_hash->{$user} eq $h2_hash->{$user});
+			unless ($dry) {
+				myprint( "setting acl $h2_fold $user $acl\n" ) ;
+				$imap2->setacl($h2_fold, $user, $acl)
+				  or myprint( "Could not set acl: $@\n" ) ;
+			}
+		}
+	}
+        return ;
+}
+
+
+sub tests_permanentflags {
+
+	my $string;
+	ok(q{} eq permanentflags(' * OK [PERMANENTFLAGS (\* \Draft \Answered)] Limited'),
+	   'permanentflags \*');
+	ok('\Draft \Answered' eq permanentflags(' * OK [PERMANENTFLAGS (\Draft \Answered)] Limited'),
+	   'permanentflags \Draft \Answered');
+	ok('\Draft \Answered'
+	   eq permanentflags('Blabla',
+	                     ' * OK [PERMANENTFLAGS (\Draft \Answered)] Limited',
+			     'Blabla'),
+	   'permanentflags \Draft \Answered'
+	);
+	ok(q{} eq permanentflags('Blabla'), 'permanentflags nothing');
+        return ;
+}
+
+sub permanentflags {
+	my @lines = @_ ;
+
+	foreach my $line (@lines) {
+		if ( $line =~ m{\[PERMANENTFLAGS\s\(([^)]+?)\)\]}x ) {
+			( $debugflags or $debug ) and myprint( "permanentflags: $line"  ) ;
+			my $permanentflags = $1 ;
+			if ( $permanentflags =~ m{\\\*}x ) {
+				$permanentflags = q{} ;
+			}
+			return($permanentflags) ;
+		} ;
+	}
+        return( q{} ) ;
+}
+
+sub tests_flags_filter {
+
+	ok( '\Seen' eq flags_filter('\Seen', '\Draft \Seen \Answered'), 'flags_filter ' );
+	ok( q{} eq flags_filter('\Seen', '\Draft  \Answered'), 'flags_filter ' );
+	ok( '\Seen' eq flags_filter('\Seen', '\Seen'), 'flags_filter ' );
+	ok( '\Seen' eq flags_filter('\Seen', ' \Seen '), 'flags_filter ' );
+	ok( '\Seen \Draft'
+	   eq flags_filter('\Seen \Draft', '\Draft \Seen \Answered'), 'flags_filter ' );
+	ok( '\Seen \Draft'
+	   eq flags_filter('\Seen \Draft', ' \Draft \Seen \Answered '), 'flags_filter ' );
+        return ;
+}
+
+sub flags_filter {
+	my( $flags, $allowed_flags ) = @_ ;
+
+	my @flags = split  /\s+/x, $flags ;
+	my %allowed_flags = map { $_ => 1 } split q{ }, $allowed_flags ;
+	my @flags_out     = map { exists $allowed_flags{$_} ? $_ : () } @flags ;
+
+	my $flags_out = join q{ }, @flags_out ;
+
+	return( $flags_out ) ;
+}
+
+sub flagscase {
+	my $flags = shift ;
+
+	my @flags = split /\s+/x, $flags ;
+	my %rfc_flags = map { $_ => 1 } split q{ }, '\Answered \Flagged \Deleted \Seen \Draft' ;
+	my @flags_out = map { exists $rfc_flags{ ucsecond( lc $_ ) } ? ucsecond( lc $_ ) : $_ } @flags ;
+
+	my $flags_out = join q{ }, @flags_out ;
+
+	return( $flags_out ) ;
+}
+
+sub tests_flagscase {
+	ok( '\Seen' eq flagscase( '\Seen' ), 'flagscase: \Seen -> \Seen' ) ;
+	ok( '\Seen' eq flagscase( '\SEEN' ), 'flagscase: \SEEN -> \Seen' ) ;
+
+	ok( '\Seen \Draft' eq flagscase( '\SEEN \DRAFT' ), 'flagscase: \SEEN \DRAFT -> \Seen \Draft' ) ;
+	ok( '\Draft \Seen' eq flagscase( '\DRAFT \SEEN' ), 'flagscase: \DRAFT \SEEN -> \Draft \Seen' ) ;
+
+	ok( '\Draft LALA \Seen' eq flagscase( '\DRAFT  LALA \SEEN' ), 'flagscase: \DRAFT  LALA \SEEN -> \Draft LALA \Seen' ) ;
+	ok( '\Draft lala \Seen' eq flagscase( '\DRAFT  lala \SEEN' ), 'flagscase: \DRAFT  lala \SEEN -> \Draft lala \Seen' ) ;
+        return ;
+}
+
+
+
+sub ucsecond {
+	my $string = shift ;
+	my $output ;
+
+	return( $string )  if ( 1 >= length $string ) ;
+	
+	$output = ( substr( $string, 0, 1) ) . ( uc substr $string, 1, 1 ) . ( substr $string, 2 ) ;
+	#myprint( "UUU $string -> $output\n"  ) ;
+	return( $output ) ;
+}
+
+
+sub tests_ucsecond {
+	ok( 'aBcde' eq ucsecond( 'abcde' ), 'ucsecond: abcde -> aBcde' ) ;
+	ok( 'ABCDE' eq ucsecond( 'ABCDE' ), 'ucsecond: ABCDE -> ABCDE'  ) ;
+	ok( 'ABCDE' eq ucsecond( 'AbCDE' ), 'ucsecond: AbCDE -> ABCDE'  ) ;
+	ok( 'ABCde' eq ucsecond( 'AbCde' ), 'ucsecond: AbCde -> ABCde'  ) ;
+	ok( 'A'     eq ucsecond( 'A' ),     'ucsecond: A  -> A'  ) ;
+	ok( 'AB'    eq ucsecond( 'Ab' ),    'ucsecond: Ab -> AB' ) ;
+	ok( '\B'    eq ucsecond( '\b' ),    'ucsecond: \b -> \B' ) ;
+	ok( '\Bcde' eq ucsecond( '\bcde' ), 'ucsecond: \bcde -> \Bcde' ) ;
+        return ;
+}
+
+
+sub select_msgs {
+	my ( $imap, $msgs_all_hash_ref, $search_cmd, $folder ) = @_ ;
+	my ( @msgs ) ;
+
+	if ( $abletosearch ) {
+		@msgs = select_msgs_by_search( $imap, $msgs_all_hash_ref, $search_cmd, $folder ) ;
+	}else{
+		@msgs = select_msgs_by_fetch( $imap, $msgs_all_hash_ref, $search_cmd, $folder ) ;
+	}
+	return(  @msgs ) ;
+
+}
+
+sub select_msgs_by_search {
+	my ( $imap, $msgs_all_hash_ref, $search_cmd, $folder ) = @_ ;
+	my ( @msgs, @msgs_all ) ;
+
+        # Need to have the whole list in msgs_all_hash_ref
+        # without calling messages() several times.
+        # Need all messages list to avoid deleting useful cache part
+        # in case of --search or --minage or --maxage
+
+	if ( ( defined  $msgs_all_hash_ref  and $usecache )
+        or ( not defined  $maxage  and not defined  $minage  and not defined  $search_cmd  )
+        ) {
+
+       		$debugdev and myprint( "Calling messages()\n"  ) ;
+		@msgs_all = $imap->messages(  ) ;
+
+                return if ( $#msgs_all == 0 && !defined  $msgs_all[0]  ) ;
+
+                if ( defined  $msgs_all_hash_ref  ) {
+                        @{ $msgs_all_hash_ref }{ @msgs_all } =  () ;
+                }
+                # return all messages
+                if ( not defined  $maxage  and not defined  $minage  and not defined  $search_cmd  ) {
+                        return( @msgs_all ) ;
+                }
+	}
+
+        if ( defined  $search_cmd  ) {
+        	@msgs = $imap->search( $search_cmd ) ;
+                return( @msgs ) ;
+        }
+
+	# we are here only if $maxage or $minage is defined
+        @msgs = select_msgs_by_age( $imap ) ;
+	return( @msgs );
+}
+
+
+sub select_msgs_by_fetch {
+	my ( $imap, $msgs_all_hash_ref, $search_cmd, $folder ) = @_ ;
+	my ( @msgs, @msgs_all, %fetch ) ;
+
+        # Need to have the whole list in msgs_all_hash_ref
+        # without calling messages() several times.
+        # Need all messages list to avoid deleting useful cache part
+        # in case of --search or --minage or --maxage
+
+
+	$debugdev and myprint( "Calling fetch_hash()\n"  ) ;
+	my $uidnext = $imap->uidnext( $folder ) || $uidnext_default ;
+	my $fetch_hash_uids = $fetch_hash_set || "1:$uidnext" ;
+	%fetch = %{$imap->fetch_hash( $fetch_hash_uids, 'INTERNALDATE' ) } ;
+
+        @msgs_all = sort { $a <=> $b } keys  %fetch  ;
+        $debugdev and myprint( "Done fetch_hash()\n"  ) ;
+
+        return if ( $#msgs_all == 0 && !defined  $msgs_all[0]  ) ;
+
+        if ( defined  $msgs_all_hash_ref  ) {
+                 @{ $msgs_all_hash_ref }{ @msgs_all } =  () ;
+        }
+        # return all messages
+        if ( not defined  $maxage  and not defined  $minage  and not defined  $search_cmd  ) {
+                return( @msgs_all ) ;
+        }
+
+        if ( defined  $search_cmd  ) {
+		myprint( "Warning: strange to see --search with --noabletosearch, an error can happen\n"  ) ;
+        	@msgs = $imap->search( $search_cmd ) ;
+                return( @msgs ) ;
+        }
+
+	# we are here only if $maxage or $minage is defined
+	my( @max, @min, $maxage_epoch, $minage_epoch ) ;
+	if ( defined  $maxage  ) { $maxage_epoch = $timestart_int - $NB_SECONDS_IN_A_DAY * $maxage ; }
+	if ( defined  $minage  ) { $minage_epoch = $timestart_int - $NB_SECONDS_IN_A_DAY * $minage ; }
+	foreach my $msg ( @msgs_all ) {
+		my $idate = $fetch{ $msg }->{'INTERNALDATE'} ;
+		#myprint( "$idate\n"  ) ;
+		if ( defined  $maxage  and ( epoch( $idate ) >= $maxage_epoch ) ) {
+			push  @max, $msg  ;
+		}
+		if ( defined  $minage  and ( epoch( $idate ) <= $minage_epoch ) ) {
+			push  @min, $msg  ;
+		}
+	}
+        @msgs = msgs_from_maxmin( \@max, \@min ) ;
+	return( @msgs ) ;
+}
+
+sub select_msgs_by_age {
+	my( $imap ) = @_ ;
+
+	my( @max, @min, @msgs, @inter, @union ) ;
+
+	if ( defined  $maxage  ) {
+		@max = $imap->sentsince( $timestart_int - $NB_SECONDS_IN_A_DAY * $maxage ) ;
+	}
+	if ( defined  $minage  ) {
+		@min = $imap->sentbefore( $timestart_int - $NB_SECONDS_IN_A_DAY * $minage ) ;
+	}
+
+	@msgs = msgs_from_maxmin( \@max, \@min ) ;
+	return( @msgs ) ;
+}
+
+sub msgs_from_maxmin {
+	my( $max_ref, $min_ref ) = @_ ;
+	my( @max, @min, @msgs, @inter, @union ) ;
+
+	@max = @{ $max_ref } ;
+	@min = @{ $min_ref } ;
+
+	SWITCH: {
+		unless( defined  $minage  ) { @msgs = @max ; last SWITCH } ;
+		unless( defined  $maxage  ) { @msgs = @min ; last SWITCH } ;
+		my ( %union, %inter ) ;
+		foreach my $m ( @min, @max ) { $union{ $m }++ && $inter{ $m }++ }
+		@inter = sort { $a <=> $b } keys  %inter  ;
+		@union = sort { $a <=> $b } keys  %union  ;
+		# normal case
+		if ( $minage <= $maxage )  { @msgs = @inter ; last SWITCH } ;
+		# just exclude messages between
+		if ( $minage > $maxage )  { @msgs = @union ; last SWITCH } ;
+
+	}
+	return( @msgs ) ;
+}
+
+sub tests_msgs_from_maxmin {
+	my @msgs ;
+	$maxage = $NUMBER_200 ;
+	@msgs = msgs_from_maxmin( [ '1', '2' ], [ '2', '3' ] ) ;
+	ok( 0 == compare_lists( [ '1', '2' ], \@msgs ), 'msgs_from_maxmin: maxage++' ) ;
+	$minage = $NUMBER_100 ;
+	@msgs = msgs_from_maxmin( [ '1', '2' ], [ '2', '3' ] ) ;
+	ok( 0 == compare_lists( [ '2' ], \@msgs ), 'msgs_from_maxmin:  -maxage++minage-' ) ;
+	$minage = $NUMBER_300 ;
+	@msgs = msgs_from_maxmin( [ '1', '2' ], [ '2', '3' ] ) ;
+	ok( 0 == compare_lists( [ '1', '2', '3' ], \@msgs ), 'msgs_from_maxmin:  ++maxage-minage++' ) ;
+	$maxage = undef ;
+	@msgs = msgs_from_maxmin( [ '1', '2' ], [ '2', '3' ] ) ;
+	ok( 0 == compare_lists( [ '2', '3' ], \@msgs ), 'msgs_from_maxmin:  ++minage-' ) ;
+	return ;
+}
+
+
+sub lastuid {
+	my $imap   = shift ;
+	my $folder = shift ;
+	my $lastuid_guess  = shift ;
+	my $lastuid ;
+
+	# rfc3501: The only reliable way to identify recent messages is to
+	#          look at message flags to see which have the \Recent flag
+	#          set, or to do a SEARCH RECENT.
+	# SEARCH RECENT doesn't work this way on courrier.
+
+	my @recent_messages ;
+	# SEARCH RECENT for each transfer can be expensive with a big folder
+	# Call commented for now
+	#@recent_messages = $imap->recent(  ) ;
+	#myprint( "Recent: @recent_messages\n" ) ;
+
+	my $max_recent ;
+	$max_recent = max( @recent_messages ) ;
+
+	if ( defined  $max_recent  and ($lastuid_guess <= $max_recent ) ) {
+		$lastuid = $max_recent ;
+	}else{
+		$lastuid = $lastuid_guess
+	}
+	return( $lastuid ) ;
+}
+
+sub size_filtered {
+	my( $h1_size, $h1_msg, $h1_fold, $h2_fold  ) = @_ ;
+
+        $h1_size = 0 if ( ! $h1_size ) ; # null if empty or undef
+	if (defined $maxsize and $h1_size > $maxsize) {
+		myprint( "msg $h1_fold/$h1_msg skipped ($h1_size exceeds maxsize limit $maxsize bytes)\n" ) ;
+		$total_bytes_skipped += $h1_size;
+		$nb_msg_skipped += 1;
+		return( 1 ) ;
+	}
+	if (defined $minsize and $h1_size <= $minsize) {
+		myprint( "msg $h1_fold/$h1_msg skipped ($h1_size smaller than minsize $minsize bytes)\n" ) ;
+		$total_bytes_skipped += $h1_size;
+		$nb_msg_skipped += 1;
+		return( 1 ) ;
+	}
+	return( 0 ) ;
+}
+
+sub message_exists {
+	my( $imap, $msg ) = @_ ;
+	return( 1 ) if not $imap->Uid(  ) ;
+
+	my $search_uid ;
+        ( $search_uid ) = $imap->search( "UID $msg" ) ;
+        #myprint( "$search ? $msg\n"  ) ;
+        return( 1 ) if ( $search_uid eq $msg ) ;
+        return( 0 ) ;
+}
+
+sub copy_message {
+	# copy
+
+	my ( $sync, $h1_msg, $h1_fold, $h2_fold, $h1_fir_ref, $permanentflags2, $cache_dir ) = @_ ;
+	( $debug or $dry) and myprint( "msg $h1_fold/$h1_msg copying to $h2_fold $dry_message\n" ) ;
+
+	my $h1_size  = $h1_fir_ref->{$h1_msg}->{'RFC822.SIZE'}  || 0 ;
+	my $h1_flags = $h1_fir_ref->{$h1_msg}->{'FLAGS'}        || q{} ;
+	my $h1_idate = $h1_fir_ref->{$h1_msg}->{'INTERNALDATE'} || q{} ;
+
+
+        if ( size_filtered( $h1_size, $h1_msg, $h1_fold, $h2_fold  ) ) {
+        	$h1_nb_msg_processed +=1 ;
+                return ;
+        }
+
+	debugsleep( $sync ) ;
+	myprint( "- msg $h1_fold/$h1_msg S[$h1_size] F[$h1_flags] I[$h1_idate] has RFC822.SIZE null!\n" ) if ( ! $h1_size )   ;
+
+
+        if ( $checkmessageexists and not message_exists( $imap1, $h1_msg ) ) {
+		$total_bytes_skipped += $h1_size;
+		$nb_msg_skipped += 1;
+        	$h1_nb_msg_processed +=1 ;
+                return ;
+        }
+        if ( $sync->{debugmemory} ) {
+                myprintf("C1: Memory consumption: %.1f MiB\n", memory_consumption(  ) / $KIBI / $KIBI) ;
+        }
+
+	my ( $string, $string_len ) ;
+        ( $string_len ) = message_for_host2( $sync, $h1_msg, $h1_fold, $h1_size, $h1_flags, $h1_idate, $h1_fir_ref, \$string ) ;
+
+        if ( $sync->{debugmemory} ) {
+                myprintf("C2: Memory consumption: %.1f MiB\n", memory_consumption(  ) / $KIBI / $KIBI) ;
+        }
+
+        # not defined or empty $string
+        if ( ( not $string ) and ( not $string_len ) ) {
+		myprint( "- msg $h1_fold/$h1_msg skipped.\n"  ) ;
+		$total_bytes_skipped += $h1_size;
+		$nb_msg_skipped += 1;
+                $h1_nb_msg_processed +=1 ;
+                return ;
+        }
+
+        # Lines too long (or not enough) => do no copy or fix
+        if ( ( defined $maxlinelength ) or ( defined $minmaxlinelength ) ) {
+		$string = linelengthstuff( $string, $h1_fold, $h1_msg, $string_len, $h1_size, $h1_flags, $h1_idate ) ;
+		if ( not defined  $string  ) {
+			$h1_nb_msg_processed +=1 ;
+			$total_bytes_skipped += $h1_size ;
+			$nb_msg_skipped += 1 ;
+			return ;
+		}
+	}
+
+	my $h1_date = date_for_host2( $h1_msg, $h1_idate ) ;
+
+	( $debug or $debugflags ) and
+        myprint( "Host1 flags init msg $h1_fold/$h1_msg date [$h1_date] flags [$h1_flags] size [$h1_size]\n"  ) ;
+
+	$h1_flags = flags_for_host2( $h1_flags, $permanentflags2 ) ;
+
+	( $debug or $debugflags ) and
+        myprint( "Host1 flags filt msg $h1_fold/$h1_msg date [$h1_date] flags [$h1_flags] size [$h1_size]\n"  ) ;
+
+	$h1_date = undef if ($h1_date eq q{});
+
+	my $new_id = append_message_on_host2( \$string, $h1_fold, $h1_msg, $string_len, $h2_fold, $h1_size, $h1_flags, $h1_date, $cache_dir ) ;
+
+	if ( $new_id and $syncflagsaftercopy ) {
+        	sync_flags_after_copy( $h1_fold, $h1_msg, $h1_flags, $h2_fold, $new_id, $permanentflags2 ) ;
+        }
+
+	if ( $sync->{debugmemory} ) {
+        	myprintf("C3: Memory consumption: %.1f MiB\n", memory_consumption(  ) / $KIBI / $KIBI) ;
+        }
+
+        return $new_id ;
+}
+
+
+
+sub linelengthstuff {
+	my( $string, $h1_fold, $h1_msg, $string_len, $h1_size, $h1_flags, $h1_idate  ) = @_ ;
+	my $maxlinelength_string = max_line_length( $string ) ;
+        $debugmaxlinelength and myprint( "msg $h1_fold/$h1_msg maxlinelength: $maxlinelength_string\n"  ) ;
+
+        if ( ( defined $minmaxlinelength )  and ( $maxlinelength_string <= $minmaxlinelength ) ) {
+		my $subject = subject( $string ) ;
+         	$debugdev and myprint( "- msg $h1_fold/$h1_msg skipped S[$h1_size] F[$h1_flags] I[$h1_idate] "
+                      	. "(Subject:[$subject]) (max line length under minmaxlinelength $minmaxlinelength bytes)\n" ) ;
+         	return ;
+        }
+
+        if ( ( defined $maxlinelength )  and ( $maxlinelength_string > $maxlinelength ) ) {
+         	my $subject = subject( $string ) ;
+		if ( $maxlinelengthcmd ) {
+			$string = pipemess( $string, $maxlinelengthcmd ) ;
+			# string undef means something was bad.
+			if ( not ( defined  $string  ) ) {
+				myprint( "- msg $h1_fold/$h1_msg {$string_len} S[$h1_size] F[$h1_flags] I[$h1_idate] "
+				      . "(Subject:[$subject]) could not be successfully transformed by --maxlinelengthcmd option\n" ) ;
+				return ;
+			}else{
+				return $string ;
+			}
+		}
+         	myprint( "- msg $h1_fold/$h1_msg skipped S[$h1_size] F[$h1_flags] I[$h1_idate] "
+                      . "(Subject:[$subject]) (line length exceeds maxlinelength $maxlinelength bytes)\n" ) ;
+		return ;
+	}
+	return $string ;
+}
+
+
+sub message_for_host2 {
+
+# global variable list: 
+# @skipmess
+# @regexmess
+# @pipemess
+# $addheader
+# $debugcontent
+# $debug
+# 
+# API current
+#
+# at failure: 
+#   * return nothing ( will then be undef or () )
+#   * $string_ref content is undef or empty
+# at success:
+#   * return string length ($string_ref content length)
+#   * $string_ref content filled with message
+
+# API future
+# 
+# 
+	my ( $sync, $h1_msg, $h1_fold, $h1_size, $h1_flags, $h1_idate, $h1_fir_ref, $string_ref ) = @_ ;
+
+        # abort when missing a parameter
+        if ( (!$sync) or  (!$h1_msg) or (!$h1_fold) or (!$h1_size) or (!defined $h1_flags) or (!$h1_idate) or (!$h1_fir_ref) or (!$string_ref) ) {
+                return ;
+        }
+
+        if ( $sync->{debugmemory} ) {
+                myprintf("M1: Memory consumption: %.1f MiB\n", memory_consumption(  ) / $KIBI / $KIBI) ;
+        }
+
+        my $imap1 = $sync->{imap1} ;
+	my $string_ok = $imap1->message_to_file( $string_ref, $h1_msg ) ;
+
+        if ( $sync->{debugmemory} ) {
+                myprintf("M2: Memory consumption: %.1f MiB\n", memory_consumption(  ) / $KIBI / $KIBI) ;
+        }
+
+	my $string_len = length_ref( $string_ref  ) ;
+
+
+	unless ( defined  $string_ok  and $string_len ) {
+		# undef or 0 length
+		my $error = join q{},
+			"- msg $h1_fold/$h1_msg {$string_len} S[$h1_size] F[$h1_flags] I[$h1_idate] could not be fetched: ",
+			$imap1->LastError || q{}, "\n"  ;
+		errors_incr( $sync, $error ) ;
+		$total_bytes_error += $h1_size if ( $h1_size ) ;
+                $h1_nb_msg_processed +=1 ;
+		return ;
+	}
+
+	if ( @skipmess ) {
+		my $match = skipmess( ${ $string_ref } ) ;
+                # string undef means the eval regex was bad.
+                if ( not ( defined  $match  ) ) {
+                	myprint(
+			"- msg $h1_fold/$h1_msg {$string_len} S[$h1_size] F[$h1_flags] I[$h1_idate]"
+                        . " could not be skipped by --skipmess option, bad regex\n" ) ;
+                	return ;
+                }
+                if ( $match ) {
+                        my $subject = subject( ${ $string_ref } ) ;
+                        myprint( "- msg $h1_fold/$h1_msg {$string_len} S[$h1_size] F[$h1_flags] I[$h1_idate]"
+                            . " (Subject:[$subject]) skipped by --skipmess\n" ) ;
+                	return ;
+                }
+	}
+
+	if ( @regexmess ) {
+		${ $string_ref } = regexmess( ${ $string_ref } ) ;
+                # string undef means the eval regex was bad.
+                if ( not ( defined  ${ $string_ref }  ) ) {
+                	myprint(
+			"- msg $h1_fold/$h1_msg {$string_len} S[$h1_size] F[$h1_flags] I[$h1_idate]"
+                        . " could not be transformed by --regexmess\n" ) ;
+                	return ;
+                }
+	}
+
+	if ( @pipemess ) {
+		${ $string_ref } = pipemess( ${ $string_ref }, @pipemess ) ;
+                # string undef means something was bad.
+                if ( not ( defined  ${ $string_ref }  ) ) {
+                	myprint(
+			"- msg $h1_fold/$h1_msg {$string_len} S[$h1_size] F[$h1_flags] I[$h1_idate]"
+                        . " could not be successfully transformed by --pipemess option\n" ) ;
+                	return ;
+                }
+	}
+
+        if ( $addheader and defined $h1_fir_ref->{$h1_msg}->{'NO_HEADER'} ) {
+                my $header = add_header( $h1_msg ) ;
+                $debug and myprint( "msg $h1_fold/$h1_msg adding custom header [$header]\n"  ) ;
+                ${ $string_ref } = $header . "\r\n" . ${ $string_ref } ;
+        }
+
+        $string_len = length_ref( $string_ref  ) ;
+
+	$debugcontent and myprint(
+		q{=} x $STD_CHAR_PER_LINE, "\n",
+		"F message content begin next line ($string_len characters long)\n",
+		${ $string_ref },
+		"F message content ended on previous line\n", q{=} x $STD_CHAR_PER_LINE, "\n" ) ;
+
+        if ( $sync->{debugmemory} ) {
+                myprintf("M3: Memory consumption: %.1f MiB\n", memory_consumption(  ) / $KIBI / $KIBI) ;
+        }
+
+	return $string_len ;
+}
+
+sub tests_message_for_host2 {
+        
+        my ( $sync, $h1_msg, $h1_fold, $h1_size, $h1_flags, $h1_idate, $h1_fir_ref, $string_ref ) ;
+        
+        is( undef, message_for_host2(  ), q{message_for_host2: no args} ) ;
+        is( undef, message_for_host2( $sync, $h1_msg, $h1_fold, $h1_size, $h1_flags, $h1_idate, $h1_fir_ref, $string_ref ), q{message_for_host2: undef args} ) ;
+
+        require Test::MockObject ;
+        my $imapT = Test::MockObject->new(  ) ;
+        $sync->{imap1} = $imapT ;
+        my $string ;
+        
+        $h1_msg = 1 ;
+        $h1_fold = 'FoldFoo';
+        $h1_size =  9 ; 
+        $h1_flags = '' ; 
+        $h1_idate = '10-Jul-2015 09:00:00 +0200' ;
+        $h1_fir_ref = {} ;
+        $string_ref = \$string ;
+        $imapT->mock( 'message_to_file',   
+                sub {
+                        my ( $imap, $string_ref, $msg ) = @_ ;
+                        ${$string_ref} = 'blablabla' ;
+                        return length ${$string_ref} ;
+                }
+        ) ;
+        is( 9, message_for_host2( $sync, $h1_msg, $h1_fold, $h1_size, $h1_flags, $h1_idate, $h1_fir_ref, $string_ref ), 
+        q{message_for_host2: msg 1 == "blablabla", length} ) ;
+        is( 'blablabla', $string, q{message_for_host2: msg 1 == "blablabla", value} ) ;
+ 
+        # so far so good
+        # now the --pipemess stuff
+
+	SKIP: {
+                Readonly my $NB_WIN_tests_message_for_host2 => 0 ;
+		skip( 'Not on MSWin32', $NB_WIN_tests_message_for_host2 ) if ('MSWin32' ne $OSNAME) ;
+		# Windows
+		# "type" command does not accept redirection of STDIN with <
+		# "sort" does
+
+	} ;
+
+	SKIP: {
+                Readonly my $NB_UNX_tests_message_for_host2 => 6 ;
+		skip( 'Not on Unix', $NB_UNX_tests_message_for_host2 ) if ('MSWin32' eq $OSNAME) ;
+		# Unix
+                
+                # no change by cat
+                @pipemess = ( 'cat' ) ;
+                is( 9, message_for_host2( $sync, $h1_msg, $h1_fold, $h1_size, $h1_flags, $h1_idate, $h1_fir_ref, $string_ref ), 
+                q{message_for_host2: --pipemess 'cat', length} ) ;
+                is( 'blablabla', $string, q{message_for_host2: --pipemess 'cat', value} ) ;
+
+                
+                # failure by false
+                @pipemess = ( 'false' ) ;
+                is( undef, message_for_host2( $sync, $h1_msg, $h1_fold, $h1_size, $h1_flags, $h1_idate, $h1_fir_ref, $string_ref ), 
+                q{message_for_host2: --pipemess 'false', length} ) ;
+                is( undef, $string, q{message_for_host2: --pipemess 'false', value} ) ;
+
+                # failure by true since no output
+                @pipemess = ( 'true' ) ;
+                is( undef, message_for_host2( $sync, $h1_msg, $h1_fold, $h1_size, $h1_flags, $h1_idate, $h1_fir_ref, $string_ref ), 
+                q{message_for_host2: --pipemess 'true', length} ) ;
+                is( undef, $string, q{message_for_host2: --pipemess 'true', value} ) ;
+        }
+        return ;
+}
+
+sub length_ref {
+        my $string_ref = shift ;
+        my $string_len = defined  ${ $string_ref }  ? length( ${ $string_ref } ) : q{} ; # length or empty string
+        return $string_len ;
+}
+
+sub tests_length_ref {
+        my $notdefined ;
+        is( q{}, length_ref( \$notdefined ), q{length_ref: value not defined} ) ;
+        my $notref ;
+        is( q{}, length_ref( $notref ), q{length_ref: param not a ref} ) ;
+
+        my $lala = 'lala' ;
+        is( 4, length_ref( \$lala ), q{length_ref: lala length == 4} ) ;
+        is( 4, length_ref( \'lili' ), q{length_ref: lili length == 4} ) ;
+        return ;
+}
+
+sub date_for_host2 {
+	my( $h1_msg, $h1_idate ) = @_ ;
+
+	my $h1_date = q{} ;
+
+	if ( $syncinternaldates ) {
+		$h1_date = $h1_idate ;
+		$debug and myprint( "internal date from host1: [$h1_date]\n"  ) ;
+		$h1_date = good_date( $h1_date ) ;
+		$debug and myprint( "internal date from host1: [$h1_date] (fixed)\n"  ) ;
+	}
+
+	if ( $idatefromheader ) {
+		$h1_date = $imap1->get_header( $h1_msg, 'Date' ) ;
+		$debug and myprint( "header date from host1: [$h1_date]\n"  ) ;
+		$h1_date = good_date( $h1_date ) ;
+		$debug and myprint( "header date from host1: [$h1_date] (fixed)\n"  ) ;
+	}
+
+	return( $h1_date ) ;
+}
+
+sub flags_for_host2 {
+	my( $h1_flags, $permanentflags2 ) = @_ ;
+	# RFC 2060: This flag can not be altered by any client
+	$h1_flags =~ s@\\Recent\s?@@xgi ;
+        my $h1_flags_re ;
+        if ( @regexflag and defined( $h1_flags_re = flags_regex( $h1_flags ) ) ) {
+                $h1_flags = $h1_flags_re ;
+        }
+	$h1_flags = flagscase( $h1_flags ) if $flagscase ;
+        $h1_flags = flags_filter( $h1_flags, $permanentflags2) if ( $permanentflags2 and $filterflags ) ;
+
+	return( $h1_flags ) ;
+}
+
+sub subject {
+	my $string = shift ;
+	my $subject = q{} ;
+
+        my $header = extract_header( $string ) ;
+
+        if( $header =~ m/^Subject:\s*([^\n\r]*)\r?$/msx ) {
+        	#myprint( "MMM[$1]\n"  ) ;
+        	$subject = $1 ;
+        }
+	return( $subject ) ;
+}
+
+sub tests_subject {
+	ok( q{} eq subject( q{} ), 'subject: null') ;
+	ok( 'toto le hero' eq subject( 'Subject: toto le hero' ), 'subject: toto le hero') ;
+	ok( 'toto le hero' eq subject( 'Subject:toto le hero' ), 'subject: toto le hero blank') ;
+	ok( 'toto le hero' eq subject( "Subject:toto le hero\r\n" ), 'subject: toto le hero\r\n') ;
+
+        my $MESS ;
+	$MESS = <<'EOF';
+From: lalala
+Subject: toto le hero
+Date: zzzzzz
+
+Boogie boogie
+EOF
+	ok( 'toto le hero' eq subject( $MESS ), 'subject: toto le hero 2') ;
+
+	$MESS = <<'EOF';
+Subject: toto le hero
+From: lalala
+Date: zzzzzz
+
+Boogie boogie
+EOF
+	ok( 'toto le hero' eq subject( $MESS ), 'subject: toto le hero 3') ;
+
+
+	$MESS = <<'EOF';
+From: lalala
+Subject: cuicui
+Date: zzzzzz
+
+Subject: toto le hero
+EOF
+	ok( 'cuicui' eq subject( $MESS ), 'subject: cuicui') ;
+
+	$MESS = <<'EOF';
+From: lalala
+Date: zzzzzz
+
+Subject: toto le hero
+EOF
+	ok( q{} eq subject( $MESS ), 'subject: null but body could') ;
+
+	return ;
+}
+
+
+# GlobVar
+# $dry
+# $max_msg_size_in_bytes
+# $imap2
+# $imap1
+# $total_bytes_error
+# $h1_nb_msg_processed
+# $h2_uidguess
+# $total_bytes_transferred
+# $nb_msg_transferred
+# $begin_transfer_time
+# $time_spent
+# ...
+#
+#
+sub append_message_on_host2 {
+	my( $string_ref, $h1_fold, $h1_msg, $string_len, $h2_fold, $h1_size, $h1_flags, $h1_date, $cache_dir ) = @_ ;
+	if ( $sync->{debugmemory} ) {
+        	myprintf("A1: Memory consumption: %.1f MiB\n", memory_consumption(  ) / $KIBI / $KIBI) ;
+        }
+
+	my $new_id ;
+	if ( ! $dry ) {
+		$max_msg_size_in_bytes = max( $h1_size, $max_msg_size_in_bytes ) ;
+		$new_id = $imap2->append_string( $h2_fold, ${ $string_ref }, $h1_flags, $h1_date ) ;
+	        if ( $sync->{debugmemory} ) {
+        	        myprintf("A2: Memory consumption: %.1f MiB\n", memory_consumption(  ) / $KIBI / $KIBI) ;
+                }
+		if ( ! $new_id){
+                	my $subject = subject( ${ $string_ref } ) ;
+                        my $error_imap = $imap2->LastError || q{} ;
+			my $error = "- msg $h1_fold/$h1_msg {$string_len} couldn't append  (Subject:[$subject]) to folder $h2_fold: $error_imap\n" ;
+			errors_incr( $sync, $error ) ;
+			$total_bytes_error += $h1_size;
+                        $h1_nb_msg_processed +=1 ;
+			return ;
+		}
+		else{
+			# good
+			# $new_id is an id if the IMAP server has the
+			# UIDPLUS capability else just a ref
+			if ( $new_id !~ m{^\d+$}x ) {
+				$new_id = lastuid( $imap2, $h2_fold, $h2_uidguess ) ;
+			}
+			$h2_uidguess += 1 ;
+			$total_bytes_transferred += $h1_size ;
+			$nb_msg_transferred += 1 ;
+                        $h1_nb_msg_processed +=1 ;
+
+                        my $time_spent = timesince( $begin_transfer_time ) ;
+                        my $rate = bytes_display_string( $total_bytes_transferred / $time_spent ) ;
+                        my $eta = eta( $time_spent,
+                                       $h1_nb_msg_processed, $h1_nb_msg_start, $nb_msg_transferred ) ;
+                        my $amount_transferred = bytes_display_string( $total_bytes_transferred ) ;
+			myprintf( "msg %s/%-19s copied to %s/%-10s %.2f msgs/s  %s/s %s copied  %s\n",
+                        $h1_fold, "$h1_msg {$string_len}", $h2_fold, $new_id, $nb_msg_transferred/$time_spent, $rate,
+                        $amount_transferred,
+                        $eta );
+                        sleep_if_needed( $time_spent, $total_bytes_transferred, $nb_msg_transferred ) ;
+                        if ( $usecache and $cacheaftercopy and $new_id =~ m{^\d+$}x ) {
+				$debugcache and myprint( "touch $cache_dir/${h1_msg}_$new_id\n"  ) ;
+				touch( "$cache_dir/${h1_msg}_$new_id" )
+                        	or croak( "Couldn't touch $cache_dir/${h1_msg}_$new_id" ) ;
+                        }
+			if ( $delete ) {
+				delete_message_on_host1( $h1_msg, $h1_fold ) ;
+			}
+			#myprint( "PRESS ENTER" ) and my $a = <> ;
+                        return( $new_id ) ;
+		}
+	}
+	else{
+		# NOOP to avoid timeout on large folders.
+		$imap2->noop(  ) ;
+		$nb_msg_skipped_dry_mode += 1 ;
+                $h1_nb_msg_processed +=1 ;
+	}
+
+	return ;
+}
+
+sub sleep_if_needed {
+	my( $time_spent, $total_bytes_transferred, $nb_msg_transferred ) = @_ ;
+        my $sleep_max_messages = sleep_max_messages( $nb_msg_transferred, $time_spent, $maxmessagespersecond ) ;
+        my $sleep_max_bytes = sleep_max_bytes( $total_bytes_transferred, $time_spent, $maxbytespersecond  ) ;
+        my $sleep_max = max( $sleep_max_messages, $sleep_max_bytes ) ;
+        if ( $sleep_max > 0 ) {
+        	myprintf( "sleeping %.2f s\n", $sleep_max ) ;
+                sleep $sleep_max ;
+        }
+	return ;
+}
+
+sub sleep_max_messages {
+	# how long we have to sleep to go under max_messages_per_second
+        my( $nb_msg_transferred, $time_spent, $maxmessagespersecond ) = @_ ;
+        if ( ( not defined  $maxmessagespersecond  ) or $maxmessagespersecond <= 0 ) { return( 0 ) } ;
+        my $sleep = ( $nb_msg_transferred / $maxmessagespersecond ) - $time_spent ;
+        # the sleep must be positive
+        return( max( 0, $sleep ) ) ;
+}
+
+
+sub tests_sleep_max_messages {
+	ok( 0 == sleep_max_messages( 4, 2, undef ),  'sleep_max_messages: maxmessagespersecond = undef') ;
+	ok( 0 == sleep_max_messages( 4, 2, 0 ),  'sleep_max_messages: maxmessagespersecond = 0') ;
+	ok( 0 == sleep_max_messages( 4, 2, $MINUS_ONE ), 'sleep_max_messages: maxmessagespersecond = -1') ;
+	ok( 0 == sleep_max_messages( 4, 2, 2 ),  'sleep_max_messages: maxmessagespersecond = 2 max reached') ;
+	ok( 2 == sleep_max_messages( 8, 2, 2 ),  'sleep_max_messages: maxmessagespersecond = 2 max over') ;
+	ok( 0 == sleep_max_messages( 2, 2, 2 ),  'sleep_max_messages: maxmessagespersecond = 2 max not reached') ;
+	return ;
+}
+
+
+sub sleep_max_bytes {
+	# how long we have to sleep to go under max_bytes_per_second
+        my( $total_bytes_transferred, $time_spent, $maxbytespersecond ) = @_ ;
+        if ( ( not defined  $maxbytespersecond  ) or $maxbytespersecond <= 0 ) { return( 0 ) } ;
+        my $sleep = ( $total_bytes_transferred / $maxbytespersecond ) - $time_spent ;
+        # the sleep must be positive
+        return( max( 0, $sleep ) ) ;
+}
+
+
+sub tests_sleep_max_bytes {
+	ok( 0 == sleep_max_bytes( 4000, 2, undef ),  'sleep_max_bytes: maxbytespersecond = undef') ;
+	ok( 0 == sleep_max_bytes( 4000, 2, 0 ),  'sleep_max_bytes: maxbytespersecond = 0') ;
+	ok( 0 == sleep_max_bytes( 4000, 2, $MINUS_ONE ), 'sleep_max_bytes: maxbytespersecond = -1') ;
+	ok( 0 == sleep_max_bytes( 4000, 2, 2000 ),  'sleep_max_bytes: maxbytespersecond = 2 max reached') ;
+	ok( 2 == sleep_max_bytes( 8000, 2, 2000 ),  'sleep_max_bytes: maxbytespersecond = 2 max over') ;
+	ok( 0 == sleep_max_bytes( 2000, 2, 2000 ),  'sleep_max_bytes: maxbytespersecond = 2 max not reached') ;
+	return ;
+}
+
+
+
+
+# 6 GlobVar: $dry_message $dry $imap1 $h1_nb_msg_deleted $expunge $expunge1
+sub delete_message_on_host1  {
+	my( $h1_msg, $h1_fold ) = @_ ;
+	my $expunge_message = q{} ;
+	$expunge_message = 'and expunged' if ( $expungeaftereach and ( $expunge or $expunge1 ) ) ;
+	myprint( "Host1 msg $h1_fold/$h1_msg marked deleted $expunge_message $dry_message\n"  ) ;
+        if ( ! $dry ) {
+        	$imap1->delete_message( $h1_msg ) ;
+        	$h1_nb_msg_deleted += 1 ;
+        	$imap1->expunge(  ) if ( $expungeaftereach and ( $expunge or $expunge1 ) ) ;
+        }
+        return ;
+}
+
+
+sub eta {
+	my( $my_time_spent, $h1_nb_processed, $h1_nb_msg_start, $nb_transferred ) = @_ ;
+	return( q{} ) if not $foldersizes ;
+
+        my $time_remaining = time_remaining( $my_time_spent, $h1_nb_processed, $h1_nb_msg_start, $nb_transferred ) ;
+        my $nb_msg_remaining = $h1_nb_msg_start - $h1_nb_processed ;
+        my $eta_date = localtime( time + $time_remaining ) ;
+        return( mysprintf( 'ETA: %s  %1.0f s  %s/%s msgs left', $eta_date, $time_remaining, $nb_msg_remaining, $h1_nb_msg_start ) ) ;
+}
+
+sub time_remaining {
+
+	my( $my_time_spent, $h1_nb_processed, $h1_nb_msg_start, $nb_transferred ) = @_ ;
+
+	my $time_remaining = ( $my_time_spent / $nb_transferred ) * ( $h1_nb_msg_start - $h1_nb_processed ) ;
+	return( $time_remaining ) ;
+}
+
+
+sub tests_time_remaining {
+
+	ok( 1 == time_remaining( 1, 1,  2, 1 ), 'time_remaining: 1, 1, 2, 1 -> 1'  ) ;
+	ok( 1 == time_remaining( 9, 9, 10, 9 ), 'time_remaining: 9, 9, 10, 9 -> 1' ) ;
+	ok( 9 == time_remaining( 1, 1, 10, 1 ), 'time_remaining: 1, 1, 10, 1 -> 1' ) ;
+	return ;
+}
+
+
+sub cache_map {
+	my ( $cache_files_ref, $h1_msgs_ref, $h2_msgs_ref ) = @_;
+	my ( %map1_2, %map2_1, %done2 ) ;
+
+	my $h1_msgs_hash_ref = {  } ;
+	my $h2_msgs_hash_ref = {  } ;
+
+	@{ $h1_msgs_hash_ref }{ @{ $h1_msgs_ref } } = (  ) ;
+	@{ $h2_msgs_hash_ref }{ @{ $h2_msgs_ref } } = (  ) ;
+
+	foreach my $file ( sort @{ $cache_files_ref } ) {
+		$debugcache and myprint( "C12: $file\n"  ) ;
+		( $uid1, $uid2 ) = match_a_cache_file( $file ) ;
+
+		if (  exists( $h1_msgs_hash_ref->{ defined  $uid1  ? $uid1 : q{} } )
+		  and exists( $h2_msgs_hash_ref->{ defined  $uid2  ? $uid2 : q{} } ) ) {
+		  	# keep only the greatest uid2
+			# 130_2301 and
+			# 130_231  => keep only 130 -> 2301
+
+			# keep only the greatest uid1
+			# 1601_260 and
+			#  161_260 => keep only 1601 -> 260
+		  	my $max_uid2 = max( $uid2, $map1_2{ $uid1 } || $MINUS_ONE ) ;
+			if ( exists $done2{ $max_uid2 } ) {
+				if ( $done2{ $max_uid2 } < $uid1 )  {
+					$map1_2{ $uid1 } = $max_uid2 ;
+					delete $map1_2{ $done2{ $max_uid2 } } ;
+					$done2{ $max_uid2 } = $uid1 ;
+				}
+			}else{
+				$map1_2{ $uid1 } = $max_uid2 ;
+				$done2{ $max_uid2 } = $uid1 ;
+			}
+		};
+
+	}
+	%map2_1 = reverse %map1_2 ;
+	return( \%map1_2, \%map2_1) ;
+}
+
+sub tests_cache_map {
+	#$debugcache = 1 ;
+	my @cache_files = qw (
+	100_200
+	101_201
+	120_220
+	142_242
+	143_243
+	177_277
+	177_278
+	177_279
+	155_255
+	180_280
+	181_280
+	182_280
+	130_231
+	130_2301
+	161_260
+	1601_260
+	) ;
+
+	my $msgs_1 = [120, 142, 143, 144, 161, 1601,           177,      182, 130 ];
+	my $msgs_2 = [     242, 243,       260,      299, 377, 279, 255, 280, 231, 2301 ];
+
+	my( $c12, $c21 ) ;
+	ok( ( $c12, $c21 ) = cache_map( \@cache_files, $msgs_1, $msgs_2 ), 'cache_map: 02' );
+	my $a1 = [ sort { $a <=> $b } keys %{ $c12 } ] ;
+	my $a2 = [ sort { $a <=> $b } keys %{ $c21 } ] ;
+	ok( 0 == compare_lists( [ 130, 142, 143,      177, 182, 1601      ], $a1 ), 'cache_map: 03' );
+	ok( 0 == compare_lists( [      242, 243, 260, 279, 280,      2301 ], $a2 ), 'cache_map: 04' );
+	ok( ! $c12->{161},        'cache_map: ! 161 ->  260' );
+	ok( 260  == $c12->{1601}, 'cache_map:  1601 ->  260' );
+	ok( 2301 == $c12->{130},  'cache_map:   130 -> 2301' );
+	#myprint( $c12->{1601}, "\n" ) ;
+	return ;
+
+}
+
+sub cache_dir_fix {
+	my $cache_dir = shift ;
+        $cache_dir =~ s/([;<>\*\|`&\$!#\(\)\[\]\{\}:'"\\])/\\$1/xg ;
+        #myprint( "cache_dir_fix: $cache_dir\n"  ) ;
+	return( $cache_dir ) ;
+}
+
+sub tests_cache_dir_fix {
+	ok( 'lalala' eq  cache_dir_fix('lalala'),  'cache_dir_fix: lalala -> lalala' );
+	ok( 'ii\\\\ii' eq  cache_dir_fix('ii\ii'), 'cache_dir_fix: ii\ii -> ii\\\\ii' );
+	ok( 'ii@ii' eq  cache_dir_fix('ii@ii'),  'cache_dir_fix: ii@ii -> ii@ii' );
+	ok( 'ii@ii\\:ii' eq  cache_dir_fix('ii@ii:ii'), 'cache_dir_fix: ii@ii:ii -> ii@ii\\:ii' );
+	ok( 'i\\\\i\\\\ii' eq  cache_dir_fix('i\i\ii'), 'cache_dir_fix: i\i\ii -> i\\\\i\\\\ii' );
+	ok( 'i\\\\ii' eq  cache_dir_fix('i\\ii'), 'cache_dir_fix: i\\ii -> i\\\\\\\\ii' );
+	ok( '\\\\ ' eq  cache_dir_fix('\\ '), 'cache_dir_fix: \\  -> \\\\\ ' );
+	ok( '\\\\ ' eq  cache_dir_fix('\ '), 'cache_dir_fix: \  -> \\\\\ ' );
+	ok( '\[bracket\]' eq  cache_dir_fix('[bracket]'), 'cache_dir_fix: [bracket] -> \[bracket\]' );
+	return ;
+}
+
+sub cache_dir_fix_win {
+	my $cache_dir = shift ;
+        $cache_dir =~ s/(\[|\])/[$1]/xg ;
+        #myprint( "cache_dir_fix_win: $cache_dir\n"  ) ;
+	return( $cache_dir ) ;
+}
+
+sub tests_cache_dir_fix_win {
+	ok( 'lalala' eq  cache_dir_fix_win('lalala'),  'cache_dir_fix_win: lalala -> lalala' );
+	ok( '[[]bracket[]]' eq  cache_dir_fix_win('[bracket]'), 'cache_dir_fix_win: [bracket] -> [[]bracket[]]' );
+	return ;
+}
+
+
+
+
+sub get_cache {
+	my ( $cache_dir, $h1_msgs_ref, $h2_msgs_ref, $h1_msgs_all_hash_ref, $h2_msgs_all_hash_ref ) = @_;
+
+	$debugcache and myprint( "Entering get_cache\n" ) ;
+
+	-d $cache_dir or return( undef ); # exit if cache directory doesn't exist
+	$debugcache and myprint( "cache_dir    : $cache_dir\n" ) ;
+
+
+        if ( 'MSWin32' ne $OSNAME ) {
+        	$cache_dir = cache_dir_fix( $cache_dir ) ;
+        }else{
+        	$cache_dir = cache_dir_fix_win( $cache_dir ) ;
+        }
+
+	$debugcache and myprint( "cache_dir_fix: $cache_dir\n"  ) ;
+
+	my @cache_files = bsd_glob( "$cache_dir/*" ) ;
+	#$debugcache and myprint( "cache_files: [@cache_files]\n"  ) ;
+
+	$debugcache and myprint( 'cache_files: ', scalar  @cache_files , " files found\n" ) ;
+
+	my( $cache_1_2_ref, $cache_2_1_ref )
+	  = cache_map( \@cache_files, $h1_msgs_ref, $h2_msgs_ref ) ;
+
+	clean_cache( \@cache_files, $cache_1_2_ref, $h1_msgs_all_hash_ref, $h2_msgs_all_hash_ref ) ;
+
+	$debugcache and myprint( "Exiting get_cache\n" ) ;
+	return( $cache_1_2_ref, $cache_2_1_ref ) ;
+}
+
+
+sub tests_get_cache {
+
+	ok( not( get_cache('/cache_no_exist') ), 'get_cache: /cache_no_exist' );
+	ok( ( not -d 'W/tmp/cache/F1/F2' or rmtree( 'W/tmp/cache/F1/F2' )), 'get_cache: rmtree W/tmp/cache/F1/F2' ) ;
+	ok( mkpath( 'W/tmp/cache/F1/F2' ), 'get_cache: mkpath W/tmp/cache/F1/F2' ) ;
+
+	my @test_files_cache = ( qw(
+	W/tmp/cache/F1/F2/100_200
+	W/tmp/cache/F1/F2/101_201
+	W/tmp/cache/F1/F2/120_220
+	W/tmp/cache/F1/F2/142_242
+	W/tmp/cache/F1/F2/143_243
+	W/tmp/cache/F1/F2/177_277
+	W/tmp/cache/F1/F2/177_377
+	W/tmp/cache/F1/F2/177_777
+	W/tmp/cache/F1/F2/155_255
+	) ) ;
+	ok( touch( @test_files_cache ), 'get_cache: touch W/tmp/cache/F1/F2/...' ) ;
+
+
+	# on cache: 100_200 101_201 142_242 143_243 177_277 177_377 177_777 155_255
+	# on live:
+	my $msgs_1 = [120, 142, 143, 144,          177      ];
+	my $msgs_2 = [     242, 243,     299, 377, 777, 255 ];
+
+        my $msgs_all_1 = { 120 => 0, 142 => 0, 143 => 0, 144 => 0, 177 => 0 } ;
+        my $msgs_all_2 = { 242 => 0, 243 => 0, 299 => 0, 377 => 0, 777 => 0, 255 => 0 } ;
+
+	my( $c12, $c21 ) ;
+	ok( ( $c12, $c21 ) = get_cache( 'W/tmp/cache/F1/F2', $msgs_1, $msgs_2, $msgs_all_1, $msgs_all_2 ), 'get_cache: 02' );
+	my $a1 = [ sort { $a <=> $b } keys %{ $c12 } ] ;
+	my $a2 = [ sort { $a <=> $b } keys %{ $c21 } ] ;
+	ok( 0 == compare_lists( [ 142, 143, 177 ], $a1 ), 'get_cache: 03' );
+	ok( 0 == compare_lists( [ 242, 243, 777 ], $a2 ), 'get_cache: 04' );
+	ok( -f 'W/tmp/cache/F1/F2/142_242', 'get_cache: file kept 142_242');
+	ok( -f 'W/tmp/cache/F1/F2/142_242', 'get_cache: file kept 143_243');
+	ok( ! -f 'W/tmp/cache/F1/F2/100_200', 'get_cache: file removed 100_200');
+	ok( ! -f 'W/tmp/cache/F1/F2/101_201', 'get_cache: file removed 101_201');
+
+	# test clean_cache executed
+	$maxage = 2 ;
+	ok( touch(@test_files_cache), 'get_cache: touch W/tmp/cache/F1/F2/...' ) ;
+	ok( ( $c12, $c21 ) = get_cache('W/tmp/cache/F1/F2', $msgs_1, $msgs_2, $msgs_all_1, $msgs_all_2 ), 'get_cache: 02' );
+	ok( -f 'W/tmp/cache/F1/F2/142_242', 'get_cache: file kept 142_242');
+	ok( -f 'W/tmp/cache/F1/F2/142_242', 'get_cache: file kept 143_243');
+	ok( ! -f 'W/tmp/cache/F1/F2/100_200', 'get_cache: file NOT removed 100_200');
+	ok( ! -f 'W/tmp/cache/F1/F2/101_201', 'get_cache: file NOT removed 101_201');
+
+
+	# strange files
+	#$debugcache = 1 ;
+	$maxage = undef ;
+	ok( ( not -d 'W/tmp/cache/rr\uee' or rmtree( 'W/tmp/cache/rr\uee' )), 'get_cache: rmtree W/tmp/cache/rr\uee' ) ;
+	ok( mkpath( 'W/tmp/cache/rr\uee' ), 'get_cache: mkpath W/tmp/cache/rr\uee' ) ;
+
+	@test_files_cache = ( qw(
+	W/tmp/cache/rr\uee/100_200
+	W/tmp/cache/rr\uee/101_201
+	W/tmp/cache/rr\uee/120_220
+	W/tmp/cache/rr\uee/142_242
+	W/tmp/cache/rr\uee/143_243
+	W/tmp/cache/rr\uee/177_277
+	W/tmp/cache/rr\uee/177_377
+	W/tmp/cache/rr\uee/177_777
+	W/tmp/cache/rr\uee/155_255
+	) ) ;
+	ok( touch(@test_files_cache), 'get_cache: touch strange W/tmp/cache/...' ) ;
+
+	# on cache: 100_200 101_201 142_242 143_243 177_277 177_377 177_777 155_255
+	# on live:
+	$msgs_1 = [120, 142, 143, 144,          177      ] ;
+	$msgs_2 = [     242, 243,     299, 377, 777, 255 ] ;
+
+        $msgs_all_1 = { 120 => q{}, 142 => q{}, 143 => q{}, 144 => q{}, 177 => q{} } ;
+        $msgs_all_2 = { 242 => q{}, 243 => q{}, 299 => q{}, 377 => q{}, 777 => q{}, 255 => q{} } ;
+
+	ok( ( $c12, $c21 ) = get_cache('W/tmp/cache/rr\uee', $msgs_1, $msgs_2, $msgs_all_1, $msgs_all_2), 'get_cache: strange path 02' );
+	$a1 = [ sort { $a <=> $b } keys %{ $c12 } ] ;
+	$a2 = [ sort { $a <=> $b } keys %{ $c21 } ] ;
+	ok( 0 == compare_lists( [ 142, 143, 177 ], $a1 ), 'get_cache: strange path 03' );
+	ok( 0 == compare_lists( [ 242, 243, 777 ], $a2 ), 'get_cache: strange path 04' );
+	ok( -f 'W/tmp/cache/rr\uee/142_242', 'get_cache: strange path file kept 142_242');
+	ok( -f 'W/tmp/cache/rr\uee/142_242', 'get_cache: strange path file kept 143_243');
+	ok( ! -f 'W/tmp/cache/rr\uee/100_200', 'get_cache: strange path file removed 100_200');
+	ok( ! -f 'W/tmp/cache/rr\uee/101_201', 'get_cache: strange path file removed 101_201');
+	return ;
+}
+
+sub match_a_cache_file {
+	my $file = shift ;
+	my ( $cache_uid1, $cache_uid2 ) ;
+
+	return( ( undef, undef ) ) if ( ! $file ) ;
+	if ( $file =~ m{(?:^|/)(\d+)_(\d+)$}x ) {
+		$cache_uid1 = $1 ;
+		$cache_uid2 = $2 ;
+	}
+	return( $cache_uid1, $cache_uid2 ) ;
+}
+
+sub tests_match_a_cache_file {
+	my ( $tuid1, $tuid2 ) ;
+	ok( ( $tuid1, $tuid2 ) = match_a_cache_file(  ), 'match_a_cache_file: no arg' ) ;
+	ok( ! defined  $tuid1 , 'match_a_cache_file: no arg 1' ) ;
+	ok( ! defined  $tuid2 , 'match_a_cache_file: no arg 2' ) ;
+
+	ok( ( $tuid1, $tuid2 ) = match_a_cache_file( q{} ), 'match_a_cache_file: empty arg' ) ;
+	ok( ! defined  $tuid1 , 'match_a_cache_file: empty arg 1' ) ;
+	ok( ! defined  $tuid2 , 'match_a_cache_file: empty arg 2' ) ;
+
+	ok( ( $tuid1, $tuid2 ) = match_a_cache_file( '000_000' ), 'match_a_cache_file: 000_000' ) ;
+	ok( '000' eq $tuid1, 'match_a_cache_file: 000_000 1' ) ;
+	ok( '000' eq $tuid2, 'match_a_cache_file: 000_000 2' ) ;
+
+	ok( ( $tuid1, $tuid2 ) = match_a_cache_file( '123_456' ), 'match_a_cache_file: 123_456' ) ;
+	ok( '123' eq $tuid1, 'match_a_cache_file: 123_456 1' ) ;
+	ok( '456' eq $tuid2, 'match_a_cache_file: 123_456 2' ) ;
+
+	ok( ( $tuid1, $tuid2 ) = match_a_cache_file( '/tmp/truc/123_456' ), 'match_a_cache_file: /tmp/truc/123_456' ) ;
+	ok( '123' eq $tuid1, 'match_a_cache_file: /tmp/truc/123_456 1' ) ;
+	ok( '456' eq $tuid2, 'match_a_cache_file: /tmp/truc/123_456 2' ) ;
+
+	ok( ( $tuid1, $tuid2 ) = match_a_cache_file( '/lala123_456' ), 'match_a_cache_file: NO /lala123_456' ) ;
+	ok( ! $tuid1, 'match_a_cache_file: /lala123_456 1' ) ;
+	ok( ! $tuid2, 'match_a_cache_file: /lala123_456 2' ) ;
+
+	ok( ( $tuid1, $tuid2 ) = match_a_cache_file( 'la123_456' ), 'match_a_cache_file: NO la123_456' ) ;
+	ok( ! $tuid1, 'match_a_cache_file: la123_456 1' ) ;
+	ok( ! $tuid2, 'match_a_cache_file: la123_456 2' ) ;
+
+	return ;
+}
+
+sub clean_cache {
+	my ( $cache_files_ref, $cache_1_2_ref, $h1_msgs_all_hash_ref, $h2_msgs_all_hash_ref )  = @_ ;
+
+	$debugcache and myprint( "Entering clean_cache\n" ) ;
+
+	$debugcache and myprint( map { "$_ -> " . $cache_1_2_ref->{ $_ } . "\n" } keys %{ $cache_1_2_ref }  ) ;
+	foreach my $file ( @{ $cache_files_ref } ) {
+		$debugcache and myprint( "$file\n"  ) ;
+		my ( $cache_uid1, $cache_uid2 ) = match_a_cache_file( $file ) ;
+		$debugcache and myprint( "u1: $cache_uid1 u2: $cache_uid2 c12: ", $cache_1_2_ref->{ $cache_uid1 } || q{}, "\n") ;
+#		  or ( ! exists( $cache_1_2_ref->{ $cache_uid1 } ) )
+#		  or ( ! ( $cache_uid2 == $cache_1_2_ref->{ $cache_uid1 } ) )
+		if ( ( not defined  $cache_uid1  )
+		  or ( not defined  $cache_uid2  )
+                  or ( not exists  $h1_msgs_all_hash_ref->{ $cache_uid1 }  )
+                  or ( not exists  $h2_msgs_all_hash_ref->{ $cache_uid2 }  )
+                ) {
+			$debugcache and myprint( "remove $file\n"  ) ;
+			unlink $file or myprint( "$!"  ) ;
+		}
+	}
+
+	$debugcache and myprint( "Exiting clean_cache\n" ) ;
+	return( 1 ) ;
+}
+
+sub tests_clean_cache {
+
+	ok( ( not -d  'W/tmp/cache/G1/G2' or rmtree( 'W/tmp/cache/G1/G2' )), 'clean_cache: rmtree W/tmp/cache/G1/G2' ) ;
+	ok( mkpath( 'W/tmp/cache/G1/G2' ), 'clean_cache: mkpath W/tmp/cache/G1/G2' ) ;
+
+	my @test_files_cache = ( qw(
+	W/tmp/cache/G1/G2/100_200
+	W/tmp/cache/G1/G2/101_201
+	W/tmp/cache/G1/G2/120_220
+	W/tmp/cache/G1/G2/142_242
+	W/tmp/cache/G1/G2/143_243
+	W/tmp/cache/G1/G2/177_277
+	W/tmp/cache/G1/G2/177_377
+	W/tmp/cache/G1/G2/177_777
+	W/tmp/cache/G1/G2/155_255
+	) ) ;
+	ok( touch(@test_files_cache), 'clean_cache: touch W/tmp/cache/G1/G2/...' ) ;
+
+	ok( -f 'W/tmp/cache/G1/G2/100_200', 'clean_cache: 100_200 before' );
+	ok( -f 'W/tmp/cache/G1/G2/142_242', 'clean_cache: 142_242 before' );
+	ok( -f 'W/tmp/cache/G1/G2/177_277', 'clean_cache: 177_277 before' );
+	ok( -f 'W/tmp/cache/G1/G2/177_377', 'clean_cache: 177_377 before' );
+	ok( -f 'W/tmp/cache/G1/G2/177_777', 'clean_cache: 177_777 before' );
+	ok( -f 'W/tmp/cache/G1/G2/155_255', 'clean_cache: 155_255 before' );
+
+	my $cache = {
+		142 => 242,
+		177 => 777,
+	} ;
+
+        my $all_1 = {
+                142 => q{},
+                177 => q{},
+        } ;
+
+        my $all_2 = {
+                200 => q{},
+                242 => q{},
+                777 => q{},
+        } ;
+	ok( clean_cache( \@test_files_cache, $cache, $all_1, $all_2 ), 'clean_cache: ' ) ;
+
+	ok( ! -f 'W/tmp/cache/G1/G2/100_200', 'clean_cache: 100_200 after' );
+	ok(   -f 'W/tmp/cache/G1/G2/142_242', 'clean_cache: 142_242 after' );
+	ok( ! -f 'W/tmp/cache/G1/G2/177_277', 'clean_cache: 177_277 after' );
+	ok( ! -f 'W/tmp/cache/G1/G2/177_377', 'clean_cache: 177_377 after' );
+	ok(   -f 'W/tmp/cache/G1/G2/177_777', 'clean_cache: 177_777 after' );
+	ok( ! -f 'W/tmp/cache/G1/G2/155_255', 'clean_cache: 155_255 after' );
+	return ;
+}
+
+sub tests_clean_cache_2 {
+
+	ok( ( not -d  'W/tmp/cache/G1/G2' or rmtree( 'W/tmp/cache/G1/G2' )), 'clean_cache_2: rmtree W/tmp/cache/G1/G2' ) ;
+	ok( mkpath( 'W/tmp/cache/G1/G2' ), 'clean_cache_2: mkpath W/tmp/cache/G1/G2' ) ;
+
+	my @test_files_cache = ( qw(
+	W/tmp/cache/G1/G2/100_200
+	W/tmp/cache/G1/G2/101_201
+	W/tmp/cache/G1/G2/120_220
+	W/tmp/cache/G1/G2/142_242
+	W/tmp/cache/G1/G2/143_243
+	W/tmp/cache/G1/G2/177_277
+	W/tmp/cache/G1/G2/177_377
+	W/tmp/cache/G1/G2/177_777
+	W/tmp/cache/G1/G2/155_255
+	) ) ;
+	ok( touch(@test_files_cache), 'clean_cache_2: touch W/tmp/cache/G1/G2/...' ) ;
+
+	ok( -f 'W/tmp/cache/G1/G2/100_200', 'clean_cache_2: 100_200 before' );
+	ok( -f 'W/tmp/cache/G1/G2/142_242', 'clean_cache_2: 142_242 before' );
+	ok( -f 'W/tmp/cache/G1/G2/177_277', 'clean_cache_2: 177_277 before' );
+	ok( -f 'W/tmp/cache/G1/G2/177_377', 'clean_cache_2: 177_377 before' );
+	ok( -f 'W/tmp/cache/G1/G2/177_777', 'clean_cache_2: 177_777 before' );
+	ok( -f 'W/tmp/cache/G1/G2/155_255', 'clean_cache_2: 155_255 before' );
+
+	my $cache = {
+		142 => 242,
+		177 => 777,
+	} ;
+
+        my $all_1 = {
+                $NUMBER_100 => q{},
+                142 => q{},
+                177 => q{},
+        } ;
+
+        my $all_2 = {
+                200 => q{},
+                242 => q{},
+                777 => q{},
+        } ;
+
+
+
+	ok( clean_cache( \@test_files_cache, $cache, $all_1, $all_2 ), 'clean_cache_2: ' ) ;
+
+	ok(   -f 'W/tmp/cache/G1/G2/100_200', 'clean_cache_2: 100_200 after' );
+	ok(   -f 'W/tmp/cache/G1/G2/142_242', 'clean_cache_2: 142_242 after' );
+	ok( ! -f 'W/tmp/cache/G1/G2/177_277', 'clean_cache_2: 177_277 after' );
+	ok( ! -f 'W/tmp/cache/G1/G2/177_377', 'clean_cache_2: 177_377 after' );
+	ok(   -f 'W/tmp/cache/G1/G2/177_777', 'clean_cache_2: 177_777 after' );
+	ok( ! -f 'W/tmp/cache/G1/G2/155_255', 'clean_cache_2: 155_255 after' );
+	return ;
+}
+
+
+
+sub tests_mkpath {
+
+	ok( 1 == 1, 'tests_mkpath: 1 == 1' ) ;
+
+	SKIP: {
+		skip( 'Tests only for Unix', 2   ) if ( 'MSWin32' eq $OSNAME ) ;
+		my $long_path_unix = '123456789/' x 30 ;
+		ok( (-d "W/tmp/tests/long/$long_path_unix" or  mkpath( "W/tmp/tests/long/$long_path_unix" ) ), 'tests_mkpath: mkpath > 300 char' ) ;
+		ok( (-d "W/tmp/tests/long/$long_path_unix" and rmtree( 'W/tmp/tests/long/' ) ), 'tests_mkpath: rmtree > 300 char' ) ;
+        } ;
+
+	SKIP: {
+		skip( 'Tests only for MSWin32', 6  ) if ( 'MSWin32' ne $OSNAME ) ;
+		my $long_path_2_prefix =  "$tmpdir\\imapsync_tests" || '\\\?\\E:\\TEMP\\imapsync_tests'  ;
+		myprint( "long_path_2_prefix: $long_path_2_prefix\n"  ) ;
+
+		my $long_path_2   = $long_path_2_prefix . '\\' . '123456789\\' x 10 . 'END' ;
+		my $long_path_300 = $long_path_2_prefix . '\\' . '123456789\\' x 30 . 'END' ;
+
+		myprint( "$long_path_2\n"  ) ;
+
+		#ok( ( -d $long_path_2_prefix and rmtree( $long_path_2_prefix ) ), 'tests_mkpath: rmtree > 200 char' ) ;
+		#ok( ( -d $long_path_2_prefix or mkpath( "\\\\\?\\E:\\\\TEMP\\imapsync_tests" ) ), 'tests_mkpath: -d  small path 1' ) ;
+
+		ok( ( -d $long_path_2_prefix or mkpath( $long_path_2_prefix ) ), 'tests_mkpath: -d mkpath small path' ) ;
+		ok( ( -d $long_path_2_prefix ), 'tests_mkpath: -d mkpath small path done' ) ;
+		ok( ( -d $long_path_2        or mkpath( $long_path_2 ) ),        'tests_mkpath: mkpath > 200 char' ) ;
+		ok( ( -d $long_path_2 ), 'tests_mkpath: -d mkpath > 200 char done' ) ;
+		ok( ( -d $long_path_2_prefix and rmtree( $long_path_2_prefix ) ), 'tests_mkpath: rmtree > 200 char' ) ;
+		ok( (! -d $long_path_2_prefix ), 'tests_mkpath: ! -d rmtree done' ) ;
+
+		myprint( "$long_path_300\n"  ) ;
+		# This one just kill the whole process without a whisper:
+		#ok( ( -d $long_path_300        or mkpath( $long_path_300 ) ),        'tests_mkpath: mkpath fails > 300 char' ) ;
+		#ok( ( -d $long_path_300 and rmtree( $long_path_300 ) ), 'tests_mkpath: rmtree \ > 300 char' ) ;
+	} ;
+
+	return 1 ;
+}
+
+sub tests_touch {
+
+	ok( (-d 'W/tmp/tests/' or  mkpath( 'W/tmp/tests/' )), 'tests_touch: mkpath W/tmp/tests/' ) ;
+	ok( 1 == touch( 'W/tmp/tests/lala'), 'tests_touch: W/tmp/tests/lala') ;
+	ok( 1 == touch( 'W/tmp/tests/\y'), 'tests_touch: W/tmp/tests/\y') ;
+	ok( 0 == touch( '/no/no/no/aaa'), 'tests_touch: not /aaa') ;
+	ok( 1 == touch( 'W/tmp/tests/lili', 'W/tmp/tests/lolo'), 'tests_touch: 2 files') ;
+	ok( 0 == touch( 'W/tmp/tests/\y', '/no/no/aaa'), 'tests_touch: 2 files, 1 fails' ) ;
+	return ;
+}
+
+
+sub touch {
+	my @files = @_ ;
+	my $failures = 0 ;
+
+	foreach my $file ( @files ) {
+		my  $fh = IO::File->new ;
+		if ( $fh->open(">> $file" ) ) {
+			$fh->close ;
+		}else{
+                	myprint( "Could not open file $file in write/append mode\n"  ) ;
+                	$failures++ ;
+                }
+	}
+	return( ! $failures );
+}
+
+
+sub tests_tmpdir_has_colon_bug {
+
+	ok( 0 == tmpdir_has_colon_bug( q{} ),        'tmpdir_has_colon_bug: ' ) ;
+	ok( 0 == tmpdir_has_colon_bug( '/tmp' ),    'tmpdir_has_colon_bug: /tmp' ) ;
+	ok( 1 == tmpdir_has_colon_bug( 'C:' ),      'tmpdir_has_colon_bug: C:' ) ;
+	ok( 1 == tmpdir_has_colon_bug( 'C:\temp' ), 'tmpdir_has_colon_bug: C:\temp' ) ;
+
+        return( 0 ) ;
+}
+
+sub tmpdir_has_colon_bug {
+	my $path = shift ;
+
+	my $path_filtered = filter_forbidden_characters( $path ) ;
+	if ( $path_filtered ne $path ) {
+        	( -d $path_filtered ) and myprint( "Path $path was previously mistakely changed to $path_filtered\n"  ) ;
+        	return( 1 ) ;
+        }
+        return( 0 ) ;
+}
+
+sub tmpdir_fix_colon_bug {
+
+        my $err = 0 ;
+        if ( not (-d $tmpdir and -r _ and -w _) ) {
+                myprint( "tmpdir $tmpdir is not valid\n"  ) ;
+                return( 0 ) ;
+        }
+        my $cachedir_new = "$tmpdir/imapsync_cache" ;
+
+        if ( not tmpdir_has_colon_bug( $cachedir_new ) ) { return( 0 ) } ;
+
+        # check if old cache directory already exists
+        my $cachedir_old = filter_forbidden_characters( $cachedir_new ) ;
+        if ( not ( -d $cachedir_old ) ) {
+                myprint( "Old cache directory $cachedir_new no exists, nothing to do\n"  ) ;
+                return( 1 ) ;
+        }
+        # check if new cache directory already exists
+        if ( -d $cachedir_new ) {
+                myprint( "New fixed cache directory $cachedir_new already exists, not moving the old one $cachedir_old. Fix this manually.\n"  ) ;
+                return( 0 ) ;
+        }else{
+                # move the old one to the new place
+                myprint( "Moving $cachedir_old to $cachedir_new Do not interrupt this task.\n"  ) ;
+                File::Copy::Recursive::rmove( $cachedir_old, $cachedir_new )
+                or do {
+                        myprint( "Could not move $cachedir_old to $cachedir_new\n"  ) ;
+                        $err++ ;
+                } ;
+                # check it succeeded
+                if ( -d $cachedir_new and -r _ and -w _ ) {
+                        myprint( "New fixed cache directory $cachedir_new ok\n"  ) ;
+                }else{
+                        myprint( "New fixed cache directory $cachedir_new does not exist\n"  ) ;
+                        $err++ ;
+                }
+                if ( -d $cachedir_old ) {
+                        myprint( "Old cache directory $cachedir_old still exists\n"  ) ;
+                        $err++ ;
+                }else{
+                        myprint( "Old cache directory $cachedir_old successfuly moved\n"  ) ;
+                }
+        }
+        return( not $err ) ;
+}
+
+
+sub tests_cache_folder {
+
+	ok( '/path/fold1/fold2' eq cache_folder( q{}, '/path', 'fold1', 'fold2'), 'cache_folder: /path, fold1, fold2 -> /path/fold1/fold2' ) ;
+	ok( '/pa_th/fold1/fold2' eq cache_folder( q{}, '/pa*th', 'fold1', 'fold2'), 'cache_folder: /pa*th, fold1, fold2 -> /path/fold1/fold2' ) ;
+	ok( '/_p_a__th/fol_d1/fold2' eq cache_folder( q{}, '/>p<a|*th', 'fol*d1', 'fold2'), 'cache_folder: />p<a|*th, fol*d1, fold2 -> /path/fol_d1/fold2' ) ;
+
+	ok( 'D:/path/fold1/fold2' eq cache_folder( 'D:', '/path', 'fold1', 'fold2'), 'cache_folder: /path, fold1, fold2 -> /path/fold1/fold2' ) ;
+	ok( 'D:/pa_th/fold1/fold2' eq cache_folder( 'D:', '/pa*th', 'fold1', 'fold2'), 'cache_folder: /pa*th, fold1, fold2 -> /path/fold1/fold2' ) ;
+	ok( 'D:/_p_a__th/fol_d1/fold2' eq cache_folder( 'D:', '/>p<a|*th', 'fol*d1', 'fold2'), 'cache_folder: />p<a|*th, fol*d1, fold2 -> /path/fol_d1/fold2' ) ;
+	ok( '//' eq cache_folder( q{}, q{}, q{}, q{}), 'cache_folder:  -> //' ) ;
+	ok( '//_______' eq cache_folder( q{}, q{}, q{}, '*|?:"<>'), 'cache_folder: *|?:"<> -> //_______' ) ;
+	return ;
+}
+
+sub cache_folder {
+	my( $cache_base, $cache_dir, $h1_fold, $h2_fold ) = @_ ;
+
+	my $sep_1 = $h1_sep || '/';
+	my $sep_2 = $h2_sep || '/';
+
+	#myprint( "$cache_dir h1_fold $h1_fold sep1 $sep_1 h2_fold $h2_fold sep2 $sep_2\n" ) ;
+	$h1_fold = convert_sep_to_slash( $h1_fold, $sep_1 ) ;
+	$h2_fold = convert_sep_to_slash( $h2_fold, $sep_2 ) ;
+
+        my $cache_folder = "$cache_base" . filter_forbidden_characters( "$cache_dir/$h1_fold/$h2_fold" ) ;
+	#myprint( "cache_folder [$cache_folder]\n"  ) ;
+        return( $cache_folder ) ;
+}
+
+sub filter_forbidden_characters  {
+	my $string = shift ;
+
+        if ( 'MSWin32' eq $OSNAME ) {
+        	# Move trailing whitespace to _ " a b /c d " -> " a b_/c d_"
+        	$string =~ s{\ (/|$)}{_$1}xg ;
+        }
+        $string =~ s{[\Q*|?:"<>\E]}{_}xg ;
+        #myprint( "[$string]\n"  ) ;
+	return( $string ) ;
+}
+
+sub tests_filter_forbidden_characters  {
+
+	ok( 'a_b' eq filter_forbidden_characters( 'a_b' ), 'filter_forbidden_characters: a_b -> a_b' ) ;
+	ok( 'a_b' eq filter_forbidden_characters( 'a*b' ), 'filter_forbidden_characters: a*b -> a_b' ) ;
+	ok( 'a_b' eq filter_forbidden_characters( 'a|b' ), 'filter_forbidden_characters: a|b -> a_b' ) ;
+	ok( 'a_b' eq filter_forbidden_characters( 'a?b' ), 'filter_forbidden_characters: a?b -> a_b' ) ;
+	ok( 'a_______b' eq filter_forbidden_characters( 'a*|?:"<>b' ), 'filter_forbidden_characters: a*|?:"<>b -> a_______b' ) ;
+
+	SKIP: {
+		skip( 'Not on MSWin32', 1 ) if ( 'MSWin32' eq $OSNAME ) ;
+		ok( ( 'a b ' eq filter_forbidden_characters( 'a b ' ) ), 'filter_forbidden_characters: "a b " -> "a b "' ) ;
+	} ;
+
+	SKIP: {
+		skip( 'Only on MSWin32', 2 ) if ( 'MSWin32' ne $OSNAME ) ;
+		ok( ( ' a b_' eq filter_forbidden_characters( ' a b ' ) ), 'filter_forbidden_characters: "a b " -> "a b_"' ) ;
+		ok( ( ' a b_/ c d_' eq filter_forbidden_characters( ' a b / c d ' ) ), 'filter_forbidden_characters: " a b / c d " -> "a b_/ c d_"' ) ;
+        } ;
+
+	return ;
+}
+
+sub convert_sep_to_slash {
+	my ( $folder, $sep ) = @_ ;
+
+	$folder =~ s{\Q$sep\E}{/}xg ;
+	return( $folder ) ;
+}
+
+sub tests_convert_sep_to_slash {
+
+	ok(q{} eq convert_sep_to_slash(q{}, '/'), 'convert_sep_to_slash: no folder');
+	ok('INBOX' eq convert_sep_to_slash('INBOX', '/'), 'convert_sep_to_slash: INBOX');
+	ok('INBOX/foo' eq convert_sep_to_slash('INBOX/foo', '/'), 'convert_sep_to_slash: INBOX/foo');
+	ok('INBOX/foo' eq convert_sep_to_slash('INBOX_foo', '_'), 'convert_sep_to_slash: INBOX_foo');
+	ok('INBOX/foo/zob' eq convert_sep_to_slash('INBOX_foo_zob', '_'), 'convert_sep_to_slash: INBOX_foo_zob');
+	ok('INBOX/foo' eq convert_sep_to_slash('INBOX.foo', '.'), 'convert_sep_to_slash: INBOX.foo');
+	ok('INBOX/foo/hi' eq convert_sep_to_slash('INBOX.foo.hi', '.'), 'convert_sep_to_slash: INBOX.foo.hi');
+	return ;
+}
+
+
+sub tests_regexmess {
+
+	ok( 'blabla' eq regexmess( 'blabla' ), 'regexmess, no regexmess, nothing to do' ) ;
+
+	@regexmess = ( 'lalala' ) ;
+	ok( not( defined regexmess( 'popopo' ) ), 'regexmess, bad regex lalala' ) ;
+
+	@regexmess = ( 's/p/Z/g' ) ;
+	ok( 'ZoZoZo' eq regexmess( 'popopo' ), 'regexmess, s/p/Z/g' ) ;
+
+	@regexmess = ( 's{c}{C}gxms' ) ;
+	ok("H1: abC\nH2: Cde\n\nBody abC"
+		   eq regexmess( "H1: abc\nH2: cde\n\nBody abc"),
+	   'regexmess, c->C');
+
+	@regexmess = ( 's{\AFrom\ }{From:}gxms' ) ;
+	ok(          q{}
+	eq regexmess(q{}),
+	'From mbox 1 add colon blank');
+
+	ok(          'From:<[email protected]>'
+	eq regexmess('From <[email protected]>'),
+	'From mbox 2 add colo');
+
+	ok(          "\n" . 'From <[email protected]>'
+	eq regexmess("\n" . 'From <[email protected]>'),
+	'From mbox 3 add colo') ;
+
+	ok(          "From: zzz\n" . 'From <[email protected]>'
+	eq regexmess("From  zzz\n" . 'From <[email protected]>'),
+	'From mbox 4 add colo') ;
+
+	@regexmess = ( 's{\AFrom\ [^\n]*(\n)?}{}gxms' ) ;
+	ok(          q{}
+	eq regexmess(q{}),
+	'From mbox 1 remove, blank');
+
+	ok(          q{}
+	eq regexmess('From <[email protected]>'),
+	'From mbox 2 remove');
+
+	ok(          "\n" . 'From <[email protected]>'
+	eq regexmess("\n" . 'From <[email protected]>'),
+	'From mbox 3 remove');
+
+	#myprint( "[", regexmess("From  zzz\n" . 'From <[email protected]>'), "]" ) ;
+	ok(          q{}            . 'From <[email protected]>'
+	eq regexmess("From  zzz\n" . 'From <[email protected]>'),
+	'From mbox 4 remove');
+
+
+	ok(
+<<'EOM'
+Date: Sat, 10 Jul 2010 05:34:45 -0700
+From:<[email protected]>
+
+Hello,
+Bye.
+EOM
+	eq regexmess(
+<<'EOM'
+From  zzz
+Date: Sat, 10 Jul 2010 05:34:45 -0700
+From:<[email protected]>
+
+Hello,
+Bye.
+EOM
+), 'From mbox 5 remove');
+
+
+@regexmess = ( 's{\A((?:[^\n]+\n)+|)^Disposition-Notification-To:[^\n]*\n(\r?\n|.*\n\r?\n)}{$1$2}xms' ) ; # SUPER SUPER BEST!
+	ok(
+<<'EOM'
+Date: Sat, 10 Jul 2010 05:34:45 -0700
+From:<[email protected]>
+
+Hello,
+Bye.
+EOM
+	eq regexmess(
+<<'EOM'
+Date: Sat, 10 Jul 2010 05:34:45 -0700
+Disposition-Notification-To: Gilles LAMIRAL <[email protected]>
+From:<[email protected]>
+
+Hello,
+Bye.
+EOM
+	),
+	'regexmess: 1 Delete header Disposition-Notification-To:');
+
+	ok(
+<<'EOM'
+Date: Sat, 10 Jul 2010 05:34:45 -0700
+From:<[email protected]>
+
+Hello,
+Bye.
+EOM
+	eq regexmess(
+<<'EOM'
+Date: Sat, 10 Jul 2010 05:34:45 -0700
+From:<[email protected]>
+Disposition-Notification-To: Gilles LAMIRAL <[email protected]>
+
+Hello,
+Bye.
+EOM
+),
+	'regexmess: 2 Delete header Disposition-Notification-To:');
+
+	ok(
+<<'EOM'
+Date: Sat, 10 Jul 2010 05:34:45 -0700
+From:<[email protected]>
+
+Hello,
+Bye.
+EOM
+	eq regexmess(
+<<'EOM'
+Disposition-Notification-To: Gilles LAMIRAL <[email protected]>
+Date: Sat, 10 Jul 2010 05:34:45 -0700
+From:<[email protected]>
+
+Hello,
+Bye.
+EOM
+),
+	'regexmess: 3 Delete header Disposition-Notification-To:');
+
+	ok(
+<<'EOM'
+Date: Sat, 10 Jul 2010 05:34:45 -0700
+From:<[email protected]>
+
+Disposition-Notification-To: Gilles LAMIRAL <[email protected]>
+Bye.
+EOM
+	eq regexmess(
+<<'EOM'
+Disposition-Notification-To: Gilles LAMIRAL <[email protected]>
+Date: Sat, 10 Jul 2010 05:34:45 -0700
+From:<[email protected]>
+
+Disposition-Notification-To: Gilles LAMIRAL <[email protected]>
+Bye.
+EOM
+),
+	'regexmess: 4 Delete header Disposition-Notification-To:');
+
+
+	ok(
+<<'EOM'
+Date: Sat, 10 Jul 2010 05:34:45 -0700
+From:<[email protected]>
+
+Disposition-Notification-To: Gilles LAMIRAL <[email protected]>
+Bye.
+EOM
+	eq regexmess(
+<<'EOM'
+Date: Sat, 10 Jul 2010 05:34:45 -0700
+From:<[email protected]>
+
+Disposition-Notification-To: Gilles LAMIRAL <[email protected]>
+Bye.
+EOM
+),
+	'regexmess: 5 Delete header Disposition-Notification-To:');
+
+
+ok(
+<<'EOM'
+Date: Sat, 10 Jul 2010 05:34:45 -0700
+From:<[email protected]>
+
+Hello,
+Disposition-Notification-To: Gilles LAMIRAL <[email protected]>
+Bye.
+EOM
+	eq regexmess(
+<<'EOM'
+Date: Sat, 10 Jul 2010 05:34:45 -0700
+From:<[email protected]>
+
+Hello,
+Disposition-Notification-To: Gilles LAMIRAL <[email protected]>
+Bye.
+EOM
+),
+	'regexmess: 6 Delete header Disposition-Notification-To:');
+
+ok(
+<<'EOM'
+Date: Sat, 10 Jul 2010 05:34:45 -0700
+From:<[email protected]>
+
+Hello,
+Disposition-Notification-To: Gilles LAMIRAL <[email protected]>
+
+Bye.
+EOM
+	eq regexmess(
+<<'EOM'
+Date: Sat, 10 Jul 2010 05:34:45 -0700
+From:<[email protected]>
+
+Hello,
+Disposition-Notification-To: Gilles LAMIRAL <[email protected]>
+
+Bye.
+EOM
+),
+	'regexmess: 7 Delete header Disposition-Notification-To:');
+
+
+ok(
+<<'EOM'
+Date: Sat, 10 Jul 2010 05:34:45 -0700
+From:<[email protected]>
+
+Hello,
+Bye.
+EOM
+	eq regexmess(
+<<'EOM'
+Date: Sat, 10 Jul 2010 05:34:45 -0700
+From:<[email protected]>
+
+Hello,
+Bye.
+EOM
+),
+	'regexmess: 8 Delete header Disposition-Notification-To:');
+
+
+ok(
+<<'EOM'
+Date: Sat, 10 Jul 2010 05:34:45 -0700
+From:<[email protected]>
+
+Hello,
+Disposition-Notification-To: Gilles LAMIRAL <[email protected]>
+Bye.
+EOM
+	eq regexmess(
+<<'EOM'
+Date: Sat, 10 Jul 2010 05:34:45 -0700
+From:<[email protected]>
+
+Hello,
+Disposition-Notification-To: Gilles LAMIRAL <[email protected]>
+Bye.
+EOM
+),
+	'regexmess: 9 Delete header Disposition-Notification-To:');
+
+
+
+ok(
+<<'EOM'
+Date: Sat, 10 Jul 2010 05:34:45 -0700
+From:<[email protected]>
+
+Hello,
+Disposition-Notification-To: Gilles LAMIRAL <[email protected]>
+
+
+Bye.
+EOM
+	eq regexmess(
+<<'EOM'
+Date: Sat, 10 Jul 2010 05:34:45 -0700
+From:<[email protected]>
+
+Hello,
+Disposition-Notification-To: Gilles LAMIRAL <[email protected]>
+
+
+Bye.
+EOM
+),
+	'regexmess: 10 Delete header Disposition-Notification-To:');
+
+ok(
+<<'EOM'
+Date: Sat, 10 Jul 2010 05:34:45 -0700
+From:<[email protected]>
+
+Hello,
+
+Disposition-Notification-To: Gilles LAMIRAL <[email protected]>
+
+Bye.
+EOM
+	eq regexmess(
+<<'EOM'
+Date: Sat, 10 Jul 2010 05:34:45 -0700
+From:<[email protected]>
+
+Hello,
+
+Disposition-Notification-To: Gilles LAMIRAL <[email protected]>
+
+Bye.
+EOM
+),
+	'regexmess: 11 Delete header Disposition-Notification-To:');
+
+ok(
+<<'EOM'
+Date: Sat, 10 Jul 2010 05:34:45 -0700
+From:<[email protected]>
+
+Hello,
+
+Disposition-Notification-To: Gilles LAMIRAL <[email protected]>
+
+Disposition-Notification-To: Gilles LAMIRAL <[email protected]>
+
+Bye.
+EOM
+	eq regexmess(
+<<'EOM'
+Date: Sat, 10 Jul 2010 05:34:45 -0700
+From:<[email protected]>
+
+Hello,
+
+Disposition-Notification-To: Gilles LAMIRAL <[email protected]>
+
+Disposition-Notification-To: Gilles LAMIRAL <[email protected]>
+
+Bye.
+EOM
+),
+	'regexmess: 12 Delete header Disposition-Notification-To:');
+
+
+@regexmess = ( 's{\A(.*?(?! ^$))^Disposition-Notification-To:(.*?)$}{$1X-Disposition-Notification-To:$2}igxms' ) ; # BAD!
+@regexmess = ( 's{\A((?:[^\n]+\n)+|)(^Disposition-Notification-To:[^\n]*\n)(\r?\n|.*\n\r?\n)}{$1X-$2$3}ims' ) ;
+
+
+ok(
+<<'EOM'
+Date: Sat, 10 Jul 2010 05:34:45 -0700
+From:<[email protected]>
+
+Hello,
+
+Disposition-Notification-To: Gilles LAMIRAL <[email protected]>
+
+Disposition-Notification-To: Gilles LAMIRAL <[email protected]>
+
+Bye.
+EOM
+	eq regexmess(
+<<'EOM'
+Date: Sat, 10 Jul 2010 05:34:45 -0700
+From:<[email protected]>
+
+Hello,
+
+Disposition-Notification-To: Gilles LAMIRAL <[email protected]>
+
+Disposition-Notification-To: Gilles LAMIRAL <[email protected]>
+
+Bye.
+EOM
+),
+	'regexmess: 13 Delete header Disposition-Notification-To:');
+
+ok(
+<<'EOM'
+Date: Sat, 10 Jul 2010 05:34:45 -0700
+X-Disposition-Notification-To: Gilles LAMIRAL <[email protected]>
+From:<[email protected]>
+
+Hello,
+
+Disposition-Notification-To: Gilles LAMIRAL <[email protected]>
+
+Disposition-Notification-To: Gilles LAMIRAL <[email protected]>
+
+Bye.
+EOM
+	eq regexmess(
+<<'EOM'
+Date: Sat, 10 Jul 2010 05:34:45 -0700
+Disposition-Notification-To: Gilles LAMIRAL <[email protected]>
+From:<[email protected]>
+
+Hello,
+
+Disposition-Notification-To: Gilles LAMIRAL <[email protected]>
+
+Disposition-Notification-To: Gilles LAMIRAL <[email protected]>
+
+Bye.
+EOM
+),
+	'regexmess: 14 Delete header Disposition-Notification-To:');
+
+ok(
+<<'EOM'
+Date: Sat, 10 Jul 2010 05:34:45 -0700
+X-Disposition-Notification-To: Gilles LAMIRAL <[email protected]>
+From:<[email protected]>
+
+Hello,
+
+Bye.
+EOM
+	eq regexmess(
+<<'EOM'
+Date: Sat, 10 Jul 2010 05:34:45 -0700
+Disposition-Notification-To: Gilles LAMIRAL <[email protected]>
+From:<[email protected]>
+
+Hello,
+
+Bye.
+EOM
+),
+	'regexmess: 15 Delete header Disposition-Notification-To:');
+
+
+ok(
+<<'EOM'
+Date: Sat, 10 Jul 2010 05:34:45 -0700
+From:<[email protected]>
+X-Disposition-Notification-To: Gilles LAMIRAL <[email protected]>
+
+Hello,
+
+Bye.
+EOM
+	eq regexmess(
+<<'EOM'
+Date: Sat, 10 Jul 2010 05:34:45 -0700
+From:<[email protected]>
+Disposition-Notification-To: Gilles LAMIRAL <[email protected]>
+
+Hello,
+
+Bye.
+EOM
+),
+	'regexmess: 16 Delete header Disposition-Notification-To:');
+
+ok(
+<<'EOM'
+X-Disposition-Notification-To: Gilles LAMIRAL <[email protected]>
+Date: Sat, 10 Jul 2010 05:34:45 -0700
+From:<[email protected]>
+
+Hello,
+
+Bye.
+EOM
+	eq regexmess(
+<<'EOM'
+Disposition-Notification-To: Gilles LAMIRAL <[email protected]>
+Date: Sat, 10 Jul 2010 05:34:45 -0700
+From:<[email protected]>
+
+Hello,
+
+Bye.
+EOM
+),
+	'regexmess: 17 Delete header Disposition-Notification-To:');
+
+
+
+# regex to play with Date: from the FAQ
+#@regexmess = 's{\A(.*?(?! ^$))^Date:(.*?)$}{$1Date:$2\nX-Date:$2}gxms'
+
+return ;
+
+}
+
+sub regexmess {
+	my ( $string ) = @_ ;
+	foreach my $regexmess ( @regexmess ) {
+		$debug and myprint( "eval \$string =~ $regexmess\n"  ) ;
+		my $ret = eval "\$string =~ $regexmess ; 1" ;
+                #myprint( "eval [$ret]\n"  ) ;
+                if ( ( not $ret ) or $@ ) {
+			myprint( "Error: eval regexmess '$regexmess': $@"  ) ;
+                        return( undef ) ;
+                }
+	}
+        $debug and myprint( "$string\n" ) ;
+	return( $string ) ;
+}
+
+
+sub tests_skipmess {
+
+	ok( not( defined skipmess( 'blabla' ) ), 'skipmess, no skipmess, no skip' ) ;
+
+	@skipmess = ('[') ;
+	ok( not( defined skipmess( 'popopo' ) ), 'skipmess, bad regex [' ) ;
+
+	@skipmess = ('lalala') ;
+	ok( not( defined skipmess( 'popopo' ) ), 'skipmess, bad regex lalala' ) ;
+
+	@skipmess = ('/popopo/') ;
+	ok( 1 == skipmess( 'popopo' ), 'skipmess, popopo match regex /popopo/' ) ;
+
+	@skipmess = ('/popopo/') ;
+	ok( 0 == skipmess( 'rrrrrr' ), 'skipmess, rrrrrr does not match regex /popopo/' ) ;
+
+	@skipmess = ('m{^$}') ;
+	ok( 1 == skipmess( q{} ),    'skipmess: empty string yes' ) ;
+	ok( 0 == skipmess( 'Hi!' ), 'skipmess: empty string no' ) ;
+
+	@skipmess = ('m{i}') ;
+	ok( 1 == skipmess( 'Hi!' ),  'skipmess: i string yes' ) ;
+	ok( 0 == skipmess( 'Bye!' ), 'skipmess: i string no' ) ;
+
+	@skipmess = ('m{[\x80-\xff]}') ;
+	ok( 0 == skipmess( 'Hi!' ),  'skipmess: i 8bit no' ) ;
+	ok( 1 == skipmess( "\xff" ), 'skipmess: \xff 8bit yes' ) ;
+
+	@skipmess = ('m{A}', 'm{B}') ;
+	ok( 0 == skipmess( 'Hi!' ),  'skipmess: A or B no' ) ;
+	ok( 0 == skipmess( 'lala' ), 'skipmess: A or B no' ) ;
+	ok( 0 == skipmess( "\xff" ), 'skipmess: A or B no' ) ;
+	ok( 1 == skipmess( 'AB' ),   'skipmess: A or B yes' ) ;
+	ok( 1 == skipmess( 'BA' ),   'skipmess: A or B yes' ) ;
+	ok( 1 == skipmess( 'AA' ),   'skipmess: A or B yes' ) ;
+	ok( 1 == skipmess( 'Ok Bye' ), 'skipmess: A or B yes' ) ;
+
+
+	@skipmess = ( 'm#\A((?:[^\n]+\n)+|)^Content-Type: Message/Partial;[^\n]*\n(?:\n|.*\n\n)#ism' ) ; # SUPER BEST!
+
+
+
+	ok( 1 == skipmess(
+<<'EOM'
+Date: Sat, 10 Jul 2010 05:34:45 -0700
+Content-Type: Message/Partial; blabla
+From:<[email protected]>
+
+Hello!
+Bye.
+EOM
+),
+    'skipmess: 1 match Content-Type: Message/Partial' ) ;
+
+	ok( 0 == skipmess(
+<<'EOM'
+Date: Sat, 10 Jul 2010 05:34:45 -0700
+From:<[email protected]>
+
+Hello!
+Bye.
+EOM
+),
+    'skipmess: 2 not match Content-Type: Message/Partial' ) ;
+
+
+	ok( 1 == skipmess(
+<<'EOM'
+Date: Sat, 10 Jul 2010 05:34:45 -0700
+From:<[email protected]>
+Content-Type: Message/Partial; blabla
+
+Hello!
+Bye.
+EOM
+),
+    'skipmess: 3 match Content-Type: Message/Partial' ) ;
+
+	ok( 0 == skipmess(
+<<'EOM'
+Date: Sat, 10 Jul 2010 05:34:45 -0700
+From:<[email protected]>
+
+Hello!
+Content-Type: Message/Partial; blabla
+Bye.
+EOM
+),
+    'skipmess: 4 not match Content-Type: Message/Partial' ) ;
+
+
+	ok( 0 == skipmess(
+<<'EOM'
+Date: Sat, 10 Jul 2010 05:34:45 -0700
+From:<[email protected]>
+
+Hello!
+Content-Type: Message/Partial; blabla
+
+Bye.
+EOM
+),
+    'skipmess: 5 not match Content-Type: Message/Partial' ) ;
+
+
+	ok( 1 == skipmess(
+<<'EOM'
+Date: Sat, 10 Jul 2010 05:34:45 -0700
+Content-Type: Message/Partial; blabla
+From:<[email protected]>
+
+Hello!
+
+Content-Type: Message/Partial; blabla
+
+Bye.
+EOM
+),
+    'skipmess: 6 match Content-Type: Message/Partial' ) ;
+
+	ok( 1 == skipmess(
+<<'EOM'
+Date: Sat, 10 Jul 2010 05:34:45 -0700
+Content-Type: Message/Partial;
+From:<[email protected]>
+
+Hello!
+Bye.
+EOM
+),
+    'skipmess: 7 match Content-Type: Message/Partial' ) ;
+
+	ok( 1 == skipmess(
+<<'EOM'
+Date: Wed, 2 Jul 2014 02:26:40 +0000
+MIME-Version: 1.0
+Content-Type: message/partial;
+	id="TAN_U_P<1404267997.00007489ed17>";
+	number=3;
+	total=3
+
+6HQ6Hh3CdXj77qEGixerQ6zHx0OnQ/Cf5On4W0Y6vtU2crABZQtD46Hx1EOh8dDz4+OnTr1G
+
+
+Hello!
+Bye.
+EOM
+),
+    'skipmess: 8 match Content-Type: Message/Partial' ) ;
+
+
+ok( 1 == skipmess(
+<<'EOM'
+Return-Path: <[email protected]>
+Received: by lamiral.info (Postfix, from userid 1000)
+        id 21EB12443BF; Mon,  2 Mar 2015 15:38:35 +0100 (CET)
+Subject: test: aethaecohngiexao
+To: <[email protected]>
+X-Mailer: mail (GNU Mailutils 2.2)
+Message-Id: <[email protected]>
+Content-Type: message/partial;
+        id="TAN_U_P<1404267997.00007489ed17>";
+        number=3;
+        total=3
+Date: Mon,  2 Mar 2015 15:38:34 +0100 (CET)
+From: [email protected] (Gilles LAMIRAL)
+
+test: aethaecohngiexao
+EOM
+),
+    'skipmess: 9 match Content-Type: Message/Partial' ) ;
+
+ok( 1 == skipmess(
+<<'EOM'
+Date: Mon,  2 Mar 2015 15:38:34 +0100 (CET)
+From: [email protected] (Gilles LAMIRAL)
+Content-Type: message/partial;
+        id="TAN_U_P<1404267997.00007489ed17>";
+        number=3;
+        total=3
+
+test: aethaecohngiexao
+EOM
+. "lalala\n" x 3000000
+),
+    'skipmess: 10 match Content-Type: Message/Partial' ) ;
+
+ok( 0 == skipmess(
+<<'EOM'
+Date: Mon,  2 Mar 2015 15:38:34 +0100 (CET)
+From: [email protected] (Gilles LAMIRAL)
+
+test: aethaecohngiexao
+EOM
+. "lalala\n" x 3000000
+),
+    'skipmess: 11 match Content-Type: Message/Partial' ) ;
+
+
+ok( 0 == skipmess(
+<<"EOM"
+From: fff\r
+To: fff\r
+Subject: Testing imapsync --skipmess\r
+Date: Mon, 22 Aug 2011 08:40:20 +0800\r
+Mime-Version: 1.0\r
+Content-Type: text/plain; charset=iso-8859-1\r
+Content-Transfer-Encoding: 7bit\r
+\r
+EOM
+. qq{!#"$%&'()*+,-./0123456789:;<=>?\@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefg\r\n } x 32730
+),
+    'skipmess: 12 not match Content-Type: Message/Partial' ) ;
+        # Complex regular subexpression recursion limit (32766) exceeded with more lines
+        # exit;
+	return ;
+}
+
+sub skipmess {
+	my ( $string ) = @_ ;
+	my $match ;
+	#myprint( "$string\n"  ) ;
+	foreach my $skipmess ( @skipmess ) {
+		$debug and myprint( "eval \$match = \$string =~ $skipmess\n"  ) ;
+		my $ret = eval "\$match = \$string =~ $skipmess ; 1"  ;
+		#myprint( "eval [$ret]\n"  ) ;
+		$debug and myprint( "match [$match]\n"  ) ;
+		if ( ( not $ret ) or $@ ) {
+			myprint( "Error: eval skipmess '$skipmess': $@"  ) ;
+			return( undef ) ;
+		}
+		return( $match ) if ( $match ) ;
+	}
+	return( $match ) ;
+}
+
+
+
+
+sub tests_bytes_display_string {
+
+        is(    'NA', bytes_display_string(       ), 'bytes_display_string: no args => NA' ) ;
+        is(    'NA', bytes_display_string( undef ), 'bytes_display_string: undef   => NA' ) ;
+        is(    'NA', bytes_display_string( 'blabla' ), 'bytes_display_string: blabla   => NA' ) ;
+        
+	ok(    '0.000 KiB' eq bytes_display_string(       0 ), 'bytes_display_string:       0' ) ;
+	ok(    '0.001 KiB' eq bytes_display_string(       1 ), 'bytes_display_string:       1' ) ;
+	ok(    '0.010 KiB' eq bytes_display_string(      10 ), 'bytes_display_string:      10' ) ;
+	ok(    '1.000 MiB' eq bytes_display_string( 1048575 ), 'bytes_display_string: 1048575' ) ;
+	ok(    '1.000 MiB' eq bytes_display_string( 1048576 ), 'bytes_display_string: 1048576' ) ;
+
+	ok(    '1.000 GiB' eq bytes_display_string( 1073741823 ), 'bytes_display_string: 1073741823 ' ) ;
+	ok(    '1.000 GiB' eq bytes_display_string( 1073741824 ), 'bytes_display_string: 1073741824 ' ) ;
+
+	ok(    '1.000 TiB' eq bytes_display_string( 1099511627775 ), 'bytes_display_string: 1099511627775' ) ;
+	ok(    '1.000 TiB' eq bytes_display_string( 1099511627776 ), 'bytes_display_string: 1099511627776' ) ;
+
+	ok(    '1.000 PiB' eq bytes_display_string( 1125899906842623 ), 'bytes_display_string: 1125899906842623' ) ;
+	ok(    '1.000 PiB' eq bytes_display_string( 1125899906842624 ), 'bytes_display_string: 1125899906842624' ) ;
+
+	ok( '1024.000 PiB' eq bytes_display_string( 1152921504606846975 ), 'bytes_display_string: 1152921504606846975' ) ;
+	ok( '1024.000 PiB' eq bytes_display_string( 1152921504606846976 ), 'bytes_display_string: 1152921504606846976' ) ;
+
+	ok( '1048576.000 PiB' eq bytes_display_string( 1180591620717411303424 ), 'bytes_display_string: 1180591620717411303424' ) ;
+
+        #myprint(  bytes_display_string( 1180591620717411303424 ), "\n"  ) ;
+	return ;
+}
+
+sub bytes_display_string {
+	my ( $bytes ) = @_ ;
+
+	my $readable_value = q{} ;
+
+        if ( ! defined( $bytes ) ) {
+                return( 'NA' ) ;
+        }
+
+        if ( not match_number( $bytes ) ) {
+                return( 'NA' ) ;
+        }
+
+        
+
+	SWITCH: {
+        	if ( abs( $bytes ) < ( 1000 * $KIBI ) ) {
+        		$readable_value = mysprintf( '%.3f KiB', $bytes / $KIBI) ;
+                	last SWITCH ;
+        	}
+        	if ( abs( $bytes ) < ( 1000 * $KIBI * $KIBI ) ) {
+        		$readable_value = mysprintf( '%.3f MiB', $bytes / ($KIBI * $KIBI) ) ;
+        	        last SWITCH ;
+        	}
+        	if ( abs( $bytes ) < ( 1000 * $KIBI * $KIBI * $KIBI) ) {
+			$readable_value = mysprintf( '%.3f GiB', $bytes / ($KIBI * $KIBI * $KIBI) ) ;
+        	        last SWITCH ;
+        	}
+        	if ( abs( $bytes ) < ( 1000 * $KIBI * $KIBI * $KIBI * $KIBI) ) {
+			$readable_value = mysprintf( '%.3f TiB', $bytes / ($KIBI * $KIBI * $KIBI * $KIBI) ) ;
+        	        last SWITCH ;
+        	} else {
+			$readable_value = mysprintf( '%.3f PiB', $bytes / ($KIBI * $KIBI * $KIBI * $KIBI * $KIBI) ) ;
+        	}
+		# if you have exabytes (EiB) of email to transfer, you have too much email!
+	}
+        #myprint( "$bytes = $readable_value\n"  ) ;
+        return( $readable_value ) ;
+}
+
+sub stats {
+        my $sync_loc = shift ;
+
+        if ( ! $sync_loc->{stats} ) {
+                return ;
+        }
+        
+	$timeend = time ;
+	my $timediff = $timeend - $sync_loc->{timestart} ;
+
+	my $timeend_str   = localtime $timeend ;
+
+	my $memory_consumption = 0 ;
+        $memory_consumption = memory_consumption(  ) || 0 ;
+	my $memory_ratio = ($max_msg_size_in_bytes) ?
+		mysprintf('%.1f', $memory_consumption / $max_msg_size_in_bytes) : 'NA' ;
+
+	my $host1_reconnect_count = $imap1->Reconnect_counter() || 0 ;
+	my $host2_reconnect_count = $imap2->Reconnect_counter() || 0 ;
+
+	myprint(  "++++ Statistics\n"  ) ;
+	myprint(  "Transfer started on               : $timestart_str\n"  ) ;
+	myprint(  "Transfer ended on                 : $timeend_str\n"  ) ;
+	myprintf( "Transfer time                     : %.1f sec\n", $timediff ) ;
+	myprint(  "Folders synced                    : $h1_folders_wanted_ct/$h1_folders_wanted_nb synced\n"  ) ;
+	myprint(  "Messages transferred              : $nb_msg_transferred "  ) ;
+	myprint(  "(could be $nb_msg_skipped_dry_mode without dry mode)" ) if ( $dry ) ;
+	myprint(  "\n" ) ;
+	myprint(  "Messages skipped                  : $nb_msg_skipped\n"  ) ;
+	myprint(  "Messages found duplicate on host1 : $h1_nb_msg_duplicate\n"  ) ;
+	myprint(  "Messages found duplicate on host2 : $h2_nb_msg_duplicate\n"  ) ;
+	myprint(  "Messages void (noheader) on host1 : $h1_nb_msg_noheader\n"  ) ;
+	myprint(  "Messages void (noheader) on host2 : $h2_nb_msg_noheader\n"  ) ;
+	myprint(  "Messages deleted on host1         : $h1_nb_msg_deleted\n"  ) ;
+	myprint(  "Messages deleted on host2         : $h2_nb_msg_deleted\n"  ) ;
+        myprintf( "Total bytes transferred           : %s (%s)\n",
+                $total_bytes_transferred,
+                bytes_display_string( $total_bytes_transferred ) ) ;
+        myprintf( "Total bytes duplicate host1       : %s (%s)\n",
+                $h1_total_bytes_duplicate,
+                bytes_display_string( $h1_total_bytes_duplicate) ) ;
+        myprintf( "Total bytes duplicate host2       : %s (%s)\n",
+                $h2_total_bytes_duplicate,
+                bytes_display_string( $h2_total_bytes_duplicate) ) ;
+        myprintf( "Total bytes skipped               : %s (%s)\n",
+                $total_bytes_skipped,
+                bytes_display_string( $total_bytes_skipped ) ) ;
+        myprintf( "Total bytes error                 : %s (%s)\n",
+                $total_bytes_error,
+                bytes_display_string( $total_bytes_error ) ) ;
+	$timediff ||= 1 ; # No division per 0
+	myprintf("Message rate                      : %.1f messages/s\n", $nb_msg_transferred / $timediff ) ;
+	myprintf("Average bandwidth rate            : %.1f KiB/s\n", $total_bytes_transferred / $KIBI / $timediff ) ;
+	#myprint(  "Reconnections to host1            : $host1_reconnect_count\n"  ) ;
+	#myprint(  "Reconnections to host2            : $host2_reconnect_count\n"  ) ;
+	myprintf("Memory consumption                : %.1f MiB\n", $memory_consumption / $KIBI / $KIBI ) ;
+        myprintf("Biggest message                   : %s bytes (%s)\n",
+                $max_msg_size_in_bytes,
+                bytes_display_string( $max_msg_size_in_bytes) ) ;
+	myprint(  "Memory/biggest message ratio      : $memory_ratio\n"  ) ;
+        if ( $foldersizesatend and $foldersizes ) {
+        
+
+        my $nb_msg_start_diff = diff_or_NA( $h2_nb_msg_start, $h1_nb_msg_start ) ;
+        my $bytes_start_diff  = diff_or_NA( $h2_bytes_start,  $h1_bytes_start  ) ;
+        
+	myprintf("Start difference host2 - host1    : %s messages, %s bytes (%s)\n", $nb_msg_start_diff,
+                                                        $bytes_start_diff,
+                                                        bytes_display_string( $bytes_start_diff ) ) ;
+
+        my $nb_msg_end_diff = diff_or_NA( $h2_nb_msg_end, $h1_nb_msg_end ) ;
+        my $bytes_end_diff  = diff_or_NA( $h2_bytes_end,  $h1_bytes_end  ) ;
+        
+	myprintf("Final difference host2 - host1    : %s messages, %s bytes (%s)\n", $nb_msg_end_diff,
+                                                        $bytes_end_diff,
+                                                        bytes_display_string( $bytes_end_diff ) ) ;
+        }
+	myprint(  "Detected $sync->{nb_errors} errors\n\n"  ) ;
+
+	myprint(  $warn_release, "\n"  ) ;
+	myprint(  thank_author(  )  ) ;
+	return ;
+}
+
+sub diff_or_NA {
+        my( $n1, $n2 ) = @ARG ;
+        
+        if ( not defined $n1 or not defined $n2 ) {
+                return 'NA' ;
+        }
+        
+        if ( not match_number( $n1 ) 
+          or not match_number( $n2 ) ) {
+                 return 'NA' ;
+        }
+        
+        return( $n1 - $n2 ) ;
+}
+
+sub match_number {
+        my $n = shift @ARG ;
+        
+        if ( not defined $n ) {
+                return 0 ;
+        }
+        if ( $n =~  /[0-9]+\.?[0-9]?/ ) {
+                return 1 ;
+        }
+        else {
+                return 0 ;
+        }
+}
+
+
+sub tests_match_number {
+
+        is( 0, match_number(   ),        'match_number: no parameters => 0' ) ;
+        is( 0, match_number( undef ),    'match_number:         undef => 0' ) ;
+        is( 0, match_number( 'blabla' ), 'match_number:        blabla => 0' ) ;
+        is( 1, match_number( 0 ),        'match_number:             0 => 1' ) ;
+        is( 1, match_number( 1 ),        'match_number:             1 => 1' ) ;
+        is( 1, match_number( 1.0 ),      'match_number:           1.0 => 1' ) ;
+        is( 1, match_number( 0.0 ),      'match_number:           0.0 => 1' ) ;
+        return ;
+}
+
+
+
+sub tests_diff_or_NA {
+
+        is( 'NA', diff_or_NA(  ),             'diff_or_NA: no parameters => NA' ) ;
+        is( 'NA', diff_or_NA( undef ),        'diff_or_NA: undef         => NA' ) ;
+        is( 'NA', diff_or_NA( undef, undef ), 'diff_or_NA: undef  undef  => NA' ) ;
+        is( 'NA', diff_or_NA( undef, 1 ),     'diff_or_NA: undef  1      => NA' ) ;
+        is( 'NA', diff_or_NA( 1, undef ),     'diff_or_NA: 1      undef  => NA' ) ;
+        is( 'NA', diff_or_NA( 'blabla', 1 ),  'diff_or_NA: blabla 1      => NA' ) ;
+        is( 'NA', diff_or_NA( 1, 'blabla' ),  'diff_or_NA: 1      blabla => NA' ) ;
+        is( 0, diff_or_NA( 1, 1 ),            'diff_or_NA: 1      1      =>  0' ) ;
+        is( 1, diff_or_NA( 1, 0 ),            'diff_or_NA: 1      0      =>  1' ) ;
+        is( -1, diff_or_NA( 0, 1 ),           'diff_or_NA: 0      1      => -1' ) ;
+        is( 0, diff_or_NA( 1.0, 1 ),          'diff_or_NA: 1.0    1      =>  0' ) ;
+        is( 1, diff_or_NA( 1.0, 0 ),          'diff_or_NA: 1.0    0      =>  1' ) ;
+        is( -1, diff_or_NA( 0, 1.0 ),         'diff_or_NA: 0      1.0    => -1' ) ;
+        return ;
+}
+
+sub thank_author {
+	return( "Homepage: http://imapsync.lamiral.info/\n" ) ;
+}
+
+
+sub load_modules {
+
+	if ( $ssl1 or $ssl2 or $tls1 or $tls2) {
+        	# not yet a "use" statement
+        	require IO::Socket::SSL ;
+		if ( $sync->{inet4} ) {
+		        IO::Socket::SSL->import( 'inet4' ) ;
+		}
+		if ( $sync->{inet6} ) {
+		        IO::Socket::SSL->import( 'inet6' ) ;
+		}
+        }
+
+       if ( ( ( not( $password1 or $passfile1 ) )
+	   or (not ( $password2 or $passfile2 ) )
+            )
+	and ( not $help ) ) {
+        	# now a "use" statement
+        	#require Term::ReadKey ;
+        }
+
+	return ;
+}
+
+
+
+sub parse_header_msg {
+	my ( $imap, $m_uid, $s_heads, $s_fir, $side, $s_hash ) = @_ ;
+
+	my $head = $s_heads->{$m_uid} ;
+	my $headnum =  scalar keys  %{ $head }   ;
+	$debug and myprint( "$side uid $m_uid head nb pass one: ", $headnum, "\n"  ) ;
+
+	if ( ( ! $headnum ) and ( $wholeheaderifneeded ) ){
+		myprint( "$side uid $m_uid no header by parse_headers so taking whole header with BODY.PEEK[HEADER]\n"  ) ;
+		$imap->fetch($m_uid, 'BODY.PEEK[HEADER]' ) ;
+		my $whole_header = $imap->_transaction_literals ;
+
+                #myprint( $whole_header ) ;
+                $head = decompose_header( $whole_header ) ;
+
+                $headnum =  scalar  keys  %{ $head }   ;
+	        $debug and myprint( "$side uid $m_uid head nb pass two: ", $headnum, "\n" ) ;
+	}
+
+        #myprint( Data::Dumper->Dump( [ $head, \%useheader ] )  ) ;
+
+	my $headstr ;
+
+        $headstr = header_construct( $head, $side, $m_uid ) ;
+
+	if ( ( ! $headstr) and ( $addheader ) and ( $side eq 'Host1' ) ) {
+        	my $header = add_header( $m_uid ) ;
+		myprint( "Host1 uid $m_uid no header found so adding our own [$header]\n" ) ;
+		$headstr .= uc  $header  ;
+		$s_fir->{$m_uid}->{NO_HEADER} = 1;
+	}
+
+	return if ( ! $headstr ) ;
+
+	my $size  = $s_fir->{$m_uid}->{'RFC822.SIZE'} ;
+	my $flags = $s_fir->{$m_uid}->{'FLAGS'} ;
+	my $idate = $s_fir->{$m_uid}->{'INTERNALDATE'} ;
+	$size = length $headstr  unless ( $size ) ;
+	my $m_md5 = md5_base64( $headstr ) ;
+	$debug and myprint( "$side uid $m_uid sig $m_md5 size $size idate $idate\n"  ) ;
+	my $key ;
+        if ($skipsize) {
+                $key = "$m_md5";
+        }
+	else {
+                $key = "$m_md5:$size";
+        }
+	# 0 return code is used to identify duplicate message hash
+	return 0 if exists $s_hash->{"$key"};
+	$s_hash->{"$key"}{'5'} = $m_md5;
+	$s_hash->{"$key"}{'s'} = $size;
+	$s_hash->{"$key"}{'D'} = $idate;
+	$s_hash->{"$key"}{'F'} = $flags;
+	$s_hash->{"$key"}{'m'} = $m_uid;
+
+	return( 1 ) ;
+}
+
+sub header_construct {
+
+	my( $head, $side, $m_uid ) = @_ ;
+
+        my $headstr ;
+	foreach my $h ( sort keys  %{ $head }  ) {
+                next if ( not ( exists $useheader{ uc  $h  } )
+                      and ( not exists  $useheader{ 'ALL' } )
+                ) ;
+		foreach my $val ( sort @{$head->{$h}} ) {
+
+                        my $H = header_line_normalize( $h, $val ) ;
+
+			# show stuff in debug mode
+			$debug and myprint( "$side uid $m_uid header [$H]", "\n"  ) ;
+
+			if ($skipheader and $H =~ m/$skipheader/xi) {
+				$debug and myprint( "$side uid $m_uid skipping header [$H]\n"  ) ;
+				next ;
+			}
+			$headstr .= "$H" ;
+		}
+	}
+	return( $headstr ) ;
+}
+
+
+sub header_line_normalize {
+	my( $header_key,  $header_val ) = @_ ;
+
+        # no 8-bit data in headers !
+        $header_val =~ s/[\x80-\xff]/X/xog;
+
+        # change tabulations to space (Gmail bug on with "Received:" on multilines)
+        $header_val =~ s/\t/\ /xgo ;
+
+        # remove the first blanks ( dbmail bug? )
+        $header_val =~ s/^\s*//xo;
+
+        # remove the last blanks ( Gmail bug )
+        $header_val =~ s/\s*$//xo;
+
+        # remove successive blanks ( Mailenable does it )
+        $header_val =~ s/\s+/ /xgo;
+
+        # remove Message-Id value domain part ( Mailenable changes it )
+        if ( ( $messageidnodomain ) and ( 'MESSAGE-ID' eq uc  $header_key  ) ) { $header_val =~ s/^([^@]+).*$/$1/xo ; }
+
+        # and uppercase header line
+        # (dbmail and dovecot)
+
+        my $header_line = uc "$header_key: $header_val" ;
+
+	return( $header_line ) ;
+}
+
+sub tests_header_line_normalize {
+
+	ok( ': ' eq header_line_normalize( q{}, q{} ), 'header_line_normalize: empty args' ) ;
+	ok( 'HHH: VVV' eq header_line_normalize( 'hhh', 'vvv' ), 'header_line_normalize: hhh vvv ' ) ;
+	ok( 'HHH: VVV' eq header_line_normalize( 'hhh', '  vvv' ), 'header_line_normalize: remove first blancs' ) ;
+	ok( 'HHH: AA BB CCC D' eq header_line_normalize( 'hhh', 'aa  bb   ccc d' ), 'header_line_normalize: remove succesive blanks' ) ;
+	ok( 'HHH: AA BB CCC' eq header_line_normalize( 'hhh', 'aa  bb   ccc   ' ), 'header_line_normalize: remove last blanks' ) ;
+	ok( 'HHH: VVV XX YY' eq header_line_normalize( 'hhh', "vvv\t\txx\tyy" ), 'header_line_normalize: tabs' ) ;
+	ok( 'HHH: XABX' eq header_line_normalize( 'hhh', "\x80AB\xff" ), 'header_line_normalize: 8bit' ) ;
+
+	return ;
+}
+
+
+sub firstline {
+        # extract the first line of a file (without \n)
+
+        my( $file ) = @_ ;
+        my $line  = q{} ;
+        my $FILE ;
+        open $FILE, '<', $file or do {
+                myprint( "Error opening file $file : $!\n" ) ;
+                return ;
+        } ;
+        $line = <$FILE> || q{} ;
+        close $FILE ;
+        chomp $line ;
+        return $line ;
+}
+
+sub tests_firstline {
+        is( 1 , string_to_file( "blabla\n", 'tmp/firstline.txt' ), 'tests_firstline: put blabla in tmp/firstline.txt' ) ;
+        is( 'blabla' , firstline( 'tmp/firstline.txt' ), 'tests_firstline: get blabla from tmp/firstline.txt' ) ;
+        is( undef , firstline( 'tmp/noexist.txt' ), 'tests_firstline: get blabla from tmp/noexist.txt' ) ;
+        is( 1 , string_to_file( q{}, 'tmp/firstline2.txt' ), 'tests_firstline: put empty string in tmp/firstline2.txt' ) ;
+        is( q{} , firstline( 'tmp/firstline2.txt' ), 'tests_firstline: get empty string from tmp/firstline2.txt' ) ;
+        is( 1 , string_to_file( "\n", 'tmp/firstline3.txt' ), 'tests_firstline: put CR in tmp/firstline3.txt' ) ;
+        is( q{} , firstline( 'tmp/firstline3.txt' ), 'tests_firstline: get empty string from tmp/firstline3.txt' ) ;
+
+        return ;
+}
+
+
+sub file_to_string {
+	my( $file ) = @_ ;
+	my @string ;
+	open my $FILE, '<', $file or die_clean( "Error with file $file : $! " ) ;
+	@string = <$FILE> ;
+	close $FILE ;
+	return( join q{}, @string ) ;
+}
+
+
+sub string_to_file {
+	my( $string, $file ) = @_ ;
+	sysopen( FILE, $file, O_WRONLY|O_TRUNC|O_CREAT, 0600) or die_clean( "$! $file" ) ;
+	print FILE $string ;
+	close FILE ;
+	return 1 ;
+}
+
+q^
+This is a multiline comment.
+Based on David Carter discussion, to do:
+* Call parameters stay the same.
+* Now always "return( $string, $error )". Descriptions below.
+OK * Still    capture STDOUT via "1> $output_tmpfile" to finish in $string and "return( $string, $error )"
+OK * Now also capture STDERR via "2> $error_tmpfile"  to finish in $error  and "return( $string, $error )"
+OK * in case of CHILD_ERROR, return( undef, $error ) 
+  and print $error, with folder/UID/maybeSubject context,
+  on console and at the end with the final error listing. Count this as a sync error.
+* in case of good command, take final $string as is, unless void. In case $error with value then print it.
+* in case of good command and final $string empty, consider it like CHILD_ERROR =>
+  return( undef, $error ) and print $error, with folder/UID/maybeSubject context,
+  on console and at the end with the final error listing. Count this as a sync error. 
+^ if 0 ; # End of multiline comment.
+
+sub pipemess {
+	my ( $string, @commands ) = @_ ;
+	my $error = q{} ;
+        foreach my $command ( @commands ) {
+                my $input_tmpfile  = "$tmpdir/imapsync_tmp_file.$PROCESS_ID.inp.txt" ;
+                my $output_tmpfile = "$tmpdir/imapsync_tmp_file.$PROCESS_ID.out.txt" ;
+                my $error_tmpfile  = "$tmpdir/imapsync_tmp_file.$PROCESS_ID.err.txt" ;
+                string_to_file( $string, $input_tmpfile  ) ;
+                ` $command < $input_tmpfile 1> $output_tmpfile 2> $error_tmpfile ` ;
+                my $is_command_ko = $CHILD_ERROR ;
+                my $error_cmd = file_to_string( $error_tmpfile ) ;
+                chomp( $error_cmd ) ;
+		$string = file_to_string( $output_tmpfile ) ;
+                my $string_len = length( $string ) ;
+                unlink $input_tmpfile, $output_tmpfile, $error_tmpfile ;
+
+		if ( $is_command_ko or ( ! $string_len ) ) {
+			my $cmd_exit_value = $CHILD_ERROR >> 8 ;
+			my $cmd_end_signal = $CHILD_ERROR & 127 ;
+                        my $signal_log = ( $cmd_end_signal ) ? " signal $cmd_end_signal and" : q{} ;
+                        my $error_log = qq{Failure: --pipemess command "$command" ended with$signal_log "$string_len" characters exit value "$cmd_exit_value" and STDERR "$error_cmd"\n} ;
+			myprint( $error_log ) ;
+			if ( wantarray ) {
+                                return @{ [ undef, $error_log ] }
+                        }else{
+                                return ;
+                        }
+		}
+                if ( $error_cmd ) {
+                        $error .= qq{STDERR of --pipemess "$command": $error_cmd\n} ;
+                        myprint(  qq{STDERR of --pipemess "$command": $error_cmd\n} ) ;
+                }
+        }
+        #myprint( "[$string]\n"  ) ;
+        if ( wantarray ) {
+                return ( $string, $error ) ;
+        }else{
+                return $string ;
+        }
+}
+
+
+
+sub tests_pipemess {
+
+	SKIP: {
+                Readonly my $NB_WIN_tests_pipemess => 3 ;
+		skip( 'Not on MSWin32', $NB_WIN_tests_pipemess ) if ('MSWin32' ne $OSNAME) ;
+		# Windows
+		# "type" command does not accept redirection of STDIN with <
+		# "sort" does
+		ok( "nochange\n" eq pipemess( 'nochange', 'sort' ), 'pipemess: nearly no change by sort' ) ;
+		ok( "nochange2\n" eq pipemess( 'nochange2', qw( sort sort ) ), 'pipemess: nearly no change by sort,sort' ) ;
+		# command not found
+		#diag( 'Warning and failure about cacaprout are on purpose' ) ;
+		ok( ! defined( pipemess( q{}, 'cacaprout' ) ), 'pipemess: command not found' ) ;
+
+	} ;
+
+        my ( $stringT, $errorT ) ;
+
+	SKIP: {
+                Readonly my $NB_UNX_tests_pipemess => 25 ;
+		skip( 'Not on Unix', $NB_UNX_tests_pipemess ) if ('MSWin32' eq $OSNAME) ;
+		# Unix
+		ok( 'nochange' eq pipemess( 'nochange', 'cat' ), 'pipemess: no change by cat' ) ;
+
+		ok( 'nochange2' eq pipemess( 'nochange2', 'cat', 'cat' ), 'pipemess: no change by cat,cat' ) ;
+
+		ok( "     1\tnumberize\n" eq pipemess( "numberize\n", 'cat -n' ), 'pipemess: numberize by cat -n' ) ;
+		ok( "     1\tnumberize\n     2\tnumberize\n" eq pipemess( "numberize\nnumberize\n", 'cat -n' ), 'pipemess: numberize by cat -n' ) ;
+
+		ok( "A\nB\nC\n" eq pipemess( "A\nC\nB\n", 'sort' ), 'pipemess: sort' ) ;
+
+		# command not found
+		#diag( 'Warning and failure about cacaprout are on purpose' ) ;
+		is( undef, pipemess( q{}, 'cacaprout' ), 'pipemess: command not found' ) ;
+
+                # success with true but no output at all
+                is( undef, pipemess( q{blabla}, 'true' ), 'pipemess: true but no output' ) ;
+
+                # failure with false and no output at all
+                is( undef, pipemess( q{blabla}, 'false' ), 'pipemess: false and no output' ) ;
+
+		# Failure since pipemess is not a real pipe, so first cat wait for standard input
+		is( q{blabla}, pipemess( q{blabla}, '( cat|cat ) ' ), 'pipemess: ok by ( cat|cat )' ) ;
+
+
+                ( $stringT, $errorT ) = pipemess( 'nochange', 'cat' ) ;
+                is( $stringT, 'nochange', 'pipemess: list context, no change by cat, string' ) ;
+                is( $errorT, q{}, 'pipemess: list context, no change by cat, no error' ) ;
+                
+                ( $stringT, $errorT ) = pipemess( 'dontcare', 'true' ) ;
+                is( $stringT, undef, 'pipemess: list context, true but no output, string' ) ;
+                like( $errorT, qr{Failure: --pipemess command "true" ended with "0" characters exit value "0" and STDERR ""},  'pipemess: list context, true but no output, error' ) ;
+
+                ( $stringT, $errorT ) = pipemess( 'dontcare', 'false' ) ;
+                is( $stringT, undef, 'pipemess: list context, false and no output, string' ) ;
+                like( $errorT, qr{Failure: --pipemess command "false" ended with "0" characters exit value "1" and STDERR ""},  'pipemess: list context, false and no output, error' ) ;
+
+                ( $stringT, $errorT ) = pipemess( 'dontcare', 'echo -n blablabla' ) ;
+                is( $stringT, q{blablabla}, 'pipemess: list context, "echo -n blablabla", string' ) ;
+                is( $errorT, q{},  'pipemess: list context, "echo blablabla", error' ) ;
+
+                
+                ( $stringT, $errorT ) = pipemess( 'dontcare', '( echo -n blablabla 3>&1 1>&2 2>&3 )' ) ;
+                is( $stringT, undef, 'pipemess: list context, "no output STDERR blablabla", string' ) ;
+                like( $errorT,  qr{blablabla"$},  'pipemess: list context, "no output STDERR blablabla", error' ) ;
+
+
+                ( $stringT, $errorT ) = pipemess( 'dontcare', '( echo -n blablabla 3>&1 1>&2 2>&3 )', 'false' ) ;
+                is( $stringT, undef, 'pipemess: list context, "no output STDERR blablabla then false", string' ) ;
+                like( $errorT,  qr{blablabla"$},  'pipemess: list context, "no output STDERR blablabla then false", error' ) ;
+
+                ( $stringT, $errorT ) = pipemess( 'dontcare', 'false', '( echo -n blablabla 3>&1 1>&2 2>&3 )' ) ;
+                is( $stringT, undef, 'pipemess: list context, "false then STDERR blablabla", string' ) ;
+                like( $errorT,  qr{Failure: --pipemess command "false" ended with "0" characters exit value "1" and STDERR ""},  'pipemess: list context, "false then STDERR blablabla", error' ) ;
+
+                ( $stringT, $errorT ) = pipemess( 'dontcare', '( echo rrrrr ; echo -n error_blablabla 3>&1 1>&2 2>&3 )' ) ;
+                like( $stringT, qr{rrrrr}, 'pipemess: list context, "STDOUT rrrrr STDERR error_blablabla", string' ) ;
+                like( $errorT,  qr{STDERR.*error_blablabla},  'pipemess: list context, "STDOUT rrrrr STDERR error_blablabla", error' ) ;
+
+	}
+
+        ( $stringT, $errorT ) = pipemess( 'dontcare', 'cacaprout' ) ;
+        is( $stringT, undef, 'pipemess: list context, cacaprout not found, string' ) ;
+        like( $errorT, qr{Failure: --pipemess command "cacaprout" ended with "0" characters exit value.*}, 'pipemess: list context, cacaprout not found, error' ) ;
+
+	return ;
+}
+
+sub tests_is_a_release_number {
+	ok(is_a_release_number($RELEASE_NUMBER_EXAMPLE_1), 'is_a_release_number 1.351') ;
+	ok(is_a_release_number($RELEASE_NUMBER_EXAMPLE_2), 'is_a_release_number 42.4242') ;
+	ok(is_a_release_number(imapsync_version()), 'is_a_release_number imapsync_version()') ;
+	ok(! is_a_release_number('blabla' ), '! is_a_release_number blabla') ;
+	return ;
+}
+
+sub is_a_release_number {
+	my $number = shift;
+
+	return( $number =~ m{^\d+\.\d+$}xo ) ;
+}
+
+sub check_last_release {
+
+	my $public_release = not_long_imapsync_version_public(  ) ;
+	$debug and myprint( "check_last_release: [$public_release]\n"  ) ;
+	return('unknown') if ($public_release eq 'unknown') ;
+	return('timeout') if ($public_release eq 'timeout') ;
+	return('unknown') if (! is_a_release_number( $public_release ) ) ;
+
+	my $imapsync_here  = imapsync_version();
+
+	if ($public_release > $imapsync_here) {
+		return("New imapsync release $public_release available");
+	}else{
+		return( 'This imapsync is up to date') ;
+	}
+}
+
+sub imapsync_version  {
+	my $rcs_imapsync = '$Id: imapsync,v 1.727 2016/08/19 10:30:36 gilles Exp gilles $ ' ;
+        my $imapsync_version ;
+
+	if ( $rcs_imapsync =~ m{,v\s+(\d+\.\d+)}xo ) {
+		$imapsync_version = $1
+        } else {
+                $imapsync_version = 'UNKNOWN' ;
+        }
+	return( $imapsync_version ) ;
+}
+
+sub tests_imapsync_basename {
+	ok( imapsync_basename() =~ m/imapsync/, 'imapsync_basename: match imapsync');
+	ok( 'blabla'   ne imapsync_basename(), 'imapsync_basename: do not equal blabla');
+	return ;
+}
+
+sub imapsync_basename {
+
+	return basename($0);
+
+}
+
+sub imapsync_version_public {
+
+	my $local_version = imapsync_version();
+	my $imapsync_basename = imapsync_basename();
+	my $agent_info = "$OSNAME system, perl "
+		. mysprintf( '%vd', $PERL_VERSION)
+		. ", Mail::IMAPClient $Mail::IMAPClient::VERSION"
+		. " $imapsync_basename";
+	my $sock = IO::Socket::INET->new(
+		PeerAddr => 'imapsync.lamiral.info',
+		PeerPort => 80,
+		Proto    => 'tcp',
+                ) ;
+	return( 'unknown' ) if not $sock ;
+	print $sock
+		"GET /prj/imapsync/VERSION HTTP/1.0\n",
+		"User-Agent: imapsync/$local_version ($agent_info)\n",
+		"Host: ks.lamiral.info\n\n";
+	my @line = <$sock>;
+	close $sock ;
+	my $last_release = $line[$LAST];
+	chomp $last_release ;
+	return($last_release) ;
+}
+
+sub not_long_imapsync_version_public {
+	#myprint( "Entering not_long_imapsync_version_public\n" ) ;
+
+	my $val;
+
+	# Doesn't work with gethostbyname (see perlipc)
+	#local $SIG{ALRM} = sub { die "alarm\n" };
+
+	if ('MSWin32' eq $OSNAME) {
+		local $SIG{ALRM} = sub { die "alarm\n" };
+	}else{
+
+        	POSIX::sigaction(SIGALRM,
+                         POSIX::SigAction->new(sub { croak 'alarm' } ) )
+        		or myprint( "Error setting SIGALRM handler: $!\n"  ) ;
+	}
+
+	my $ret = eval {
+		alarm 3 ;
+		{
+			$val = imapsync_version_public(  ) ;
+                        #sleep 4 ;
+			#myprint( "End of imapsync_version_public\n"  ) ;
+		}
+		alarm 0 ;
+                1 ;
+	} ;
+        #myprint( "eval [$ret]\n"  ) ;
+	if ( ( not $ret ) or $@ ) {
+		#myprint( "$@" ) ;
+		if ($@ =~ /alarm/) {
+		# timed out
+			return('timeout');
+		}else{
+			alarm 0 ;
+			return('unknown'); # propagate unexpected errors
+		}
+	}else {
+	# Good!
+		return($val);
+	}
+}
+
+sub localhost_info {
+
+	my($infos) = join q{},
+	    "Here is a [$OSNAME] system (",
+	    join(q{ },
+	         uname(),
+	         ),
+                 ")\n",
+	         'with Perl ',
+	         mysprintf( '%vd', $PERL_VERSION),
+	         " Mail::IMAPClient $Mail::IMAPClient::VERSION",
+             ;
+	return($infos) ;
+}
+
+sub memory_consumption {
+	# memory consumed by imapsync until now in bytes
+	return( ( memory_consumption_of_pids(  ) )[0] );
+}
+
+sub tests_memory_consumption {
+
+	like( memory_consumption(  ),  qr{\d+},'memory_consumption no args') ;
+	like( memory_consumption( 1 ), qr{\d+},'memory_consumption 1') ;
+	like( memory_consumption( $PROCESS_ID ), qr{\d+},"memory_consumption_of_pids $PROCESS_ID") ;
+
+	like( memory_consumption_ratio(), qr{\d+},   'memory_consumption_ratio' ) ;
+	like( memory_consumption_ratio(1), qr{\d+},  'memory_consumption_ratio 1' ) ;
+	like( memory_consumption_ratio(10), qr{\d+}, 'memory_consumption_ratio 10' ) ;
+
+	like( memory_consumption(), qr{\d+}, "memory_consumption\n" ) ;
+	return ;
+}
+
+
+
+sub memory_consumption_of_pids {
+
+	my @pid = @_;
+	@pid = (@pid) ? @pid : ($PROCESS_ID) ;
+
+	#myprint( "PIDs: @pid\n" ) ;
+	my @val;
+	if ('MSWin32' eq $OSNAME) {
+		@val = memory_consumption_of_pids_win32(@pid);
+	}else{
+		# Unix
+		my @ps = qx{ ps -o vsz -p @pid } ;
+                #myprint( @ps ) ;
+                #my @ps = backtick( "ps -o vsz -p @pid" ) ;
+		shift @ps; # First line is column name "VSZ"
+		chomp @ps;
+		# convert to octets
+                
+		@val = map { $_ * $KIBI } @ps;
+	}
+	return( @val ) ;
+}
+
+sub memory_consumption_of_pids_win32 {
+	# Windows
+	my @PID = @_;
+	my %PID;
+	# hash of pids as key values
+	map { $PID{$_}++ } @PID;
+
+	# Does not work but should reading the tasklist documentation
+	#@ps = qx{ tasklist /FI "PID eq @PID" };
+
+	my @ps = qx{ tasklist /NH /FO CSV } ;
+        #my @ps = backtick( 'tasklist /NH /FO CSV' ) ;
+	#myprint( "-" x $STD_CHAR_PER_LINE, "\n", @ps, "-" x $STD_CHAR_PER_LINE, "\n" ) ;
+	my @val;
+	foreach my $line (@ps) {
+		my($name, $pid, $mem) = (split ',', $line )[0,1,4];
+		next if (! $pid);
+		#myprint( "[$name][$pid][$mem]" ) ;
+		if ($PID{remove_qq($pid)}) {
+			#myprint( "MATCH !\n" ) ;
+			chomp $mem ;
+			$mem = remove_qq($mem);
+			$mem = remove_Ko($mem);
+			$mem = remove_not_num($mem);
+			#myprint( "[$mem]\n" ) ;
+			push @val, $mem * $KIBI;
+		}
+	}
+	return(@val);
+}
+
+sub backtick {
+	my $command = shift ;
+	my ( $writer, $reader, $err ) ;
+        my @output ;
+        open3( $writer, $reader, $err, $command ) ;
+        @output = <$reader>;  #Output here
+        #my @errors = <$err>;    #Errors here, instead of the console
+        $debugdev and myprint( @output  ) ;
+        return( @output ) ;
+}
+
+sub tests_backtick {
+
+        SKIP: {
+		skip( 'Tests for MSWin32', 3 ) if ('MSWin32' ne $OSNAME) ;
+		my @output ;
+		@output = backtick( 'echo Hello World!' ) ;
+		# Add \r on Windows.
+		ok( "Hello World!\r\n" eq $output[0], 'backtick: echo Hello World!' ) ;
+		$debug and myprint( "[@output]"  ) ;
+		@output = backtick( 'echo Hello & echo World!' ) ;
+		ok( "Hello \r\n" eq $output[0], 'backtick: echo Hello & echo World!' ) ;
+		ok( "World!\r\n" eq $output[1], 'backtick: echo Hello & echo World!' ) ;
+		$debug and myprint( "[@output][$output[0]][$output[1]]"  ) ;
+        } ;
+	SKIP: {
+		skip( 'Tests for Unix', 3 ) if ('MSWin32' eq $OSNAME) ;
+		my @output ;
+		@output = backtick( 'echo Hello World!' ) ;
+		ok( "Hello World!\n" eq $output[0], 'backtick: echo Hello World!' ) ;
+		$debug and myprint( "[@output]"  ) ;
+		@output = backtick( "echo Hello\necho World!" ) ;
+		ok( "Hello\n" eq $output[0], 'backtick: echo Hello; echo World!' ) ;
+		ok( "World!\n" eq $output[1], 'backtick: echo Hello; echo World!' ) ;
+		$debug and myprint( "[@output]"  ) ;
+	}
+        return ;
+}
+
+sub remove_not_num {
+
+	my $string = shift;
+	$string =~ tr/0-9//cd;
+	#myprint( "tr [$string]\n" ) ;
+	return($string);
+}
+
+sub tests_remove_not_num {
+
+	ok('123' eq remove_not_num(123), 'remove_not_num( 123 )' ) ;
+	ok('123' eq remove_not_num('123'), q{remove_not_num( '123' )} ) ;
+	ok('123' eq remove_not_num('12 3'), q{remove_not_num( '12 3' )} ) ;
+	ok('123' eq remove_not_num('a 12 3 Ko'), q{remove_not_num( 'a 12 3 Ko' )} ) ;
+	return ;
+}
+
+sub remove_Ko {
+	my $string = shift;
+	if ($string =~ /^(.*)\sKo$/xo) {
+		return($1);
+	}else{
+		return($string);
+	}
+}
+
+sub remove_qq {
+	my $string = shift;
+	if ($string =~ /^"(.*)"$/xo) {
+		return($1);
+	}else{
+		return($string);
+	}
+}
+
+sub memory_consumption_ratio {
+
+	my ($base) = @_;
+	$base ||= 1;
+	my $consu = memory_consumption();
+	return($consu / $base);
+}
+
+
+sub date_from_rcs {
+	my $d = shift ;
+
+	my %num2mon = qw( 01 Jan 02 Feb 03 Mar 04 Apr 05 May 06 Jun 07 Jul 08 Aug 09 Sep 10 Oct 11 Nov 12 Dec ) ;
+        if ($d =~ m{(\d{4})/(\d{2})/(\d{2})\s(\d{2}):(\d{2}):(\d{2})}xo ) {
+                # Handles the following format
+                # 2015/07/10 11:05:59 -- Generated by RCS Date tag.
+		#myprint( "$d\n"  ) ;
+                #myprint( "header: [$1][$2][$3][$4][$5][$6]\n"  ) ;
+                my ($year, $month, $day, $hour, $min, $sec) = ($1,$2,$3,$4,$5,$6) ;
+                $month = $num2mon{$month} ;
+                $d = "$day-$month-$year $hour:$min:$sec +0000" ;
+		#myprint( "$d\n"  ) ;
+	}
+	return( $d ) ;
+}
+
+sub tests_date_from_rcs {
+	ok('19-Sep-2015 16:11:07 +0000'
+	eq date_from_rcs('Date: 2015/09/19 16:11:07 '), 'date_from_rcs from RCS date' ) ;
+	return ;
+}
+
+sub good_date {
+        # two incoming formats:
+        # header    Tue, 24 Aug 2010 16:00:00 +0200
+	# internal       24-Aug-2010 16:00:00 +0200
+
+        # outgoing format: internal date format
+        #   24-Aug-2010 16:00:00 +0200
+
+    my $d = shift ;
+    return(q{}) if not defined $d;
+
+	SWITCH: {
+    	if ( $d =~ m{(\d?)(\d-...-\d{4})(\s\d{2}:\d{2}:\d{2})(\s(?:\+|-)\d{4})?}xo ) {
+		#myprint( "internal: [$1][$2][$3][$4]\n"  ) ;
+		my ($day_1, $date_rest, $hour, $zone) = ($1,$2,$3,$4) ;
+		$day_1 = '0' if ($day_1 eq q{}) ;
+		$zone  = ' +0000'  if not defined $zone ;
+		$d = $day_1 . $date_rest . $hour . $zone ;
+                last SWITCH ;
+        }
+
+	if ($d =~ m{(?:\w{3,},\s)?(\d{1,2}),?\s+(\w{3,})\s+(\d{2,4})\s+(\d{1,2})(?::|\.)(\d{1,2})(?:(?::|\.)(\d{1,2}))?\s*((?:\+|-)\d{4})?}xo ) {
+        	# Handles any combination of following formats
+                # Tue, 24 Aug 2010 16:00:00 +0200 -- Standard
+                # 24 Aug 2010 16:00:00 +0200 -- Missing Day of Week
+                # Tue, 24 Aug 97 16:00:00 +0200 -- Two digit year
+                # Tue, 24 Aug 1997 16.00.00 +0200 -- Periods instead of colons
+                # Tue, 24 Aug 1997  16:00:00 +0200 -- Extra whitespace between year and hour
+                # Tue, 24 Aug 1997 6:5:2 +0200 -- Single digit hour, min, or second
+                # Tue, 24, Aug 1997 16:00:00 +0200 -- Extra comma
+
+                #myprint( "header: [$1][$2][$3][$4][$5][$6][$7][$8]\n" ) ;
+                my ($day, $month, $year, $hour, $min, $sec, $zone) = ($1,$2,$3,$4,$5,$6,$7,$8);
+                $year = '19' . $year if length($year) == 2 && $year =~ m/^[789]/xo;
+                $year = '20' . $year if length($year) == 2;
+
+                $month = substr $month, 0, 3 if length($month) > 4;
+                $day  = mysprintf( '%02d', $day);
+                $hour = mysprintf( '%02d', $hour);
+                $min  = mysprintf( '%02d', $min);
+                $sec  = '00' if not defined  $sec  ;
+                $sec  = mysprintf( '%02d', $sec ) ;
+                $zone = '+0000' if not defined  $zone  ;
+                $d    = "$day-$month-$year $hour:$min:$sec $zone" ;
+		last SWITCH ;
+	}
+
+	if ($d =~ m{(?:.{3})\s(...)\s+(\d{1,2})\s(\d{1,2}):(\d{1,2}):(\d{1,2})\s(?:\w{3})?\s?(\d{4})}xo ) {
+        	# Handles any combination of following formats
+                # Sun Aug 20 11:55:09 2006
+                # Wed Jan 24 11:58:38 MST 2007
+                # Wed Jan  2 08:40:57 2008
+
+                #myprint( "header: [$1][$2][$3][$4][$5][$6]\n" ) ;
+                my ($month, $day, $hour, $min, $sec, $year) = ($1,$2,$3,$4,$5,$6);
+                $day  = mysprintf( '%02d', $day  ) ;
+                $hour = mysprintf( '%02d', $hour ) ;
+                $min  = mysprintf( '%02d', $min  ) ;
+                $sec  = mysprintf( '%02d', $sec  ) ;
+                $d    = "$day-$month-$year $hour:$min:$sec +0000" ;
+		last SWITCH ;
+	}
+        my %num2mon = qw( 01 Jan 02 Feb 03 Mar 04 Apr 05 May 06 Jun 07 Jul 08 Aug 09 Sep 10 Oct 11 Nov 12 Dec ) ;
+
+        if ($d =~ m{(\d{4})/(\d{2})/(\d{2})\s(\d{2}):(\d{2}):(\d{2})}xo ) {
+                # Handles the following format
+                # 2015/07/10 11:05:59 -- Generated by RCS Date tag.
+		#myprint( "$d\n"  ) ;
+                #myprint( "header: [$1][$2][$3][$4][$5][$6]\n"  ) ;
+                my ($year, $month, $day, $hour, $min, $sec) = ($1,$2,$3,$4,$5,$6) ;
+                $month = $num2mon{$month} ;
+                $d = "$day-$month-$year $hour:$min:$sec +0000" ;
+		#myprint( "$d\n"  ) ;
+		last SWITCH ;
+	}
+
+        if ($d =~ m{(\d{2})/(\d{2})/(\d{2})\s(\d{2}):(\d{2}):(\d{2})}xo ) {
+                # Handles the following format
+                # 02/06/09 22:18:08 -- Generated by AVTECH TemPageR devices
+
+                #myprint( "header: [$1][$2][$3][$4][$5][$6]\n" ) ;
+                my ($month, $day, $year, $hour, $min, $sec) = ($1,$2,$3,$4,$5,$6);
+                $year = '20' . $year;
+                $month = $num2mon{$month};
+                $d = "$day-$month-$year $hour:$min:$sec +0000";
+		last SWITCH ;
+	}
+
+	if ($d =~ m{\w{6,},\s(\w{3})\w+\s+(\d{1,2}),\s(\d{4})\s(\d{2}):(\d{2})\s(AM|PM)}xo ) {
+        	# Handles the following format
+                # Saturday, December 14, 2002 05:00 PM - KBtoys.com order confirmations
+
+                my ($month, $day, $year, $hour, $min, $apm) = ($1,$2,$3,$4,$5,$6);
+
+                $hour += 12 if $apm eq 'PM' ;
+                $day = mysprintf( '%02d', $day ) ;
+                $d = "$day-$month-$year $hour:$min:00 +0000" ;
+                last SWITCH ;
+	}
+
+	if ($d =~ m{(\w{3})\s(\d{1,2})\s(\d{4})\s(\d{2}):(\d{2}):(\d{2})\s((?:\+|-)\d{4})}xo ) {
+        	# Handles the following format
+                # Saturday, December 14, 2002 05:00 PM - jr.com order confirmations
+
+                my ($month, $day, $year, $hour, $min, $sec, $zone) = ($1,$2,$3,$4,$5,$6,$7);
+
+                $day = mysprintf( '%02d', $day ) ;
+                $d = "$day-$month-$year $hour:$min:$sec $zone";
+                last SWITCH ;
+	}
+
+	if ($d =~ m{(\d{1,2})-(\w{3})-(\d{4})}xo ) {
+        	# Handles the following format
+                # 21-Jun-2001 - register.com domain transfer email circa 2001
+
+                my ($day, $month, $year) = ($1,$2,$3);
+                $day = mysprintf( '%02d', $day);
+                $d = "$day-$month-$year 11:11:11 +0000";
+		last SWITCH ;
+	}
+
+    	# unknown or unmatch => return same string
+    	return($d);
+    }
+
+    $d = qq("$d") ;
+    return( $d ) ;
+}
+
+
+sub tests_good_date {
+
+	ok(q{} eq good_date(), 'good_date no arg');
+	ok('"24-Aug-2010 16:00:00 +0200"' eq good_date('24-Aug-2010 16:00:00 +0200'), 'good_date internal 2digit zone');
+	ok('"24-Aug-2010 16:00:00 +0000"' eq good_date('24-Aug-2010 16:00:00'), 'good_date internal 2digit no zone');
+	ok('"01-Sep-2010 16:00:00 +0200"' eq good_date( '1-Sep-2010 16:00:00 +0200'), 'good_date internal SP 1digit');
+	ok('"24-Aug-2010 16:00:00 +0200"' eq good_date('Tue, 24 Aug 2010 16:00:00 +0200'), 'good_date header 2digit zone');
+	ok('"01-Sep-2010 16:00:00 +0000"' eq good_date('Wed, 1 Sep 2010 16:00:00'), 'good_date header SP 1digit zone');
+	ok('"01-Sep-2010 16:00:00 +0200"' eq good_date('Wed, 1 Sep 2010 16:00:00 +0200'), 'good_date header SP 1digit zone');
+	ok('"01-Sep-2010 16:00:00 +0200"' eq good_date('Wed, 1 Sep 2010 16:00:00 +0200 (CEST)'), 'good_date header SP 1digit zone');
+        ok('"06-Feb-2009 22:18:08 +0000"' eq good_date('02/06/09 22:18:08'), 'good_date header TemPageR');
+        ok('"02-Jan-2008 08:40:57 +0000"' eq good_date('Wed Jan  2 08:40:57 2008'), 'good_date header dice.com support 1digit day');
+        ok('"20-Aug-2006 11:55:09 +0000"' eq good_date('Sun Aug 20 11:55:09 2006'), 'good_date header dice.com support 2digit day');
+        ok('"24-Jan-2007 11:58:38 +0000"' eq good_date('Wed Jan 24 11:58:38 MST 2007'), 'good_date header status-now.com');
+        ok('"24-Aug-2010 16:00:00 +0200"' eq good_date('24 Aug 2010 16:00:00 +0200'), 'good_date header missing date of week');
+        ok('"24-Aug-2067 16:00:00 +0200"' eq good_date('Tue, 24 Aug 67 16:00:00 +0200'), 'good_date header 2digit year');
+        ok('"24-Aug-1977 16:00:00 +0200"' eq good_date('Tue, 24 Aug 77 16:00:00 +0200'), 'good_date header 2digit year');
+        ok('"24-Aug-1987 16:00:00 +0200"' eq good_date('Tue, 24 Aug 87 16:00:00 +0200'), 'good_date header 2digit year');
+        ok('"24-Aug-1997 16:00:00 +0200"' eq good_date('Tue, 24 Aug 97 16:00:00 +0200'), 'good_date header 2digit year');
+        ok('"24-Aug-2004 16:00:00 +0200"' eq good_date('Tue, 24 Aug 04 16:00:00 +0200'), 'good_date header 2digit year');
+        ok('"24-Aug-1997 16:00:00 +0200"' eq good_date('Tue, 24 Aug 1997 16.00.00 +0200'), 'good_date header period time sep');
+        ok('"24-Aug-1997 16:00:00 +0200"' eq good_date('Tue, 24 Aug 1997  16:00:00 +0200'), 'good_date header extra white space type1');
+        ok('"24-Aug-1997 05:06:02 +0200"' eq good_date('Tue, 24 Aug 1997 5:6:2 +0200'), 'good_date header 1digit time vals');
+        ok('"24-Aug-1997 05:06:02 +0200"' eq good_date('Tue, 24, Aug 1997 05:06:02 +0200'), 'good_date header extra commas');
+        ok('"01-Oct-2003 12:45:24 +0000"' eq good_date('Wednesday, 01 October 2003 12:45:24 CDT'), 'good_date header no abbrev');
+        ok('"11-Jan-2005 17:58:27 -0500"' eq good_date('Tue,  11  Jan 2005 17:58:27 -0500'), 'good_date extra white space');
+        ok('"18-Dec-2002 15:07:00 +0000"' eq good_date('Wednesday, December 18, 2002 03:07 PM'), 'good_date kbtoys.com orders');
+        ok('"16-Dec-2004 02:01:49 -0500"' eq good_date('Dec 16 2004 02:01:49 -0500'), 'good_date jr.com orders');
+        ok('"21-Jun-2001 11:11:11 +0000"' eq good_date('21-Jun-2001'), 'good_date register.com domain transfer');
+        ok('"18-Nov-2012 18:34:38 +0100"' eq good_date('Sun, 18 Nov 2012 18:34:38 +0100'), 'good_date pop2imap bug (Westeuropäische Normalzeit)');
+	ok('"19-Sep-2015 16:11:07 +0000"' eq good_date('Date: 2015/09/19 16:11:07 '), 'good_date from RCS date' ) ;
+	return ;
+}
+
+
+sub tests_list_keys_in_2_not_in_1 {
+
+	my @list;
+	ok( ! list_keys_in_2_not_in_1( {}, {}), 'list_keys_in_2_not_in_1: {} {}');
+	ok( 0 == compare_lists( [], [ list_keys_in_2_not_in_1( {}, {} ) ] ), 'list_keys_in_2_not_in_1: {} {}');
+	ok( 0 == compare_lists( ['a','b'], [ list_keys_in_2_not_in_1( {}, {'a' => 1, 'b' => 1}) ]), 'list_keys_in_2_not_in_1: {} {a, b}');
+	ok( 0 == compare_lists( ['b'],     [ list_keys_in_2_not_in_1( {'a' => 1}, {'a' => 1, 'b' => 1}) ]), 'list_keys_in_2_not_in_1: {a} {a, b}');
+	ok( 0 == compare_lists( [],        [ list_keys_in_2_not_in_1( {'a' => 1, 'b' => 1}, {'a' => 1, 'b' => 1}) ]), 'list_keys_in_2_not_in_1: {a, b} {a, b}');
+	ok( 0 == compare_lists( [],        [ list_keys_in_2_not_in_1( {'a' => 1, 'b' => 1, 'c' => 1}, {'a' => 1, 'b' => 1}) ]), 'list_keys_in_2_not_in_1: {a, b, c} {a, b}');
+	ok( 0 == compare_lists( ['b'],     [ list_keys_in_2_not_in_1( {'a' => 1, 'c' => 1}, {'a' => 1, 'b' => 1}) ]), 'list_keys_in_2_not_in_1: {a, b, c} {a, b}');
+
+	return ;
+}
+
+sub list_keys_in_2_not_in_1 {
+
+	my $folders1_ref = shift;
+	my $folders2_ref = shift;
+	my @list;
+
+	foreach my $folder ( sort keys %{ $folders2_ref } ) {
+		next if exists $folders1_ref->{$folder};
+		push @list, $folder;
+	}
+	return(@list);
+}
+
+
+sub list_folders_in_2_not_in_1 {
+
+	my (@h2_folders_not_in_h1, %h2_folders_not_in_h1) ;
+	@h2_folders_not_in_h1 = list_keys_in_2_not_in_1( \%h1_folders_all, \%h2_folders_all) ;
+	map { $h2_folders_not_in_h1{$_} = 1} @h2_folders_not_in_h1 ;
+	@h2_folders_not_in_h1 = list_keys_in_2_not_in_1( \%h2_folders_from_1_all, \%h2_folders_not_in_h1) ;
+
+	return( reverse @h2_folders_not_in_h1 );
+}
+
+sub delete_folders_in_2_not_in_1 {
+
+	foreach my $folder (@h2_folders_not_in_1) {
+		if ( defined  $delete2foldersonly  and eval "\$folder !~ $delete2foldersonly" ) {
+			myprint( "Not deleting $folder because of --delete2foldersonly $delete2foldersonly\n"  ) ;
+			next ;
+		}
+		if ( defined  $delete2foldersbutnot  and eval "\$folder =~ $delete2foldersbutnot" ) {
+			myprint( "Not deleting $folder because of --delete2foldersbutnot $delete2foldersbutnot\n"  ) ;
+			next ;
+		}
+		my $res = $dry ; # always success in dry mode!
+		$imap2->unsubscribe( $folder ) if ( ! $dry ) ;
+		$res = $imap2->delete( $folder ) if ( ! $dry ) ;
+		if ( $res ) {
+			myprint( "Deleted $folder", "$dry_message", "\n"  ) ;
+		}else{
+			myprint( "Deleting $folder failed", "\n"  ) ;
+		}
+	}
+	return ;
+}
+
+sub delete_folder {
+        my ( $sync, $imap, $folder, $Side ) = @_ ;
+        if ( ! $sync )   { return ; }
+        if ( ! $imap )   { return ; }
+        if ( ! $folder ) { return ; }
+        $Side ||= 'HostX' ;
+        
+        my $res = $sync->{dry} ; # always success in dry mode!
+        if ( ! $sync->{dry} ) {
+                $imap->unsubscribe( $folder ) ;
+                $res = $imap->delete( $folder ) ;
+        }
+        if ( $res ) {
+        	myprint( "$Side deleted $folder", $sync->{dry_message}, "\n"  ) ;
+                return 1 ;
+        }else{
+        	myprint( "$Side deleting $folder failed", "\n"  ) ;
+                return ;
+        }
+}
+
+sub delete1emptyfolders {
+        my $sync = shift ;
+        if ( ! $sync ) { return ; } # abort if no parameter
+        if ( ! $sync->{delete1emptyfolders} ) { return ; } # abort if --delete1emptyfolders off
+        my $imap = $sync->{imap1} ;
+        if ( ! $imap ) { return ; } # abort if no imap
+        if ( $imap->IsUnconnected(  ) ) { return ; } # abort if diesconnected
+        
+        my %folders_kept ;
+        myprint( qq{Host1 deleting empty folders\n} ) ;
+        foreach my $folder ( reverse sort @{ $sync->{h1_folders_wanted} } ) {
+                my $parenthood = $imap->is_parent( $folder ) ;
+                if ( defined $parenthood and $parenthood ) {
+                        myprint( "Host1 folder $folder has subfolders\n" ) ;
+                        $folders_kept{ $folder }++ ;
+                        next ;
+                }
+                my $nb_messages_select = examine_folder_and_count( $imap, $folder, 'Host1' ) ;
+                if ( ! defined $nb_messages_select ) { next ; } # Select failed => Neither continue nor keep this folder }
+                my $nb_messages_search = scalar( @{ $imap->messages(  ) } ) ;
+                if ( 0 != $nb_messages_select and 0 != $nb_messages_search ) {
+                        myprint( "Host1 folder $folder has messages: $nb_messages_search (search) $nb_messages_select (select)\n" ) ;
+                        $folders_kept{ $folder }++ ;
+                        next ;
+                }
+                if ( 0 != $nb_messages_select + $nb_messages_search ) {
+                        myprint( "Host1 folder $folder odd messages count: $nb_messages_search (search) $nb_messages_select (select)\n" ) ;
+                        $folders_kept{ $folder }++ ;
+                        next ;
+                }
+                # Here we must have 0 messages by messages() aka "SEARCH ALL" and also "EXAMINE"
+                if ( uc $folder eq 'INBOX' ) {
+                        myprint( "Host1 Not deleting $folder\n" ) ;
+                        $folders_kept{ $folder }++ ;
+                        next ; 
+                }
+                myprint( "Host1 deleting empty folder $folder\n" ) ;
+                # can not delete a SELECTed or EXAMINEd folder so closing it
+                # could changed be SELECT INBOX
+                $imap->close(  ) ; # close after examine does not expunge; anyway expunging an empty folder... 
+                if ( delete_folder( $sync, $imap, $folder, 'Host1' ) ) {
+                        next ; # Deleted, good!
+                }else{
+                        $folders_kept{ $folder }++ ;
+                        next ; # Not deleted, bad!
+                }
+        }
+        remove_deleted_folders_from_wanted_list( $sync, %folders_kept ) ;
+        myprint( qq{Host1 ended deleting empty folders\n} ) ;
+        return ;
+}
+
+sub remove_deleted_folders_from_wanted_list {
+        my ( $sync, %folders_kept ) = @ARG ;
+        
+        my @h1_folders_wanted_init = @{ $sync->{h1_folders_wanted} } ;
+        my @h1_folders_wanted_last ;
+        foreach my $folder ( @h1_folders_wanted_init ) {
+                if ( $folders_kept{ $folder } ) {
+                        push @h1_folders_wanted_last, $folder ;
+                }
+        }
+        @{ $sync->{h1_folders_wanted} } = @h1_folders_wanted_last ;
+        return ;
+}
+
+sub examine_folder_and_count {
+        my ( $imap, $folder, $Side ) = @_ ;
+        $Side ||= 'HostX' ;
+        
+        if ( ! examine_folder( $imap, $folder, $Side ) ) {
+                return ;
+        }
+        my $nb_messages_select = count_from_select( $imap->History ) ;
+        return $nb_messages_select ;
+}
+
+
+sub tests_delete1emptyfolders {
+
+        is( undef, delete1emptyfolders(  ), q{delete1emptyfolders: undef} ) ;
+        my $syncT ;
+        is( undef, delete1emptyfolders( $syncT ), q{delete1emptyfolders: undef 2} ) ;
+        my $imapT ;
+        $syncT->{imap1} = $imapT ;
+        is( undef, delete1emptyfolders( $syncT ), q{delete1emptyfolders: undef imap} ) ;
+        
+        require Test::MockObject ;
+        $imapT = Test::MockObject->new(  ) ;
+        $syncT->{imap1} = $imapT ;
+
+        $imapT->set_true( 'IsUnconnected' ) ;
+        is( undef, delete1emptyfolders( $syncT ), q{delete1emptyfolders: Unconnected imap} ) ;
+
+        # Now connected tests
+        $imapT->set_false( 'IsUnconnected' ) ;
+        $imapT->mock( 'LastError', sub { q{LastError mocked} } ) ;
+        
+        $syncT->{delete1emptyfolders} = 0 ;
+        tests_delete1emptyfolders_unit(
+                $syncT,
+                [ qw{ INBOX DELME1 DELME2 } ],
+                [ qw{ INBOX DELME1 DELME2 } ],
+                q{tests_delete1emptyfolders: --delete1emptyfolders OFF}
+        ) ;
+
+        # All are parents => no deletion at all
+        $imapT->set_true( 'is_parent' ) ;
+        $syncT->{delete1emptyfolders} = 1 ;
+        tests_delete1emptyfolders_unit(
+                $syncT,
+                [ qw{ INBOX DELME1 DELME2 } ],
+                [ qw{ INBOX DELME1 DELME2 } ],
+                q{tests_delete1emptyfolders: --delete1emptyfolders ON}
+        ) ;
+
+        # No parents but examine false for all => skip all
+        $imapT->set_false( 'is_parent', 'examine' ) ;
+        
+        tests_delete1emptyfolders_unit(
+                $syncT,
+                [ qw{ INBOX DELME1 DELME2 } ],
+                [  ],
+                q{tests_delete1emptyfolders: EXAMINE fails}
+        ) ;
+
+        # examine ok for all but History bad => skip all
+        $imapT->set_true( 'examine' ) ;
+        $imapT->mock( 'History', sub { ( q{History badly mocked} ) } ) ;
+        tests_delete1emptyfolders_unit(
+                $syncT,
+                [ qw{ INBOX DELME1 DELME2 } ],
+                [  ],
+                q{tests_delete1emptyfolders: examine ok but History badly mocked so count messages fails}
+        ) ;
+
+        # History good but some messages EXISTS == messages() => no deletion
+        $imapT->mock( 'History', sub { ( q{* 2 EXISTS} ) } ) ;
+        $imapT->mock( 'messages', sub { [ qw{ UID_1 UID_2 } ] } ) ;
+        tests_delete1emptyfolders_unit(
+                $syncT,
+                [ qw{ INBOX DELME1 DELME2 } ],
+                [ qw{ INBOX DELME1 DELME2 } ],
+                q{tests_delete1emptyfolders: History EXAMINE ok, several messages}
+        ) ;
+
+        # 0 EXISTS but != messages() => no deletion
+        $imapT->mock( 'History', sub { ( q{* 0 EXISTS} ) } ) ;
+        $imapT->mock( 'messages', sub { [ qw{ UID_1 UID_2 } ] } ) ;
+        tests_delete1emptyfolders_unit(
+                $syncT,
+                [ qw{ INBOX DELME1 DELME2 } ],
+                [ qw{ INBOX DELME1 DELME2 } ],
+                q{tests_delete1emptyfolders: 0 EXISTS but 2 by messages()}
+        ) ;
+
+        # 1 EXISTS but != 0 == messages() => no deletion
+        $imapT->mock( 'History', sub { ( q{* 1 EXISTS} ) } ) ;
+        $imapT->mock( 'messages', sub { [ ] } ) ;
+        tests_delete1emptyfolders_unit(
+                $syncT,
+                [ qw{ INBOX DELME1 DELME2 } ],
+                [ qw{ INBOX DELME1 DELME2 } ],
+                q{tests_delete1emptyfolders: 1 EXISTS but 0 by messages()}
+        ) ;
+
+        # 0 EXISTS and 0 == messages() => deletion except INBOX
+        $imapT->mock( 'History', sub { ( q{* 0 EXISTS} ) } ) ;
+        $imapT->mock( 'messages', sub { [ ] } ) ;
+        $imapT->set_true( qw{ delete close unsubscribe } ) ;
+        $syncT->{dry_message} = q{ (not really since in a mocked test)} ;
+        tests_delete1emptyfolders_unit(
+                $syncT,
+                [ qw{ INBOX DELME1 DELME2 } ],
+                [ qw{ INBOX } ],
+                q{tests_delete1emptyfolders: 0 EXISTS 0 by messages() delete folders, keep INBOX}
+        ) ;
+
+
+
+
+        return ;
+}
+
+sub tests_delete1emptyfolders_unit {
+        my $syncT  = shift ;
+        my $folders1wanted_init_ref = shift ;
+        my $folders1wanted_after_ref = shift ;
+        my $comment = shift || q{delete1emptyfolders:} ;
+        
+        my @folders1wanted_init  = @{ $folders1wanted_init_ref } ;
+        my @folders1wanted_after = @{ $folders1wanted_after_ref } ;
+
+        @{ $syncT->{h1_folders_wanted} } = @folders1wanted_init ;
+        
+        is_deeply( $syncT->{h1_folders_wanted}, \@folders1wanted_init, qq{$comment, init check} ) ;
+        delete1emptyfolders( $syncT ) ;
+        is_deeply( $syncT->{h1_folders_wanted}, \@folders1wanted_after, qq{$comment, after check} ) ;
+        return ;
+}
+
+sub extract_header {
+        my $string = shift ;
+
+        my ( $header ) = split  /\n\n/x, $string ;
+        if ( ! $header ) { return( q{} ) ; }
+        #myprint( "[$header]\n"  ) ;
+        return( $header ) ;
+}
+
+sub tests_extract_header {
+
+
+my $h = <<'EOM';
+Message-Id: <[email protected]>
+Date: Wed, 28 Apr 2010 12:18:17 +0200 (CEST)
+From: [email protected] (Gilles LAMIRAL)
+EOM
+chomp $h ;
+ok( $h eq extract_header(
+<<'EOM'
+Message-Id: <[email protected]>
+Date: Wed, 28 Apr 2010 12:18:17 +0200 (CEST)
+From: [email protected] (Gilles LAMIRAL)
+
+body
+lalala
+EOM
+), 'extract_header: 1') ;
+
+
+
+	return ;
+}
+
+sub decompose_header{
+        my $string = shift ;
+
+        # a hash, for a keyword header KEY value are list of strings [VAL1, VAL1_other, etc]
+        # Think of multiple "Received:" header lines.
+        my $header = {  } ;
+
+        my ($key, $val ) ;
+        my @line = split /\n|\r\n/x, $string ;
+        foreach my $line ( @line ) {
+                #myprint( "DDD $line\n"  ) ;
+                # End of header
+                last if ( $line =~ m{^$}xo ) ;
+                # Key: value
+                if ( $line =~ m/(^[^:]+):\s(.*)/xo ) {
+                        $key = $1 ;
+                        $val = $2 ;
+                        $debugdev and myprint( "DDD KV [$key] [$val]\n"  ) ;
+                        push  @{ $header->{ $key } }, $val  ;
+                # blanc and value => value from previous line continues
+                }elsif( $line =~ m/^(\s+)(.*)/xo ) {
+                        $val = $2 ;
+                        $debugdev and myprint( "DDD  V [$val]\n"  ) ;
+                        @{ $header->{ $key } }[ $LAST ] .= " $val" if $key ;
+                # dirty line?
+                }else{
+                        next ;
+                }
+        }
+
+        #myprint( Data::Dumper->Dump( [ $header ] )  ) ;
+
+        return( $header ) ;
+}
+
+
+sub tests_decompose_header{
+
+        my $header_dec ;
+
+        $header_dec = decompose_header(
+<<'EOH'
+KEY_1: VAL_1
+KEY_2: VAL_2
+  VAL_2_+
+        VAL_2_++
+KEY_3: VAL_3
+KEY_1: VAL_1_other
+KEY_4: VAL_4
+	VAL_4_+
+KEY_5 BLANC:  VAL_5
+
+KEY_6_BAD_BODY: VAL_6
+EOH
+        ) ;
+
+        ok( 'VAL_3'
+        eq $header_dec->{ 'KEY_3' }[0], 'decompose_header: VAL_3' ) ;
+
+        ok( 'VAL_1'
+        eq $header_dec->{ 'KEY_1' }[0], 'decompose_header: VAL_1' ) ;
+
+        ok( 'VAL_1_other'
+        eq $header_dec->{ 'KEY_1' }[1], 'decompose_header: VAL_1_other' ) ;
+
+        ok( 'VAL_2 VAL_2_+ VAL_2_++'
+        eq $header_dec->{ 'KEY_2' }[0], 'decompose_header: VAL_2 VAL_2_+ VAL_2_++' ) ;
+
+        ok( 'VAL_4 VAL_4_+'
+        eq $header_dec->{ 'KEY_4' }[0], 'decompose_header: VAL_4 VAL_4_+' ) ;
+
+        ok( ' VAL_5'
+        eq $header_dec->{ 'KEY_5 BLANC' }[0], 'decompose_header: KEY_5 BLANC' ) ;
+
+        ok( not( defined  $header_dec->{ 'KEY_6_BAD_BODY' }[0]  ), 'decompose_header: KEY_6_BAD_BODY' ) ;
+
+
+        $header_dec = decompose_header(
+<<'EOH'
+Message-Id: <[email protected]>
+Date: Wed, 28 Apr 2010 12:18:17 +0200 (CEST)
+From: [email protected] (Gilles LAMIRAL)
+EOH
+        ) ;
+
+        ok( '<[email protected]>'
+        eq $header_dec->{ 'Message-Id' }[0], 'decompose_header: 1' ) ;
+
+        $header_dec = decompose_header(
+<<'EOH'
+Return-Path: <[email protected]>
+Received: by plume.est.belle (Postfix, from userid 1000)
+        id 120A71624742; Wed, 28 Apr 2010 01:46:40 +0200 (CEST)
+Subject: test:eekahceishukohpe
+EOH
+) ;
+        ok(
+'by plume.est.belle (Postfix, from userid 1000) id 120A71624742; Wed, 28 Apr 2010 01:46:40 +0200 (CEST)'
+        eq $header_dec->{ 'Received' }[0], 'decompose_header: 2' ) ;
+
+        $header_dec = decompose_header(
+<<'EOH'
+Received: from plume (localhost [127.0.0.1])
+        by plume.est.belle (Postfix) with ESMTP id C6EB73F6C9
+        for <gilles@localhost>; Mon, 26 Nov 2007 10:39:06 +0100 (CET)
+Received: from plume [192.168.68.7]
+        by plume with POP3 (fetchmail-6.3.6)
+        for <gilles@localhost> (single-drop); Mon, 26 Nov 2007 10:39:06 +0100 (CET)
+EOH
+        ) ;
+        ok(
+        'from plume (localhost [127.0.0.1]) by plume.est.belle (Postfix) with ESMTP id C6EB73F6C9 for <gilles@localhost>; Mon, 26 Nov 2007 10:39:06 +0100 (CET)'
+        eq $header_dec->{ 'Received' }[0], 'decompose_header: 3' ) ;
+        ok(
+        'from plume [192.168.68.7] by plume with POP3 (fetchmail-6.3.6) for <gilles@localhost> (single-drop); Mon, 26 Nov 2007 10:39:06 +0100 (CET)'
+        eq $header_dec->{ 'Received' }[1], 'decompose_header: 3' ) ;
+
+# Bad header beginning with a blank character
+        $header_dec = decompose_header(
+<<'EOH'
+ KEY_1: VAL_1
+KEY_2: VAL_2
+  VAL_2_+
+        VAL_2_++
+KEY_3: VAL_3
+KEY_1: VAL_1_other
+EOH
+        ) ;
+
+        ok( 'VAL_3'
+        eq $header_dec->{ 'KEY_3' }[0], 'decompose_header: Bad header VAL_3' ) ;
+
+        ok( 'VAL_1_other'
+        eq $header_dec->{ 'KEY_1' }[0], 'decompose_header: Bad header VAL_1_other' ) ;
+
+        ok( 'VAL_2 VAL_2_+ VAL_2_++'
+        eq $header_dec->{ 'KEY_2' }[0], 'decompose_header: Bad header VAL_2 VAL_2_+ VAL_2_++' ) ;
+
+	return ;
+}
+
+sub epoch {
+        # incoming format:
+	# internal date 24-Aug-2010 16:00:00 +0200
+
+        # outgoing format: epoch
+
+
+        my $d = shift ;
+        return(q{}) if not defined $d;
+
+        my ( $mday, $month, $year, $hour, $min, $sec, $sign, $zone_h, $zone_m ) ;
+        my $time ;
+
+        if ( $d =~ m{(\d{1,2})-([A-Z][a-z]{2})-(\d{4})\s(\d{2}):(\d{2}):(\d{2})\s((?:\+|-))(\d{2})(\d{2})}xo ) {
+                #myprint( "internal: [$1][$2][$3][$4][$5][$6][$7][$8][$9]\n"  ) ;
+                ( $mday, $month, $year, $hour, $min, $sec, $sign, $zone_h, $zone_m )
+                =  ( $1,   $2,     $3,    $4,    $5,  $6,    $7,     $8,     $9 ) ;
+                #myprint( "( $mday, $month, $year, $hour, $min, $sec, $sign, $zone_h, $zone_m )\n"  ) ;
+
+                $sign = +1 if ( '+' eq $sign ) ;
+                $sign = $MINUS_ONE if ( '-' eq $sign ) ;
+
+                $time = timegm( $sec, $min, $hour, $mday, $month_abrev{$month}, $year )
+                        - $sign * ( 3600 * $zone_h + 60 * $zone_m ) ;
+
+                #myprint( "$time ", scalar localtime($time), "\n");
+        }
+        return( $time ) ;
+}
+
+sub tests_epoch {
+        ok( '1282658400' eq epoch( '24-Aug-2010 16:00:00 +0200' ), 'epoch 24-Aug-2010 16:00:00 +0200 -> 1282658400' ) ;
+        ok( '1282658400' eq epoch( '24-Aug-2010 14:00:00 +0000' ), 'epoch 24-Aug-2010 14:00:00 +0000 -> 1282658400' ) ;
+        ok( '1282658400' eq epoch( '24-Aug-2010 12:00:00 -0200' ), 'epoch 24-Aug-2010 12:00:00 -0200 -> 1282658400' ) ;
+        ok( '1282658400' eq epoch( '24-Aug-2010 16:01:00 +0201' ), 'epoch 24-Aug-2010 16:01:00 +0201 -> 1282658400' ) ;
+        ok( '1282658400' eq epoch( '24-Aug-2010 14:01:00 +0001' ), 'epoch 24-Aug-2010 14:01:00 +0001 -> 1282658400' ) ;
+
+        ok( '1280671200' eq epoch( '1-Aug-2010 16:00:00 +0200' ), 'epoch 1-Aug-2010 16:00:00 +0200 -> 1280671200' ) ;
+        ok( '1280671200' eq epoch( '1-Aug-2010 14:00:00 +0000' ), 'epoch 1-Aug-2010 14:00:00 +0000 -> 1280671200' ) ;
+        ok( '1280671200' eq epoch( '1-Aug-2010 12:00:00 -0200' ), 'epoch 1-Aug-2010 12:00:00 -0200 -> 1280671200' ) ;
+        ok( '1280671200' eq epoch( '1-Aug-2010 16:01:00 +0201' ), 'epoch 1-Aug-2010 16:01:00 +0201 -> 1280671200' ) ;
+        ok( '1280671200' eq epoch( '1-Aug-2010 14:01:00 +0001' ), 'epoch 1-Aug-2010 14:01:00 +0001 -> 1280671200' ) ;
+	return ;
+}
+
+sub add_header {
+	my $header_uid = shift || 'mistake' ;
+	my $header_Message_Id = 'Message-Id: <' . $header_uid . '@imapsync>' ;
+        return( $header_Message_Id ) ;
+}
+
+sub tests_add_header {
+	ok( 'Message-Id: <mistake@imapsync>' eq add_header(), 'add_header no arg' ) ;
+	ok( 'Message-Id: <123456789@imapsync>' eq add_header(123456789), 'add_header 123456789' ) ;
+
+	return ;
+}
+
+sub tests_Banner{
+
+	my $imap = Mail::IMAPClient->new(  ) ;
+        ok( 'lalala' eq $imap->Banner('lalala'), 'Banner set lalala' ) ;
+        ok( 'lalala' eq $imap->Banner(), 'Banner returns lalala' ) ;
+	return ;
+}
+
+
+
+
+sub max_line_length {
+	my $string = shift ;
+        my $max = 0 ;
+
+        while ( $string =~ m/([^\n]*\n?)/msxg ) {
+        	$max = max( $max, length $1 ) ;
+        }
+	return( $max ) ;
+}
+
+sub tests_max_line_length {
+	ok( 0 == max_line_length( q{} ), 'max_line_length: 0 == null string' ) ;
+	ok( 1 == max_line_length( "\n" ), 'max_line_length: 1 == \n' ) ;
+	ok( 1 == max_line_length( "\n\n" ), 'max_line_length: 1 == \n\n' ) ;
+	ok( 1 == max_line_length( "\n" x 500 ), 'max_line_length: 1 == 500 \n' ) ;
+	ok( 1 == max_line_length( 'a' ), 'max_line_length: 1 == a' ) ;
+	ok( 2 == max_line_length( "a\na" ), 'max_line_length: 2 == a\na' ) ;
+	ok( 2 == max_line_length( "a\na\n" ), 'max_line_length: 2 == a\na\n' ) ;
+	ok( 3 == max_line_length( "a\nab\n" ), 'max_line_length: 3 == a\nab\n' ) ;
+	ok( 3 == max_line_length( "a\nab\n" x 10000 ), 'max_line_length: 3 == 10000 a\nab\n' ) ;
+	ok( 3 == max_line_length( "a\nab\nabc" ), 'max_line_length: 3 == a\nab\nabc' ) ;
+
+	ok( 4 == max_line_length( "a\nab\nabc\n" ), 'max_line_length: 4 == a\nab\nabc\n' ) ;
+	ok( 5 == max_line_length( "a\nabcd\nabc\n" ), 'max_line_length: 5 == a\nabcd\nabc\n' ) ;
+	ok( 5 == max_line_length( "a\nabcd\nabc\n\nabcd\nabcd\nabcd\nabcd\nabcd\nabcd\nabcd\nabcd" ), 'max_line_length: 5 == a\nabcd\nabc\n\nabcd\nabcd\nabcd\nabcd\nabcd\nabcd\nabcd\nabcd' ) ;
+	return ;
+}
+
+sub setlogfile {
+        my( $mysync ) = shift ;
+        $mysync->{logdir}  = defined $mysync->{logdir}  ? $mysync->{logdir}  : 'LOG_imapsync' ;
+        $mysync->{logfile} = defined $mysync->{logfile} ? "$mysync->{logdir}/$mysync->{logfile}" :
+                logfile( $mysync->{timestart}, $mysync->{user2}, $mysync->{logdir} ) ;
+        #myprint( "logdir  = $mysync->{logdir}\n"  ) ;
+        #myprint( "logfile = $mysync->{logfile}\n"  ) ;
+        return( $mysync->{logfile} ) ;
+}
+
+sub tests_setlogfile {
+        my $mysync = {
+                timestart => 2,
+                user2     => 'user2',
+        } ;
+
+        ok( 'LOG_imapsync/1970_01_01_01_00_02_user2.txt' eq setlogfile( $mysync ),
+                'setlogfile: default is like LOG_imapsync/1970_01_01_01_00_02_user2.txt' ) ;
+
+        $mysync->{logdir}  = undef ;
+        $mysync->{logfile} = undef ;
+        ok( 'LOG_imapsync/1970_01_01_01_00_02_user2.txt' eq setlogfile( $mysync ),
+                'setlogfile: logdir undef, LOG_imapsync/1970_01_01_01_00_02_user2.txt' ) ;
+
+        $mysync->{logdir} = q{} ;
+        $mysync->{logfile} = undef ;
+        ok( '1970_01_01_01_00_02_user2.txt' eq setlogfile( $mysync ),
+                'setlogfile: logdir empty, 1970_01_01_01_00_02_user2.txt' ) ;
+
+        $mysync->{logdir} = 'vallogdir' ;
+        $mysync->{logfile} = undef ;
+        ok( 'vallogdir/1970_01_01_01_00_02_user2.txt' eq setlogfile( $mysync ),
+                'setlogfile: logdir vallogdir, vallogdir/1970_01_01_01_00_02_user2.txt' ) ;
+
+        $mysync->{logdir}  = 'vallogdir' ;
+        $mysync->{logfile} = 'vallogfile.txt' ;
+        ok( 'vallogdir/vallogfile.txt' eq setlogfile( $mysync ),
+                'setlogfile: logdir vallogdir, logfile vallogfile.txt, vallogdir/vallogfile.txt' ) ;
+
+        return ;
+}
+
+
+sub logfile {
+	my ( $time, $suffix, $dir ) = @_ ;
+
+	$time   ||= 0 ;
+	$suffix ||= q{} ;
+	my $sep_suffix = ( $suffix ) ? '_' : q{} ;
+        $dir    ||= q{} ;
+	my $sep_dir = ( $dir ) ? '/' : q{} ;
+
+	my $date_str = POSIX::strftime( '%Y_%m_%d_%H_%M_%S', localtime $time ) ;
+        my $logfile = "${dir}${sep_dir}${date_str}${sep_suffix}${suffix}.txt" ;
+	$debug and myprint( "date_str: $date_str\n"  ) ;
+	$debug and myprint( "logfile : $logfile\n"  ) ;
+	return( $logfile ) ;
+}
+
+sub tests_logfile {
+	SKIP: {
+		# Too hard to have a well known timezone on Windows
+		skip( 'Too hard to have a well known timezone on Windows', 6 ) if ( 'MSWin32' eq $OSNAME ) ;
+
+		local $ENV{TZ} = 'GMT' ;
+		{ POSIX::tzset unless ('MSWin32' eq $OSNAME) ;
+			ok( '1970_01_01_00_00_00.txt' eq logfile(  ),           'logfile: no args    => 1970_01_01_00_00_00.txt' ) ;
+			ok( '1970_01_01_00_00_00.txt' eq logfile( 0 ),          'logfile: 0          => 1970_01_01_00_00_00.txt' ) ;
+			ok( '1970_01_01_00_01_01.txt' eq logfile( 61 ),         'logfile: 0          => 1970_01_01_00_01_01.txt' ) ;
+			ok( '2010_08_24_14_00_00.txt' eq logfile( 1282658400 ), 'logfile: 1282658400 => 2010_08_24_14_00_00.txt' ) ;
+			ok( '2010_08_24_14_01_01.txt' eq logfile( 1282658461 ), 'logfile: 1282658461 => 2010_08_24_14_01_01.txt' ) ;
+			ok( '2010_08_24_14_01_01_poupinette.txt' eq logfile( 1282658461, 'poupinette' ), 'logfile: 1282658461 poupinette => 2010_08_24_14_01_01_poupinette.txt' ) ;
+                }
+		POSIX::tzset unless ('MSWin32' eq $OSNAME) ;
+	} ;
+	return ;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+sub tests_million_folders_baby_2 {
+	my %long ;
+	@long{ 1 .. 900_000 } = (1) x 900_000 ;
+	#myprint( %long, "\n"  ) ;
+	my $pasglop = 0 ;
+	foreach my $elem (  1 .. 900_000 ) {
+		#$debug and myprint( "$elem "  ) ;
+		if ( not exists  $long{ $elem }  ) {
+			$pasglop++ ;
+		}
+	}
+        ok( 0 == $pasglop, 'tests_million_folders_baby_2: search among 900_000' ) ;
+	# myprint( "$pasglop\n"  ) ;
+        return ;
+}
+
+
+
+sub tests_always_fail {
+	ok( 0 == 1, '0 == 1' ) ;
+	ok( 1 == 1, '1 == 1' ) ;
+        return ;
+}
+
+sub logfileprepa {
+	my $logfile = shift ;
+
+	my $dirname = dirname( $logfile ) ;
+	is_valid_directory( $dirname ) || return( 0 ) ;
+	return( 1 ) ;
+}
+
+sub teelaunch {
+        my $mysync = shift ;
+	my $logfile = $mysync->{logfile} ;
+	logfileprepa( $logfile ) || croak "Error no valid directory to write log file $logfile : $!" ;
+	my $logfile_handle ;
+	open $logfile_handle, '>', $logfile
+	  or croak( "Can not open $logfile for write: $!" ) ;
+	my $tee = IO::Tee->new( $logfile_handle, \*STDOUT ) ;
+	*STDERR = *$tee{IO} ;
+	select $tee ;
+        $tee->autoflush( 1 ) ;
+        $mysync->{logfile_handle} = $logfile_handle ;
+        $mysync->{tee} = $tee ;
+	return $logfile_handle ;
+}
+
+sub getpwuid_any_os {
+        my $uid = shift ;
+
+        return( scalar  getlogin ) if ( 'MSWin32' eq $OSNAME ) ; # Windows system
+        return( scalar  getpwuid $uid ) ; # Unix system
+}
+
+
+
+sub usage {
+	my $localhost_info = localhost_info();
+	my $thank = thank_author();
+	my $imapsync_release = q{};
+	$imapsync_release = check_last_release() if (not defined $releasecheck);
+        my $escape_char = ( 'MSWin32' eq $OSNAME ) ? '^' : '\\';
+        myprint( <<"EOF" ) ;
+
+ usage: $0 [options]
+
+ Several options are mandatory.
+ str means string
+ int means integer
+ reg means regular expression
+ cmd means command
+
+ --dry               : Makes imapsync doing nothing, just print what would
+                       be done without --dry.
+
+ --host1        str  : Source or "from" imap server. Mandatory.
+ --port1        int  : Port to connect on host1. Default is 143, 993 if --ssl1
+ --user1        str  : User to login on host1. Mandatory.
+ --showpasswords     : Shows passwords on output instead of "MASKED".
+                       Useful to restart a complete run by just reading the log.
+ --password1    str  : Password for the user1.
+ --host2        str  : "destination" imap server. Mandatory.
+ --port2        int  : Port to connect on host2. Default is 143, 993 if --ssl2
+ --user2        str  : User to login on host2. Mandatory.
+ --password2    str  : Password for the user2.
+
+ --passfile1    str  : Password file for the user1. It must contain the
+                       password on the first line. This option avoids to show
+                       the password on the command line like --password1 does.
+ --passfile2    str  : Password file for the user2. Contains the password.
+
+ --ssl1              : Use a SSL connection on host1.
+ --ssl2              : Use a SSL connection on host2.
+ --tls1              : Use a TLS connection on host1.
+ --tls2              : Use a TLS connection on host2.
+ --debugssl     int  : SSL debug mode from 0 to 4.
+ --sslargs1     str  : Pass any ssl parameter for host1 ssl or tls connection. Example:
+                       --sslargs1 SSL_verify_mode=1 --sslargs1 SSL_version=SSLv3
+                       See all possibilities in the new() method of IO::Socket::SSL
+                       http://search.cpan.org/perldoc?IO::Socket::SSL#Description_Of_Methods
+ --sslargs2     str  : Pass any ssl parameter for host2 ssl or tls connection.
+                       See --sslargs1
+
+ --timeout1     int  : Connection timeout in seconds for host1.
+                       Default is 120 and 0 means no timeout at all.
+ --timeout2     int  : Connection timeout in seconds for host2.
+                       Default is 120 and 0 means no timeout at all.
+
+ --authmech1    str  : Auth mechanism to use with host1:
+                       PLAIN, LOGIN, CRAM-MD5 etc. Use UPPERCASE.
+ --authmech2    str  : Auth mechanism to use with host2. See --authmech1
+
+ --authuser1    str  : User to auth with on host1 (admin user).
+                       Avoid using --authmech1 SOMETHING with --authuser1.
+ --authuser2    str  : User to auth with on host2 (admin user).
+ --proxyauth1        : Use proxyauth on host1. Requires --authuser1.
+                       Required by Sun/iPlanet/Netscape IMAP servers to
+                       be able to use an administrative user.
+ --proxyauth2        : Use proxyauth on host2. Requires --authuser2.
+
+ --authmd51          : Use MD5 authentification for host1.
+ --authmd52          : Use MD5 authentification for host2.
+ --domain1      str  : Domain on host1 (NTLM authentication).
+ --domain2      str  : Domain on host2 (NTLM authentication).
+
+
+ --folder       str  : Sync this folder.
+ --folder       str  : and this one, etc.
+ --folderrec    str  : Sync this folder recursively.
+ --folderrec    str  : and this one, etc.
+
+ --folderfirst  str  : Sync this folder first. --folderfirst "Work"
+ --folderfirst  str  : then this one, etc.
+ --folderlast   str  : Sync this folder last. --folderlast "[Gmail]/All Mail"
+ --folderlast   str  : then this one, etc.
+
+ --nomixfolders      : Do not merge folders when host1 is case sensitive
+                       while host2 is not (like Exchange). Only the first
+                       similar folder is synced (ex: Sent SENT sent -> Sent).
+
+ --skipemptyfolders  : Empty host1 folders are not created on host2.
+
+ --include      reg  : Sync folders matching this regular expression
+ --include      reg  : or this one, etc.
+                       in case both --include --exclude options are
+                       use, include is done before.
+ --exclude      reg  : Skips folders matching this regular expression
+                       Several folders to avoid:
+                        --exclude 'fold1|fold2|f3' skips fold1, fold2 and f3.
+ --exclude      reg  : or this one, etc.
+
+ --subfolder2   str  : Move whole host1 folders hierarchy under this
+                       host2 folder  str    .
+                       It does it by adding two --regextrans2 options before
+                       all others. Add --debug to see what's really going on.
+
+ --automap           : guesses folders mapping, for folders like
+                       "Sent", "Junk", "Drafts", "All", "Archive", "Flagged".
+ --f1f2    str1=str2 : Force folder str1 to be synced to str2,
+                       --f1f2 overrides --automap and --regextrans2.
+ --regextrans2  reg  : Apply the whole regex to each destination folders.
+ --regextrans2  reg  : and this one. etc.
+                       When you play with the --regextrans2 option, first
+                       add also the safe options --dry --justfolders
+                       Then, when happy, remove --dry, remove --justfolders.
+                       Have in mind that --regextrans2 is applied after prefix
+                       and separator inversion. For examples see
+                       http://imapsync.lamiral.info/FAQ.d/FAQ.Folders_Mapping.txt
+
+ --tmpdir       str  : Where to store temporary files and subdirectories.
+                       Will be created if it doesn't exist.
+                       Default is system specific, Unix is /tmp but
+                       it's often small and deleted at reboot.
+                       --tmpdir /var/tmp should be better.
+ --pidfile      str  : The file where imapsync pid is written.
+ --pidfilelocking    : Abort if pidfile already exists. Usefull to avoid
+                       concurrent transfers on the same mailbox.
+
+ --nolog             : Turn off logging on file
+ --logfile      str  : Change the default log filename (can be dirname/filename).
+ --logdir       str  : Change the default log directory. Default is LOG_imapsync
+
+ --prefix1      str  : Remove prefix to all destination folders
+                       (usually INBOX. or INBOX/ or an empty string "")
+                       you have to use --prefix1 if host1 imap server
+                       does not have NAMESPACE capability, so imapsync
+                       suggests to use it. All other cases are bad.
+ --prefix2      str  : Add prefix to all host2 folders. See --prefix1
+ --sep1         str  : Host1 separator in case NAMESPACE is not supported.
+ --sep2         str  : Host2 separator in case NAMESPACE is not supported.
+
+ --skipmess     reg  : Skips messages maching the regex.
+                       Example: 'm/[\\x80-ff]/' # to avoid 8bits messages.
+                       --skipmess is applied before --regexmess
+ --skipmess     reg  : or this one, etc.
+
+ --pipemess     cmd  : Apply this cmd command to each message content
+                       before the copy.
+ --pipemess     cmd  : and this one, etc.
+
+ --disarmreadreceipts : Disarms read receipts (host2 Exchange issue)
+
+ --regexmess    reg  : Apply the whole regex to each message before transfer.
+                       Example: 's/\\000/ /g' # to replace null by space.
+ --regexmess    reg  : and this one, etc.
+
+ --regexflag    reg  : Apply the whole regex to each flags list.
+                       Example: 's/\"Junk"//g' # to remove "Junk" flag.
+ --regexflag    reg  : and this one, etc.
+
+ --delete            : Deletes messages on host1 server after a successful
+                       transfer. Option --delete has the following behavior:
+                       it marks messages as deleted with the IMAP flag
+                       \\Deleted, then messages are really deleted with an
+                       EXPUNGE IMAP command.
+
+ --delete2           : Delete messages in host2 that are not in
+                       host1 server. Useful for backup or pre-sync.
+ --delete2duplicates : Delete messages in host2 that are duplicates.
+                       Works only without --useuid since duplicates are
+                       detected with an header part of each message.
+
+ --delete2folders    : Delete folders in host2 that are not in host1 server.
+                       For safety, first try it like this (it is safe):
+                       --delete2folders --dry --justfolders --nofoldersizes
+ --delete2foldersonly   reg : Deleted only folders matching regex.
+                              Example: --delete2foldersonly "/^Junk\$|^INBOX.Junk\$/"
+ --delete2foldersbutnot reg : Do not delete folders matching regex.
+                              Example: --delete2foldersbutnot "/Tasks\$|Contacts\$|Foo\$/"
+ --noexpunge         : Do not expunge messages on host1.
+                       Expunge really deletes messages marked deleted.
+                       Expunge is made at the beginning, on host1 only.
+                       Newly transferred messages are also expunged if
+                       option --delete is given.
+                       No expunge is done on host2 account (unless --expunge2)
+ --expunge1          : Expunge messages on host1 after messages transfer.
+ --expunge2          : Expunge messages on host2 after messages transfer.
+ --uidexpunge2       : uidexpunge messages on the host2 account
+                       that are not on the host1 account, requires --delete2
+ --nomixfolders      : Avoid merging folders that are considered different on
+                       host1 but the same on destination host2 because of
+                       case sensitivities and insensitivities.
+
+ --syncinternaldates : Sets the internal dates on host2 same as host1.
+                       Turned on by default. Internal date is the date
+                       a message arrived on a host (mtime).
+ --idatefromheader   : Sets the internal dates on host2 same as the
+                       "Date:" headers.
+
+ --maxsize      int  : Skip messages larger  (or equal) than  int  bytes
+ --minsize      int  : Skip messages smaller (or equal) than  int  bytes
+ --maxage       int  : Skip messages older than  int  days.
+                       final stats (skipped) don't count older messages
+                       see also --minage
+ --minage       int  : Skip messages newer than  int  days.
+                       final stats (skipped) don't count newer messages
+                       You can do (+ are the messages selected):
+                       past|----maxage+++++++++++++++>now
+                       past|+++++++++++++++minage---->now
+                       past|----maxage+++++minage---->now (intersection)
+                       past|++++minage-----maxage++++>now (union)
+
+ --search       str  : Selects only messages returned by this IMAP SEARCH
+                       command. Applied on both sides.
+ --search1      str  : Same as --search for selecting host1 messages only.
+ --search2      str  : Same as --search for selecting host2 messages only.
+                       --search CRIT equals --search1 CRIT --search2 CRIT
+
+ --exitwhenover int  : Stop syncing when total bytes transferred reached.
+                       Gmail per day allows
+                       2500000000 = 2.5 GB downloaded from Gmail as host2
+                        500000000 = 500 MB uploaded to Gmail as host1.
+
+ --maxlinelength int : skip messages with a line length longer than  int  bytes.
+                       RFC 2822 says it must be no more than 1000 bytes.
+
+ --useheader    str  : Use this header to compare messages on both sides.
+                       Ex: Message-ID or Subject or Date.
+ --useheader    str    and this one, etc.
+
+ --subscribed        : Transfers subscribed folders.
+ --subscribe         : Subscribe to the folders transferred on the
+                       host2 that are subscribed on host1. On by default.
+ --subscribeall      : Subscribe to the folders transferred on the
+                       host2 even if they are not subscribed on host1.
+
+ --nofoldersizes     : Do not calculate the size of each folder in bytes
+                       and message counts. Default is to calculate them.
+ --nofoldersizesatend: Do not calculate the size of each folder in bytes
+                       and message counts at the end. Default is on.
+ --justfoldersizes   : Exit after having printed the folder sizes.
+
+ --syncacls          : Synchronises acls (Access Control Lists).
+ --nosyncacls        : Does not synchronize acls. This is the default.
+                       Acls in IMAP are not standardized, be careful.
+
+ --usecache          : Use cache to speedup.
+ --nousecache        : Do not use cache. Caveat: --useuid --nousecache creates
+                       duplicates on multiple runs.
+ --useuid            : Use uid instead of header as a criterium to recognize
+                       messages. Option --usecache is then implied unless
+                       --nousecache is used.
+
+ --debug             : Debug mode.
+ --debugfolders      : Debug mode for the folders part only.
+ --debugcontent      : Debug content of the messages transfered. Huge ouput.
+ --debugflags        : Debug mode for flags.
+ --debugimap1        : IMAP debug mode for host1. Very verbose.
+ --debugimap2        : IMAP debug mode for host2. Very verbose.
+ --debugimap         : IMAP debug mode for host1 and host2.
+ --debugmemory       : Debug mode showing memory consumption after each copy.
+
+ --errorsmax     int : Exit when int number of errors is reached. Default is 50.
+
+ --tests             : Run local non-regression tests. Exit code 0 means all ok.
+ --testslive         : Run a live test with test1.lamiral.info imap server.
+                       Useful to check the basics. Needs internet connexion.
+
+ --version           : Print only software version.
+ --noreleasecheck    : Do not check for new imapsync release (a http request).
+ --releasecheck      : Check for new imapsync release (a http request).
+ --noid              : Do not send/receive ID command to imap servers.
+ --justconnect       : Just connect to both servers and print useful
+                       information. Need only --host1 and --host2 options.
+ --justlogin         : Just login to both host1 and host2 with users
+                       credentials, then exit.
+ --justfolders       : Do only things about folders (ignore messages).
+
+ --help              : print this help.
+
+ Example: to synchronize imap account "test1" on "test1.lamiral.info"
+                     to  imap account "test2" on "test2.lamiral.info"
+                     with test1 password "secret1"
+                     and  test2 password "secret2"
+
+ $0 $escape_char
+    --host1 test1.lamiral.info --user1 test1 --password1 secret1 $escape_char
+    --host2 test2.lamiral.info --user2 test2 --password2 secret2
+
+$localhost_info
+$rcs
+$imapsync_release
+
+$thank
+EOF
+	return( 1 ) ;
+}
+
+
+sub usage_complete {
+	myprint( <<'EOF'  ) ;
+--skipheader   reg     : Don't take into account header keyword
+                         matching  reg    ex: --skipheader 'X.*'
+
+--skipsize             : Don't take message size into account to compare
+                         messages on both sides. On by default.
+			 Use --no-skipsize for using size comparaison.
+--allowsizemismatch    : allow RFC822.SIZE != fetched msg size
+                         consider also --skipsize to avoid duplicate messages
+                         when running syncs more than one time per mailbox
+
+--reconnectretry1  int : reconnect to host1 if connection is lost up to
+                          int  times per imap command (default is 3)
+--reconnectretry2  int : same as --reconnectretry1 but for host2
+--split1      int      : split the requests in several parts on host1.
+                          int  is the number of messages handled per request.
+                         default is like --split1 500.
+--split2      int      : same thing on host2.
+--nofixInboxINBOX      : Don't fix Inbox INBOX mapping.
+EOF
+	return ;
+}
+
+
+
+sub get_options {
+	# In CGI context arguments are not in @ARGV but in QUERY_STRING variable (with GET).
+	my $numopt = scalar  @ARGV  || length $ENV{'QUERY_STRING'} ;
+	my $argv   = join "\x00", @ARGV ;
+
+	if ( $argv =~ m/-delete\x002/x ) {
+		myprint( "May be you mean --delete2 instead of --delete 2\n"  ) ;
+		exit 1 ;
+	}
+	$sync->{f1f2} = {} ;
+        my $opt_ret = Imapsync::Getopt::Long::GetOptions(
+        'debug!'        => \$debug,
+        'debuglist!'    => \$debuglist,
+        'debugcontent!' => \$debugcontent,
+        'debugsleep=f'  => \$sync->{debugsleep},
+        'debugflags!'   => \$debugflags,
+        'debugimap!'    => \$debugimap,
+        'debugimap1!'   => \$debugimap1,
+        'debugimap2!'   => \$debugimap2,
+        'debugdev!'     => \$debugdev,
+        'debugmemory!'  => \$sync->{debugmemory},
+        'debugfolders!' => \$sync->{debugfolders},
+        'debugssl=i'    => \$sync->{debugssl},
+	'debugbasket=s' => \@debugbasket,
+	'debugcgi!'     => \$debugcgi,
+        'host1=s'     => \$host1,
+        'host2=s'     => \$host2,
+        'port1=i'     => \$port1,
+        'port2=i'     => \$port2,
+	'inet4'       => \$sync->{inet4},
+	'inet6'       => \$sync->{inet6},
+        'user1=s'     => \$user1,
+        'user2=s'     => \$user2,
+        'domain1=s'   => \$domain1,
+        'domain2=s'   => \$domain2,
+        'password1=s' => \$password1,
+        'password2=s' => \$password2,
+        'passfile1=s' => \$passfile1,
+        'passfile2=s' => \$passfile2,
+        'authmd5!'    => \$authmd5,
+        'authmd51!'   => \$authmd51,
+        'authmd52!'   => \$authmd52,
+        'sep1=s'      => \$sep1,
+        'sep2=s'      => \$sep2,
+        'folder=s'    => \@folder,
+        'folderrec=s' => \@folderrec,
+        'include=s'   => \@include,
+        'exclude=s'   => \@exclude,
+        'folderfirst=s' => \@folderfirst,
+        'folderlast=s' => \@folderlast,
+        'prefix1=s'   => \$prefix1,
+        'prefix2=s'   => \$prefix2,
+	'subfolder2=s' => \$subfolder2,
+        'fixslash2!'   => \$fixslash2,
+        'fixInboxINBOX!' => \$fixInboxINBOX,
+        'regextrans2=s' => \@regextrans2,
+        'mixfolders!' => \$mixfolders,
+        'skipemptyfolders!' => \$skipemptyfolders,
+        'regexmess=s' => \@regexmess,
+        'skipmess=s' => \@skipmess,
+        'pipemess=s' => \@pipemess,
+	'pipemesscheck!' => \$pipemesscheck,
+        'disarmreadreceipts!' => \$disarmreadreceipts,
+        'regexflag=s' => \@regexflag,
+        'filterflags!' => \$filterflags,
+        'flagscase!'  => \$flagscase,
+        'syncflagsaftercopy!' => \$syncflagsaftercopy,
+        'delete|delete1!' => \$delete,
+        'delete2!'    => \$delete2,
+        'delete2duplicates!' => \$delete2duplicates,
+        'delete2folders!'    => \$delete2folders,
+        'delete2foldersonly=s' => \$delete2foldersonly,
+        'delete2foldersbutnot=s' => \$delete2foldersbutnot,
+        'syncinternaldates!' => \$syncinternaldates,
+        'idatefromheader!'   => \$idatefromheader,
+        'syncacls!'   => \$syncacls,
+        'maxsize=i'   => \$maxsize,
+        'minsize=i'   => \$minsize,
+        'maxage=i'    => \$maxage,
+        'minage=i'    => \$minage,
+        'search=s'    => \$search,
+        'search1=s'   => \$search1,
+        'search2=s'   => \$search2,
+        'foldersizes!' => \$foldersizes,
+        'foldersizesatend!' => \$foldersizesatend,
+        'dry!'        => \$dry,
+        'expunge!'    => \$expunge,
+        'expunge1!'    => \$expunge1,
+        'expunge2!'    => \$expunge2,
+        'uidexpunge2!' => \$uidexpunge2,
+        'subscribed!' => \$subscribed,
+        'subscribe!'  => \$subscribe,
+        'subscribeall|subscribe_all!'  => \$subscribeall,
+        'justbanner!' => \$justbanner,
+        'justconnect!'=> \$justconnect,
+        'justfolders!'=> \$justfolders,
+        'justfoldersizes!' => \$justfoldersizes,
+        'fast!'       => \$fast,
+        'version'     => \$version,
+        'help'        => \$help,
+        'timeout=i'   => \$timeout,
+        'timeout1=i'   => \$sync->{h1}->{timeout},
+        'timeout2=i'   => \$sync->{h2}->{timeout},
+        'skipheader=s' => \$skipheader,
+        'useheader=s' => \@useheader,
+        'wholeheaderifneeded!'   => \$wholeheaderifneeded,
+        'messageidnodomain!' => \$messageidnodomain,
+        'skipsize!'   => \$skipsize,
+        'allowsizemismatch!' => \$allowsizemismatch,
+        'fastio1!'     => \$fastio1,
+        'fastio2!'     => \$fastio2,
+        'ssl1!'        => \$ssl1,
+        'ssl2!'        => \$ssl2,
+        'ssl1_ssl_version=s' => \$sync->{h1}->{sslargs}->{SSL_version},
+        'ssl2_ssl_version=s' => \$sync->{h2}->{sslargs}->{SSL_version},
+        'sslargs1=s%'        => \$sync->{h1}->{sslargs},
+        'sslargs2=s%'        => \$sync->{h2}->{sslargs},
+        'tls1!'        => \$tls1,
+        'tls2!'        => \$tls2,
+        'uid1!'        => \$uid1,
+        'uid2!'        => \$uid2,
+        'authmech1=s' => \$authmech1,
+        'authmech2=s' => \$authmech2,
+        'authuser1=s' => \$authuser1,
+        'authuser2=s' => \$authuser2,
+        'proxyauth1'  => \$proxyauth1,
+        'proxyauth2'  => \$proxyauth2,
+        'split1=i'    => \$split1,
+        'split2=i'    => \$split2,
+        'buffersize=i' => \$buffersize,
+        'reconnectretry1=i' => \$reconnectretry1,
+        'reconnectretry2=i' => \$reconnectretry2,
+        'tests!'       => \$tests,
+        'testsdebug|tests_debug!' => \$testsdebug,
+        'testslive!'   => \$testslive,
+        'justlogin!'  => \$justlogin,
+        'tmpdir=s'    => \$tmpdir,
+        'pidfile=s'    => \$sync->{pidfile},
+        'pidfilelocking!' => \$sync->{pidfilelocking},
+        'releasecheck!' => \$releasecheck,
+        'modulesversion|modules_version!' => \$modulesversion,
+        'usecache!'    => \$usecache,
+        'cacheaftercopy!' => \$cacheaftercopy,
+        'debugcache!' => \$debugcache,
+        'useuid!'     => \$useuid,
+        'addheader!'  => \$addheader,
+        'exitwhenover=i' => \$exitwhenover,
+        'checkselectable!' => \$checkselectable,
+        'checkmessageexists!' => \$checkmessageexists,
+        'expungeaftereach!' => \$expungeaftereach,
+        'abletosearch!' => \$abletosearch,
+        'showpasswords!' => \$showpasswords,
+        'maxlinelength=i' => \$maxlinelength,
+        'maxlinelengthcmd=s' => \$maxlinelengthcmd,
+        'minmaxlinelength=i' => \$minmaxlinelength,
+        'debugmaxlinelength!' => \$debugmaxlinelength,
+        'fixcolonbug!'           => \$fixcolonbug,
+        'create_folder_old!'     => \$create_folder_old,
+        'maxmessagespersecond=f' => \$maxmessagespersecond,
+        'maxbytespersecond=i'    => \$maxbytespersecond,
+        'skipcrossduplicates!'   => \$skipcrossduplicates,
+        'debugcrossduplicates!'  => \$debugcrossduplicates,
+        'log!'                   => \$sync->{log},
+        'logfile=s'        => \$sync->{logfile},
+        'logdir=s'         => \$sync->{logdir},
+        'errorsmax=i'      => \$sync->{errorsmax},
+        'errorsdump!'      => \$sync->{errorsdump},
+        'fetch_hash_set=s' => \$fetch_hash_set,
+        'automap!'         => \$sync->{automap},
+        'justautomap!'     => \$sync->{justautomap},
+        'id!'              => \$sync->{id},
+        'f1f2=s%'          => \$sync->{f1f2},
+        'justfolderlists!' => \$sync->{justfolderlists},
+        'delete1emptyfolders' => \$sync->{delete1emptyfolders},
+        ) ;
+
+
+	$debugcgi and myprint( map { "$_ => $ENV{$_}\n" } sort keys  %ENV   ) ;
+	$debugcgi and myprint( "@debugbasket\n"  ) ;
+        $debug and myprint( "get options: [$opt_ret]\n"  ) ;
+
+        # just the version
+        myprint( imapsync_version(  ), "\n" ) and exit 0 if ( $version ) ;
+        # $tmpdir is used in tests_pipemess()
+	$tmpdir ||= File::Spec->tmpdir(  ) ;
+	if ( $tests or $testsdebug ) {
+		$test_builder = Test::More->builder ;
+		if ( $tests ) { tests(  ) ; }
+		if ( $testsdebug ) { testsdebug(  ) ; }
+		#$test_builder->reset(  ) ;
+		exit ;
+	}
+
+	#$help = 1 if ! $numopt;
+	load_modules(  );
+
+	# exit with --help option or no option at all
+	$debug and myprint( "numopt:$numopt\n"  ) ;
+        usage(  ) and exit  if ( $help or not $numopt ) ;
+
+	# don't go on if options are not all known.
+        exit $EX_USAGE unless ( $opt_ret ) ;
+
+	# init live varaiables
+	testslive(  ) if ( $testslive ) ;
+
+	return ;
+}
+
+sub testslive {
+	$host1 = 'test1.lamiral.info' ;
+	$user1 = 'test1' ;
+	$password1 = 'secret1' ;
+	$host2 = 'test2.lamiral.info' ;
+	$user2 = 'test2' ;
+	$password2 ='secret2' ;
+	return ;
+}
+
+sub testsdebug {
+      SKIP: {
+                skip 'No test in normal run' if ( not $testsdebug ) ;
+                #tests_bytes_display_string(  ) ;
+                #tests_ucsecond(  ) ;
+                #tests_mkpath(  ) ;
+                #eval { tests_mkpath(  ) ; } or ok( 0 == 1,  'tests_mkpath fail badly?' ) ;
+                #tests_format_for_imap_arg(  ) ;
+                #tests_is_a_release_number(  ) ;
+                #tests_delete1emptyfolders(  ) ;
+                #tests_memory_consumption(  ) ;
+                #tests_imap2_folder_name() ;
+                #tests_length_ref(  ) ;
+		#tests_is_valid_directory(  ) ;
+                #tests_firstline(  ) ;
+                #tests_diff_or_NA(  ) ;
+                #tests_match_number(  ) ;
+                #tests_all_defined(  ) ;
+                #tests_guess_separator(  ) ;
+                tests_pipemess(  ) ;
+                #tests_message_for_host2(  ) ;
+                done_testing(  ) ;
+                note('End of imapsync --tests_debug') ;
+        }
+        return ;
+}
+
+sub tests {
+
+      SKIP: {
+                skip 'No test in normal run' if ( not $tests ) ;
+                tests_folder_routines(  ) ;
+                tests_compare_lists(  ) ;
+                tests_regexmess();
+                tests_skipmess(  ) ;
+                tests_flags_regex();
+                tests_ucsecond(  ) ;
+                tests_permanentflags();
+                tests_flags_filter(  ) ;
+                tests_separator_invert(  ) ;
+                tests_imap2_folder_name() ;
+                tests_command_line_nopassword();
+                tests_good_date(  ) ;
+                tests_max();
+                tests_remove_not_num();
+                tests_memory_consumption( ) ;
+                tests_is_a_release_number();
+                tests_imapsync_basename();
+                tests_list_keys_in_2_not_in_1();
+                tests_convert_sep_to_slash(  ) ;
+                tests_match_a_cache_file(  ) ;
+                tests_cache_map(  ) ;
+                tests_get_cache(  ) ;
+                tests_clean_cache(  ) ;
+                tests_clean_cache_2(  ) ;
+                tests_touch(  ) ;
+                tests_flagscase(  ) ;
+                eval { tests_mkpath(  ) ; } or ok( 0 == 1,  'tests_mkpath fail badly?' ) ;
+                tests_extract_header(  ) ;
+                tests_decompose_header(  ) ;
+                tests_epoch(  ) ;
+                tests_add_header(  ) ;
+                tests_cache_dir_fix(  ) ;
+                tests_cache_dir_fix_win(  ) ;
+                tests_filter_forbidden_characters(  ) ;
+                tests_cache_folder(  ) ;
+                tests_time_remaining(  ) ;
+                tests_decompose_regex(  ) ;
+                tests_Banner(  ) ;
+                tests_backtick(  ) ;
+                tests_bytes_display_string(  ) ;
+                tests_header_line_normalize(  ) ;
+                tests_fix_Inbox_INBOX_mapping(  ) ;
+                tests_max_line_length(  ) ;
+                tests_subject(  ) ;
+                tests_msgs_from_maxmin(  ) ;
+                tests_tmpdir_has_colon_bug(  ) ;
+                tests_sleep_max_messages(  ) ;
+                tests_sleep_max_bytes(  ) ;
+                tests_logfile(  ) ;
+                tests_setlogfile(  ) ;
+                tests_jux_utf8(  ) ;
+                tests_pipemess(  ) ;
+                tests_jux_utf8_list(  ) ;
+                tests_guess_prefix(  ) ;
+                tests_guess_separator(  ) ;
+                tests_format_for_imap_arg(  ) ;
+                tests_imapsync_id(  ) ;
+                tests_date_from_rcs(  ) ;
+                tests_quota_extract_storage_limit_in_bytes(  ) ;
+                tests_quota_extract_storage_current_in_bytes(  ) ;
+                tests_guess_special(  ) ;
+		tests_is_valid_directory(  ) ;
+                tests_delete1emptyfolders(  ) ;
+                tests_message_for_host2(  ) ;
+                tests_length_ref(  ) ;
+                tests_firstline(  ) ;               
+                tests_diff_or_NA(  ) ;
+                #tests_always_fail(  ) ;
+                tests_match_number(  ) ;
+                tests_all_defined(  ) ;
+                done_testing( 693 ) ;
+                note('End of imapsync --tests') ;
+        }
+        return ;
+}
+
+
+
+# IMAPClient 3.xx ads
+
+package Mail::IMAPClient;
+
+sub Tls {
+	my $self  = shift ;
+	my $value = shift ;
+	if ( defined  $value  ) { $self->{TLS} = $value }
+	return $self->{TLS};
+}
+
+sub Reconnect_counter {
+	my $self  = shift ;
+        my $value = shift ;
+	$self->{Reconnect_counter} = 0 if ( not defined  $self->{Reconnect_counter}  ) ;
+	if ( defined  $value  ) { $self->{Reconnect_counter} = $value }
+	return( $self->{Reconnect_counter} ) ;
+}
+
+
+sub Banner {
+	my $self  = shift ;
+	my $value = shift ;
+	if ( defined $value ) { $self->{ BANNER } = $value }
+	return $self->{ BANNER };
+}
+
+sub capability_update {
+	my $self = shift ;
+
+	delete $self->{CAPABILITY} ;
+	return( $self->capability ) ;
+}
+
+
+package Imapsync::Getopt::Long ;
+# Started as a copy of Luke Ross Getopt::Long::CGI
+# https://metacpan.org/release/Getopt-Long-CGI
+# So this section is under the same license as Getopt-Long-CGI Luke Ross wants it,
+# which was Perl 5.6 or later licenses at the date of the copy.
+
+use strict ;
+use warnings ;
+
+use Getopt::Long(  ) ;
+
+
+sub GetOptions {
+    my %options = @_ ;
+
+    if ( not $ENV{SERVER_SOFTWARE} ) {
+        # Not CGI - pass upstream for normal command line handling
+        return Getopt::Long::GetOptions( %options ) ;
+    }
+    my $b_ref = $options{'debugbasket=s'} ;
+    require CGI ;
+    require CGI::Carp ;
+    CGI::Carp->import( 'fatalsToBrowser' ) ;
+
+    my $cgi = CGI->new(  ) ;
+    $cgi->param( 'debugcgi' ) and myprint( "<h2>Current Values</h2>\n" . $cgi->Dump  ) ;
+
+    foreach my $key (sort keys %options) {
+        my $val = $options{$key};
+	#push( @{$b_ref}, "opt:[$key] val:[$val]" . ( ('SCALAR' eq ref($val) and defined  $$val  ) ? " [$$val]" : q{} ) . "\n" ) ;
+        if ( $key !~ m/^([\w\d\|]+)([=:][isf])?([\+!\@\%])?$/ ) {
+		push  @{$b_ref}, "Unknown opt: [$key]\n"  ;
+            next ; # Unknown item
+        }
+
+        my $name = [split '|', $1, 1 ]->[0];
+
+        if (($3 || q{}) eq '+') {
+            ${ $val } = $cgi->param($name); # "Incremental" integer
+        } elsif ($2) {
+            my @values = $cgi->param($name);
+            my $type = $2;
+            if (($3 || q{}) eq '%' or ref($val) eq 'HASH') {
+                my %values = map { split /=/, $_, 1 } @values;
+                if ($type =~ m/i$/) {
+                    foreach my $k (keys %values) {
+                        $values{$k} = int $values{$k} ;
+                    }
+                } elsif ($type =~ m/f$/) {
+                    foreach my $k (keys %values) {
+                        $values{$k} = 0 + $values{$k}
+                    }
+                }
+                if ( ref($val) eq 'CODE') {
+                    while(my($k, $v) = each %values) {
+                       $val->($name, $k, $v);
+                    }
+                } elsif ( 'REF' eq ref $val ) {
+			#push( @{$b_ref}, "refref($$val): " . ref($$val) . " %values= ", %values, "\n\n" ) ;
+			%{ ${ $val } } = %values;
+		} else {
+			#push( @{$b_ref}, "ref($val): " . ref($val) . " %values= ", %values, "\n\n" ) ;
+			%{ $val } = %values;
+                }
+            } else {
+                if ($type =~ m/i$/) {
+                    @values = map { int $_ } @values;
+                } elsif ($type =~ m/f$/) {
+                    @values = map { 0 + $_ } @values;
+                }
+                if (($3 || q{}) eq '@' or ref($val) eq 'ARRAY') {
+                    if (ref($val) eq 'CODE') {
+                        $val->($name, \@values)
+                    } else {
+                        @{ $val } = @values ;
+                    }
+                } else {
+                    if (ref($val) eq 'CODE') {
+                        $val->($name, $values[0]);
+                    } else {
+                    	${ $val } = $values[0];
+                    }
+                }
+            }
+        } else {
+            # Checkbox
+            ${ $val } = $cgi->param($name) ? 1 : undef ;
+	    #push( @{$b_ref}, "param($name) ref($val): " . ref($val) . " val=[$$val]\n\n" ) ;
+        }
+    }
+    return( 1 ) ;
+}
+
+
diff --git a/data/Dockerfiles/dovecot/imapsync_cron.pl b/data/Dockerfiles/dovecot/imapsync_cron.pl
new file mode 100755
index 00000000..5c47eb47
--- /dev/null
+++ b/data/Dockerfiles/dovecot/imapsync_cron.pl
@@ -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);
diff --git a/data/Dockerfiles/dovecot/postlogin.sh b/data/Dockerfiles/dovecot/postlogin.sh
new file mode 100755
index 00000000..343910ff
--- /dev/null
+++ b/data/Dockerfiles/dovecot/postlogin.sh
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+export MASTER_USER=$USER
+exec "$@"
diff --git a/data/Dockerfiles/dovecot/rspamd-pipe b/data/Dockerfiles/dovecot/rspamd-pipe
new file mode 100755
index 00000000..f9236e17
--- /dev/null
+++ b/data/Dockerfiles/dovecot/rspamd-pipe
@@ -0,0 +1,8 @@
+#!/bin/bash
+if [[ ${2} == "learn_spam" ]]; then
+/usr/bin/curl --data-binary @- http://rspamd:11334/learnspam < /dev/stdin
+elif [[ ${2} == "learn_ham" ]]; then
+/usr/bin/curl --data-binary @- http://rspamd:11334/learnham < /dev/stdin
+fi
+# Always return 0 to satisfy Dovecot...
+exit 0
diff --git a/data/Dockerfiles/dovecot/supervisord.conf b/data/Dockerfiles/dovecot/supervisord.conf
new file mode 100644
index 00000000..45f9ddd5
--- /dev/null
+++ b/data/Dockerfiles/dovecot/supervisord.conf
@@ -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
diff --git a/.nojekyll b/data/Dockerfiles/memcached/.empty
similarity index 100%
rename from .nojekyll
rename to data/Dockerfiles/memcached/.empty
diff --git a/data/Dockerfiles/mysql/.empty b/data/Dockerfiles/mysql/.empty
new file mode 100644
index 00000000..e69de29b
diff --git a/data/Dockerfiles/nginx/.empty b/data/Dockerfiles/nginx/.empty
new file mode 100644
index 00000000..e69de29b
diff --git a/data/Dockerfiles/pdns/Dockerfile b/data/Dockerfiles/pdns/Dockerfile
new file mode 100644
index 00000000..b56dcf9c
--- /dev/null
+++ b/data/Dockerfiles/pdns/Dockerfile
@@ -0,0 +1,26 @@
+FROM ubuntu:xenial
+MAINTAINER Andre Peters <[email protected]>
+
+ENV DEBIAN_FRONTEND noninteractive
+ENV LC_ALL C
+
+RUN dpkg-divert --local --rename --add /sbin/initctl \
+    && ln -sf /bin/true /sbin/initctl \
+    && dpkg-divert --local --rename --add /usr/bin/ischroot \
+    && ln -sf /bin/true /usr/bin/ischroot
+
+RUN echo 'deb http://repo.powerdns.com/ubuntu xenial-rec-40 main' > /etc/apt/sources.list.d/pdns.list
+
+RUN echo 'Package: pdns-*\n\
+Pin: origin repo.powerdns.com\n\
+Pin-Priority: 600\n' > /etc/apt/preferences.d/pdns
+
+RUN apt-key adv --fetch-keys http://repo.powerdns.com/FD380FBB-pub.asc \
+	&& apt-get update \
+	&& apt-get install -y --force-yes pdns-recursor
+
+CMD ["/usr/sbin/pdns_recursor"]
+
+EXPOSE 53/udp
+
+RUN apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
diff --git a/data/Dockerfiles/php-fpm/Dockerfile b/data/Dockerfiles/php-fpm/Dockerfile
new file mode 100644
index 00000000..ad4b105d
--- /dev/null
+++ b/data/Dockerfiles/php-fpm/Dockerfile
@@ -0,0 +1,18 @@
+FROM php:7.1-fpm
+MAINTAINER Andre Peters <[email protected]>
+
+ENV DEBIAN_FRONTEND noninteractive
+
+RUN apt-get update \
+        && apt-get install -y zlib1g-dev libicu-dev g++ libidn11-dev libxml2-dev
+
+RUN docker-php-ext-configure intl
+RUN docker-php-ext-install intl pdo pdo_mysql xmlrpc
+RUN pear install channel://pear.php.net/Net_IDNA2-0.1.1 Auth_SASL Net_IMAP NET_SMTP Net_IDNA2 Mail_mime
+
+COPY ./docker-entrypoint.sh /
+
+EXPOSE 9000
+
+ENTRYPOINT ["/docker-entrypoint.sh"]
+CMD ["php-fpm"]
diff --git a/data/Dockerfiles/php-fpm/docker-entrypoint.sh b/data/Dockerfiles/php-fpm/docker-entrypoint.sh
new file mode 100755
index 00000000..8f57a6d9
--- /dev/null
+++ b/data/Dockerfiles/php-fpm/docker-entrypoint.sh
@@ -0,0 +1,7 @@
+#!/bin/bash
+set -e
+
+if [[ ! -d "/data/dkim/txt" || ! -d "/data/dkim/keys" ]] ; then	mkdir -p /data/dkim/{txt,keys} ; chown -R www-data:www-data /data/dkim; fi
+if [[ $(stat -c %U /data/dkim/) != "www-data" ]] ; then chown -R www-data:www-data /data/dkim ; fi
+
+exec "$@"
diff --git a/data/Dockerfiles/postfix/Dockerfile b/data/Dockerfiles/postfix/Dockerfile
new file mode 100644
index 00000000..a3781bd4
--- /dev/null
+++ b/data/Dockerfiles/postfix/Dockerfile
@@ -0,0 +1,33 @@
+From ubuntu:xenial
+MAINTAINER Andre Peters <[email protected]>
+
+ENV DEBIAN_FRONTEND noninteractive
+ENV LC_ALL C
+
+RUN dpkg-divert --local --rename --add /sbin/initctl \
+	&& ln -sf /bin/true /sbin/initctl \
+	&& dpkg-divert --local --rename --add /usr/bin/ischroot \
+	&& ln -sf /bin/true /usr/bin/ischroot
+
+RUN apt-get update
+RUN apt-get install -y --no-install-recommends supervisor \
+	postfix \
+	sasl2-bin \
+	libsasl2-modules \
+	postfix \
+	postfix-mysql \
+	postfix-pcre \
+	syslog-ng \
+	syslog-ng-core \
+	ca-certificates
+
+RUN sed -i -E 's/^(\s*)system\(\);/\1unix-stream("\/dev\/log");/' /etc/syslog-ng/syslog-ng.conf
+
+COPY supervisord.conf /etc/supervisor/supervisord.conf
+COPY postfix.sh /opt/postfix.sh
+
+EXPOSE 588
+
+CMD exec /usr/bin/supervisord -c /etc/supervisor/supervisord.conf
+
+RUN apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
diff --git a/data/Dockerfiles/postfix/postfix.sh b/data/Dockerfiles/postfix/postfix.sh
new file mode 100755
index 00000000..3dd108d0
--- /dev/null
+++ b/data/Dockerfiles/postfix/postfix.sh
@@ -0,0 +1,16 @@
+#!/bin/bash
+
+trap "postfix stop" EXIT
+
+sed -i "/^user/c\user = ${DBUSER}" /opt/postfix/conf/sql/*
+sed -i "/^password/c\password = ${DBPASS}" /opt/postfix/conf/sql/*
+sed -i "/^dbname/c\dbname = ${DBNAME}" /opt/postfix/conf/sql/*
+
+postconf -c /opt/postfix/conf
+if [[ $? != 0 ]]; then
+	echo "Postfix configuration error, refusing to start."
+	exit 1
+else
+	postfix -c /opt/postfix/conf start
+	sleep infinity
+fi
diff --git a/data/Dockerfiles/postfix/supervisord.conf b/data/Dockerfiles/postfix/supervisord.conf
new file mode 100644
index 00000000..4268899d
--- /dev/null
+++ b/data/Dockerfiles/postfix/supervisord.conf
@@ -0,0 +1,17 @@
+[supervisord]
+nodaemon=true
+
+[program:syslog-ng]
+command=/usr/sbin/syslog-ng --foreground  --no-caps
+redirect_stderr=true
+autostart=true
+stdout_syslog=true
+
+[program:postfix]
+command=/opt/postfix.sh
+autorestart=true
+
+[program:postfix-maillog]
+command=/usr/bin/tail -f /var/log/mail.log
+stdout_logfile=/dev/fd/1
+stdout_logfile_maxbytes=0
diff --git a/data/Dockerfiles/redis/.empty b/data/Dockerfiles/redis/.empty
new file mode 100644
index 00000000..e69de29b
diff --git a/data/Dockerfiles/rmilter/Dockerfile b/data/Dockerfiles/rmilter/Dockerfile
new file mode 100644
index 00000000..364c78c1
--- /dev/null
+++ b/data/Dockerfiles/rmilter/Dockerfile
@@ -0,0 +1,26 @@
+FROM ubuntu:xenial
+MAINTAINER Andre Peters <[email protected]>
+
+ENV DEBIAN_FRONTEND noninteractive
+ENV LC_ALL C
+
+RUN dpkg-divert --local --rename --add /sbin/initctl \
+    && ln -sf /bin/true /sbin/initctl \
+    && dpkg-divert --local --rename --add /usr/bin/ischroot \
+    && ln -sf /bin/true /usr/bin/ischroot
+
+RUN apt-key adv --fetch-keys http://rspamd.com/apt-stable/gpg.key \
+	&& echo "deb http://rspamd.com/apt-stable/ xenial main" > /etc/apt/sources.list.d/rspamd.list \
+	&& apt-get update \
+	&& apt-get --no-install-recommends -y --force-yes install rmilter cron syslog-ng syslog-ng-core supervisor
+
+COPY supervisord.conf /etc/supervisor/supervisord.conf
+
+EXPOSE 9000
+
+RUN sed -i -E 's/^(\s*)system\(\);/\1unix-stream("\/dev\/log");/' /etc/syslog-ng/syslog-ng.conf
+RUN touch /var/log/mail.log && chmod 640 /var/log/mail.log && chown root:adm /var/log/mail.log
+
+CMD exec /usr/bin/supervisord -c /etc/supervisor/supervisord.conf
+
+RUN apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
diff --git a/data/Dockerfiles/rmilter/supervisord.conf b/data/Dockerfiles/rmilter/supervisord.conf
new file mode 100644
index 00000000..41e1328f
--- /dev/null
+++ b/data/Dockerfiles/rmilter/supervisord.conf
@@ -0,0 +1,18 @@
+[supervisord]
+nodaemon=true
+
+[program:syslog-ng]
+command=/usr/sbin/syslog-ng --foreground  --no-caps
+redirect_stderr=true
+autostart=true
+stdout_syslog=true
+
+[program:rmilter]
+command=/usr/sbin/rmilter -n -c /etc/rmilter.conf.d/rmilter.conf
+user=_rmilter
+autorestart=true
+
+[program:rmilter-syslog]
+command=/usr/bin/tail -f /var/log/mail.log
+stdout_logfile=/dev/fd/1
+stdout_logfile_maxbytes=0
diff --git a/data/Dockerfiles/rspamd/Dockerfile b/data/Dockerfiles/rspamd/Dockerfile
new file mode 100644
index 00000000..d4d4508d
--- /dev/null
+++ b/data/Dockerfiles/rspamd/Dockerfile
@@ -0,0 +1,27 @@
+FROM ubuntu:xenial
+MAINTAINER Andre Peters <[email protected]>
+
+ENV DEBIAN_FRONTEND noninteractive
+ENV LC_ALL C
+
+RUN dpkg-divert --local --rename --add /sbin/initctl \
+    && ln -sf /bin/true /sbin/initctl \
+    && dpkg-divert --local --rename --add /usr/bin/ischroot \
+    && ln -sf /bin/true /usr/bin/ischroot
+
+RUN apt-key adv --fetch-keys http://rspamd.com/apt-stable/gpg.key \
+    && echo "deb http://rspamd.com/apt-stable/ xenial main" > /etc/apt/sources.list.d/rspamd.list \
+    && apt-get update \
+    && apt-get -y install rspamd ca-certificates
+
+RUN echo '.include $LOCAL_CONFDIR/local.d/rspamd.conf.local' > /etc/rspamd/rspamd.conf.local
+# "Hardcoded" - we need them
+RUN echo 'settings = "http://nginx:8081/settings.php";' > /etc/rspamd/modules.d/settings.conf
+
+CMD ["/usr/bin/rspamd","-f", "-u", "_rspamd", "-g", "_rspamd"]
+
+RUN apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
+
+USER _rspamd
+
+EXPOSE 11333 11334
diff --git a/data/Dockerfiles/sogo/Dockerfile b/data/Dockerfiles/sogo/Dockerfile
new file mode 100644
index 00000000..43960438
--- /dev/null
+++ b/data/Dockerfiles/sogo/Dockerfile
@@ -0,0 +1,51 @@
+FROM ubuntu:xenial
+MAINTAINER Andre Peters <[email protected]>
+
+ENV DEBIAN_FRONTEND noninteractive
+ENV LC_ALL C
+ENV GOSU_VERSION 1.9
+
+RUN dpkg-divert --local --rename --add /sbin/initctl \
+    && ln -sf /bin/true /sbin/initctl \
+    && dpkg-divert --local --rename --add /usr/bin/ischroot \
+    && ln -sf /bin/true /usr/bin/ischroot
+
+RUN 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 \
+    && dpkgArch="$(dpkg --print-architecture | awk -F- '{ print $NF }')" \
+    && wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch" \
+    && wget -O /usr/local/bin/gosu.asc "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch.asc" \
+    && export GNUPGHOME="$(mktemp -d)" \
+    && gpg --keyserver ha.pool.sks-keyservers.net --recv-keys B42F6819007F00F88E364FD4036A9C25BF357DD4 \
+    && gpg --batch --verify /usr/local/bin/gosu.asc /usr/local/bin/gosu \
+    && rm -r "$GNUPGHOME" /usr/local/bin/gosu.asc \
+    && chmod +x /usr/local/bin/gosu \
+    && gosu nobody true
+
+RUN apt-key adv --keyserver keys.gnupg.net --recv-key 0x810273C4 \
+	&& echo "deb http://packages.inverse.ca/SOGo/nightly/3/ubuntu/ xenial xenial" > /etc/apt/sources.list.d/sogo.list \
+	&& apt-get update \
+	&& apt-get -y --force-yes install sogo sogo-activesync 
+
+RUN sed -i -E 's/^(\s*)system\(\);/\1unix-stream("\/dev\/log");/' /etc/syslog-ng/syslog-ng.conf
+RUN echo '* * * * *   sogo   /usr/sbin/sogo-ealarms-notify' > /etc/cron.d/sogo
+RUN echo '* * * * *   sogo   /usr/sbin/sogo-tool expire-sessions 60' >> /etc/cron.d/sogo
+RUN echo '0 0 * * *   sogo   /usr/sbin/sogo-tool update-autoreply -p /etc/sogo/sieve.creds' >> /etc/cron.d/sogo
+
+COPY ./reconf-domains.sh /
+COPY supervisord.conf /etc/supervisor/supervisord.conf
+
+#EXPOSE 20000
+#EXPOSE 9191
+#EXPOSE 9192
+
+CMD exec /usr/bin/supervisord -c /etc/supervisor/supervisord.conf
+
+RUN apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
diff --git a/data/Dockerfiles/sogo/reconf-domains.sh b/data/Dockerfiles/sogo/reconf-domains.sh
new file mode 100755
index 00000000..7ec4e173
--- /dev/null
+++ b/data/Dockerfiles/sogo/reconf-domains.sh
@@ -0,0 +1,98 @@
+#!/bin/bash
+
+# Wait for MySQL to warm-up
+while mysqladmin ping --host mysql --silent; do
+
+# Recreate view
+
+mysql --host mysql -u ${DBUSER} -p${DBPASS} ${DBNAME} -e "DROP VIEW IF EXISTS sogo_view"
+
+mysql --host mysql -u ${DBUSER} -p${DBPASS} ${DBNAME} << EOF
+CREATE VIEW sogo_view (c_uid, domain, c_name, c_password, c_cn, mail, aliases, ad_aliases, home, kind, multiple_bookings) AS
+SELECT mailbox.username, mailbox.domain, mailbox.username, mailbox.password, mailbox.name, mailbox.username, IFNULL(ga.aliases, ''), IFNULL(gda.ad_alias, ''), CONCAT('/var/vmail/', maildir), mailbox.kind, mailbox.multiple_bookings FROM mailbox
+LEFT OUTER JOIN grouped_mail_aliases ga ON ga.username = mailbox.username
+LEFT OUTER JOIN grouped_domain_alias_address gda ON gda.username = mailbox.username
+WHERE mailbox.active = '1';
+EOF
+
+
+mkdir -p /var/lib/sogo/GNUstep/Defaults/
+
+# Generate plist header with timezone data
+cat <<EOF > /var/lib/sogo/GNUstep/Defaults/sogod.plist
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//GNUstep//DTD plist 0.9//EN" "http://www.gnustep.org/plist-0_9.xml">
+<plist version="0.9">
+<dict>
+    <key>OCSAclURL</key>
+    <string>mysql://${DBUSER}:${DBPASS}@mysql:3306/${DBNAME}/sogo_acl</string>
+    <key>OCSCacheFolderURL</key>
+    <string>mysql://${DBUSER}:${DBPASS}@mysql:3306/${DBNAME}/sogo_cache_folder</string>
+    <key>OCSEMailAlarmsFolderURL</key>
+    <string>mysql://${DBUSER}:${DBPASS}@mysql:3306/${DBNAME}/sogo_alarms_folder</string>
+    <key>DomainFieldName</key>
+    <string>domain</string>
+    <key>OCSFolderInfoURL</key>
+    <string>mysql://${DBUSER}:${DBPASS}@mysql:3306/${DBNAME}/sogo_folder_info</string>
+    <key>OCSSessionsFolderURL</key>
+    <string>mysql://${DBUSER}:${DBPASS}@mysql:3306/${DBNAME}/sogo_sessions_folder</string>
+    <key>OCSStoreURL</key>
+    <string>mysql://${DBUSER}:${DBPASS}@mysql:3306/${DBNAME}/sogo_store</string>
+    <key>SOGoProfileURL</key>
+    <string>mysql://${DBUSER}:${DBPASS}@mysql:3306/${DBNAME}/sogo_user_profile</string>
+    <key>SOGoTimeZone</key>
+    <string>${TZ}</string>
+    <key>domains</key>
+    <dict>
+EOF
+
+# Generate multi-domain setup
+while read line
+	do
+	echo "        <key>${line}</key>
+        <dict>
+            <key>SOGoMailDomain</key>
+            <string>${line}</string>
+            <key>SOGoUserSources</key>
+            <array>
+                <dict>
+                    <key>MailFieldNames</key>
+                    <array>
+                        <string>aliases</string>
+                        <string>ad_aliases</string>
+                    </array>
+                    <key>KindFieldName</key>
+                    <string>kind</string>
+                    <key>MultipleBookingsFieldName</key>
+                    <string>multiple_bookings</string>
+                    <key>canAuthenticate</key>
+                    <string>YES</string>
+                    <key>displayName</key>
+                    <string>GAL</string>
+                    <key>id</key>
+                    <string>${line}</string>
+                    <key>isAddressBook</key>
+                    <string>YES</string>
+                    <key>type</key>
+                    <string>sql</string>
+                    <key>userPasswordAlgorithm</key>
+                    <string>ssha256</string>
+                    <key>viewURL</key>
+                    <string>mysql://${DBUSER}:${DBPASS}@mysql:3306/${DBNAME}/sogo_view</string>
+                </dict>
+            </array>
+        </dict>" >> /var/lib/sogo/GNUstep/Defaults/sogod.plist
+done < <(mysql --host mysql -u ${DBUSER} -p${DBPASS} ${DBNAME} -e "SELECT domain FROM domain;" -B -N)
+
+# Generate footer
+echo '    </dict>
+</dict>
+</plist>' >> /var/lib/sogo/GNUstep/Defaults/sogod.plist
+
+# Fix permissions
+chown sogo:sogo -R /var/lib/sogo/
+chmod 600 /var/lib/sogo/GNUstep/Defaults/sogod.plist
+
+sleep 99999
+
+done
diff --git a/data/Dockerfiles/sogo/supervisord.conf b/data/Dockerfiles/sogo/supervisord.conf
new file mode 100644
index 00000000..5f2d4d28
--- /dev/null
+++ b/data/Dockerfiles/sogo/supervisord.conf
@@ -0,0 +1,46 @@
+[supervisord]
+nodaemon=true
+
+[program:syslog-ng]
+command=/usr/sbin/syslog-ng --foreground  --no-caps
+redirect_stderr=true
+autostart=true
+stdout_syslog=true
+
+[group:sogo-group]
+programs=reconf-domains,sogo
+
+[program:sogo]
+command=/usr/sbin/sogod
+user=sogo
+autorestart=true
+priority=20
+
+[program:reconf-domains]
+command=/reconf-domains.sh
+autorestart=true
+priority=10
+
+[program:sogo-syslog]
+command=/usr/bin/tail -f /var/log/syslog -f /var/log/sogo/sogo.log
+stdout_logfile=/dev/fd/1
+stdout_logfile_maxbytes=0
+
+[program:cron]
+command=/usr/sbin/cron -f
+autorestart=true
+
+[program:sogo-webres]
+command=/usr/bin/python -u -m SimpleHTTPServer 9192
+directory=/usr/lib/GNUstep/SOGo/
+user=sogo
+autorestart=true
+
+[inet_http_server]
+port=9191
+
+[rpcinterface:supervisor]
+supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
+
+[supervisorctl]
+serverurl=http://localhost:9191
diff --git a/data/assets/passwd/generate_passwords.sh b/data/assets/passwd/generate_passwords.sh
new file mode 100755
index 00000000..78613150
--- /dev/null
+++ b/data/assets/passwd/generate_passwords.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+
+echo DBPASS=$(openssl rand -base64 32 | tr -dc _A-Z-a-z-0-9)
+echo DBROOT=$(openssl rand -base64 32 | tr -dc _A-Z-a-z-0-9)
diff --git a/data/assets/ssl/ca-key.pem b/data/assets/ssl/ca-key.pem
new file mode 100644
index 00000000..ce767c9c
--- /dev/null
+++ b/data/assets/ssl/ca-key.pem
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEogIBAAKCAQEAr+geb+b9p5PgNMDUwtK3NxtmUJrHvnCSxkkLEBPFp/vVvEQV
+eJQN3WNQVXjkqaAXQzWSkTz/URiKAKMSL/nx1+Es6HRXdR79GMc6kY1GBFMfhSwt
+BzblNZ++WpD4JB3bL5prMrf2HiTqe617dlENoqK0kBEDGZq3En07aV+MIRuT8HSW
+B4j1/St2DAKJYGCGVqPDgpFt7+aR8aXGkvoCMKpgiB32tNkmdDfzOdWmAdFS1KaV
+RK+ap9WmwNdMH0BzCud9vhuJjdUZlhfmWuuT5XaGU+fftIlI191XYOEZHe44TLy1
+xWwXUhc+BmhhSj3k75lNvtAxydVrZIyoVxpLHwIDAQABAoIBAARMxkGyAc1Q3hAs
+Dodco0Hjl5Ks1ekf01apfm28Lf63NzhM6cFyzQv2W4ZbWCuVUDxCWPzX4t3Wnbj6
+Q32MvI6sYG6mOWURhtpONG5OZ8G/Tmvw8oDUpLG03/BSzt4DJNJ7EdfBi3CdMmYn
+jXcM8Cpjk8pZwBumHoeDLCqdPU2p1onq+iwMdQDKfq1ESBwEuXIO82x9y/O6zxNZ
+HlWFDS5hLEt0UVWIV5aGK0/M/kzOCKwy7MMbJhHPYKy7Yvno2t+fJ6jiwswyEsCz
+M2Z3DUWzgrT4DdWSV2Bns0X/xID5kH7EXm7GqsudbHHJNnr7LWuowFSOzAX47t+N
+JgrRIaECgYEA1iFfpgO/awiNQLV1VpgA3pqzmFddEh74sFqILkSGd2fxH6BbgXJB
+z+7Cu6jij8Odvf48suKsG9K0OwAehnR/ZgkrQmnKftf/pEZ+iLdsS/T8IZAXjQ4g
+cLlYOOMuqIi8Ev1nT0/++IaSKro0CSDuuNtkqRCQ/ZMJtWzFxw3yZRcCgYEA0k1m
+vaxvkSXqrYvFjDkPM5gUXcE4wx9t6Y4iwmRISLe6bJcfVfaT9oCz4o6p4dxZsoXw
+SFSazFedqwQlzXMrrBd2rHYgg13N9LU9TggZXtSMOdye+dxk24P7GmJdWH5+S5vm
+TqATs105AD95p7X8+H1TpW4liwW+eNjFqqO8HzkCgYBIuVj01z0BqwveOEK7wDA7
+aisoyDMR3nbz3it2G0vX7fNUnG+3jQiRCDQW7ArWbMd8KYaP8rAlWvBfQXEclSBX
+lTGeArQFVHK8Zjy/Thx3x6KB+6AkBfI6lphB6daE4ruNb4bQxwh/e6TU4hyeJRMu
+sUSErt2vYWrgtSqOqkvyzQKBgBKExFZRd+WVLCwqEbQ+VgtaCfkTibcM7nXRkVgC
+0qasnxru19CPDQp43N5HZ8g+yhtBVh5YbOUvle+4Rsfnq2HVExsur8BBo2A4EXTs
+m6dRGiQCPHGOKcd2wMbbAJNJWD+6M7aavAFgZSOTc1gEW4laJ+J6Z43tbI9hr05O
+asNpAoGAFje1QFMYw8s8ZTYVrfwafgDB/BNnQRVnQwTZRXcabAT5O0Wc8hTDPOun
+aBNJ3oKSwBvt7Snsn1G/oeH0aZ6cI8kkYE+dyQ5YqSZZUbJF6g771iNv4lMvxRBN
+BT/ihKzXb/t47z6W8dL5N1BSZ0KrwIrEkqCeHBlAW+G/FArrbZU=
+-----END RSA PRIVATE KEY-----
diff --git a/data/assets/ssl/ca.pem b/data/assets/ssl/ca.pem
new file mode 100644
index 00000000..505f5698
--- /dev/null
+++ b/data/assets/ssl/ca.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIICzjCCAbigAwIBAgIRALtgn1eSIUIhoGv9nq94NYEwCwYJKoZIhvcNAQELMBIx
+EDAOBgNVBAoTB21haWxjb3cwHhcNMTYxMjEzMTAxMTAwWhcNMTkxMTI4MTAxMTAw
+WjASMRAwDgYDVQQKEwdtYWlsY293MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
+CgKCAQEAr+geb+b9p5PgNMDUwtK3NxtmUJrHvnCSxkkLEBPFp/vVvEQVeJQN3WNQ
+VXjkqaAXQzWSkTz/URiKAKMSL/nx1+Es6HRXdR79GMc6kY1GBFMfhSwtBzblNZ++
+WpD4JB3bL5prMrf2HiTqe617dlENoqK0kBEDGZq3En07aV+MIRuT8HSWB4j1/St2
+DAKJYGCGVqPDgpFt7+aR8aXGkvoCMKpgiB32tNkmdDfzOdWmAdFS1KaVRK+ap9Wm
+wNdMH0BzCud9vhuJjdUZlhfmWuuT5XaGU+fftIlI191XYOEZHe44TLy1xWwXUhc+
+BmhhSj3k75lNvtAxydVrZIyoVxpLHwIDAQABoyMwITAOBgNVHQ8BAf8EBAMCAKww
+DwYDVR0TAQH/BAUwAwEB/zALBgkqhkiG9w0BAQsDggEBAF36qnigBDvvqRI0xksc
+NkGFMB3JLKznOZ0DrWUx121/GHFaNNNeI7ECmyk2eRYKCEZNnxWa1/LJ7GWn7lRU
+JD3OeWbhBUgA+HXoKl/jzXokuXMjYi/eFrgOofk2AqNDA5ioduS6A4vL8UDQc+74
+WSS8za4zoVR4GtxqDG+msRzTNVWXRcaaaWSrWMfZtQcEKIeQGDkcccvZ+mzlFUsH
+G1xxKuOPGjAwrxda4x+FY/dYdPbRV8ua0RQmYUMnROv507QnGZ9FdzdrvisZ67xx
+5BfxbApAyxDD/p7B4Zh1daga2LYGRwMJuwYjXlw9uNxJVQwcxg4nppWF1KZ4Fz0c
+EjY=
+-----END CERTIFICATE-----
diff --git a/data/assets/ssl/cert.pem b/data/assets/ssl/cert.pem
new file mode 100644
index 00000000..96d16bec
--- /dev/null
+++ b/data/assets/ssl/cert.pem
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDBDCCAe6gAwIBAgIQeJMoL/3dxhxhT9EwuRTL/DALBgkqhkiG9w0BAQswEjEQ
+MA4GA1UEChMHbWFpbGNvdzAeFw0xNjEyMTMxMDExMDBaFw0xOTExMjgxMDExMDBa
+MC0xEDAOBgNVBAoTB21haWxjb3cxGTAXBgNVBAMTEG1haWwuZXhhbXBsZS5vcmcw
+ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDRg0xT3At9DSb3H5OMp3K1
+MpXAgYyotSK6TS61fC0QEHy2fMXiws7Agcye6Ln7CG63Fe1eN2jkdlefy9xJivS8
+y5w0M8i168v5znzC8fnylL2iOiSYfK/B/oEqfU7YH4RcegO53oDDIUZmi4Frgnu7
+39VVOU1ZyHEVqGJ2H2aAIkoZRjGzumD9Ym4LWGidtKJzBgFt/qmhUeWXipM8w281
+XkQnJU79+x2ywnJSvEZ3r/ZVJC7kbjiVw+/k15k9Cxk6Ik8wmJ0X/+xWxoZomHQI
+1LM0VKAS/iaU95dn2bplvL6jTiiyWAbrMjSKs4XbPt/fIbOicNkj6+CFy0MVfyyH
+AgMBAAGjPzA9MA4GA1UdDwEB/wQEAwIAqDAdBgNVHSUEFjAUBggrBgEFBQcDAgYI
+KwYBBQUHAwEwDAYDVR0TAQH/BAIwADALBgkqhkiG9w0BAQsDggEBAI/jBJa1P8nB
+eHUN5muQmjBVDVOYyWAAEapOe2HYsBcpjaB2H8Iw3DQzJtz6peYeYSCmHRVqFLCm
+VPrq36l9mPUotyPDPlQQAxCj9R2+WbGaJO+N/E1F8FQ94dr3jqwUyfjVPoqEjmIH
+NFkvbA0RJOeBm9oYGdhM0wjOBV9c9MTHFG82nQ/zQeTuPb7GXuKIOXYCxoLNOZMw
+UJ02Cqjv5ImrgOhcstAKX3Ip0urSvZUGvtPla4CGh+M6yDFJ08GzX6OiMIH207RW
+jAbUXXERSUv/7hysdDjGo5HZjCeMzVu9KAxoZXqnmvkk8g2swKWtWBRcoeU1VGx0
+Bx4Q4KMjuYQ=
+-----END CERTIFICATE-----
diff --git a/data/assets/ssl/dhparams.pem b/data/assets/ssl/dhparams.pem
new file mode 100644
index 00000000..b245f051
--- /dev/null
+++ b/data/assets/ssl/dhparams.pem
@@ -0,0 +1,8 @@
+-----BEGIN DH PARAMETERS-----
+MIIBCAKCAQEA9iHB0CRDhV8wfBgqnmvuJpl0fzL3qL75R4ZvQHlfMNLrxuIz2x9D
+9zcDhPcBTVzV5Ay0AAkke4wP6r6wDQqXqBP4Y8IOkYAyLh3jM40jfHQzQt+5JdQl
+ond3kiscBsFOch/vMfSLMu3lAb0YhPNTvrxhMz7LcVAWYl82swASupdiKR+MgaQr
+XsugpmDKsHW60VmIM9B7K9Y+rNHwvMWkmISd0KxA8oOy1WJvsVEissMALZDE3c4w
+2xHmO2lXxgEx3aez28736t4m/KW3g9Zr31a1M0KusmfY//fGkPk4NUrLBOS2xrgp
+Y/rG1qSBdcVyerM0Ki93qCyHKYu4ene0OwIBAg==
+-----END DH PARAMETERS-----
diff --git a/data/assets/ssl/key.pem b/data/assets/ssl/key.pem
new file mode 100644
index 00000000..cedf35a0
--- /dev/null
+++ b/data/assets/ssl/key.pem
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEA0YNMU9wLfQ0m9x+TjKdytTKVwIGMqLUiuk0utXwtEBB8tnzF
+4sLOwIHMnui5+whutxXtXjdo5HZXn8vcSYr0vMucNDPItevL+c58wvH58pS9ojok
+mHyvwf6BKn1O2B+EXHoDud6AwyFGZouBa4J7u9/VVTlNWchxFahidh9mgCJKGUYx
+s7pg/WJuC1honbSicwYBbf6poVHll4qTPMNvNV5EJyVO/fsdssJyUrxGd6/2VSQu
+5G44lcPv5NeZPQsZOiJPMJidF//sVsaGaJh0CNSzNFSgEv4mlPeXZ9m6Zby+o04o
+slgG6zI0irOF2z7f3yGzonDZI+vghctDFX8shwIDAQABAoIBAQC9kiLnIgxXGyZt
+pmmYdA6re1jatZ2zLSp+DcY8ul3/0hs195IKCyCOOSQPiR520Pt0t+duP46uYZIJ
+aakp9gxaI5Vz+oMacH/AyaBDuDTj1Mf9WMSyIOfbDVCMRJOppGLcVh62+Gfjp2EO
++h2hTJBuvypFkbK2kVIZOaHVpbXWKw1oYuEcTftk9XfxxvfSMw1HQ12/P2CAcbaa
+jPmVbisunv6kpXtewSBTcaLSYWJf1MYD5Hi8fzkD2FJSXYbfQd8RKvT2rj6FA7ux
+CDMzbYhdnd7lc63OARCIjfCRNtDT1cZ3gR1CQHD98lWxmPQIZukv+w7s/bSrFgnQ
+ROZ0ghBJAoGBAOmE/3d5FDmp0aJNxXynKcRGdpEEM4O40RIdqa2eR6Pa7aTRosao
+z0qVgdFuJrqjlB3jgedxXEX1M0abCUzzM9Q5F7JLl+KsjwRwpkIOkPiyUncLp7LK
+QbY3tvYBIdpjlF1USOMGRL4j11hqr4vQC/yPBF7jj81kCZDTbmZhp82jAoGBAOWu
+ql5QFUOlmqkuWIAFkiLEZhOu+ptqkE+zG50CCGMJIX0dJ2PHXFyNGInomAeT0nbI
+pbnK3x7KeEKiGrAqZFNCTHhApTwkrIj0L/RQbMDZ7u7j1AEUVNFEhIm62kg84FtG
+xtfxVxredE+NQc/tyV3hXegdNZxegALirlcMKIvNAoGAWFwIxk48Ru1o8z72QQqH
+lUsMRicOzwK5qV8r+xPvC6MlVL42F3F8rj4QFwzU/r4yp3SUjNyqC5aSRl8Xj9Re
+gijwPHi6Cf09SHLPliMo29GtvnnchJxfbPF7+23GP3p6gy4HPk/65u9s5nnH3uFk
+B7ad8sGsgg0eSXyXQ4okEn0CgYEAnogPuedGthlxBgMiPMMbmfm7hyyId4t3Ljuu
+/JExnsHnpobf8EPjoVIWNOIhRWGnrCtUEEhR9tvDZCKljyDDfKBPTdU496lMmX8K
+NnToi7gg7iy84T3aSVMktDgPgDrclMPmbZh8CeSvnVUfrtgu3Ci4+4Rlw5eKffNe
+aGDQ/6UCgYAbUq9mRT2WOXIo+Dchi9VzDWgtfOw5VEyqkSpb7hPiIYx5jNaENnVK
+cAi3iqbBgPJBuMlTrKmmaxdmssGOEZNJLuuXLDbCU+f5cpu5PQ4crC6UtRI5rlhp
+8Yc+oiv3HWbSw3sVRpMFB6NP4DnvgFW3B2Wdfb/lNzPCKWqBsX7gWw==
+-----END RSA PRIVATE KEY-----
diff --git a/data/conf/dovecot/dovecot.conf b/data/conf/dovecot/dovecot.conf
new file mode 100644
index 00000000..0565b99e
--- /dev/null
+++ b/data/conf/dovecot/dovecot.conf
@@ -0,0 +1,247 @@
+auth_mechanisms = plain login
+#mail_debug = yes
+log_path = /var/log/mail.log
+disable_plaintext_auth = yes
+# Uncomment on NFS share
+#mmap_disable = yes
+#mail_fsync = always
+#mail_nfs_index = yes
+#mail_nfs_storage = yes
+login_log_format_elements = "user=<%u> method=%m rip=%r lip=%l mpid=%e %c %k"
+mail_home = /var/vmail/%d/%n
+mail_location = maildir:~/
+mail_plugins = quota acl zlib antispam
+auth_username_chars = abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890.-_@
+ssl_protocols = !SSLv3 !SSLv2
+ssl_prefer_server_ciphers = yes
+ssl_cipher_list = EDH+CAMELLIA:EDH+aRSA:EECDH+aRSA+AESGCM:EECDH+aRSA+SHA256:EECDH:+CAMELLIA128:+AES128:+SSLv3:!aNULL:!eNULL:!LOW:!3DES:!MD5:!EXP:!PSK:!DSS:!RC4:!SEED:!IDEA:!ECDSA:kEDH:CAMELLIA128-SHA:AES128-SHA
+ssl_options = no_compression
+# Automatically regenerates every week
+ssl_dh_parameters_length = 2048
+log_timestamp = "%Y-%m-%d %H:%M:%S "
+recipient_delimiter = +
+auth_master_user_separator = *
+mail_prefetch_count = 30
+passdb {
+  driver = passwd-file
+  args = /etc/dovecot/dovecot-master.passwd
+  master = yes
+  pass = yes
+}
+passdb {
+  args = /etc/dovecot/sql/dovecot-mysql.conf
+  driver = sql
+}
+namespace inbox {
+  inbox = yes
+  location =
+  separator = /
+  mailbox "Trash" {
+    auto = subscribe
+    special_use = \Trash
+  }
+  mailbox "Deleted Messages" {
+    special_use = \Trash
+  }
+  mailbox "Deleted Items" {
+    special_use = \Trash
+  }
+  mailbox "Gelöschte Objekte" {
+    special_use = \Trash
+  }
+  mailbox "Papierkorb" {
+    special_use = \Trash
+  }
+  mailbox "Itens Excluidos" {
+    special_use = \Trash
+  }
+  mailbox "Itens Excluídos" {
+    special_use = \Trash
+  }
+  mailbox "Lixeira" {
+    special_use = \Trash
+  }
+  mailbox "Prullenbak" {
+    special_use = \Trash
+  }
+  mailbox "Verwijderde items" {
+    special_use = \Trash
+  }
+  mailbox "Archive" {
+    auto = subscribe
+    special_use = \Archive
+  }
+  mailbox "Archiv" {
+    special_use = \Archive
+  }
+  mailbox "Archives" {
+    special_use = \Archive
+  }
+  mailbox "Arquivo" {
+    special_use = \Archive
+  }
+  mailbox "Arquivos" {
+    special_use = \Archive
+  }
+  mailbox "Archief" {
+    special_use = \Archive
+  }
+  mailbox "Sent" {
+    auto = subscribe
+    special_use = \Sent
+  }
+  mailbox "Sent Messages" {
+    special_use = \Sent
+  }
+  mailbox "Sent Items" {
+    special_use = \Sent
+  }
+  mailbox "Gesendet" {
+    special_use = \Sent
+  }
+  mailbox "Gesendete Objekte" {
+    special_use = \Sent
+  }
+  mailbox "Itens Enviados" {
+    special_use = \Sent
+  }
+  mailbox "Enviados" {
+    special_use = \Sent
+  }
+  mailbox "Verzonden items" {
+    special_use = \Sent
+  }
+  mailbox "Verzonden" {
+    special_use = \Sent
+  }
+  mailbox "Drafts" {
+    auto = subscribe
+    special_use = \Drafts
+  }
+  mailbox "Entwürfe" {
+    special_use = \Drafts
+  }
+  mailbox "Rascunhos" {
+    special_use = \Drafts
+  }
+  mailbox "Concepten" {
+    special_use = \Drafts
+  }
+  mailbox "Junk" {
+    auto = subscribe
+    special_use = \Junk
+  }
+  mailbox "Junk E-mail" {
+    special_use = \Junk
+  }
+  mailbox "Spam" {
+    special_use = \Junk
+  }
+  mailbox "Lixo Eletrônico" {
+    special_use = \Junk
+  }
+  mailbox "Ongewenste e-mail" {
+    special_use = \Junk
+  }
+  prefix =
+}
+namespace {
+    type = shared
+    separator = /
+    prefix = Shared/%%u/
+    location = maildir:%%h/:INDEXPVT=~/Shared/%%u
+    subscriptions = no
+    list = yes
+}
+protocols = imap sieve lmtp pop3
+service dict {
+  unix_listener dict {
+    mode = 0660
+    user = vmail
+    group = vmail
+  }
+}
+service auth {
+  inet_listener auth-inet {
+    port = 10001
+  }
+  unix_listener auth-master {
+    mode = 0600
+    user = vmail
+  }
+  unix_listener auth-userdb {
+    mode = 0600
+    user = vmail
+  }
+  user = root
+}
+service managesieve-login {
+  inet_listener sieve {
+    port = 4190
+  }
+  service_count = 1
+  process_min_avail = 2
+  vsz_limit = 128M
+}
+service imap {
+  executable = imap imap-postlogin
+}
+service managesieve {
+  process_limit = 256
+}
+service lmtp {
+  inet_listener lmtp-inet {
+    port = 24
+  }
+  user = vmail
+}
+listen = *,[::]
+ssl_cert = </etc/ssl/mail/cert.pem
+ssl_key = </etc/ssl/mail/key.pem
+userdb {
+  args = /etc/dovecot/sql/dovecot-mysql.conf
+  driver = sql
+}
+protocol imap {
+  mail_plugins = quota imap_quota imap_acl acl zlib imap_zlib antispam
+}
+protocol lmtp {
+  mail_plugins = quota sieve acl zlib
+  auth_socket_path = /var/run/dovecot/auth-master
+}
+protocol sieve {
+  managesieve_logout_format = bytes=%i/%o
+}
+plugin {
+  acl_anyone = allow
+  acl_shared_dict = file:/var/vmail/shared-mailboxes.db
+  acl = vfile
+  quota = dict:Userquota::proxy::sqlquota
+  quota_rule2 = Trash:storage=+100%%
+  antispam_backend = mailtrain
+  antispam_spam    = Junk
+  antispam_trash   = Trash
+  antispam_mail_sendmail = /usr/local/bin/rspamd-pipe
+  antispam_mail_spam     = learn_spam
+  antispam_mail_notspam  = learn_ham
+  # Do not complain about empty parameter
+  antispam_mail_sendmail_args = --blind
+  sieve = /var/vmail/sieve/%u.sieve
+  sieve_after = /var/vmail/sieve/global.sieve
+  sieve_max_script_size = 1M
+  sieve_quota_max_scripts = 0
+  sieve_quota_max_storage = 0
+}
+dict {
+  sqlquota = mysql:/etc/dovecot/sql/dovecot-dict-sql.conf
+}
+remote 127.0.0.1 {
+  disable_plaintext_auth = no
+}
+submission_host = postfix:588
+mail_max_userip_connections = 500
+service imap-postlogin {
+  executable = script-login /usr/local/bin/postlogin.sh
+  unix_listener imap-postlogin {
+  }
+}
diff --git a/data/conf/dovecot/sieve_after b/data/conf/dovecot/sieve_after
new file mode 100644
index 00000000..0b43dbcf
--- /dev/null
+++ b/data/conf/dovecot/sieve_after
@@ -0,0 +1,24 @@
+require "fileinto";
+require "mailbox";
+require "variables";
+require "subaddress";
+require "envelope";
+
+if header :contains "X-Spam-Flag" "YES" {
+  fileinto "Junk";
+}
+
+if allof (
+  envelope :detail :matches "to" "*",
+  header :contains "X-Moo-Tag" "YES",
+  mailboxexists "INBOX/${s}"
+  ) {
+    fileinto "INBOX/${s}";
+}
+elsif allof (
+  envelope :detail :matches "to" "*",
+  header :contains "X-Moo-Tag" "YES"
+  ) {
+    set :lower "s" "${1}";
+    fileinto :create "INBOX/${s}";
+}
diff --git a/data/conf/dovecot/sql/dovecot-dict-sql.conf b/data/conf/dovecot/sql/dovecot-dict-sql.conf
new file mode 100644
index 00000000..0271326e
--- /dev/null
+++ b/data/conf/dovecot/sql/dovecot-dict-sql.conf
@@ -0,0 +1,15 @@
+connect = "host=mysql dbname=mailcow user=mailcow password=mysafepasswd"
+
+map {
+  pattern = priv/quota/storage
+  table = quota2
+  username_field = username
+  value_field = bytes
+}
+map {
+  pattern = priv/quota/messages
+  table = quota2
+  username_field = username
+  value_field = messages
+}
+
diff --git a/data/conf/dovecot/sql/dovecot-mysql.conf b/data/conf/dovecot/sql/dovecot-mysql.conf
new file mode 100644
index 00000000..edf5ca6c
--- /dev/null
+++ b/data/conf/dovecot/sql/dovecot-mysql.conf
@@ -0,0 +1,6 @@
+driver = mysql
+connect = "host=mysql dbname=mailcow user=mailcow password=mysafepasswd"
+default_pass_scheme = SSHA256
+password_query = SELECT password FROM mailbox WHERE username = '%u' AND domain IN (SELECT domain FROM domain WHERE domain='%d' AND active='1')
+user_query = SELECT CONCAT('maildir:/var/vmail/',maildir) AS mail, 5000 AS uid, 5000 AS gid, concat('*:bytes=', quota) AS quota_rule FROM mailbox WHERE username = '%u' AND active = '1'
+iterate_query = SELECT username FROM mailbox WHERE active='1';
diff --git a/data/conf/mysql/my.cnf b/data/conf/mysql/my.cnf
new file mode 100644
index 00000000..668e390a
--- /dev/null
+++ b/data/conf/mysql/my.cnf
@@ -0,0 +1,14 @@
+[mysqld]
+character-set-client-handshake = FALSE
+character-set-server           = utf8mb4
+collation-server               = utf8mb4_unicode_ci
+innodb_file_per_table          = TRUE
+innodb_file_format             = barracuda
+innodb_large_prefix            = TRUE
+#sql_mode=IGNORE_SPACE,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
+
+[client]
+default-character-set = utf8mb4
+
+[mysql]
+default-character-set = utf8mb4
diff --git a/data/conf/nginx/dynmaps.conf b/data/conf/nginx/dynmaps.conf
new file mode 100644
index 00000000..86d52fd7
--- /dev/null
+++ b/data/conf/nginx/dynmaps.conf
@@ -0,0 +1,18 @@
+server {
+  listen 8081;
+  index index.php index.html;
+  server_name _;
+  error_log  /var/log/nginx/error.log;
+  access_log /var/log/nginx/access.log;
+  root /dynmaps;
+
+  location ~ \.php$ {
+    try_files $uri =404;
+    fastcgi_split_path_info ^(.+\.php)(/.+)$;
+    fastcgi_pass phpfpm:9000;
+    fastcgi_index index.php;
+    include fastcgi_params;
+    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
+    fastcgi_param PATH_INFO $fastcgi_path_info;
+  }
+}
diff --git a/data/conf/nginx/listen.template b/data/conf/nginx/listen.template
new file mode 100644
index 00000000..e454e832
--- /dev/null
+++ b/data/conf/nginx/listen.template
@@ -0,0 +1 @@
+listen ${HTTPS_PORT};
diff --git a/data/conf/nginx/site.conf b/data/conf/nginx/site.conf
new file mode 100644
index 00000000..467b56e4
--- /dev/null
+++ b/data/conf/nginx/site.conf
@@ -0,0 +1,136 @@
+proxy_cache_path /tmp levels=1:2 keys_zone=sogo:10m inactive=24h  max_size=1g;
+server {
+  include /etc/nginx/conf.d/listen.active;
+  include /etc/nginx/mime.types;
+  charset utf-8;
+  override_charset on;
+  ssl on;
+  ssl_certificate /etc/ssl/mail/cert.pem;
+  ssl_certificate_key /etc/ssl/mail/key.pem;
+  ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
+  ssl_prefer_server_ciphers on;
+  ssl_ciphers 'EDH+CAMELLIA:EDH+aRSA:EECDH+aRSA+AESGCM:EECDH+aRSA+SHA256:EECDH:+CAMELLIA128:+AES128:+SSLv3:!aNULL:!eNULL:!LOW:!3DES:!MD5:!EXP:!PSK:!DSS:!RC4:!SEED:!IDEA:!ECDSA:kEDH:CAMELLIA128-SHA:AES128-SHA';
+  add_header Strict-Transport-Security "max-age=15768000; includeSubDomains";
+  ssl_ecdh_curve secp384r1;
+  index index.php index.html;
+  server_name _ autodiscover.* autoconfig.*;
+  error_log  /var/log/nginx/error.log;
+  access_log /var/log/nginx/access.log;
+  root /web;
+
+  location = /principals/ {
+    rewrite ^ https://$host/SOGo/dav;
+    allow all;
+  }
+
+  location ~ \.php$ {
+    try_files $uri =404;
+    fastcgi_split_path_info ^(.+\.php)(/.+)$;
+    fastcgi_pass phpfpm:9000;
+    fastcgi_index index.php;
+    include /etc/nginx/fastcgi_params;
+    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
+    fastcgi_param PATH_INFO $fastcgi_path_info;
+    fastcgi_param PHP_VALUE "max_execution_time = 1200
+                             max_input_time = 1200
+                             memory_limit = 64M";
+    fastcgi_read_timeout 1200;
+  }
+
+  rewrite ^(/save.+)$ /rspamd$1 last;
+  location /rspamd/ {
+    proxy_pass       http://172.22.1.253:11334/;
+    proxy_set_header Host      $host;
+    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+    add_header Strict-Transport-Security "max-age=31536000; includeSubdomains";
+    add_header X-Content-Type-Options nosniff;
+    add_header X-Frame-Options SAMEORIGIN;
+    add_header X-XSS-Protection "1; mode=block";
+  }
+
+  location ^~ /inc/init.sql {
+    deny all;
+  }
+
+  if ($host ~* autodiscover\.(.*)) {
+    rewrite ^(.*) /autodiscover.php last;
+  }
+
+  if ($host ~* autoconfig\.(.*)) {
+    rewrite ^(.*) /autoconfig.php last;
+  }
+
+  location ^~ /Microsoft-Server-ActiveSync {
+    proxy_pass http://172.22.1.252:20000/SOGo/Microsoft-Server-ActiveSync;
+    proxy_connect_timeout 1000;
+    proxy_next_upstream timeout error;
+    proxy_send_timeout 1000;
+    proxy_read_timeout 1000;
+    proxy_buffer_size 8k;
+    proxy_buffers 4 32k;
+    proxy_temp_file_write_size 64k;
+    proxy_busy_buffers_size 64k;
+    proxy_set_header X-Real-IP $remote_addr;
+    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+    proxy_set_header Host $host;
+    proxy_set_header x-webobjects-server-protocol HTTP/1.0;
+    proxy_set_header x-webobjects-remote-host $remote_addr;
+    proxy_set_header x-webobjects-server-name $server_name;
+    proxy_set_header x-webobjects-server-url $scheme://$host:$server_port;
+    proxy_set_header x-webobjects-server-port $server_port;
+    client_body_buffer_size 128k;
+    client_max_body_size 100m;
+  }
+
+  location ^~ /SOGo {
+    proxy_pass http://172.22.1.252:20000;
+    proxy_set_header X-Real-IP $remote_addr;
+    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+    proxy_set_header Host $host;
+    proxy_set_header x-webobjects-server-protocol HTTP/1.0;
+    proxy_set_header x-webobjects-remote-host $remote_addr;
+    proxy_set_header x-webobjects-server-name $server_name;
+    proxy_set_header x-webobjects-server-url $scheme://$host:$server_port;
+    proxy_set_header x-webobjects-server-port $server_port;
+    #proxy_connect_timeout 90;
+    #proxy_send_timeout 90;
+    #proxy_read_timeout 90;
+    #proxy_buffer_size 4k;
+    #proxy_buffers 4 32k;
+    #proxy_busy_buffers_size 64k;
+    #proxy_temp_file_write_size 64k;
+    client_body_buffer_size 128k;
+    client_max_body_size 100m;
+    break;
+  }
+
+  location /SOGo.woa/WebServerResources/ {
+    proxy_pass http://172.22.1.252:9192/WebServerResources/;
+    proxy_set_header Host $host;
+    proxy_cache sogo;
+    proxy_cache_valid 200 1d;
+    proxy_cache_use_stale error timeout invalid_header updating http_500 http_502 http_503 http_504;
+    #alias /usr/lib/GNUstep/SOGo/WebServerResources/;
+    allow all;
+  }
+
+  location /SOGo/WebServerResources/ {
+    proxy_pass http://172.22.1.252:9192/WebServerResources/;
+    proxy_set_header Host $host;
+    proxy_cache sogo;
+    proxy_cache_valid 200 1d;
+    proxy_cache_use_stale error timeout invalid_header updating http_500 http_502 http_503 http_504;
+    #alias /usr/lib/GNUstep/SOGo/WebServerResources/;
+    allow all;
+  }
+
+  location (^/SOGo/so/ControlPanel/Products/[^/]*UI/Resources/.*\.(jpg|png|gif|css|js)$ {
+    proxy_pass http://172.22.1.252:9192/$1.SOGo/Resources/$2;
+    proxy_set_header Host $host;
+    proxy_cache sogo;
+    proxy_cache_valid 200 1d;
+    proxy_cache_use_stale error timeout invalid_header updating http_500 http_502 http_503 http_504;
+    #alias /usr/lib/GNUstep/SOGo/$1.SOGo/Resources/$2;
+  }
+
+}
diff --git a/data/conf/pdns/pdns_custom.lua b/data/conf/pdns/pdns_custom.lua
new file mode 100644
index 00000000..18588bf2
--- /dev/null
+++ b/data/conf/pdns/pdns_custom.lua
@@ -0,0 +1 @@
+addNTA("mailcow-network", "nta for local")
diff --git a/data/conf/pdns/recursor.conf b/data/conf/pdns/recursor.conf
new file mode 100644
index 00000000..f3270b5b
--- /dev/null
+++ b/data/conf/pdns/recursor.conf
@@ -0,0 +1,41 @@
+allow-from=127.0.0.0/8, 192.168.0.0/16, 172.16.0.0/12, 10.0.0.0/8
+config-dir=/etc/powerdns
+daemon=no
+disable-syslog=yes
+dnssec=process
+dnssec-log-bogus=yes
+dont-query=10.0.0.0/8, 100.64.0.0/10, 169.254.0.0/16, 192.168.0.0/16, 172.16.0.0/12, ::1/128, fc00::/7, fe80::/10, 0.0.0.0/8, 192.0.0.0/24, 192.0.2.0/24, 198.51.100.0/24, 203.0.113.0/24, 240.0.0.0/4, ::/96, ::ffff:0:0/96, 100::/64, 2001:db8::/32
+export-etc-hosts=off
+# forward-zones=
+forward-zones-recurse=mailcow-network.=127.0.0.11
+local-address=0.0.0.0
+local-port=53
+loglevel=6
+# lowercase-outgoing=no
+lua-config-file=/etc/powerdns/pdns_custom.lua
+# max-cache-entries=1000000
+# max-cache-ttl=86400
+# max-mthreads=2048
+# max-negative-ttl=3600
+# max-packetcache-entries=500000
+# max-qperq=50
+# max-tcp-clients=128
+# max-tcp-per-client=0
+# max-total-msec=7000
+# minimum-ttl-override=0
+# network-timeout=1500
+# packetcache-servfail-ttl=60
+# packetcache-ttl=3600
+quiet=yes
+# security-poll-suffix=secpoll.powerdns.com.
+# serve-rfc1918=yes
+# server-down-max-fails=64
+# server-down-throttle-time=60
+setgid=pdns
+setuid=pdns
+# spoof-nearmiss-max=20
+# stack-size=200000
+# threads=2
+# trace=off
+version-string=PowerDNS Recursor
+webserver=no
diff --git a/data/conf/postfix/main.cf b/data/conf/postfix/main.cf
new file mode 100644
index 00000000..1a96eff2
--- /dev/null
+++ b/data/conf/postfix/main.cf
@@ -0,0 +1,93 @@
+biff = no
+append_dot_mydomain = no
+smtpd_tls_cert_file = /etc/ssl/mail/cert.pem
+smtpd_tls_key_file = /etc/ssl/mail/key.pem
+smtpd_use_tls=yes
+smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache
+smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache
+smtpd_relay_restrictions = permit_mynetworks permit_sasl_authenticated defer_unauth_destination
+alias_maps = hash:/etc/aliases
+alias_database = hash:/etc/aliases
+relayhost =
+mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16
+mailbox_size_limit = 0
+recipient_delimiter = +
+inet_interfaces = all
+inet_protocols = all
+bounce_queue_lifetime = 1d
+broken_sasl_auth_clients = yes
+disable_vrfy_command = yes
+maximal_backoff_time = 1800s
+maximal_queue_lifetime = 1d
+message_size_limit = 26214400
+milter_default_action = accept
+milter_protocol = 6
+minimal_backoff_time = 300s
+plaintext_reject_code = 550
+postscreen_access_list = permit_mynetworks, cidr:/opt/postfix/conf/postscreen_access.cidr
+postscreen_bare_newline_enable = no
+postscreen_blacklist_action = drop
+postscreen_cache_cleanup_interval = 24h
+postscreen_cache_map = proxy:btree:$data_directory/postscreen_cache
+postscreen_dnsbl_action = enforce
+postscreen_dnsbl_sites = b.barracudacentral.org=127.0.0.2*7 dnsbl.inps.de=127.0.0.2*7 bl.mailspike.net=127.0.0.2*5 bl.mailspike.net=127.0.0.[10;11;12]*4 dnsbl.sorbs.net=127.0.0.10*8 dnsbl.sorbs.net=127.0.0.5*6 dnsbl.sorbs.net=127.0.0.7*3 dnsbl.sorbs.net=127.0.0.8*2 dnsbl.sorbs.net=127.0.0.6*2 dnsbl.sorbs.net=127.0.0.9*2 zen.spamhaus.org=127.0.0.[10;11]*8 zen.spamhaus.org=127.0.0.[4..7]*6 zen.spamhaus.org=127.0.0.3*4 zen.spamhaus.org=127.0.0.2*3 hostkarma.junkemailfilter.com=127.0.0.2*3 hostkarma.junkemailfilter.com=127.0.0.4*1 hostkarma.junkemailfilter.com=127.0.1.2*1 wl.mailspike.net=127.0.0.[18;19;20]*-2 hostkarma.junkemailfilter.com=127.0.0.1*-2
+postscreen_dnsbl_threshold = 8
+postscreen_dnsbl_ttl = 5m
+postscreen_greet_action = enforce
+postscreen_greet_banner = $smtpd_banner
+postscreen_greet_ttl = 2d
+postscreen_greet_wait = 3s
+postscreen_non_smtp_command_enable = no
+postscreen_pipelining_enable = no
+proxy_read_maps = proxy:mysql:/opt/postfix/conf/sql/mysql_virtual_sender_acl.cf, proxy:mysql:/opt/postfix/conf/sql/mysql_tls_enforce_out_policy.cf, proxy:mysql:/opt/postfix/conf/sql/mysql_tls_enforce_in_policy.cf, $local_recipient_maps $mydestination $virtual_alias_maps $virtual_alias_domains $virtual_mailbox_maps $virtual_mailbox_domains $relay_recipient_maps $relay_domains $canonical_maps $sender_canonical_maps $recipient_canonical_maps $relocated_maps $transport_maps $mynetworks $smtpd_sender_login_maps
+queue_run_delay = 300s
+relay_domains = proxy:mysql:/opt/postfix/conf/sql/mysql_virtual_relay_domain_maps.cf
+relay_recipient_maps = proxy:mysql:/opt/postfix/conf/sql/mysql_relay_recipient_maps.cf
+sender_dependent_default_transport_maps = proxy:mysql:/opt/postfix/conf/sql/mysql_tls_enforce_out_policy.cf
+smtp_tls_CAfile = /etc/ssl/certs/ca-certificates.crt
+smtp_tls_cert_file = /etc/ssl/mail/cert.pem
+smtp_tls_key_file = /etc/ssl/mail/key.pem
+smtp_tls_loglevel = 1
+smtp_dns_support_level = dnssec
+smtp_tls_security_level = dane
+smtpd_data_restrictions = reject_unauth_pipelining, permit
+smtpd_delay_reject = yes
+smtpd_error_sleep_time = 10s
+smtpd_hard_error_limit = ${stress?1}${stress:5}
+smtpd_helo_required = yes
+smtpd_proxy_timeout = 600s
+smtpd_recipient_restrictions = permit_sasl_authenticated, permit_mynetworks, check_recipient_access proxy:mysql:/opt/postfix/conf/sql/mysql_tls_enforce_in_policy.cf, reject_invalid_helo_hostname, reject_unknown_reverse_client_hostname, reject_unauth_destination
+smtpd_sasl_auth_enable = yes
+smtpd_sasl_authenticated_header = yes
+smtpd_sasl_path = inet:dovecot:10001
+smtpd_sasl_type = dovecot
+smtpd_sender_login_maps = proxy:mysql:/opt/postfix/conf/sql/mysql_virtual_sender_acl.cf
+smtpd_sender_restrictions = reject_authenticated_sender_login_mismatch, permit_mynetworks, reject_sender_login_mismatch, permit_sasl_authenticated, reject_unlisted_sender, reject_unknown_sender_domain
+smtpd_soft_error_limit = 3
+smtpd_tls_auth_only = yes
+smtpd_tls_dh1024_param_file = /etc/ssl/mail/dhparams.pem
+smtpd_tls_eecdh_grade = strong
+smtpd_tls_exclude_ciphers = ECDHE-RSA-RC4-SHA, RC4, aNULL
+smtpd_tls_loglevel = 1
+smtp_tls_mandatory_protocols = !SSLv2, !SSLv3
+smtp_tls_protocols = !SSLv2, !SSLv3
+lmtp_tls_mandatory_protocols = !SSLv2, !SSLv3
+lmtp_tls_protocols = !SSLv2, !SSLv3
+smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3
+smtpd_tls_protocols = !SSLv2, !SSLv3
+smtpd_tls_mandatory_ciphers = high
+smtpd_tls_security_level = may
+tls_ssl_options = NO_COMPRESSION
+tls_high_cipherlist = EDH+CAMELLIA:EDH+aRSA:EECDH+aRSA+AESGCM:EECDH+aRSA+SHA256:EECDH:+CAMELLIA128:+AES128:+SSLv3:!aNULL:!eNULL:!LOW:!3DES:!MD5:!EXP:!PSK:!DSS:!RC4:!SEED:!IDEA:!ECDSA:kEDH:CAMELLIA128-SHA:AES128-SHA
+virtual_alias_maps = proxy:mysql:/opt/postfix/conf/sql/mysql_virtual_alias_maps.cf, proxy:mysql:/opt/postfix/conf/sql/mysql_virtual_spamalias_maps.cf, proxy:mysql:/opt/postfix/conf/sql/mysql_virtual_alias_domain_maps.cf, proxy:mysql:/opt/postfix/conf/sql/mysql_virtual_alias_domain_catchall_maps.cf
+virtual_gid_maps = static:5000
+virtual_mailbox_base = /var/vmail/
+virtual_mailbox_domains = proxy:mysql:/opt/postfix/conf/sql/mysql_virtual_domains_maps.cf
+virtual_mailbox_maps = proxy:mysql:/opt/postfix/conf/sql/mysql_virtual_mailbox_maps.cf, proxy:mysql:/opt/postfix/conf/sql/mysql_virtual_alias_domain_mailbox_maps.cf
+virtual_minimum_uid = 104
+virtual_transport = lmtp:inet:dovecot:24
+virtual_uid_maps = static:5000
+smtpd_milters = inet:rmilter:9900
+non_smtpd_milters = inet:rmilter:9900
+milter_mail_macros = i {mail_addr} {client_addr} {client_name} {auth_authen}
+mydestination = localhost.localdomain, localhost
diff --git a/data/conf/postfix/master.cf b/data/conf/postfix/master.cf
new file mode 100644
index 00000000..e48ea92e
--- /dev/null
+++ b/data/conf/postfix/master.cf
@@ -0,0 +1,45 @@
+smtp       inet  n       -       n       -       1       postscreen
+smtpd      pass  -       -       n       -       -       smtpd
+  -o smtpd_helo_restrictions=permit_mynetworks,reject_non_fqdn_helo_hostname
+smtps    inet  n       -       n       -       -       smtpd
+  -o smtpd_tls_wrappermode=yes
+  -o smtpd_client_restrictions=permit_mynetworks,permit_sasl_authenticated,reject
+submission inet n       -       n       -       -       smtpd
+  -o smtpd_client_restrictions=permit_mynetworks,permit_sasl_authenticated,reject
+  -o smtpd_enforce_tls=yes
+  -o smtpd_tls_security_level=encrypt
+  -o tls_preempt_cipherlist=yes
+588 inet n      -       n       -       -       smtpd
+  -o smtpd_client_restrictions=permit_mynetworks,permit_sasl_authenticated,reject
+  -o smtpd_tls_auth_only=no
+smtp_enforced_tls      unix  -       -       n       -       -       smtp
+  -o smtp_tls_security_level=encrypt
+  -o syslog_name=enforced-tls-smtp
+  -o smtp_delivery_status_filter=pcre:/opt/postfix/conf/smtp_dsn_filter
+tlsproxy   unix  -       -       n       -       0       tlsproxy
+dnsblog    unix  -       -       n       -       0       dnsblog
+pickup     fifo  n       -       n       60      1       pickup
+cleanup    unix  n       -       n       -       0       cleanup
+qmgr       fifo  n       -       n       300     1       qmgr
+tlsmgr     unix  -       -       n       1000?   1       tlsmgr
+rewrite    unix  -       -       n       -       -       trivial-rewrite
+bounce     unix  -       -       n       -       0       bounce
+defer      unix  -       -       n       -       0       bounce
+trace      unix  -       -       n       -       0       bounce
+verify     unix  -       -       n       -       1       verify
+flush      unix  n       -       n       1000?   0       flush
+proxymap   unix  -       -       n       -       -       proxymap
+proxywrite unix  -       -       n       -       1       proxymap
+smtp       unix  -       -       n       -       -       smtp
+relay      unix  -       -       n       -       -       smtp
+showq      unix  n       -       n       -       -       showq
+error      unix  -       -       n       -       -       error
+retry      unix  -       -       n       -       -       error
+discard    unix  -       -       n       -       -       discard
+local      unix  -       n       n       -       -       local
+virtual    unix  -       n       n       -       -       virtual
+lmtp       unix  -       -       n       -       -       lmtp
+anvil      unix  -       -       n       -       1       anvil
+scache     unix  -       -       n       -       1       scache
+maildrop   unix  -       n       n       -       -       pipe flags=DRhu
+    user=vmail argv=/usr/bin/maildrop -d ${recipient}
diff --git a/data/conf/postfix/postscreen_access.cidr b/data/conf/postfix/postscreen_access.cidr
new file mode 100644
index 00000000..c76ec681
--- /dev/null
+++ b/data/conf/postfix/postscreen_access.cidr
@@ -0,0 +1,654 @@
+# Generated by Postwhite v1.30 on Thu Dec  8 21:11:27 CET 2016
+# https://github.com/stevejenkins/postwhite/
+# 651 total rules
+2a00:1450:4000::/36     permit
+2a01:111:f400::/48      permit
+2a04:35c0::/29  permit
+2c0f:fb50:4000::/36     permit
+5.135.24.0/24   permit
+8.20.114.31     permit
+8.25.194.0/23   permit
+8.25.196.0/23   permit
+12.130.86.238   permit
+13.111.0.0/19   permit
+17.36.0.0/16    permit
+17.41.0.0/16    permit
+17.110.0.0/15   permit
+17.120.0.0/16   permit
+17.133.0.0/16   permit
+17.139.0.0/16   permit
+17.142.0.0/15   permit
+17.151.1.0/24   permit
+17.158.0.0/15   permit
+17.162.0.0/15   permit
+17.164.0.0/16   permit
+17.171.37.0/24  permit
+17.172.0.0/16   permit
+23.21.83.90     permit
+23.23.237.213   permit
+23.96.52.53     permit
+23.100.122.175  permit
+23.103.128.0/19 permit
+23.103.131.7    permit
+23.103.191.0/24 permit
+23.103.198.0/23 permit
+23.103.200.0/21 permit
+23.103.208.0/21 permit
+23.103.224.0/19 permit
+23.253.182.0/24 permit
+23.253.182.103  permit
+23.253.183.0/24 permit
+23.253.183.145  permit
+23.253.183.146  permit
+23.253.183.147  permit
+23.253.183.148  permit
+23.253.183.150  permit
+27.126.146.0/24 permit
+37.59.69.128/25 permit
+37.59.249.0/24  permit
+37.188.97.188/32        permit
+40.92.0.0/14    permit
+40.96.32.50     permit
+40.97.113.34    permit
+40.97.113.210   permit
+40.97.153.146   permit
+40.97.155.26    permit
+40.97.156.114   permit
+40.97.160.2     permit
+40.97.164.146   permit
+40.97.166.138   permit
+40.97.170.154   permit
+40.107.0.0/17   permit
+40.107.128.0/18 permit
+41.74.192.0/22  permit
+41.74.196.0/22  permit
+41.74.200.0/22  permit
+41.74.201.0/24  permit
+41.74.204.0/22  permit
+41.74.205.0/24  permit
+46.19.168.0/23  permit
+50.18.45.249    permit
+50.18.121.236   permit
+50.18.121.248   permit
+50.18.123.221   permit
+50.18.124.70    permit
+50.18.125.97    permit
+50.18.125.237   permit
+50.18.126.162   permit
+50.22.164.201   permit
+50.23.218.192/27        permit
+50.31.32.0/19   permit
+50.31.36.197    permit
+50.31.36.199    permit
+50.31.36.205    permit
+50.31.36.208    permit
+50.31.36.213    permit
+50.31.60.1      permit
+50.31.156.96/27 permit
+50.207.218.237  permit
+51.4.71.62      permit
+52.0.20.102     permit
+52.95.48.152/29 permit
+52.95.49.88/29  permit
+52.205.61.79    permit
+54.172.97.247   permit
+54.173.229.38   permit
+54.214.39.184   permit
+54.240.0.0/18   permit
+54.241.16.209   permit
+54.243.205.80   permit
+54.244.242.0/24 permit
+62.17.146.128/26        permit
+63.80.14.0/23   permit
+63.111.28.137   permit
+63.128.21.0/24  permit
+64.4.22.64/26   permit
+64.18.0.0/20    permit
+64.20.241.45    permit
+64.34.47.128/27 permit
+64.34.57.192/26 permit
+64.79.155.0/24  permit
+64.79.155.192   permit
+64.127.115.252  permit
+64.132.88.0/23  permit
+64.132.92.0/24  permit
+64.135.77.0/24  permit
+64.135.83.0/24  permit
+64.233.160.0/19 permit
+65.39.215.0/24  permit
+65.54.51.64/26  permit
+65.54.61.64/26  permit
+65.54.121.120/29        permit
+65.54.121.124/31        permit
+65.54.190.0/24  permit
+65.54.241.0/24  permit
+65.55.33.64/28  permit
+65.55.34.0/24   permit
+65.55.42.224/28 permit
+65.55.52.224/27 permit
+65.55.77.28     permit
+65.55.78.128/25 permit
+65.55.81.48/28  permit
+65.55.81.54/31  permit
+65.55.85.12     permit
+65.55.88.0/24   permit
+65.55.90.0/24   permit
+65.55.94.0/25   permit
+65.55.111.0/24  permit
+65.55.113.64/26 permit
+65.55.116.0/25  permit
+65.55.126.0/25  permit
+65.55.169.0/24  permit
+65.55.174.0/25  permit
+65.55.178.128/27        permit
+65.55.234.192/26        permit
+65.110.161.77   permit
+65.212.180.36   permit
+65.242.92.0/24  permit
+65.242.92.15    permit
+66.77.16.201/32 permit
+66.102.0.0/20   permit
+66.135.215.0/24 permit
+66.135.222.1    permit
+66.211.168.230/31       permit
+66.211.184.0/23 permit
+66.220.144.128/25       permit
+66.220.155.0/24 permit
+66.220.155.128/25       permit
+66.220.157.0/25 permit
+66.231.80.0/20  permit
+66.249.80.0/20  permit
+67.23.31.6      permit
+67.72.99.26     permit
+67.221.168.65   permit
+67.228.2.24/30  permit
+67.228.21.184/29        permit
+67.228.37.4/30  permit
+67.228.50.32/27 permit
+67.228.50.54/31 permit
+67.231.145.42   permit
+67.231.153.30   permit
+68.232.192.0/20 permit
+69.63.178.128/25        permit
+69.63.179.25    permit
+69.63.184.0/25  permit
+69.65.42.195    permit
+69.65.49.192/29 permit
+69.162.98.0/24  permit
+69.171.232.0/24 permit
+69.171.232.128/25       permit
+69.171.244.0/24 permit
+70.37.151.128/25        permit
+70.42.149.35    permit
+72.3.185.0/24   permit
+72.3.237.64/28  permit
+72.5.230.111/32 permit
+72.14.192.0/18  permit
+72.21.192.0/19  permit
+72.21.212.0/25  permit
+72.21.217.142/32        permit
+72.32.154.0/24  permit
+72.32.217.0/24  permit
+72.32.243.0/24  permit
+72.249.147.250/32       permit
+74.63.63.115    permit
+74.63.63.121    permit
+74.63.194.126   permit
+74.63.234.75    permit
+74.63.236.0/24  permit
+74.86.113.28/30 permit
+74.86.129.240/30        permit
+74.86.131.208/30        permit
+74.86.132.208/30        permit
+74.86.160.160/30        permit
+74.86.164.188/30        permit
+74.86.171.192/30        permit
+74.86.195.28/30 permit
+74.86.207.36/30 permit
+74.86.226.216/30        permit
+74.86.236.240/30        permit
+74.86.241.250/31        permit
+74.112.64.26    permit
+74.112.67.243   permit
+74.112.170.21/32        permit
+74.125.0.0/16   permit
+74.201.84.0/24  permit
+74.201.152.59/32        permit
+74.201.154.0/24 permit
+74.201.155.25/32        permit
+74.201.155.26/32        permit
+74.201.155.27/32        permit
+74.201.155.28/32        permit
+74.201.155.79/32        permit
+74.202.227.52/32        permit
+74.208.4.192/26 permit
+74.208.5.64/26  permit
+74.208.122.0/26 permit
+74.209.250.0/24 permit
+74.209.250.84   permit
+75.126.200.128/27       permit
+75.126.253.0/24 permit
+75.126.253.48   permit
+80.231.25.0/24  permit
+80.231.219.0/24 permit
+81.223.46.0/27  permit
+82.165.159.0/24 permit
+85.222.130.192/26       permit
+85.222.138.192/26       permit
+86.61.88.25     permit
+87.238.80.0/21  permit
+87.253.232.0/21 permit
+91.194.248.0/23 permit
+91.198.22.0/24  permit
+91.211.240.0/24 permit
+91.211.242.0/24 permit
+91.211.243.0/24 permit
+91.220.42.0/24  permit
+94.236.119.0/26 permit
+94.245.112.0/27 permit
+94.245.112.10/31        permit
+94.245.120.64/26        permit
+96.43.144.0/20  permit
+96.43.144.64/28 permit
+96.43.144.64/31 permit
+96.43.147.64/28 permit
+96.43.148.64/28 permit
+96.43.148.64/31 permit
+96.43.151.64/28 permit
+96.43.152.64/27 permit
+96.43.153.64/27 permit
+96.46.150.192/27        permit
+101.53.164.192/26       permit
+103.11.200.0/22 permit
+103.13.69.0/24  permit
+103.28.42.0/24  permit
+103.237.104.0/22        permit
+104.40.211.35   permit
+104.43.195.251  permit
+104.47.0.0/17   permit
+104.130.96.0/28 permit
+104.130.122.0/23        permit
+104.245.209.192/26      permit
+106.50.16.0/28  permit
+107.0.11.224/27 permit
+108.174.0.0/24  permit
+108.174.0.215   permit
+108.174.3.0/24  permit
+108.174.6.0/24  permit
+108.175.18.45   permit
+108.175.30.45   permit
+108.177.8.0/21  permit
+108.177.96.0/19 permit
+111.221.23.128/25       permit
+111.221.26.0/27 permit
+111.221.66.0/25 permit
+111.221.69.128/25       permit
+111.221.112.0/21        permit
+124.47.150.0/24 permit
+124.47.189.0/24 permit
+129.41.77.70    permit
+129.41.169.249  permit
+131.107.0.0/16  permit
+131.107.1.18    permit
+131.107.1.19    permit
+131.107.1.20    permit
+131.107.1.37    permit
+131.107.1.44    permit
+131.107.1.48    permit
+131.107.1.56    permit
+131.253.30.0/24 permit
+131.253.121.20  permit
+134.170.113.0/26        permit
+134.170.140.0/24        permit
+134.170.141.64/26       permit
+134.170.143.0/24        permit
+134.170.174.0/24        permit
+136.146.128.64/27       permit
+136.146.208.16/28       permit
+136.146.210.16/28       permit
+136.147.46.192/26       permit
+136.147.62.192/26       permit
+136.147.128.0/20        permit
+136.147.176.0/20        permit
+146.88.28.0/24  permit
+146.101.78.0/24 permit
+147.243.1.47    permit
+147.243.1.48    permit
+147.243.1.153   permit
+147.243.128.24  permit
+147.243.128.26  permit
+151.101.37.140  permit
+157.55.0.192/26 permit
+157.55.1.128/26 permit
+157.55.2.0/25   permit
+157.55.9.128/25 permit
+157.55.11.0/25  permit
+157.55.49.0/25  permit
+157.55.61.0/24  permit
+157.55.157.128/25       permit
+157.55.158.0/23 permit
+157.55.225.0/25 permit
+157.55.234.0/24 permit
+157.56.24.0/25  permit
+157.56.110.0/23 permit
+157.56.112.0/24 permit
+157.56.120.128/26       permit
+157.56.172.28   permit
+157.56.232.0/21 permit
+157.56.240.0/20 permit
+157.56.248.0/21 permit
+157.151.208.65  permit
+162.88.4.0/24   permit
+162.88.36.0/24  permit
+162.248.185.121 permit
+163.47.180.0/22 permit
+165.254.167.152/30      permit
+165.254.167.156/31      permit
+165.254.167.162/31      permit
+165.254.168.66/31       permit
+165.254.168.68/31       permit
+165.254.168.70/31       permit
+165.254.168.72/31       permit
+166.78.68.0/22  permit
+166.78.68.221   permit
+166.78.69.146   permit
+166.78.69.169   permit
+166.78.69.170   permit
+166.78.71.131   permit
+167.89.0.0/17   permit
+167.89.16.30    permit
+167.89.16.183   permit
+167.89.16.245   permit
+167.89.25.84    permit
+167.89.32.5     permit
+167.89.32.50    permit
+167.89.46.159   permit
+167.89.46.185   permit
+167.89.60.95    permit
+167.89.62.118   permit
+167.89.64.9     permit
+167.89.65.0     permit
+167.89.65.53    permit
+167.89.65.100   permit
+167.89.74.233   permit
+167.89.75.33    permit
+167.89.75.126   permit
+167.89.75.136   permit
+167.89.75.164   permit
+167.89.101.2    permit
+167.89.101.192/28       permit
+167.220.67.238  permit
+172.217.0.0/19  permit
+173.0.84.224/28 permit
+173.0.94.244/30 permit
+173.193.132.0/23        permit
+173.193.132.134/31      permit
+173.193.210.32/27       permit
+173.194.0.0/16  permit
+173.203.79.182  permit
+173.203.81.39   permit
+173.224.160.128/25      permit
+173.224.161.128/25      permit
+173.228.155.0/24        permit
+174.36.80.208/28        permit
+174.36.84.8/29  permit
+174.36.84.16/29 permit
+174.36.84.32/29 permit
+174.36.84.144/29        permit
+174.36.84.240/29        permit
+174.36.85.248/30        permit
+174.36.92.96/27 permit
+174.36.114.128/30       permit
+174.36.114.140/30       permit
+174.36.114.148/30       permit
+174.36.114.152/29       permit
+174.37.67.28/30 permit
+174.37.226.64/27        permit
+174.129.194.241 permit
+174.129.203.189 permit
+174.137.46.0/24 permit
+176.32.105.0/24 permit
+176.32.127.0/24 permit
+178.32.48.128   permit
+178.33.111.144  permit
+178.33.137.208/28       permit
+178.33.221.0/24 permit
+178.236.10.128/26       permit
+178.249.98.16/29        permit
+178.249.202.16/29       permit
+180.189.28.0/24 permit
+182.50.76.0/22  permit
+182.50.78.64/28 permit
+184.173.105.0/24        permit
+184.173.153.0/24        permit
+185.4.120.0/24  permit
+185.4.122.0/24  permit
+185.12.80.0/22  permit
+185.28.196.0/22 permit
+185.90.20.0/22  permit
+188.172.128.0/20        permit
+191.239.213.197 permit
+192.28.128.0/18 permit
+192.30.252.0/22 permit
+192.64.236.0/24 permit
+192.64.237.0/24 permit
+192.64.238.0/24 permit
+192.161.144.0/20        permit
+192.230.81.86   permit
+192.237.158.0/23        permit
+192.237.159.42  permit
+192.237.159.43  permit
+192.254.112.0/20        permit
+192.254.112.60  permit
+192.254.112.98/31       permit
+192.254.113.10  permit
+192.254.113.101 permit
+192.254.114.176 permit
+192.254.115.72  permit
+192.254.118.63  permit
+193.28.178.0/25 permit
+194.64.234.128/27       permit
+194.64.234.129  permit
+194.154.193.192/27      permit
+195.54.172.0/23 permit
+195.130.217.0/24        permit
+198.2.128.0/18  permit
+198.2.128.0/24  permit
+198.2.132.0/22  permit
+198.2.136.0/23  permit
+198.2.177.0/24  permit
+198.2.178.0/24  permit
+198.2.179.0/24  permit
+198.2.180.0/24  permit
+198.2.186.0/23  permit
+198.21.0.0/21   permit
+198.21.3.166    permit
+198.21.4.224    permit
+198.37.144.0/20 permit
+198.37.145.250  permit
+198.37.149.128  permit
+198.37.151.26   permit
+198.61.254.0/23 permit
+198.61.254.231  permit
+198.178.234.57  permit
+198.245.80.0/20 permit
+199.15.176.173  permit
+199.15.212.0/22 permit
+199.15.214.169/32       permit
+199.16.156.0/22 permit
+199.19.0.0/21   permit
+199.59.148.0/22 permit
+199.83.132.86   permit
+199.101.161.130 permit
+199.101.162.0/25        permit
+199.122.120.0/21        permit
+199.127.232.0/22        permit
+199.187.117.209 permit
+199.187.117.233 permit
+199.187.117.234/31      permit
+199.187.117.236/31      permit
+199.187.118.201 permit
+199.187.118.202/31      permit
+199.187.118.204 permit
+199.187.118.209 permit
+199.201.64.23   permit
+199.201.65.23   permit
+199.255.192.0/22        permit
+202.129.242.0/23        permit
+202.177.148.100 permit
+202.177.148.110 permit
+203.32.4.25     permit
+203.55.21.0/24  permit
+203.62.195.0/24 permit
+203.81.17.0/24  permit
+203.122.32.250  permit
+203.145.57.160/27       permit
+204.13.11.48/29 permit
+204.13.11.48/30 permit
+204.13.248.0/22 permit
+204.14.232.0/21 permit
+204.14.232.64/28        permit
+204.14.234.64/28        permit
+204.14.238.0/27 permit
+204.29.186.0/23 permit
+204.75.142.0/24 permit
+204.92.114.187  permit
+204.92.114.203  permit
+204.92.114.204/31       permit
+204.153.121.0/24        permit
+205.139.110.0/23        permit
+205.201.128.0/20        permit
+205.201.131.128/25      permit
+205.201.132.14  permit
+205.201.134.128/25      permit
+205.201.136.0/23        permit
+205.201.137.229 permit
+205.201.139.0/24        permit
+205.201.140.14  permit
+205.207.104.0/22        permit
+205.217.25.132  permit
+205.217.25.135  permit
+205.251.233.32/32       permit
+205.251.233.36/32       permit
+206.25.247.143  permit
+206.25.247.155  permit
+206.165.246.80/29       permit
+206.191.224.0/19        permit
+206.246.157.1   permit
+207.46.4.128/25 permit
+207.46.22.35    permit
+207.46.22.98    permit
+207.46.22.101   permit
+207.46.50.72    permit
+207.46.50.82    permit
+207.46.50.192/26        permit
+207.46.50.224   permit
+207.46.51.64/26 permit
+207.46.52.71    permit
+207.46.52.79    permit
+207.46.58.128/25        permit
+207.46.100.0/24 permit
+207.46.101.128/26       permit
+207.46.116.128/29       permit
+207.46.117.0/24 permit
+207.46.132.128/27       permit
+207.46.163.0/24 permit
+207.46.198.0/25 permit
+207.46.200.0/27 permit
+207.67.38.0/24  permit
+207.67.98.192/27        permit
+207.68.176.0/26 permit
+207.68.176.96/27        permit
+207.82.80.0/24  permit
+207.126.144.0/20        permit
+207.171.160.0/19        permit
+207.211.30.0/24 permit
+207.211.31.0/25 permit
+207.211.41.113  permit
+207.218.90.0/24 permit
+207.250.68.0/24 permit
+208.40.232.70   permit
+208.43.21.28/30 permit
+208.43.21.64/29 permit
+208.43.21.72/30 permit
+208.43.239.136/30       permit
+208.64.132.0/22 permit
+208.66.139.0/25 permit
+208.74.204.0/22 permit
+208.74.204.9    permit
+208.75.120.0/22 permit
+208.75.122.246  permit
+208.76.56.0/21  permit
+208.78.68.0/22  permit
+208.82.236.96/28        permit
+208.82.237.96/28        permit
+208.82.238.96/28        permit
+208.85.50.137   permit
+208.89.13.233   permit
+208.89.13.234/31        permit
+208.89.13.236/31        permit
+208.89.14.201   permit
+208.89.14.202/31        permit
+208.89.14.204   permit
+208.89.14.209   permit
+208.117.48.0/20 permit
+208.185.229.45  permit
+208.201.241.163 permit
+209.43.22.0/28  permit
+209.46.117.168  permit
+209.46.117.179  permit
+209.61.151.0/24 permit
+209.67.98.46    permit
+209.67.98.59    permit
+209.85.128.0/17 permit
+212.4.136.0/26  permit
+212.123.28.40/32        permit
+212.227.15.0/24 permit
+212.227.17.0/27 permit
+212.227.126.128/25      permit
+213.165.64.0/23 permit
+213.167.75.0/24 permit
+213.167.81.0/24 permit
+213.199.128.139 permit
+213.199.128.145 permit
+213.199.138.181 permit
+213.199.138.191 permit
+213.199.154.0/24        permit
+213.199.161.128/27      permit
+213.199.177.0/26        permit
+213.199.180.0/24        permit
+216.17.150.242  permit
+216.17.150.251  permit
+216.32.180.0/23 permit
+216.46.168.197  permit
+216.46.168.222  permit
+216.58.192.0/19 permit
+216.99.5.67     permit
+216.99.5.68     permit
+216.113.160.0/24        permit
+216.113.172.0/25        permit
+216.113.175.0/24        permit
+216.136.162.65  permit
+216.136.162.120/29      permit
+216.136.168.80/28       permit
+216.146.32.0/20 permit
+216.198.0.0/18  permit
+216.203.30.55   permit
+216.203.33.178/31       permit
+216.205.24.0/24 permit
+216.229.156.0/25        permit
+216.239.32.0/19 permit
+217.72.207.0/27 permit
+217.77.141.52   permit
+217.77.141.59   permit
+217.175.193.0/24        permit
+217.175.194.0/23        permit
+217.175.196.0/24        permit
+2001:4860:4000::/36     permit
+2404:6800:4000::/36     permit
+2607:f8b0:4000::/36     permit
+2620:109:c003:104::/64  permit
+2620:109:c006:104::/64  permit
+2620:109:c00d:104::/64  permit
+2620:119:50c0:207::/64  permit
+2800:3f0:4000::/36      permit
diff --git a/data/conf/postfix/smtp_dsn_filter b/data/conf/postfix/smtp_dsn_filter
new file mode 100644
index 00000000..2fb5101e
--- /dev/null
+++ b/data/conf/postfix/smtp_dsn_filter
@@ -0,0 +1,6 @@
+/^4(\.\d+\.\d+ TLS is required, but host \S+ refused to start TLS: .+)/
+        5$1
+/^4(\.\d+\.\d+ TLS is required, but was not offered by host .+)/
+        5$1
+/^4.7.5(.*)/
+        5.7.5$1
diff --git a/data/conf/postfix/sql/mysql_relay_recipient_maps.cf b/data/conf/postfix/sql/mysql_relay_recipient_maps.cf
new file mode 100644
index 00000000..9eb7b159
--- /dev/null
+++ b/data/conf/postfix/sql/mysql_relay_recipient_maps.cf
@@ -0,0 +1,5 @@
+user = mailcow
+password = mysafepasswd
+hosts = mysql
+dbname = mailcow
+query = SELECT DISTINCT CASE WHEN '%d' IN (SELECT domain FROM domain WHERE relay_all_recipients=1 AND domain='%d' AND backupmx=1) THEN '%s' ELSE (SELECT goto FROM alias WHERE address='%s' AND active='1') END AS result;
diff --git a/data/conf/postfix/sql/mysql_tls_enforce_in_policy.cf b/data/conf/postfix/sql/mysql_tls_enforce_in_policy.cf
new file mode 100644
index 00000000..de40b580
--- /dev/null
+++ b/data/conf/postfix/sql/mysql_tls_enforce_in_policy.cf
@@ -0,0 +1,5 @@
+user = mailcow
+password = mysafepasswd
+hosts = mysql
+dbname = mailcow
+query = SELECT IF( EXISTS( SELECT 'TLS_ACTIVE' FROM alias LEFT OUTER JOIN mailbox ON mailbox.username = alias.address WHERE (address='%s' OR address IN (SELECT CONCAT('%u', '@', target_domain) FROM alias_domain WHERE alias_domain='%d')) AND mailbox.tls_enforce_in = '1' AND mailbox.active = '1'), 'reject_plaintext_session', 'DUNNO') AS 'tls_enforce_in';
diff --git a/data/conf/postfix/sql/mysql_tls_enforce_out_policy.cf b/data/conf/postfix/sql/mysql_tls_enforce_out_policy.cf
new file mode 100644
index 00000000..34d61331
--- /dev/null
+++ b/data/conf/postfix/sql/mysql_tls_enforce_out_policy.cf
@@ -0,0 +1,5 @@
+user = mailcow
+password = mysafepasswd
+hosts = mysql
+dbname = mailcow
+query = SELECT IF( EXISTS( SELECT 'TLS_ACTIVE' FROM alias LEFT OUTER JOIN mailbox ON mailbox.username = alias.address WHERE (address='%s' OR address IN (SELECT CONCAT('%u', '@', target_domain) FROM alias_domain WHERE alias_domain='%d')) AND mailbox.tls_enforce_out = '1' AND mailbox.active = '1'), 'smtp_enforced_tls:', 'DUNNO') AS 'tls_enforce_out';
diff --git a/data/conf/postfix/sql/mysql_virtual_alias_domain_catchall_maps.cf b/data/conf/postfix/sql/mysql_virtual_alias_domain_catchall_maps.cf
new file mode 100644
index 00000000..484a0eac
--- /dev/null
+++ b/data/conf/postfix/sql/mysql_virtual_alias_domain_catchall_maps.cf
@@ -0,0 +1,6 @@
+user = mailcow
+password = mysafepasswd
+hosts = mysql
+dbname = mailcow
+query = SELECT goto FROM alias,alias_domain WHERE alias_domain.alias_domain = '%d' and alias.address = CONCAT('@', alias_domain.target_domain) AND alias.active = 1 AND alias_domain.active='1'
+
diff --git a/data/conf/postfix/sql/mysql_virtual_alias_domain_mailbox_maps.cf b/data/conf/postfix/sql/mysql_virtual_alias_domain_mailbox_maps.cf
new file mode 100644
index 00000000..03061109
--- /dev/null
+++ b/data/conf/postfix/sql/mysql_virtual_alias_domain_mailbox_maps.cf
@@ -0,0 +1,5 @@
+user = mailcow
+password = mysafepasswd
+hosts = mysql
+dbname = mailcow
+query = SELECT maildir FROM mailbox,alias_domain WHERE alias_domain.alias_domain = '%d' and mailbox.username = CONCAT('%u', '@', alias_domain.target_domain) AND mailbox.active = 1 AND alias_domain.active='1'
diff --git a/data/conf/postfix/sql/mysql_virtual_alias_domain_maps.cf b/data/conf/postfix/sql/mysql_virtual_alias_domain_maps.cf
new file mode 100644
index 00000000..2718bb00
--- /dev/null
+++ b/data/conf/postfix/sql/mysql_virtual_alias_domain_maps.cf
@@ -0,0 +1,5 @@
+user = mailcow
+password = mysafepasswd
+hosts = mysql
+dbname = mailcow
+query = SELECT goto FROM alias,alias_domain WHERE alias_domain.alias_domain = '%d' and alias.address = CONCAT('%u', '@', alias_domain.target_domain) AND alias.active = 1 AND alias_domain.active='1'
diff --git a/data/conf/postfix/sql/mysql_virtual_alias_maps.cf b/data/conf/postfix/sql/mysql_virtual_alias_maps.cf
new file mode 100644
index 00000000..a72c8bd8
--- /dev/null
+++ b/data/conf/postfix/sql/mysql_virtual_alias_maps.cf
@@ -0,0 +1,5 @@
+user = mailcow
+password = mysafepasswd
+hosts = mysql
+dbname = mailcow
+query = SELECT goto FROM alias WHERE address='%s' AND active='1';
diff --git a/data/conf/postfix/sql/mysql_virtual_domains_maps.cf b/data/conf/postfix/sql/mysql_virtual_domains_maps.cf
new file mode 100644
index 00000000..22e00938
--- /dev/null
+++ b/data/conf/postfix/sql/mysql_virtual_domains_maps.cf
@@ -0,0 +1,5 @@
+user = mailcow
+password = mysafepasswd
+hosts = mysql
+dbname = mailcow
+query          = SELECT alias_domain from alias_domain WHERE alias_domain='%s' AND active='1' UNION SELECT domain FROM domain WHERE domain='%s' AND active = '1' AND backupmx = '0'
diff --git a/data/conf/postfix/sql/mysql_virtual_mailbox_maps.cf b/data/conf/postfix/sql/mysql_virtual_mailbox_maps.cf
new file mode 100644
index 00000000..bf07cdb2
--- /dev/null
+++ b/data/conf/postfix/sql/mysql_virtual_mailbox_maps.cf
@@ -0,0 +1,5 @@
+user = mailcow
+password = mysafepasswd
+hosts = mysql
+dbname = mailcow
+query = SELECT maildir FROM mailbox WHERE username='%s' AND active = '1'
diff --git a/data/conf/postfix/sql/mysql_virtual_relay_domain_maps.cf b/data/conf/postfix/sql/mysql_virtual_relay_domain_maps.cf
new file mode 100644
index 00000000..6994d02d
--- /dev/null
+++ b/data/conf/postfix/sql/mysql_virtual_relay_domain_maps.cf
@@ -0,0 +1,5 @@
+user = mailcow
+password = mysafepasswd
+hosts = mysql
+dbname = mailcow
+query = SELECT domain FROM domain WHERE domain='%s' AND backupmx = '1' AND active = '1'
diff --git a/data/conf/postfix/sql/mysql_virtual_sender_acl.cf b/data/conf/postfix/sql/mysql_virtual_sender_acl.cf
new file mode 100644
index 00000000..02107e6e
--- /dev/null
+++ b/data/conf/postfix/sql/mysql_virtual_sender_acl.cf
@@ -0,0 +1,5 @@
+user = mailcow
+password = mysafepasswd
+hosts = mysql
+dbname = mailcow
+query = SELECT goto FROM alias WHERE address='%s' AND active='1' AND domain IN(SELECT domain FROM domain WHERE domain='%d' AND active='1') UNION SELECT logged_in_as FROM sender_acl WHERE send_as='@%d' OR send_as='%s' OR send_as IN ( SELECT CONCAT ('@',target_domain) FROM alias_domain WHERE alias_domain = '%d') OR send_as IN ( SELECT CONCAT ('%u','@',target_domain) FROM alias_domain WHERE alias_domain = '%d' ) AND logged_in_as NOT IN (SELECT goto FROM alias WHERE address='%s') UNION SELECT goto FROM alias,alias_domain WHERE alias_domain.alias_domain = '%d' AND alias.address = CONCAT('%u','@',alias_domain.target_domain) AND alias.active ='1' AND alias_domain.active='1'
diff --git a/data/conf/postfix/sql/mysql_virtual_spamalias_maps.cf b/data/conf/postfix/sql/mysql_virtual_spamalias_maps.cf
new file mode 100644
index 00000000..ac8d78ac
--- /dev/null
+++ b/data/conf/postfix/sql/mysql_virtual_spamalias_maps.cf
@@ -0,0 +1,5 @@
+user = mailcow
+password = mysafepasswd
+hosts = mysql
+dbname = mailcow
+query = SELECT goto FROM spamalias WHERE address='%s' AND validity >= UNIX_TIMESTAMP()
diff --git a/data/conf/rmilter/rmilter.conf b/data/conf/rmilter/rmilter.conf
new file mode 100644
index 00000000..f84408cd
--- /dev/null
+++ b/data/conf/rmilter/rmilter.conf
@@ -0,0 +1,42 @@
+bind_socket = inet:9900;
+spamd {
+        servers = r:rspamd:11333;
+        connect_timeout = 1s;
+        results_timeout = 20s;
+        error_time = 10;
+        dead_time = 300;
+        maxerrors = 10;
+        reject_message = "Spam or virus message rejected due to high detection score";
+        whitelist = 127.0.0.1/32, [::1]/128;
+        spamd_soft_fail = yes;
+        rspamd_metric = "default";
+        extended_spam_headers = yes;
+        spam_header = "X-Spam-Flag";
+        spam_header_value = "YES";
+};
+redis {
+        servers_grey = redis:6379;
+        servers_limits = redis:6379;
+        servers_id = redis:6379;
+        id_prefix = "message_id.";
+        grey_prefix = "grey.";
+        white_prefix = "white.";
+        connect_timeout = 1s;
+        error_time = 10;
+        dead_time = 300;
+        maxerrors = 10;
+};
+tempdir = /tmp;
+tempfiles_mode = 00600;
+max_size = 20M;
+strict_auth = yes;
+use_dcc = no;
+limits {
+        enable = false;
+};
+greylisting {
+        enable = false;
+}
+dkim {
+        enable = false;
+};
diff --git a/data/conf/rspamd/dynmaps/authoritative.php b/data/conf/rspamd/dynmaps/authoritative.php
new file mode 100644
index 00000000..b2c101f7
--- /dev/null
+++ b/data/conf/rspamd/dynmaps/authoritative.php
@@ -0,0 +1,22 @@
+<?php
+ini_set('error_reporting', 0);
+header('Content-Type: text/plain');
+require_once "vars.inc.php";
+$dsn = $database_type . ':host=' . $database_host . ';dbname=' . $database_name;
+$opt = [
+    PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
+    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
+    PDO::ATTR_EMULATE_PREPARES   => false,
+];
+$pdo = new PDO($dsn, $database_user, $database_pass, $opt);
+$stmt = $pdo->query("SELECT `domain` FROM `domain`");
+$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
+while ($row = array_shift($rows)) {
+  echo strtolower(trim($row['domain'])) . PHP_EOL;
+}
+$stmt = $pdo->query("SELECT `alias_domain` FROM `alias_domain`");
+$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
+while ($row = array_shift($rows)) {
+  echo strtolower(trim($row['alias_domain'])) . PHP_EOL;
+}
+?>
\ No newline at end of file
diff --git a/data/conf/rspamd/dynmaps/settings.php b/data/conf/rspamd/dynmaps/settings.php
new file mode 100644
index 00000000..a281b981
--- /dev/null
+++ b/data/conf/rspamd/dynmaps/settings.php
@@ -0,0 +1,224 @@
+<?php
+/*
+The match section performs AND operation on different matches: for example, if you have from and rcpt in the same rule,
+then the rule matches only when from AND rcpt match. For similar matches, the OR rule applies: if you have multiple rcpt matches,
+then any of these will trigger the rule. If a rule is triggered then no more rules are matched.
+*/
+ini_set('error_reporting', '0');
+
+header('Content-Type: text/plain');
+require_once "vars.inc.php";
+
+$dsn = $database_type . ':host=' . $database_host . ';dbname=' . $database_name;
+$opt = [
+    PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
+    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
+    PDO::ATTR_EMULATE_PREPARES   => false,
+];
+$pdo = new PDO($dsn, $database_user, $database_pass, $opt);
+?>
+settings {
+<?php
+$stmt = $pdo->query("SELECT DISTINCT `object` FROM `filterconf` WHERE `option` = 'highspamlevel' OR `option` = 'lowspamlevel'");
+$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
+
+while ($row = array_shift($rows)) {
+	$username_sane = preg_replace("/[^a-zA-Z0-9]+/", "", $row['object']);
+?>
+	score_<?=$username_sane;?> {
+		priority = low;
+<?php
+	$stmt = $pdo->prepare("SELECT `option`, `value` FROM `filterconf` 
+		WHERE (`option` = 'highspamlevel' OR `option` = 'lowspamlevel')
+			AND `object`= :object");
+	$stmt->execute(array(':object' => $row['object']));
+	$spamscore = $stmt->fetchAll(PDO::FETCH_COLUMN|PDO::FETCH_GROUP);
+
+	$stmt = $pdo->prepare("SELECT GROUP_CONCAT(REPLACE(`value`, '*', '.*') SEPARATOR '|') AS `value` FROM `filterconf`
+		WHERE (`object`= :object OR `object`= :object_domain)
+			AND (`option` = 'blacklist_from' OR `option` = 'whitelist_from')");
+	$stmt->execute(array(':object' => $row['object'], ':object_domain' => substr(strrchr($row['object'], "@"), 1)));
+	$grouped_lists = $stmt->fetchAll(PDO::FETCH_COLUMN);
+	$value_sane = preg_replace("/\.\./", ".", (preg_replace("/\*/", ".*", $grouped_lists[0])));
+?>
+		from = "/^((?!<?=$value_sane;?>).)*$/";
+		rcpt = "<?=$row['object'];?>";
+<?php
+	$stmt = $pdo->prepare("SELECT `address` FROM `alias` WHERE `goto` = :object_goto AND `address` NOT LIKE '@%' AND `address` != :object_address");
+	$stmt->execute(array(':object_goto' => $row['object'], ':object_address' => $row['object']));
+	$rows_aliases_1 = $stmt->fetchAll(PDO::FETCH_ASSOC);
+	while ($row_aliases_1 = array_shift($rows_aliases_1)) {
+?>
+		rcpt = "<?=$row_aliases_1['address'];?>";
+<?php
+	}
+	$stmt = $pdo->prepare("SELECT CONCAT(`local_part`, '@', `alias_domain`.`alias_domain`) AS `aliases` FROM `mailbox` 
+		LEFT OUTER JOIN `alias_domain` on `mailbox`.`domain` = `alias_domain`.`target_domain`
+		WHERE `mailbox`.`username` = :object");
+	$stmt->execute(array(':object' => $row['object']));
+	$rows_aliases_2 = $stmt->fetchAll(PDO::FETCH_ASSOC);
+  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'];?>";
+<?php
+    }
+	}
+?>
+		apply "default" {
+			actions {
+				reject = <?=$spamscore['highspamlevel'][0];?>;
+				greylist = <?=$spamscore['lowspamlevel'][0] - 1;?>;
+				"add header" = <?=$spamscore['lowspamlevel'][0];?>;
+			}
+		}
+	}
+<?php
+}
+
+/*
+// Start whitelist
+*/
+
+$stmt = $pdo->query("SELECT DISTINCT `object` FROM `filterconf` WHERE `option` = 'whitelist_from'");
+$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
+while ($row = array_shift($rows)) {
+	$username_sane = preg_replace("/[^a-zA-Z0-9]+/", "", $row['object']);
+?>
+	whitelist_<?=$username_sane;?> {
+<?php
+	$stmt = $pdo->prepare("SELECT GROUP_CONCAT(REPLACE(`value`, '*', '.*') SEPARATOR '|') AS `value` FROM `filterconf`
+		WHERE `object`= :object
+			AND `option` = 'whitelist_from'");
+	$stmt->execute(array(':object' => $row['object']));
+	$grouped_lists = $stmt->fetchAll(PDO::FETCH_COLUMN);
+	$value_sane = preg_replace("/\.\./", ".", (preg_replace("/\*/", ".*", $grouped_lists[0])));
+?>
+		from = "/(<?=$value_sane;?>)/";
+<?php
+	if (!filter_var(trim($row['object']), FILTER_VALIDATE_EMAIL)) {
+?>
+		priority = medium;
+		rcpt = "/.*@<?=$row['object'];?>/";
+<?php
+		$stmt = $pdo->prepare("SELECT `alias_domain` FROM `alias_domain`
+			WHERE `target_domain` = :object");
+		$stmt->execute(array(':object' => $row['object']));
+		$rows_domain_aliases = $stmt->fetchAll(PDO::FETCH_ASSOC);
+    array_filter($rows_domain_aliases);
+		while ($row_domain_aliases = array_shift($rows_domain_aliases)) {
+?>
+		rcpt = "/.*@<?=$row_domain_aliases['alias_domain'];?>/";
+<?php
+		}
+	}
+	else {
+?>
+		priority = high;
+		rcpt = "<?=$row['object'];?>";
+<?php
+	}
+	$stmt = $pdo->prepare("SELECT `address` FROM `alias` WHERE `goto` = :object_goto AND `address` NOT LIKE '@%' AND `address` != :object_address");
+	$stmt->execute(array(':object_goto' => $row['object'], ':object_address' => $row['object']));
+	$rows_aliases_wl_1 = $stmt->fetchAll(PDO::FETCH_ASSOC);
+  array_filter($rows_aliases_wl_1);
+	while ($row_aliases_wl_1 = array_shift($rows_aliases_wl_1)) {
+?>
+		rcpt = "<?=$row_aliases_wl_1['address'];?>";
+<?php
+	}
+	$stmt = $pdo->prepare("SELECT CONCAT(`local_part`, '@', `alias_domain`.`alias_domain`) AS `aliases` FROM `mailbox` 
+		LEFT OUTER JOIN `alias_domain` on `mailbox`.`domain` = `alias_domain`.`target_domain`
+		WHERE `mailbox`.`username` = :object");
+	$stmt->execute(array(':object' => $row['object']));
+	$rows_aliases_wl_2 = $stmt->fetchAll(PDO::FETCH_ASSOC);
+  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'];?>";
+<?php
+    }
+	}
+?>
+		apply "default" {
+			MAILCOW_MOO = -999.0;
+		}
+	}
+<?php
+}
+
+/*
+// Start blacklist
+*/
+
+$stmt = $pdo->query("SELECT DISTINCT `object` FROM `filterconf` WHERE `option` = 'blacklist_from'");
+$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
+while ($row = array_shift($rows)) {
+	$username_sane = preg_replace("/[^a-zA-Z0-9]+/", "", $row['object']);
+?>
+	blacklist_<?=$username_sane;?> {
+<?php
+	$stmt = $pdo->prepare("SELECT GROUP_CONCAT(REPLACE(`value`, '*', '.*') SEPARATOR '|') AS `value` FROM `filterconf`
+		WHERE `object`= :object
+			AND `option` = 'blacklist_from'");
+	$stmt->execute(array(':object' => $row['object']));
+	$grouped_lists = $stmt->fetchAll(PDO::FETCH_COLUMN);
+	$value_sane = preg_replace("/\.\./", ".", (preg_replace("/\*/", ".*", $grouped_lists[0])));
+?>
+		from = "/(<?=$value_sane;?>)/";
+<?php
+	if (!filter_var(trim($row['object']), FILTER_VALIDATE_EMAIL)) {
+?>
+		priority = medium;
+		rcpt = "/.*@<?=$row['object'];?>/";
+<?php
+		$stmt = $pdo->prepare("SELECT `alias_domain` FROM `alias_domain`
+			WHERE `target_domain` = :object");
+		$stmt->execute(array(':object' => $row['object']));
+		$rows_domain_aliases = $stmt->fetchAll(PDO::FETCH_ASSOC);
+    array_filter($rows_domain_aliases);
+		while ($row_domain_aliases = array_shift($rows_domain_aliases)) {
+?>
+		rcpt = "/.*@<?=$row_domain_aliases['alias_domain'];?>/";
+<?php
+		}
+	}
+	else {
+?>
+		priority = high;
+		rcpt = "<?=$row['object'];?>";
+<?php
+	}
+	$stmt = $pdo->prepare("SELECT `address` FROM `alias` WHERE `goto` = :object_goto AND `address` NOT LIKE '@%' AND `address` != :object_address");
+	$stmt->execute(array(':object_goto' => $row['object'], ':object_address' => $row['object']));
+	$rows_aliases_bl_1 = $stmt->fetchAll(PDO::FETCH_ASSOC);
+  array_filter($rows_aliases_bl_1);
+	while ($row_aliases_bl_1 = array_shift($rows_aliases_bl_1)) {
+?>
+		rcpt = "<?=$row_aliases_bl_1['address'];?>";
+<?php
+	}
+	$stmt = $pdo->prepare("SELECT CONCAT(`local_part`, '@', `alias_domain`.`alias_domain`) AS `aliases` FROM `mailbox` 
+		LEFT OUTER JOIN `alias_domain` on `mailbox`.`domain` = `alias_domain`.`target_domain`
+		WHERE `mailbox`.`username` = :object");
+	$stmt->execute(array(':object' => $row['object']));
+	$rows_aliases_bl_2 = $stmt->fetchAll(PDO::FETCH_ASSOC);
+  array_filter($rows_aliases_bl_2);
+	while ($row_aliases_bl_2 = array_shift($rows_aliases_bl_2)) {
+    if (!empty($row_aliases_bl_2['aliases'])) {
+?>
+		rcpt = "<?=$row_aliases_bl_2['aliases'];?>";
+<?php
+    }
+	}
+?>
+		apply "default" {
+			MAILCOW_MOO = 999.0;
+		}
+	}
+<?php
+}
+?>
+}
\ No newline at end of file
diff --git a/data/conf/rspamd/dynmaps/tags.php b/data/conf/rspamd/dynmaps/tags.php
new file mode 100644
index 00000000..a084d141
--- /dev/null
+++ b/data/conf/rspamd/dynmaps/tags.php
@@ -0,0 +1,22 @@
+<?php
+ini_set('error_reporting', 0);
+header('Content-Type: text/plain');
+require_once "vars.inc.php";
+$dsn = $database_type . ':host=' . $database_host . ';dbname=' . $database_name;
+$opt = [
+    PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
+    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
+    PDO::ATTR_EMULATE_PREPARES   => false,
+];
+$pdo = new PDO($dsn, $database_user, $database_pass, $opt);
+$stmt = $pdo->query("SELECT `username` FROM `mailbox` WHERE `wants_tagged_subject` = '1'");
+$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
+while ($row = array_shift($rows)) {
+  echo strtolower(trim($row['username'])) . PHP_EOL;
+}
+$stmt = $pdo->query("SELECT CONCAT(mailbox.local_part, '@', alias_domain.alias_domain) as `tag_ad` FROM `mailbox` INNER JOIN `alias_domain` ON mailbox.domain = alias_domain.target_domain WHERE mailbox.wants_tagged_subject='1';");
+$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
+while ($row = array_shift($rows)) {
+  echo strtolower(trim($row['tag_ad'])) . PHP_EOL;
+}
+?>
diff --git a/data/conf/rspamd/dynmaps/vars.inc.php b/data/conf/rspamd/dynmaps/vars.inc.php
new file mode 120000
index 00000000..f59ab89b
--- /dev/null
+++ b/data/conf/rspamd/dynmaps/vars.inc.php
@@ -0,0 +1 @@
+../../../web/inc/vars.inc.php
\ No newline at end of file
diff --git a/data/conf/rspamd/local.d/dkim.conf b/data/conf/rspamd/local.d/dkim.conf
new file mode 100644
index 00000000..c199c6ae
--- /dev/null
+++ b/data/conf/rspamd/local.d/dkim.conf
@@ -0,0 +1,34 @@
+sign_condition =<<EOD
+return function(task)
+  local smtp_from = task:get_from('smtp')
+  local mime_from = task:get_from('mime')
+  local rspamd_logger = require "rspamd_logger"
+  if smtp_from[1]['domain'] ~= nil and smtp_from[1]['domain'] ~= '' then
+    domain = smtp_from[1]['domain']
+    rspamd_logger.infox(task, "set domain found in smtp from field to %s", domain)
+    if not task:get_user() then
+      rspamd_logger.infox(task, "found domain in smtp header field, but user is not authenticated - skipped")
+      return false
+    end
+  elseif mime_from[1]['domain'] ~= nil and mime_from[1]['domain'] ~= '' then
+    domain = mime_from[1]['domain']
+    rspamd_logger.infox(task, "set domain found in mime from field to %s", domain)
+  else
+    rspamd_logger.infox(task, "cannot determine domain for dkim signing")
+    return false
+  end
+  local keyfile = io.open("/data/dkim/keys/" .. domain .. ".dkim")
+  if keyfile then
+    rspamd_logger.infox(task, "found dkim key file for domain %s", domain)
+    keyfile:close()
+    return {
+      key = "/data/dkim/keys/" .. domain .. ".dkim",
+      domain = domain,
+      selector = "dkim"
+    }
+  else
+    rspamd_logger.infox(task, "no key file for domain %s - skipped", domain)
+  end
+  return false
+end
+EOD;
diff --git a/data/conf/rspamd/local.d/metrics.conf b/data/conf/rspamd/local.d/metrics.conf
new file mode 100644
index 00000000..4fd0953c
--- /dev/null
+++ b/data/conf/rspamd/local.d/metrics.conf
@@ -0,0 +1,19 @@
+actions {
+	reject = 15;
+	add_header = 5;
+	greylist = 4;
+}
+symbol "MAILCOW_AUTH" {
+	description = "mailcow authenticated";
+	score = -20.0;
+}
+group "bayes" {
+	symbol "BAYES_SPAM" {
+		weight = 7.5;
+		description = "Message probably spam, probability: ";
+	}
+	symbol "BAYES_HAM" {
+		weight = -2.5;
+		description = "Message probably ham, probability: ";
+	}
+}
diff --git a/data/conf/rspamd/local.d/options.inc b/data/conf/rspamd/local.d/options.inc
new file mode 100644
index 00000000..87dc8770
--- /dev/null
+++ b/data/conf/rspamd/local.d/options.inc
@@ -0,0 +1,3 @@
+dns {
+    enable_dnssec = true;
+}
diff --git a/data/conf/rspamd/local.d/redis.conf b/data/conf/rspamd/local.d/redis.conf
new file mode 100644
index 00000000..f5644a30
--- /dev/null
+++ b/data/conf/rspamd/local.d/redis.conf
@@ -0,0 +1 @@
+servers = "redis:6379";
diff --git a/data/conf/rspamd/local.d/rspamd.conf.local b/data/conf/rspamd/local.d/rspamd.conf.local
new file mode 100644
index 00000000..9f2f8f1d
--- /dev/null
+++ b/data/conf/rspamd/local.d/rspamd.conf.local
@@ -0,0 +1 @@
+# rspamd.conf.local
diff --git a/data/conf/rspamd/local.d/statistic.conf b/data/conf/rspamd/local.d/statistic.conf
new file mode 100644
index 00000000..4b410842
--- /dev/null
+++ b/data/conf/rspamd/local.d/statistic.conf
@@ -0,0 +1,59 @@
+classifier "bayes" {
+    tokenizer {
+    name = "osb";
+    }
+
+    backend = "redis";
+    servers = "redis:6379";
+    min_tokens = 11;
+    min_learns = 20;
+    autolearn = true;
+
+    per_user = <<EOD
+return function(task)
+    local rcpt = task:get_recipients(1)
+
+if rcpt then
+    one_rcpt = rcpt[1]
+    if one_rcpt['domain'] then
+        return one_rcpt['domain']
+    end
+end
+
+return nil
+end
+EOD
+
+    statfile {
+        symbol = "BAYES_HAM";
+        spam = false;
+    }
+    statfile {
+        symbol = "BAYES_SPAM";
+        spam = true;
+    }
+    learn_condition =<<EOD
+return function(task, is_spam, is_unlearn)
+    local prob = task:get_mempool():get_variable('bayes_prob', 'double')
+
+    if prob then
+        local in_class = false
+        local cl
+        if is_spam then
+            cl = 'spam'
+            in_class = prob >= 0.95
+        else
+            cl = 'ham'
+            in_class = prob <= 0.05
+        end
+
+        if in_class then
+            return false,string.format('already in class %s; probability %.2f%%',
+            cl, math.abs((prob - 0.5) * 200.0))
+        end
+    end
+
+    return true
+end
+EOD
+}
diff --git a/data/conf/rspamd/lua/rspamd.local.lua b/data/conf/rspamd/lua/rspamd.local.lua
new file mode 100644
index 00000000..0d74e9d0
--- /dev/null
+++ b/data/conf/rspamd/lua/rspamd.local.lua
@@ -0,0 +1,75 @@
+rspamd_config.MAILCOW_AUTH = {
+	callback = function(task)
+		local uname = task:get_user()
+		if uname then
+			return 1
+		end
+	end
+}
+
+rspamd_config.MAILCOW_MOO = function (task)
+	return true
+end
+
+local modify_subject_map = rspamd_config:add_map({
+  url = 'http://nginx:8081/tags.php',
+  type = 'map',
+  description = 'Map of users to use subject tags for'
+})
+
+local auth_domain_map = rspamd_config:add_map({
+  url = 'http://nginx:8081/authoritative.php',
+  type = 'map',
+  description = 'Map of domains we are authoritative for'
+})
+
+rspamd_config.ADD_DELIMITER_TAG = {
+  callback = function(task)
+    local util = require("rspamd_util")
+    local rspamd_logger = require "rspamd_logger"
+
+    local user_env_tagged = task:get_recipients(1)[1]['user']
+    local user_to_tagged = task:get_recipients(2)[1]['user']
+
+    local domain = task:get_recipients(1)[1]['domain']
+
+    local user_env, tag_env = user_env_tagged:match("([^+]+)+(.*)")
+    local user_to, tag_to = user_to_tagged:match("([^+]+)+(.*)")
+
+    local authdomain = auth_domain_map:get_key(domain)
+
+    if tag_env then
+      tag = tag_env
+      user = user_env
+    elseif tag_to then
+      tag = tag_to
+      user = user_env
+    end
+
+    if tag and authdomain then
+      rspamd_logger.infox("Domain %s is part of mailcow, start reading tag settings", domain)
+      local user_untagged = user .. '@' .. domain
+      rspamd_logger.infox("Querying tag settings for user %1", user_untagged)
+      if modify_subject_map:get_key(user_untagged) then
+        rspamd_logger.infox("User wants subject modified for tagged mail")
+        local sbj = task:get_header('Subject')
+        if tag then
+          rspamd_logger.infox("Found tag %1, will modify subject header", tag)
+          new_sbj = '=?UTF-8?B?' .. tostring(util.encode_base64('[' .. tag .. '] ' .. sbj)) .. '?='
+          task:set_rmilter_reply({
+            remove_headers = {['Subject'] = 1},
+            add_headers = {['Subject'] = new_sbj}
+          })
+        end
+      else
+        rspamd_logger.infox("Add X-Moo-Tag header")
+        task:set_rmilter_reply({
+          add_headers = {['X-Moo-Tag'] = 'YES'}
+        })
+      end
+    else
+      rspamd_logger.infox("Skip delimiter handling for untagged message or authenticated user")
+    end
+    return false
+  end
+}
diff --git a/data/conf/rspamd/override.d/logging.inc b/data/conf/rspamd/override.d/logging.inc
new file mode 100644
index 00000000..64a2b7d4
--- /dev/null
+++ b/data/conf/rspamd/override.d/logging.inc
@@ -0,0 +1,3 @@
+type = "console";
+systemd = false;
+.include "$CONFDIR/logging.inc"
diff --git a/data/conf/rspamd/override.d/worker-controller.inc b/data/conf/rspamd/override.d/worker-controller.inc
new file mode 100644
index 00000000..3845bc0d
--- /dev/null
+++ b/data/conf/rspamd/override.d/worker-controller.inc
@@ -0,0 +1,7 @@
+bind_socket = "*:11334";
+enable_password = "$2$pppq86q9uns51zd5ekfxecj7bxwaefo3$p7f9xdhamydjhtypcr639it3kqeiknx3dk9on7skjypyi8uwwcmy";
+secure_ip = "192.168.0.0/16";
+secure_ip = "172.16.0.0/12";
+secure_ip = "10.0.0.0/8";
+secure_ip = "127.0.0.1";
+secure_ip = "::1";
diff --git a/data/conf/rspamd/override.d/worker-normal.inc b/data/conf/rspamd/override.d/worker-normal.inc
new file mode 100644
index 00000000..aac8fc17
--- /dev/null
+++ b/data/conf/rspamd/override.d/worker-normal.inc
@@ -0,0 +1 @@
+bind_socket = "*:11333";
diff --git a/data/conf/sogo/sogo.conf b/data/conf/sogo/sogo.conf
new file mode 100644
index 00000000..9a5bd29a
--- /dev/null
+++ b/data/conf/sogo/sogo.conf
@@ -0,0 +1,76 @@
+{
+    SOGoCalendarDefaultRoles = (
+        PublicViewer,
+        ConfidentialDAndTViewer,
+        PrivateDAndTViewer
+    );
+
+    WOWorkersCount = "20";
+    SOGoACLsSendEMailNotifications = YES;
+    SOGoAppointmentSendEMailNotifications = YES;
+    SOGoDraftsFolderName = "Drafts";
+    SOGoJunkFolderName= "Junk";
+    SOGoMailDomain = "sogo.local";
+    SOGoEnableEMailAlarms = NO;
+    SOGoFoldersSendEMailNotifications = YES;
+    SOGoForwardEnabled = YES;
+
+    // Multi-domain setup
+    // Domains are isolated, you can define visibility options here.
+    // Example:
+
+    // SOGoDomainsVisibility = (
+    //  (domain1.tld, domain5.tld),
+    //  (domain3.tld, domain2.tld)
+    // );
+
+    SOGoIMAPServer = "imap://dovecot:143/?tls=YES";
+    SOGoSieveServer = "sieve://dovecot:4190/?tls=YES";
+    SOGoSMTPServer = "postfix:588";
+    WOPort = "0.0.0.0:20000";
+    SOGoMemcachedHost = "memcached";
+
+    SOGoLanguage = English;
+    SOGoMailAuxiliaryUserAccountsEnabled = YES;
+    SOGoMailCustomFromEnabled = YES;
+    SOGoMailingMechanism = smtp;
+    SOGoSMTPAuthenticationType = plain;
+
+    SxVMemLimit = 512;
+
+    SOGoMaximumPingInterval = 354;
+
+    SOGoInternalSyncInterval = 30;
+    SOGoMaximumSyncInterval = 354;
+
+    SOGoMaximumSyncWindowSize = 0;
+    SOGoMaximumSyncResponseSize = 1024;
+
+    WOWatchDogRequestTimeout = 10;
+    WOListenQueueSize = 300;
+    WONoDetach = YES;
+
+    SOGoIMAPAclConformsToIMAPExt = Yes;
+    SOGoPageTitle = "SOGo Groupware";
+    SOGoFirstDayOfWeek = "1";
+
+    SOGoSieveFolderEncoding = "UTF-8";
+    SOGoPasswordChangeEnabled = NO;
+    SOGoSentFolderName = "Sent";
+    SOGoMailShowSubscribedFoldersOnly = NO;
+    NGImap4ConnectionStringSeparator = "/";
+    SOGoSieveScriptsEnabled = YES;
+    SOGoTrashFolderName = "Trash";
+    SOGoVacationEnabled = YES;
+
+    MySQL4Encoding = "utf8mb4";
+  //SOGoDebugRequests = YES;
+  //SoDebugBaseURL = YES;
+  //ImapDebugEnabled = YES;
+  //SOGoEASDebugEnabled = YES;
+  //LDAPDebugEnabled = YES;
+  //PGDebugEnabled = YES;
+  //MySQL4DebugEnabled = YES;
+  //SOGoUIxDebugEnabled = YES;
+  //WODontZipResponse = YES;
+}
diff --git a/data/web/add.php b/data/web/add.php
new file mode 100644
index 00000000..664d96ad
--- /dev/null
+++ b/data/web/add.php
@@ -0,0 +1,385 @@
+<?php
+require_once("inc/prerequisites.inc.php");
+$AuthUsers = array("admin", "domainadmin", "user");
+if (!isset($_SESSION['mailcow_cc_role']) OR !in_array($_SESSION['mailcow_cc_role'], $AuthUsers)) {
+	header('Location: /');
+	exit();
+}
+require_once("inc/header.inc.php");
+?>
+<div class="container">
+	<div class="row">
+		<div class="col-md-12">
+			<div class="panel panel-default">
+				<div class="panel-heading">
+					<h3 class="panel-title"><?=$lang['add']['title'];?></h3>
+				</div>
+				<div class="panel-body">
+<?php
+if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "admin"  || $_SESSION['mailcow_cc_role'] == "domainadmin")) {
+	if (isset($_GET['domain']) && $_SESSION['mailcow_cc_role'] == "admin") {
+?>
+				<h4><?=$lang['add']['domain'];?></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="domain"><?=$lang['add']['domain'];?>:</label>
+						<div class="col-sm-10">
+						<input type="text" autocorrect="off" autocapitalize="none" class="form-control" name="domain" id="domain">
+						</div>
+					</div>
+					<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">
+						</div>
+					</div>
+					<div class="form-group">
+						<label class="control-label col-sm-2" for="aliases"><?=$lang['add']['max_aliases'];?></label>
+						<div class="col-sm-10">
+						<input type="number" class="form-control" name="aliases" id="aliases" value="400">
+						</div>
+					</div>
+					<div class="form-group">
+						<label class="control-label col-sm-2" for="mailboxes"><?=$lang['add']['max_mailboxes'];?></label>
+						<div class="col-sm-10">
+						<input type="number" class="form-control" name="mailboxes" id="mailboxes" value="10">
+						</div>
+					</div>
+					<div class="form-group">
+						<label class="control-label col-sm-2" for="maxquota"><?=$lang['add']['mailbox_quota_m'];?></label>
+						<div class="col-sm-10">
+						<input type="number" class="form-control" name="maxquota" id="maxquota" value="3072">
+						</div>
+					</div>
+					<div class="form-group">
+						<label class="control-label col-sm-2" for="quota"><?=$lang['add']['domain_quota_m'];?></label>
+						<div class="col-sm-10">
+						<input type="number" class="form-control" name="quota" id="quota" value="10240">
+						</div>
+					</div>
+					<div class="form-group">
+						<label class="control-label col-sm-2"><?=$lang['add']['backup_mx_options'];?></label>
+						<div class="col-sm-10">
+							<div class="checkbox">
+							<label><input type="checkbox" name="backupmx"> <?=$lang['add']['relay_domain'];?></label>
+							<br />
+							<label><input type="checkbox" name="relay_all_recipients"> <?=$lang['add']['relay_all'];?></label>
+							<p><?=$lang['add']['relay_all_info'];?></p>
+							</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="mailbox_add_domain" class="btn btn-success"><?=$lang['add']['save'];?></button>
+						</div>
+					</div>
+					<p><span class="glyphicon glyphicon-exclamation-sign text-danger"></span> <?=$lang['add']['restart_sogo_hint'];?></p>
+				</form>
+<?php
+	}
+	elseif (isset($_GET['alias'])) {
+?>
+				<h4><?=$lang['add']['alias'];?></h4>
+				<p><?=$lang['add']['alias_spf_fail'];?></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="address"><?=$lang['add']['alias_address'];?></label>
+						<div class="col-sm-10">
+							<textarea autocorrect="off" autocapitalize="none" class="form-control" rows="5" name="address" id="address"></textarea>
+							<p><?=$lang['add']['alias_address_info'];?></p>
+						</div>
+					</div>
+					<div class="form-group">
+						<label class="control-label col-sm-2" for="goto"><?=$lang['add']['target_address'];?></label>
+						<div class="col-sm-10">
+							<textarea autocorrect="off" autocapitalize="none" class="form-control" rows="5" id="goto" name="goto"></textarea>
+							<p><?=$lang['add']['target_address_info'];?></p>
+						</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="mailbox_add_alias" class="btn btn-success "><?=$lang['add']['save'];?></button>
+						</div>
+					</div>
+				</form>
+	<?php
+	}
+	elseif (isset($_GET['aliasdomain'])) {
+	?>
+				<h4><?=$lang['add']['alias_domain'];?></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="alias_domain"><?=$lang['add']['alias_domain'];?></label>
+						<div class="col-sm-10">
+							<textarea autocorrect="off" autocapitalize="none" class="form-control" rows="5" name="alias_domain" id="alias_domain"></textarea>
+							<p><?=$lang['add']['alias_domain_info'];?></p>
+						</div>
+					</div>
+					<div class="form-group">
+						<label class="control-label col-sm-2" for="target_domain"><?=$lang['add']['target_domain'];?></label>
+						<div class="col-sm-10">
+							<select name="target_domain" id="target_domain" title="<?=$lang['add']['select'];?>" required>
+							<?php
+              foreach (mailbox_get_domains() as $domain) {
+								echo "<option>".htmlspecialchars($domain)."</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">
+							<button type="submit" name="mailbox_add_alias_domain" class="btn btn-success "><?=$lang['add']['save'];?></button>
+						</div>
+					</div>
+				</form>
+	<?php
+	}
+	elseif (isset($_GET['mailbox'])) {
+	?>
+				<h4><?=$lang['add']['mailbox'];?></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="local_part"><?=$lang['add']['mailbox_username'];?></label>
+						<div class="col-sm-10">
+							<input type="text" pattern="[A-Za-z0-9\.!#$%&'*+/=?^_`{|}~-]+" autocorrect="off" autocapitalize="none" class="form-control" name="local_part" id="local_part" 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 id="addSelectDomain" 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="name"><?=$lang['add']['full_name'];?></label>
+						<div class="col-sm-10">
+						<input type="text" class="form-control" name="name" id="name">
+						</div>
+					</div>
+					<div class="form-group">
+						<label class="control-label col-sm-2" for="addInputQuota"><?=$lang['add']['quota_mb'];?>
+							<br /><span id="quotaBadge" class="badge">max. - MiB</span>
+						</label>
+						<div class="col-sm-10">
+						<input type="text" class="form-control" name="quota" min="1" max="" id="addInputQuota" disabled value="<?=$lang['add']['select_domain'];?>" required>
+						</div>
+					</div>
+					<div class="form-group">
+						<label class="control-label col-sm-2" for="password"><?=$lang['add']['password'];?></label>
+						<div class="col-sm-10">
+						<input type="password" class="form-control" name="password" id="password" placeholder="">
+						</div>
+					</div>
+					<div class="form-group">
+						<label class="control-label col-sm-2" for="password2"><?=$lang['add']['password_repeat'];?></label>
+						<div class="col-sm-10">
+						<input type="password" class="form-control" name="password2" id="password2" placeholder="">
+						</div>
+					</div>
+					<div class="form-group">
+						<div class="col-sm-offset-2 col-sm-10">
+							<div class="checkbox">
+							<label><input type="checkbox" name="active" checked> <?=$lang['add']['active'];?></label>
+							</div>
+						</div>
+					</div>
+					<div class="form-group">
+						<div class="col-sm-offset-2 col-sm-10">
+							<button type="submit" name="mailbox_add_mailbox" class="btn btn-success "><?=$lang['add']['save'];?></button>
+						</div>
+					</div>
+				</form>
+	<?php
+	}
+	elseif (isset($_GET['resource'])) {
+	?>
+				<h4><?=$lang['add']['resource'];?></h4>
+				<form class="form-horizontal" role="form" method="post" action="<?=($FORM_ACTION == "previous") ? $_SESSION['return_to'] : null;?>">
+					<div class="form-group">
+						<label class="control-label col-sm-2" for="description"><?=$lang['add']['description'];?></label>
+						<div class="col-sm-10">
+							<input type="text" class="form-control" name="description" id="description" required>
+						</div>
+					</div>
+					<div class="form-group">
+						<label class="control-label col-sm-2" for="domain"><?=$lang['add']['domain'];?>:</label>
+						<div class="col-sm-10">
+							<select name="domain" id="domain" title="<?=$lang['add']['select'];?>" required>
+							<?php
+              foreach (mailbox_get_domains() as $domain) {
+								echo "<option>".htmlspecialchars($domain)."</option>";
+							}
+							?>
+							</select>
+						</div>
+					</div>
+					<div class="form-group">
+						<label class="control-label col-sm-2" for="domain"><?=$lang['add']['kind'];?>:</label>
+						<div class="col-sm-10">
+							<select name="kind" id="kind" title="<?=$lang['add']['select'];?>" required>
+								<option value="location">Location</option>
+								<option value="group">Group</option>
+								<option value="thing">Thing</option>
+							</select>
+						</div>
+					</div>
+					<div class="form-group">
+						<div class="col-sm-offset-2 col-sm-10">
+							<div class="checkbox">
+							<label><input type="checkbox" name="active" checked> <?=$lang['add']['active'];?></label>
+							</div>
+						</div>
+					</div>
+					<div class="form-group">
+						<div class="col-sm-offset-2 col-sm-10">
+							<div class="checkbox">
+							<label><input type="checkbox" name="multiple_bookings" checked> <?=$lang['add']['multiple_bookings'];?></label>
+							</div>
+						</div>
+					</div>
+					<div class="form-group">
+						<div class="col-sm-offset-2 col-sm-10">
+							<button type="submit" name="mailbox_add_resource" class="btn btn-success "><?=$lang['add']['save'];?></button>
+						</div>
+					</div>
+				</form>
+	<?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'])) {
+?>
+				<h4><?=$lang['add']['syncjob'];?></h4>
+				<p><?=$lang['add']['syncjob_hint'];?></p>
+				<form class="form-horizontal" role="form" method="post" action="<?=($FORM_ACTION == "previous") ? $_SESSION['return_to'] : null;?>">
+					<div class="form-group">
+						<label class="control-label col-sm-2" for="host1"><?=$lang['add']['hostname'];?></label>
+						<div class="col-sm-10">
+						<input type="text" class="form-control" name="host1" id="host1" required>
+						</div>
+					</div>
+					<div class="form-group">
+						<label class="control-label col-sm-2" for="port1">Port</label>
+						<div class="col-sm-10">
+						<input type="number" class="form-control" name="port1" id="port1" min="1" max="65535" value="143" required>
+						</div>
+					</div>
+					<div class="form-group">
+						<label class="control-label col-sm-2" for="user1"><?=$lang['add']['username'];?></label>
+						<div class="col-sm-10">
+						<input type="text" class="form-control" name="user1" id="user1" required>
+						</div>
+					</div>
+					<div class="form-group">
+						<label class="control-label col-sm-2" for="password1"><?=$lang['add']['password'];?></label>
+						<div class="col-sm-10">
+						<input type="text" class="form-control" name="password1" id="password1" required>
+						</div>
+					</div>
+					<div class="form-group">
+						<label class="control-label col-sm-2" for="enc1"><?=$lang['add']['enc_method'];?></label>
+						<div class="col-sm-10">
+							<select name="enc1" id="enc1" title="<?=$lang['add']['select'];?>" required>
+                <option selected>TLS</option>
+                <option>SSL</option>
+                <option>PLAIN</option>
+							</select>
+						</div>
+					</div>
+					<div class="form-group">
+						<label class="control-label col-sm-2" for="mins_interval"><?=$lang['add']['mins_interval'];?></label>
+						<div class="col-sm-10">
+              <input type="number" class="form-control" name="mins_interval" min="10" max="3600" value="20" required>
+						</div>
+					</div>
+					<div class="form-group">
+						<label class="control-label col-sm-2" for="subfolder2"><?=$lang['edit']['subfolder2'];?></label>
+						<div class="col-sm-10">
+						<input type="text" class="form-control" name="subfolder2" id="subfolder2" value="External">
+						</div>
+					</div>
+					<div class="form-group">
+						<label class="control-label col-sm-2" for="maxage"><?=$lang['edit']['maxage'];?></label>
+						<div class="col-sm-10">
+						<input type="number" class="form-control" name="maxage" id="maxage" min="0" max="32000" value="0">
+						</div>
+					</div>
+					<div class="form-group">
+						<label class="control-label col-sm-2" for="exclude"><?=$lang['add']['exclude'];?></label>
+						<div class="col-sm-10">
+						<input type="text" class="form-control" name="exclude" id="exclude" value="(?i)spam|(?i)junk">
+						</div>
+					</div>
+					<div class="form-group">
+						<div class="col-sm-offset-2 col-sm-10">
+							<div class="checkbox">
+							<label><input type="checkbox" name="delete2duplicates" checked> <?=$lang['add']['delete2duplicates'];?></label>
+							</div>
+						</div>
+					</div>
+					<div class="form-group">
+						<div class="col-sm-offset-2 col-sm-10">
+							<div class="checkbox">
+							<label><input type="checkbox" name="active" checked> <?=$lang['add']['active'];?></label>
+							</div>
+						</div>
+					</div>
+					<div class="form-group">
+						<div class="col-sm-offset-2 col-sm-10">
+							<button type="submit" name="add_syncjob" value="1" class="btn btn-success "><?=$lang['add']['save'];?></button>
+						</div>
+					</div>
+				</form>
+	<?php
+	}
+}
+
+else {
+?>
+				<div class="alert alert-danger" role="alert"><?=$lang['danger']['access_denied'];?></div>
+<?php
+}
+?>
+				</div>
+			</div>
+		</div>
+	</div>
+<a href="<?=$_SESSION['return_to'];?>">&#8592; <?=$lang['add']['previous'];?></a>
+</div> <!-- /container -->
+<script src="js/add.js"></script>
+<?php
+require_once("inc/footer.inc.php");
+?>
diff --git a/data/web/admin.php b/data/web/admin.php
new file mode 100644
index 00000000..5377616f
--- /dev/null
+++ b/data/web/admin.php
@@ -0,0 +1,311 @@
+<?php
+require_once("inc/prerequisites.inc.php");
+
+if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == "admin") {
+require_once("inc/header.inc.php");
+$_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
+$tfa_data = get_tfa();
+?>
+<div class="container">
+  <h4><span class="glyphicon glyphicon-user" aria-hidden="true"></span> <?=$lang['admin']['access'];?></h4>
+
+  <div class="panel-group" id="accordion_access">
+    <div class="panel panel-danger">
+      <div class="panel-heading"><?=$lang['admin']['admin_details'];?></div>
+      <div class="panel-body">
+        <form class="form-horizontal" autocapitalize="none" autocorrect="off" role="form" method="post">
+        <?php $admindetails = get_admin_details(); ?>
+          <div class="form-group">
+            <label class="control-label col-sm-3" for="admin_user"><?=$lang['admin']['admin'];?>:</label>
+            <div class="col-sm-9">
+              <input type="text" class="form-control" name="admin_user" id="admin_user" value="<?=htmlspecialchars($admindetails['username']);?>" required>
+              &rdsh; <kbd>a-z A-Z - _ .</kbd>
+            </div>
+          </div>
+          <div class="form-group">
+            <label class="control-label col-sm-3" for="admin_pass"><?=$lang['admin']['password'];?>:</label>
+            <div class="col-sm-9">
+            <input type="password" class="form-control" name="admin_pass" id="admin_pass" placeholder="<?=$lang['admin']['unchanged_if_empty'];?>">
+            </div>
+          </div>
+          <div class="form-group">
+            <label class="control-label col-sm-3" for="admin_pass2"><?=$lang['admin']['password_repeat'];?>:</label>
+            <div class="col-sm-9">
+            <input type="password" class="form-control" name="admin_pass2" id="admin_pass2">
+            </div>
+          </div>
+          <div class="form-group">
+            <div class="col-sm-offset-3 col-sm-9">
+              <button type="submit" name="edit_admin_account" class="btn btn-default"><?=$lang['admin']['save'];?></button>
+            </div>
+          </div>
+        </form>
+        <hr>
+        <div class="row">
+          <div class="col-sm-3 col-xs-5 text-right"><?=$lang['tfa']['tfa'];?>:</div>
+          <div class="col-sm-9 col-xs-7">
+            <p id="tfa_pretty"><?=$tfa_data['pretty'];?></p>
+              <div id="tfa_additional">
+                <?php if($tfa_data['additional']):
+                foreach ($tfa_data['additional'] as $key_info): ?>
+                <form style="display:inline;" method="post">
+                  <input type="hidden" name="unset_tfa_key" value="<?=$key_info['id'];?>" />
+                  <div style="padding:4px;margin:4px" class="label label-<?=($_SESSION['tfa_id'] == $key_info['id']) ? 'success' : 'default'; ?>">
+                  <?=$key_info['key_id'];?>
+                  <a href="#" style="font-weight:bold;color:white" onClick="$(this).closest('form').submit()">[<?=strtolower($lang['admin']['remove']);?>]</a>
+                  </div>
+                </form>
+                <?php endforeach;
+                endif;?>
+              </div>
+              <br />
+          </div>
+        </div>
+        <div class="row">
+          <div class="col-sm-3 col-xs-5 text-right"><?=$lang['tfa']['set_tfa'];?>:</div>
+          <div class="col-sm-9 col-xs-7">
+            <select data-width="auto" id="selectTFA" class="selectpicker" title="<?=$lang['tfa']['select'];?>">
+              <option value="yubi_otp"><?=$lang['tfa']['yubi_otp'];?></option>
+              <option value="u2f"><?=$lang['tfa']['u2f'];?></option>
+              <option value="none"><?=$lang['tfa']['none'];?></option>
+            </select>
+          </div>
+        </div>
+      </div>
+    </div>
+    <div class="panel panel-default">
+    <div style="cursor:pointer;" class="panel-heading" data-toggle="collapse" data-parent="#accordion_access" data-target="#collapseDomAdmins">
+      <span class="accordion-toggle"><?=$lang['admin']['domain_admins'];?></span>
+    </div>
+      <div id="collapseDomAdmins" class="panel-collapse collapse">
+        <div class="panel-body">
+          <form method="post">
+            <div class="table-responsive">
+            <table class="table table-striped sortable-theme-bootstrap" data-sortable id="domainadminstable">
+              <thead>
+              <tr>
+                <th class="sort-table" style="min-width: 100px;"><?=$lang['admin']['username'];?></th>
+                <th class="sort-table" style="min-width: 166px;"><?=$lang['admin']['admin_domains'];?></th>
+                <th class="sort-table" style="min-width: 76px;"><?=$lang['admin']['active'];?></th>
+                <th class="sort-table" style="min-width: 76px;"><?=$lang['tfa']['tfa'];?></th>
+                <th style="text-align: right; min-width: 200px;" data-sortable="false"><?=$lang['admin']['action'];?></th>
+              </tr>
+              </thead>
+              <tbody>
+                <?php
+                foreach (get_domain_admins() as $domain_admin) {
+                  $da_data = get_domain_admin_details($domain_admin); 
+                  if (!empty($da_data)):
+                ?>
+                <tr id="data">
+                  <td><?=htmlspecialchars(strtolower($domain_admin));?></td>
+                  <td>
+                  <?php
+                  foreach ($da_data['selected_domains'] as $domain) {
+                    echo htmlspecialchars($domain).'<br />';
+                  }
+                  ?>
+                  </td>
+                  <td><?=$da_data['active'];?></td>
+                  <td><?=empty($da_data['tfa_active_int']) ? "✘" : "✔";?></td>
+                  <td style="text-align: right;">
+                    <div class="btn-group">
+                      <a href="edit.php?domainadmin=<?=$domain_admin;?>" class="btn btn-xs btn-default"><span class="glyphicon glyphicon-pencil"></span> <?=$lang['admin']['edit'];?></a>
+                      <a href="delete.php?domainadmin=<?=$domain_admin;?>" class="btn btn-xs btn-danger"><span class="glyphicon glyphicon-trash"></span> <?=$lang['admin']['remove'];?></a>
+                    </div>
+                  </td>
+                  </td>
+                </tr>
+
+                <?php
+                else:
+                ?>
+                  <tr id="no-data"><td colspan="4" style="text-align: center; font-style: italic;"><?=$lang['admin']['no_record'];?></td></tr>
+                <?php
+                endif;
+                }
+                ?>
+              </tbody>
+            </table>
+            </div>
+          </form>
+          <small>
+          <legend><?=$lang['admin']['add_domain_admin'];?></legend>
+          <form class="form-horizontal" role="form" method="post">
+            <div class="form-group">
+              <label class="control-label col-sm-2" for="username"><?=$lang['admin']['username'];?>:</label>
+              <div class="col-sm-10">
+                <input type="text" class="form-control" name="username" id="username" required>
+                &rdsh; <kbd>a-z A-Z - _ .</kbd>
+              </div>
+            </div>
+            <div class="form-group">
+              <label class="control-label col-sm-2" for="name"><?=$lang['admin']['admin_domains'];?>:</label>
+              <div class="col-sm-10">
+                <select title="<?=$lang['admin']['search_domain_da'];?>" style="width:100%" name="domain[]" size="5" multiple>
+                <?php
+                foreach (mailbox_get_domains() as $domain) {
+                  echo "<option>".htmlspecialchars($domain)."</option>";
+                }
+                ?>
+                </select>
+              </div>
+            </div>
+            <div class="form-group">
+              <label class="control-label col-sm-2" for="password"><?=$lang['admin']['password'];?>:</label>
+              <div class="col-sm-10">
+              <input type="password" class="form-control" name="password" id="password" placeholder="">
+              </div>
+            </div>
+            <div class="form-group">
+              <label class="control-label col-sm-2" for="password2"><?=$lang['admin']['password_repeat'];?>:</label>
+              <div class="col-sm-10">
+              <input type="password" class="form-control" name="password2" id="password2" placeholder="">
+              </div>
+            </div>
+            <div class="form-group">
+              <div class="col-sm-offset-2 col-sm-10">
+                <div class="checkbox">
+                <label><input type="checkbox" name="active" checked> <?=$lang['admin']['active'];?></label>
+                </div>
+              </div>
+            </div>
+            <div class="form-group">
+              <div class="col-sm-offset-2 col-sm-10">
+                <button type="submit" name="add_domain_admin" class="btn btn-default"><?=$lang['admin']['add'];?></button>
+              </div>
+            </div>
+          </form>
+          </small>
+        </div>
+      </div>
+    </div>
+  </div>
+
+  <h4><span class="glyphicon glyphicon-wrench" aria-hidden="true"></span> <?=$lang['admin']['configuration'];?></h4>
+  <div class="panel panel-default">
+  <div class="panel-heading"><?=$lang['admin']['dkim_keys'];?></div>
+  <div id="collapseDKIM" class="panel-collapse">
+  <div class="panel-body">
+    <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">&nbsp;</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">&nbsp;</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 -->
+<script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.11.4/jquery-ui.min.js" integrity="sha384-YWP9O4NjmcGo4oEJFXvvYSEzuHIvey+LbXkBNJ1Kd0yfugEZN9NCQNpRYBVC1RvA" crossorigin="anonymous"></script>
+<script src="js/sorttable.js"></script>
+<script src="js/admin.js"></script>
+<?php
+require_once("inc/footer.inc.php");
+} else {
+	header('Location: /');
+	exit();
+}
+?>
diff --git a/data/web/autoconfig.php b/data/web/autoconfig.php
new file mode 100644
index 00000000..563df5d5
--- /dev/null
+++ b/data/web/autoconfig.php
@@ -0,0 +1,69 @@
+<?php
+require_once "inc/vars.inc.php";
+if (empty($mailcow_hostname)) { exit(); }
+header("Content-Type: application/xml");
+?>
+<?='<?xml version="1.0"?>';?>
+<clientConfig version="1.1">
+    <emailProvider id="<?=$mailcow_hostname;?>">
+
+      <displayName>A mailcow mail server</displayName>
+      <displayShortName>mail server</displayShortName>
+
+      <incomingServer type="imap">
+         <hostname><?=$mailcow_hostname;?></hostname>
+         <port>993</port>
+         <socketType>SSL</socketType>
+         <username>%EMAILADDRESS%</username>
+         <authentication>password-cleartext</authentication>
+      </incomingServer>
+      <incomingServer type="imap">
+         <hostname><?=$mailcow_hostname;?></hostname>
+         <port>143</port>
+         <socketType>STARTTLS</socketType>
+         <username>%EMAILADDRESS%</username>
+         <authentication>password-cleartext</authentication>
+      </incomingServer>
+
+      <incomingServer type="pop3">
+         <hostname><?=$mailcow_hostname;?></hostname>
+         <port>995</port>
+         <socketType>SSL</socketType>
+         <username>%EMAILADDRESS%</username>
+         <authentication>password-cleartext</authentication>
+      </incomingServer>
+      <incomingServer type="pop3">
+         <hostname><?=$mailcow_hostname;?></hostname>
+         <port>110</port>
+         <socketType>STARTTLS</socketType>
+         <username>%EMAILADDRESS%</username>
+         <authentication>password-cleartext</authentication>
+      </incomingServer>
+
+      <outgoingServer type="smtp">
+         <hostname><?=$mailcow_hostname;?></hostname>
+         <port>465</port>
+         <socketType>SSL</socketType>
+         <username>%EMAILADDRESS%</username>
+         <authentication>password-cleartext</authentication>
+      </outgoingServer>
+
+      <outgoingServer type="smtp">
+         <hostname><?=$mailcow_hostname;?></hostname>
+         <port>587</port>
+         <socketType>STARTTLS</socketType>
+         <username>%EMAILADDRESS%</username>
+         <authentication>password-cleartext</authentication>
+      </outgoingServer>
+
+      <enable visiturl="https://<?=$mailcow_hostname;?>/admin.php">
+         <instruction>If you didn't change the password given to you by the administrator or if you didn't change it in a long time, please consider doing that now.</instruction>
+         <instruction lang="de">Sollten Sie das Ihnen durch den Administrator vergebene Passwort noch nicht geändert haben, empfehlen wir dies nun zu tun. Auch ein altes Passwort sollte aus Sicherheitsgründen geändert werden.</instruction>
+      </enable>
+
+    </emailProvider>
+
+    <webMail>
+      <loginPage url="https://<?=$mailcow_hostname;?>/SOGo/" />
+    </webMail>
+</clientConfig>
diff --git a/data/web/autodiscover.php b/data/web/autodiscover.php
new file mode 100644
index 00000000..d729306f
--- /dev/null
+++ b/data/web/autodiscover.php
@@ -0,0 +1,142 @@
+<?php
+require_once 'inc/vars.inc.php';
+require_once 'inc/functions.inc.php';
+
+ini_set('error_reporting', '0');
+$config = array(
+     'useEASforOutlook' => 'yes',
+     'autodiscoverType' => 'activesync',
+     'imap' => array(
+       'server' => $mailcow_hostname,
+       'port' => '993',
+       'ssl' => 'on',
+     ),
+     'smtp' => array(
+       'server' => $mailcow_hostname,
+       'port' => '465',
+       'ssl' => 'on'
+     ),
+     'activesync' => array(
+       'url' => 'https://'.$mailcow_hostname.'/Microsoft-Server-ActiveSync'
+     )
+);
+
+if(file_exists('inc/vars.local.inc.php')) {
+	include_once 'inc/vars.local.inc.php';
+}
+
+/* ---------- DO NOT MODIFY ANYTHING BEYOND THIS LINE. IGNORE AT YOUR OWN RISK. ---------- */
+
+if ($config['useEASforOutlook'] == 'no') {
+	if (strpos($_SERVER['HTTP_USER_AGENT'], 'Outlook')) {
+		$config['autodiscoverType'] = 'imap';
+	}
+}
+
+$dsn = "$database_type:host=$database_host;dbname=$database_name";
+$opt = [
+		PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
+		PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
+		PDO::ATTR_EMULATE_PREPARES   => false,
+];
+$pdo = new PDO($dsn, $database_user, $database_pass, $opt);
+$login_user = strtolower(trim($_SERVER['PHP_AUTH_USER']));
+$as = check_login($login_user, $_SERVER['PHP_AUTH_PW']);
+
+if (!isset($_SERVER['PHP_AUTH_USER']) OR $as !== "user") {
+	header('WWW-Authenticate: Basic realm=""');
+	header('HTTP/1.0 401 Unauthorized');
+	exit;
+} else {
+	if (isset($_SERVER['PHP_AUTH_USER']) && isset($_SERVER['PHP_AUTH_PW'])) {
+		if ($as === "user") {
+      header("Content-Type: application/xml");
+      echo '<?xml version="1.0" encoding="utf-8" ?><Autodiscover xmlns="http://schemas.microsoft.com/exchange/autodiscover/responseschema/2006">';
+
+      $data = trim(file_get_contents("php://input"));
+      if(!$data) {
+        list($usec, $sec) = explode(' ', microtime());
+        echo '<Response>';
+        echo '<Error Time="' . date('H:i:s', $sec) . substr($usec, 0, strlen($usec) - 2) . '" Id="2477272013">';
+        echo '<ErrorCode>600</ErrorCode><Message>Invalid Request</Message><DebugData /></Error>';
+        echo '</Response>';
+        echo '</Autodiscover>';
+        exit(0);
+      }
+      $discover = new SimpleXMLElement($data);
+      $email = $discover->Request->EMailAddress;
+
+      if ($config['autodiscoverType'] == 'imap') {
+      ?>
+  <Response xmlns="http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a">
+      <Account>
+          <AccountType>email</AccountType>
+          <Action>settings</Action>
+          <Protocol>
+              <Type>IMAP</Type>
+              <Server><?php echo $config['imap']['server']; ?></Server>
+              <Port><?php echo $config['imap']['port']; ?></Port>
+              <DomainRequired>off</DomainRequired>
+              <LoginName><?php echo $email; ?></LoginName>
+              <SPA>off</SPA>
+              <SSL><?php echo $config['imap']['ssl']; ?></SSL>
+              <AuthRequired>on</AuthRequired>
+          </Protocol>
+          <Protocol>
+              <Type>SMTP</Type>
+              <Server><?php echo $config['smtp']['server']; ?></Server>
+              <Port><?php echo $config['smtp']['port']; ?></Port>
+              <DomainRequired>off</DomainRequired>
+              <LoginName><?php echo $email; ?></LoginName>
+              <SPA>off</SPA>
+              <SSL><?php echo $config['smtp']['ssl']; ?></SSL>
+              <AuthRequired>on</AuthRequired>
+              <UsePOPAuth>on</UsePOPAuth>
+              <SMTPLast>off</SMTPLast>
+          </Protocol>
+      </Account>
+  </Response>
+      <?php
+      }
+      else if ($config['autodiscoverType'] == 'activesync') {
+        $username = trim($email);
+        try {
+          $stmt = $pdo->prepare("SELECT `name` FROM `mailbox` WHERE `username`= :username");
+          $stmt->execute(array(':username' => $username));
+          $MailboxData = $stmt->fetch(PDO::FETCH_ASSOC);
+        }
+        catch(PDOException $e) {
+          die("Failed to determine name from SQL");
+        }
+        if (!empty($MailboxData['name'])) {
+          $displayname = utf8_encode($MailboxData['name']);
+        }
+        else {
+          $displayname = $email;
+        }
+      ?>
+  <Response xmlns="http://schemas.microsoft.com/exchange/autodiscover/mobilesync/responseschema/2006">
+      <Culture>en:en</Culture>
+      <User>
+          <DisplayName><?php echo $displayname; ?></DisplayName>
+          <EMailAddress><?php echo $email; ?></EMailAddress>
+      </User>
+      <Action>
+          <Settings>
+              <Server>
+                  <Type>MobileSync</Type>
+                  <Url><?php echo $config['activesync']['url']; ?></Url>
+                  <Name><?php echo $config['activesync']['url']; ?></Name>
+              </Server>
+          </Settings>
+      </Action>
+  </Response>
+      <?php
+      }
+      ?>
+</Autodiscover>
+<?php
+		}
+	}
+}
+?>
diff --git a/data/web/call_sogo_ctrl.php b/data/web/call_sogo_ctrl.php
new file mode 100644
index 00000000..c548bd56
--- /dev/null
+++ b/data/web/call_sogo_ctrl.php
@@ -0,0 +1,40 @@
+<?php
+session_start();
+$AuthUsers = array("admin");
+if (!isset($_SESSION['mailcow_cc_role']) OR !in_array($_SESSION['mailcow_cc_role'], $AuthUsers)) {
+	echo "Not allowed." . PHP_EOL;
+	exit();
+}
+if ($_GET['ACTION'] == "start") {
+	$request = xmlrpc_encode_request("supervisor.startProcessGroup", 'sogo-group', array('encoding'=>'utf-8'));
+	$context = stream_context_create(array('http' => array(
+	'method' => "POST",
+	'header' => "Content-Length: " . strlen($request),
+	'content' => $request
+	)));
+	$file = @file_get_contents("http://sogo:9191/RPC2", false, $context) or die("Cannot connect to $remote_server:$listener_port");
+	$response = xmlrpc_decode($file);
+	if (isset($response['faultString'])) {
+		echo '<b><span class="pull-right text-warning">' . $response['faultString'] . '</span></b>';
+	}
+	else {
+		echo '<b><span class="pull-right text-success">OK</span></b>';
+	}
+}
+elseif ($_GET['ACTION'] == "stop") {
+	$request = xmlrpc_encode_request("supervisor.stopProcessGroup", 'sogo-group', array('encoding'=>'utf-8'));
+	$context = stream_context_create(array('http' => array(
+	'method' => "POST",
+	'header' => "Content-Length: " . strlen($request),
+	'content' => $request
+	)));
+	$file = @file_get_contents("http://sogo:9191/RPC2", false, $context) or die("Cannot connect to $remote_server:$listener_port");
+	$response = xmlrpc_decode($file);
+	if (isset($response['faultString'])) {
+		echo '<b><span class="pull-right text-warning">' . $response['faultString'] . '</span></b>';
+	}
+	else {
+		echo '<b><span class="pull-right text-success">OK</span></b>';
+	}
+}
+?>
\ No newline at end of file
diff --git a/data/web/css/bootstrap-select.min.css b/data/web/css/bootstrap-select.min.css
new file mode 100644
index 00000000..d178d824
--- /dev/null
+++ b/data/web/css/bootstrap-select.min.css
@@ -0,0 +1,6 @@
+/*!
+ * Bootstrap-select v1.12.2 (http://silviomoreto.github.io/bootstrap-select)
+ *
+ * Copyright 2013-2017 bootstrap-select
+ * Licensed under MIT (https://github.com/silviomoreto/bootstrap-select/blob/master/LICENSE)
+ */select.bs-select-hidden,select.selectpicker{display:none!important}.bootstrap-select{width:220px\9}.bootstrap-select>.dropdown-toggle{width:100%;padding-right:25px;z-index:1}.bootstrap-select>.dropdown-toggle.bs-placeholder,.bootstrap-select>.dropdown-toggle.bs-placeholder:active,.bootstrap-select>.dropdown-toggle.bs-placeholder:focus,.bootstrap-select>.dropdown-toggle.bs-placeholder:hover{color:#999}.bootstrap-select>select{position:absolute!important;bottom:0;left:50%;display:block!important;width:.5px!important;height:100%!important;padding:0!important;opacity:0!important;border:none}.bootstrap-select>select.mobile-device{top:0;left:0;display:block!important;width:100%!important;z-index:2}.error .bootstrap-select .dropdown-toggle,.has-error .bootstrap-select .dropdown-toggle{border-color:#b94a48}.bootstrap-select.fit-width{width:auto!important}.bootstrap-select:not([class*=col-]):not([class*=form-control]):not(.input-group-btn){width:220px}.bootstrap-select .dropdown-toggle:focus{outline:thin dotted #333!important;outline:5px auto -webkit-focus-ring-color!important;outline-offset:-2px}.bootstrap-select.form-control{margin-bottom:0;padding:0;border:none}.bootstrap-select.form-control:not([class*=col-]){width:100%}.bootstrap-select.form-control.input-group-btn{z-index:auto}.bootstrap-select.form-control.input-group-btn:not(:first-child):not(:last-child)>.btn{border-radius:0}.bootstrap-select.btn-group:not(.input-group-btn),.bootstrap-select.btn-group[class*=col-]{float:none;display:inline-block;margin-left:0}.bootstrap-select.btn-group.dropdown-menu-right,.bootstrap-select.btn-group[class*=col-].dropdown-menu-right,.row .bootstrap-select.btn-group[class*=col-].dropdown-menu-right{float:right}.form-group .bootstrap-select.btn-group,.form-horizontal .bootstrap-select.btn-group,.form-inline .bootstrap-select.btn-group{margin-bottom:0}.form-group-lg .bootstrap-select.btn-group.form-control,.form-group-sm .bootstrap-select.btn-group.form-control{padding:0}.form-group-lg .bootstrap-select.btn-group.form-control .dropdown-toggle,.form-group-sm .bootstrap-select.btn-group.form-control .dropdown-toggle{height:100%;font-size:inherit;line-height:inherit;border-radius:inherit}.form-inline .bootstrap-select.btn-group .form-control{width:100%}.bootstrap-select.btn-group.disabled,.bootstrap-select.btn-group>.disabled{cursor:not-allowed}.bootstrap-select.btn-group.disabled:focus,.bootstrap-select.btn-group>.disabled:focus{outline:0!important}.bootstrap-select.btn-group.bs-container{position:absolute;height:0!important;padding:0!important}.bootstrap-select.btn-group.bs-container .dropdown-menu{z-index:1060}.bootstrap-select.btn-group .dropdown-toggle .filter-option{display:inline-block;overflow:hidden;width:100%;text-align:left}.bootstrap-select.btn-group .dropdown-toggle .caret{position:absolute;top:50%;right:12px;margin-top:-2px;vertical-align:middle}.bootstrap-select.btn-group[class*=col-] .dropdown-toggle{width:100%}.bootstrap-select.btn-group .dropdown-menu{min-width:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.bootstrap-select.btn-group .dropdown-menu.inner{position:static;float:none;border:0;padding:0;margin:0;border-radius:0;-webkit-box-shadow:none;box-shadow:none}.bootstrap-select.btn-group .dropdown-menu li{position:relative}.bootstrap-select.btn-group .dropdown-menu li.active small{color:#fff}.bootstrap-select.btn-group .dropdown-menu li.disabled a{cursor:not-allowed}.bootstrap-select.btn-group .dropdown-menu li a{cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.bootstrap-select.btn-group .dropdown-menu li a.opt{position:relative;padding-left:2.25em}.bootstrap-select.btn-group .dropdown-menu li a span.check-mark{display:none}.bootstrap-select.btn-group .dropdown-menu li a span.text{display:inline-block}.bootstrap-select.btn-group .dropdown-menu li small{padding-left:.5em}.bootstrap-select.btn-group .dropdown-menu .notify{position:absolute;bottom:5px;width:96%;margin:0 2%;min-height:26px;padding:3px 5px;background:#f5f5f5;border:1px solid #e3e3e3;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);box-shadow:inset 0 1px 1px rgba(0,0,0,.05);pointer-events:none;opacity:.9;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.bootstrap-select.btn-group .no-results{padding:3px;background:#f5f5f5;margin:0 5px;white-space:nowrap}.bootstrap-select.btn-group.fit-width .dropdown-toggle .filter-option{position:static}.bootstrap-select.btn-group.fit-width .dropdown-toggle .caret{position:static;top:auto;margin-top:-1px}.bootstrap-select.btn-group.show-tick .dropdown-menu li.selected a span.check-mark{position:absolute;display:inline-block;right:15px;margin-top:5px}.bootstrap-select.btn-group.show-tick .dropdown-menu li a span.text{margin-right:34px}.bootstrap-select.show-menu-arrow.open>.dropdown-toggle{z-index:1061}.bootstrap-select.show-menu-arrow .dropdown-toggle:before{content:'';border-left:7px solid transparent;border-right:7px solid transparent;border-bottom:7px solid rgba(204,204,204,.2);position:absolute;bottom:-4px;left:9px;display:none}.bootstrap-select.show-menu-arrow .dropdown-toggle:after{content:'';border-left:6px solid transparent;border-right:6px solid transparent;border-bottom:6px solid #fff;position:absolute;bottom:-4px;left:10px;display:none}.bootstrap-select.show-menu-arrow.dropup .dropdown-toggle:before{bottom:auto;top:-3px;border-top:7px solid rgba(204,204,204,.2);border-bottom:0}.bootstrap-select.show-menu-arrow.dropup .dropdown-toggle:after{bottom:auto;top:-3px;border-top:6px solid #fff;border-bottom:0}.bootstrap-select.show-menu-arrow.pull-right .dropdown-toggle:before{right:12px;left:auto}.bootstrap-select.show-menu-arrow.pull-right .dropdown-toggle:after{right:13px;left:auto}.bootstrap-select.show-menu-arrow.open>.dropdown-toggle:after,.bootstrap-select.show-menu-arrow.open>.dropdown-toggle:before{display:block}.bs-actionsbox,.bs-donebutton,.bs-searchbox{padding:4px 8px}.bs-actionsbox{width:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.bs-actionsbox .btn-group button{width:50%}.bs-donebutton{float:left;width:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.bs-donebutton .btn-group button{width:100%}.bs-searchbox+.bs-actionsbox{padding:0 8px 4px}.bs-searchbox .form-control{margin-bottom:0;width:100%;float:none}
\ No newline at end of file
diff --git a/data/web/css/bootstrap-slider.min.css b/data/web/css/bootstrap-slider.min.css
new file mode 100644
index 00000000..e55300b2
--- /dev/null
+++ b/data/web/css/bootstrap-slider.min.css
@@ -0,0 +1,41 @@
+/*! =======================================================
+                      VERSION  9.7.2              
+========================================================= */
+/*! =========================================================
+ * bootstrap-slider.js
+ *
+ * Maintainers:
+ *		Kyle Kemp
+ *			- Twitter: @seiyria
+ *			- Github:  seiyria
+ *		Rohit Kalkur
+ *			- Twitter: @Rovolutionary
+ *			- Github:  rovolution
+ *
+ * =========================================================
+  *
+ * bootstrap-slider is released under the MIT License
+ * Copyright (c) 2017 Kyle Kemp, Rohit Kalkur, and contributors
+ * 
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ * 
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * ========================================================= */.slider{display:inline-block;vertical-align:middle;position:relative}.slider.slider-horizontal{width:210px;height:20px}.slider.slider-horizontal .slider-track{height:10px;width:100%;margin-top:-5px;top:50%;left:0}.slider.slider-horizontal .slider-selection,.slider.slider-horizontal .slider-track-low,.slider.slider-horizontal .slider-track-high{height:100%;top:0;bottom:0}.slider.slider-horizontal .slider-tick,.slider.slider-horizontal .slider-handle{margin-left:-10px}.slider.slider-horizontal .slider-tick.triangle,.slider.slider-horizontal .slider-handle.triangle{position:relative;top:50%;transform:translateY(-50%);border-width:0 10px 10px 10px;width:0;height:0;border-bottom-color:#0480be;margin-top:0}.slider.slider-horizontal .slider-tick-container{white-space:nowrap;position:absolute;top:0;left:0;width:100%}.slider.slider-horizontal .slider-tick-label-container{white-space:nowrap;margin-top:20px}.slider.slider-horizontal .slider-tick-label-container .slider-tick-label{padding-top:4px;display:inline-block;text-align:center}.slider.slider-horizontal.slider-rtl .slider-track{left:initial;right:0}.slider.slider-horizontal.slider-rtl .slider-tick,.slider.slider-horizontal.slider-rtl .slider-handle{margin-left:initial;margin-right:-10px}.slider.slider-horizontal.slider-rtl .slider-tick-container{left:initial;right:0}.slider.slider-vertical{height:210px;width:20px}.slider.slider-vertical .slider-track{width:10px;height:100%;left:25%;top:0}.slider.slider-vertical .slider-selection{width:100%;left:0;top:0;bottom:0}.slider.slider-vertical .slider-track-low,.slider.slider-vertical .slider-track-high{width:100%;left:0;right:0}.slider.slider-vertical .slider-tick,.slider.slider-vertical .slider-handle{margin-top:-10px}.slider.slider-vertical .slider-tick.triangle,.slider.slider-vertical .slider-handle.triangle{border-width:10px 0 10px 10px;width:1px;height:1px;border-left-color:#0480be;border-right-color:#0480be;margin-left:0;margin-right:0}.slider.slider-vertical .slider-tick-label-container{white-space:nowrap}.slider.slider-vertical .slider-tick-label-container .slider-tick-label{padding-left:4px}.slider.slider-vertical.slider-rtl .slider-track{left:initial;right:25%}.slider.slider-vertical.slider-rtl .slider-selection{left:initial;right:0}.slider.slider-vertical.slider-rtl .slider-tick.triangle,.slider.slider-vertical.slider-rtl .slider-handle.triangle{border-width:10px 10px 10px 0}.slider.slider-vertical.slider-rtl .slider-tick-label-container .slider-tick-label{padding-left:initial;padding-right:4px}.slider.slider-disabled .slider-handle{background-image:-webkit-linear-gradient(top,#dfdfdf 0,#bebebe 100%);background-image:-o-linear-gradient(top,#dfdfdf 0,#bebebe 100%);background-image:linear-gradient(to bottom,#dfdfdf 0,#bebebe 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdfdfdf',endColorstr='#ffbebebe',GradientType=0)}.slider.slider-disabled .slider-track{background-image:-webkit-linear-gradient(top,#e5e5e5 0,#e9e9e9 100%);background-image:-o-linear-gradient(top,#e5e5e5 0,#e9e9e9 100%);background-image:linear-gradient(to bottom,#e5e5e5 0,#e9e9e9 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe5e5e5',endColorstr='#ffe9e9e9',GradientType=0);cursor:not-allowed}.slider input{display:none}.slider .tooltip.top{margin-top:-36px}.slider .tooltip-inner{white-space:nowrap;max-width:none}.slider .hide{display:none}.slider-track{position:absolute;cursor:pointer;background-image:-webkit-linear-gradient(top,#f5f5f5 0,#f9f9f9 100%);background-image:-o-linear-gradient(top,#f5f5f5 0,#f9f9f9 100%);background-image:linear-gradient(to bottom,#f5f5f5 0,#f9f9f9 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5',endColorstr='#fff9f9f9',GradientType=0);-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);border-radius:4px}.slider-selection{position:absolute;background-image:-webkit-linear-gradient(top,#f9f9f9 0,#f5f5f5 100%);background-image:-o-linear-gradient(top,#f9f9f9 0,#f5f5f5 100%);background-image:linear-gradient(to bottom,#f9f9f9 0,#f5f5f5 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff9f9f9',endColorstr='#fff5f5f5',GradientType=0);-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;border-radius:4px}.slider-selection.tick-slider-selection{background-image:-webkit-linear-gradient(top,#89cdef 0,#81bfde 100%);background-image:-o-linear-gradient(top,#89cdef 0,#81bfde 100%);background-image:linear-gradient(to bottom,#89cdef 0,#81bfde 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff89cdef',endColorstr='#ff81bfde',GradientType=0)}.slider-track-low,.slider-track-high{position:absolute;background:transparent;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;border-radius:4px}.slider-handle{position:absolute;top:0;width:20px;height:20px;background-color:#337ab7;background-image:-webkit-linear-gradient(top,#149bdf 0,#0480be 100%);background-image:-o-linear-gradient(top,#149bdf 0,#0480be 100%);background-image:linear-gradient(to bottom,#149bdf 0,#0480be 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff149bdf',endColorstr='#ff0480be',GradientType=0);filter:none;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05);border:0 solid transparent}.slider-handle.round{border-radius:50%}.slider-handle.triangle{background:transparent none}.slider-handle.custom{background:transparent none}.slider-handle.custom::before{line-height:20px;font-size:20px;content:'\2605';color:#726204}.slider-tick{position:absolute;width:20px;height:20px;background-image:-webkit-linear-gradient(top,#f9f9f9 0,#f5f5f5 100%);background-image:-o-linear-gradient(top,#f9f9f9 0,#f5f5f5 100%);background-image:linear-gradient(to bottom,#f9f9f9 0,#f5f5f5 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff9f9f9',endColorstr='#fff5f5f5',GradientType=0);-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;filter:none;opacity:.8;border:0 solid transparent}.slider-tick.round{border-radius:50%}.slider-tick.triangle{background:transparent none}.slider-tick.custom{background:transparent none}.slider-tick.custom::before{line-height:20px;font-size:20px;content:'\2605';color:#726204}.slider-tick.in-selection{background-image:-webkit-linear-gradient(top,#89cdef 0,#81bfde 100%);background-image:-o-linear-gradient(top,#89cdef 0,#81bfde 100%);background-image:linear-gradient(to bottom,#89cdef 0,#81bfde 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff89cdef',endColorstr='#ff81bfde',GradientType=0);opacity:1}
\ No newline at end of file
diff --git a/data/web/css/bootstrap-switch.min.css b/data/web/css/bootstrap-switch.min.css
new file mode 100644
index 00000000..cbfa013b
--- /dev/null
+++ b/data/web/css/bootstrap-switch.min.css
@@ -0,0 +1,10 @@
+/**
+  * bootstrap-switch - Turn checkboxes and radio buttons into toggle switches.
+  *
+  * @version v3.3.3
+  * @homepage http://www.bootstrap-switch.org
+  * @author Mattia Larentis <[email protected]> (http://larentis.eu)
+  * @license Apache-2.0
+  */
+
+.bootstrap-switch{display:inline-block;direction:ltr;cursor:pointer;border-radius:4px;border:1px solid #ccc;position:relative;text-align:left;overflow:hidden;line-height:8px;z-index:0;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;vertical-align:middle;-webkit-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;-o-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.bootstrap-switch .bootstrap-switch-container{display:inline-block;top:0;border-radius:4px;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}.bootstrap-switch .bootstrap-switch-handle-off,.bootstrap-switch .bootstrap-switch-handle-on,.bootstrap-switch .bootstrap-switch-label{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;cursor:pointer;display:inline-block!important;height:100%;padding:6px 12px;font-size:14px;line-height:20px}.bootstrap-switch .bootstrap-switch-handle-off,.bootstrap-switch .bootstrap-switch-handle-on{text-align:center;z-index:1}.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-primary,.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-primary{color:#fff;background:#337ab7}.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-info,.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-info{color:#fff;background:#5bc0de}.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-success,.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-success{color:#fff;background:#5cb85c}.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-warning,.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-warning{background:#f0ad4e;color:#fff}.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-danger,.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-danger{color:#fff;background:#d9534f}.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-default,.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-default{color:#000;background:#eee}.bootstrap-switch .bootstrap-switch-label{text-align:center;margin-top:-1px;margin-bottom:-1px;z-index:100;color:#333;background:#fff}.bootstrap-switch .bootstrap-switch-handle-on{border-bottom-left-radius:3px;border-top-left-radius:3px}.bootstrap-switch .bootstrap-switch-handle-off{border-bottom-right-radius:3px;border-top-right-radius:3px}.bootstrap-switch input[type=radio],.bootstrap-switch input[type=checkbox]{position:absolute!important;top:0;left:0;margin:0;z-index:-1;opacity:0;filter:alpha(opacity=0)}.bootstrap-switch.bootstrap-switch-mini .bootstrap-switch-handle-off,.bootstrap-switch.bootstrap-switch-mini .bootstrap-switch-handle-on,.bootstrap-switch.bootstrap-switch-mini .bootstrap-switch-label{padding:1px 5px;font-size:12px;line-height:1.5}.bootstrap-switch.bootstrap-switch-small .bootstrap-switch-handle-off,.bootstrap-switch.bootstrap-switch-small .bootstrap-switch-handle-on,.bootstrap-switch.bootstrap-switch-small .bootstrap-switch-label{padding:5px 10px;font-size:12px;line-height:1.5}.bootstrap-switch.bootstrap-switch-large .bootstrap-switch-handle-off,.bootstrap-switch.bootstrap-switch-large .bootstrap-switch-handle-on,.bootstrap-switch.bootstrap-switch-large .bootstrap-switch-label{padding:6px 16px;font-size:18px;line-height:1.3333333}.bootstrap-switch.bootstrap-switch-disabled,.bootstrap-switch.bootstrap-switch-indeterminate,.bootstrap-switch.bootstrap-switch-readonly{cursor:default!important}.bootstrap-switch.bootstrap-switch-disabled .bootstrap-switch-handle-off,.bootstrap-switch.bootstrap-switch-disabled .bootstrap-switch-handle-on,.bootstrap-switch.bootstrap-switch-disabled .bootstrap-switch-label,.bootstrap-switch.bootstrap-switch-indeterminate .bootstrap-switch-handle-off,.bootstrap-switch.bootstrap-switch-indeterminate .bootstrap-switch-handle-on,.bootstrap-switch.bootstrap-switch-indeterminate .bootstrap-switch-label,.bootstrap-switch.bootstrap-switch-readonly .bootstrap-switch-handle-off,.bootstrap-switch.bootstrap-switch-readonly .bootstrap-switch-handle-on,.bootstrap-switch.bootstrap-switch-readonly .bootstrap-switch-label{opacity:.5;filter:alpha(opacity=50);cursor:default!important}.bootstrap-switch.bootstrap-switch-animate .bootstrap-switch-container{-webkit-transition:margin-left .5s;-o-transition:margin-left .5s;transition:margin-left .5s}.bootstrap-switch.bootstrap-switch-inverse .bootstrap-switch-handle-on{border-radius:0 3px 3px 0}.bootstrap-switch.bootstrap-switch-inverse .bootstrap-switch-handle-off{border-radius:3px 0 0 3px}.bootstrap-switch.bootstrap-switch-focused{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)}.bootstrap-switch.bootstrap-switch-inverse.bootstrap-switch-off .bootstrap-switch-label,.bootstrap-switch.bootstrap-switch-on .bootstrap-switch-label{border-bottom-right-radius:3px;border-top-right-radius:3px}.bootstrap-switch.bootstrap-switch-inverse.bootstrap-switch-on .bootstrap-switch-label,.bootstrap-switch.bootstrap-switch-off .bootstrap-switch-label{border-bottom-left-radius:3px;border-top-left-radius:3px}
\ No newline at end of file
diff --git a/data/web/css/mailbox.css b/data/web/css/mailbox.css
new file mode 100644
index 00000000..b5c69343
--- /dev/null
+++ b/data/web/css/mailbox.css
@@ -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;
+}
\ No newline at end of file
diff --git a/data/web/css/mailcow.css b/data/web/css/mailcow.css
new file mode 100644
index 00000000..a47252bc
--- /dev/null
+++ b/data/web/css/mailcow.css
@@ -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;}
\ No newline at end of file
diff --git a/data/web/css/tables.css b/data/web/css/tables.css
new file mode 100644
index 00000000..651e1665
--- /dev/null
+++ b/data/web/css/tables.css
@@ -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;
+}
\ No newline at end of file
diff --git a/data/web/delete.php b/data/web/delete.php
new file mode 100644
index 00000000..6ba93d31
--- /dev/null
+++ b/data/web/delete.php
@@ -0,0 +1,212 @@
+<?php
+require_once("inc/prerequisites.inc.php");
+$AuthUsers = array("admin", "domainadmin", "user");
+if (!isset($_SESSION['mailcow_cc_role']) OR !in_array($_SESSION['mailcow_cc_role'], $AuthUsers)) {
+	header('Location: /');
+	exit();
+}
+require_once("inc/header.inc.php");
+?>
+<div class="container">
+	<div class="row">
+		<div class="col-md-12">
+			<div class="panel panel-default">
+				<div class="panel-heading">
+					<h3 class="panel-title"><?=$lang['delete']['title'];?></h3>
+				</div>
+				<div class="panel-body">
+<?php
+if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "admin"  || $_SESSION['mailcow_cc_role'] == "domainadmin")) {
+		// DELETE DOMAIN
+		if (isset($_GET["domain"]) &&
+			is_valid_domain_name($_GET["domain"]) &&
+			!empty($_GET["domain"]) &&
+			$_SESSION['mailcow_cc_role'] == "admin") {
+			$domain = $_GET["domain"];
+			?>
+				<div class="alert alert-warning" role="alert"><?=sprintf($lang['delete']['remove_domain_warning'], htmlspecialchars($_GET["domain"]));?></div>
+				<p><?=$lang['delete']['remove_domain_details'];?></p>
+				<form class="form-horizontal" role="form" method="post" action="/mailbox.php">
+				<input type="hidden" name="domain" value="<?php echo htmlspecialchars($domain) ?>">
+					<div class="form-group">
+						<div class="col-sm-offset-1 col-sm-10">
+							<button type="submit" name="mailbox_delete_domain" class="btn btn-default btn-sm"><?=$lang['delete']['remove_button'];?></button>
+						</div>
+					</div>
+				</form>
+			<?php
+		}
+		// DELETE ALIAS
+		elseif (isset($_GET["alias"]) &&
+			(filter_var($_GET["alias"], FILTER_VALIDATE_EMAIL) || is_valid_domain_name(substr(strrchr($_GET["alias"], "@"), 1))) &&
+			!empty($_GET["alias"])) {
+				$domain = substr(strrchr($_GET["alias"], "@"), 1);
+				if (hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $domain)) {
+				?>
+					<div class="alert alert-warning" role="alert"><?=sprintf($lang['delete']['remove_alias_warning'], htmlspecialchars($_GET["alias"]));?></div>
+					<p><?=$lang['delete']['remove_alias_details'];?></p>
+					<form class="form-horizontal" role="form" method="post" action="/mailbox.php">
+					<input type="hidden" name="address" value="<?php echo htmlspecialchars($_GET["alias"]) ?>">
+						<div class="form-group">
+							<div class="col-sm-offset-1 col-sm-10">
+								<button type="submit" name="mailbox_delete_alias" 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 ALIAS DOMAIN
+		elseif (
+			isset($_GET["aliasdomain"]) &&
+			is_valid_domain_name($_GET["aliasdomain"]) && 
+			!empty($_GET["aliasdomain"])) {
+        $alias_domain = $_GET["aliasdomain"];
+        $result = mailbox_get_alias_domain_details($alias_domain);
+				if (!empty($result)) {
+				?>
+					<div class="alert alert-warning" role="alert"><?=sprintf($lang['delete']['remove_domainalias_warning'], htmlspecialchars($_GET["aliasdomain"]));?></div>
+					<form class="form-horizontal" role="form" method="post" action="/mailbox.php">
+					<input type="hidden" name="alias_domain" value="<?php echo htmlspecialchars($alias_domain) ?>">
+						<div class="form-group">
+							<div class="col-sm-offset-1 col-sm-10">
+								<button type="submit" name="mailbox_delete_alias_domain" 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 DOMAIN ADMIN
+		elseif (isset($_GET["domainadmin"]) &&
+			ctype_alnum(str_replace(array('_', '.', '-'), '', $_GET["domainadmin"])) &&
+			!empty($_GET["domainadmin"]) &&
+        $_SESSION['mailcow_cc_role'] == "admin") {
+				$domain_admin = $_GET["domainadmin"];
+				?>
+				<div class="alert alert-warning" role="alert"><?=sprintf($lang['delete']['remove_domainadmin_warning'], htmlspecialchars($_GET["domainadmin"]));?></div>
+				<form class="form-horizontal" role="form" method="post" action="/admin.php">
+				<input type="hidden" name="username" value="<?=htmlspecialchars($domain_admin);?>">
+					<div class="form-group">
+						<div class="col-sm-offset-1 col-sm-10">
+							<button type="submit" name="delete_domain_admin" class="btn btn-default btn-sm"><?=$lang['delete']['remove_button'];?></button>
+						</div>
+					</div>
+				</form>
+				<?php
+		}
+		// DELETE MAILBOX
+		elseif (isset($_GET["mailbox"]) &&
+			filter_var($_GET["mailbox"], FILTER_VALIDATE_EMAIL) &&
+			!empty($_GET["mailbox"])) {
+				$mailbox = $_GET["mailbox"];
+				if (hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $mailbox)) {
+				?>
+					<div class="alert alert-warning" role="alert"><?=sprintf($lang['delete']['remove_mailbox_warning'], htmlspecialchars($mailbox));?></div>
+					<p><?=$lang['delete']['remove_mailbox_details'];?></p>
+					<form class="form-horizontal" role="form" method="post" action="/mailbox.php">
+					<input type="hidden" name="username" value="<?=htmlspecialchars($mailbox);?>">
+						<div class="form-group">
+							<div class="col-sm-offset-1 col-sm-10">
+								<button type="submit" name="mailbox_delete_mailbox" class="btn btn-default btn-sm"><?=$lang['delete']['remove_button'];?></button>
+							</div>
+						</div>
+					</form>
+				<?php
+				}
+				else {
+				?>
+					<div class="alert alert-info" role="alert"><?=$lang['info']['no_action'];?></div>
+				<?php
+				}
+		}
+		// DELETE RESOURCE
+		elseif (isset($_GET["resource"]) &&
+			filter_var($_GET["resource"], FILTER_VALIDATE_EMAIL) &&
+			!empty($_GET["resource"])) {
+				$resource = $_GET["resource"];
+				if (hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $resource)) {
+				?>
+					<div class="alert alert-warning" role="alert"><?=sprintf($lang['delete']['remove_resource_warning'], htmlspecialchars($resource));?></div>
+					<p><?=$lang['delete']['remove_resource_details'];?></p>
+					<form class="form-horizontal" role="form" method="post" action="/mailbox.php">
+					<input type="hidden" name="name" value="<?=htmlspecialchars($resource);?>">
+						<div class="form-group">
+							<div class="col-sm-offset-1 col-sm-10">
+								<button type="submit" name="mailbox_delete_resource" class="btn btn-default btn-sm"><?=$lang['delete']['remove_button'];?></button>
+							</div>
+						</div>
+					</form>
+				<?php
+				}
+				else {
+				?>
+					<div class="alert alert-info" role="alert"><?=$lang['info']['no_action'];?></div>
+				<?php
+				}
+		}
+		else {
+		?>
+			<div class="alert alert-info" role="alert"><?=$lang['info']['no_action'];?></div>
+		<?php
+		}
+}
+elseif (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "user")) {
+		// DELETE SYNCJOB
+		if (isset($_GET["syncjob"]) &&
+			is_numeric($_GET["syncjob"]) &&
+      filter_var($_SESSION['mailcow_cc_username'], FILTER_VALIDATE_EMAIL)) {
+        $id = $_GET["syncjob"];
+        $result = get_syncjob_details($id);
+        if (!empty($result)) {
+				?>
+					<div class="alert alert-warning" role="alert"><?=sprintf($lang['delete']['remove_syncjob_warning'], htmlspecialchars($result['user2']));?></div>
+					<p><?=$lang['delete']['remove_syncjob_details'];?></p>
+					<form class="form-horizontal" role="form" method="post" action="/user.php">
+					<input type="hidden" name="username" value="<?=htmlspecialchars($mailbox);?>">
+						<div class="form-group">
+							<div class="col-sm-offset-1 col-sm-10">
+								<input type="hidden" name="id" value="<?=$_GET["syncjob"];?>">
+								<button type="submit" name="delete_syncjob" value="1" class="btn btn-default btn-sm"><?=$lang['delete']['remove_button'];?></button>
+							</div>
+						</div>
+					</form>
+				<?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
+		}
+}
+else {
+?>
+	<div class="alert alert-danger" role="alert"><?=$lang['danger']['access_denied'];?></div>
+<?php
+}
+?>
+				</div>
+			</div>
+		</div>
+	</div>
+<a href="<?=$_SESSION['return_to'];?>">&#8592; <?=$lang['delete']['previous'];?></a>
+</div> <!-- /container -->
+<?php
+require_once("inc/footer.inc.php");
+?>
diff --git a/data/web/edit.php b/data/web/edit.php
new file mode 100644
index 00000000..996c21f8
--- /dev/null
+++ b/data/web/edit.php
@@ -0,0 +1,664 @@
+<?php
+require_once("inc/prerequisites.inc.php");
+$AuthUsers = array("admin", "domainadmin", "user");
+if (!isset($_SESSION['mailcow_cc_role']) OR !in_array($_SESSION['mailcow_cc_role'], $AuthUsers)) {
+	header('Location: /');
+	exit();
+}
+require_once("inc/header.inc.php");
+?>
+<div class="container">
+	<div class="row">
+		<div class="col-md-12">
+			<div class="panel panel-default">
+				<div class="panel-heading">
+					<h3 class="panel-title"><?=$lang['edit']['title'];?></h3>
+				</div>
+				<div class="panel-body">
+<?php
+if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "admin"  || $_SESSION['mailcow_cc_role'] == "domainadmin")) {
+		if (isset($_GET["alias"]) &&
+			!empty($_GET["alias"])) {
+				$alias = $_GET["alias"];
+        $result = mailbox_get_alias_details($alias);
+				if (!empty($result)) {
+				?>
+					<h4><?=$lang['edit']['alias'];?></h4>
+					<br />
+					<form class="form-horizontal" role="form" method="post" action="<?=($FORM_ACTION == "previous") ? $_SESSION['return_to'] : null;?>">
+					<input type="hidden" name="address" value="<?=htmlspecialchars($alias);?>">
+						<div class="form-group">
+							<label class="control-label col-sm-2" for="goto"><?=$lang['edit']['target_address'];?></label>
+							<div class="col-sm-10">
+								<textarea class="form-control" autocapitalize="none" autocorrect="off" rows="10" id="goto" name="goto"><?=htmlspecialchars($result['goto']) ?></textarea>
+							</div>
+						</div>
+						<div class="form-group">
+							<div class="col-sm-offset-2 col-sm-10">
+								<div class="checkbox">
+								<label><input type="checkbox" name="active" <?php if (isset($result['active_int']) && $result['active_int']=="1") { echo "checked"; }; ?>> <?=$lang['edit']['active'];?></label>
+								</div>
+							</div>
+						</div>
+						<div class="form-group">
+							<div class="col-sm-offset-2 col-sm-10">
+								<button type="submit" name="mailbox_edit_alias" 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
+				}
+		}
+		elseif (isset($_GET['domainadmin']) && 
+			ctype_alnum(str_replace(array('_', '.', '-'), '', $_GET["domainadmin"])) &&
+			!empty($_GET["domainadmin"]) &&
+			$_GET["domainadmin"] != 'admin' &&
+			$_SESSION['mailcow_cc_role'] == "admin") {
+				$domain_admin = $_GET["domainadmin"];
+        $result = get_domain_admin_details($domain_admin);
+				if (!empty($result)) {
+				?>
+				<h4><?=$lang['edit']['domain_admin'];?></h4>
+				<br />
+				<form class="form-horizontal" role="form" method="post" action="<?=($FORM_ACTION == "previous") ? $_SESSION['return_to'] : null;?>">
+				<input type="hidden" name="username_now" value="<?=htmlspecialchars($domain_admin);?>">
+					<div class="form-group">
+						<label class="control-label col-sm-2" for="username"><?=$lang['edit']['username'];?></label>
+						<div class="col-sm-10">
+              <input class="form-control" type="text" name="username" value="<?=htmlspecialchars($domain_admin);?>" />
+						</div>
+					</div>
+					<div class="form-group">
+						<label class="control-label col-sm-2" for="domain"><?=$lang['edit']['domains'];?></label>
+						<div class="col-sm-10">
+							<select id="domain" name="domain[]" multiple>
+							<?php
+							foreach ($result['selected_domains'] as $domain):
+							?>
+								<option selected><?=htmlspecialchars($domain);?></option>
+							<?php
+							endforeach;
+							foreach ($result['unselected_domains'] as $domain):
+							?>
+								<option><?=htmlspecialchars($domain);?></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="">
+						</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" <?php if (isset($result['active_int']) && $result['active_int']=="1") { echo "checked"; }; ?>> <?=$lang['edit']['active'];?></label>
+							</div>
+						</div>
+					</div>
+					<div class="form-group">
+						<div class="col-sm-offset-2 col-sm-10">
+							<div class="checkbox">
+							<label><input type="checkbox" name="disable_tfa"> <?=$lang['tfa']['disable_tfa'];?></label>
+							</div>
+						</div>
+					</div>
+					<div class="form-group">
+						<div class="col-sm-offset-2 col-sm-10">
+							<button type="submit" name="edit_domain_admin" class="btn btn-success btn-sm"><?=$lang['edit']['save'];?></button>
+						</div>
+					</div>
+				</form>
+			<?php
+			}
+			else {
+			?>
+				<div class="alert alert-info" role="alert"><?=$lang['info']['no_action'];?></div>
+			<?php
+			}
+	}
+	elseif (isset($_GET['domain']) &&
+		is_valid_domain_name($_GET["domain"]) &&
+		!empty($_GET["domain"])) {
+			$domain = $_GET["domain"];
+      $result = mailbox_get_domain_details($domain);
+			if (!empty($result)) {
+			?>
+				<h4><?=$lang['edit']['domain'];?></h4>
+				<form class="form-horizontal" role="form" method="post" action="<?=($FORM_ACTION == "previous") ? $_SESSION['return_to'] : null;?>">
+				<input type="hidden" name="domain" value="<?=htmlspecialchars($domain);?>">
+					<div class="form-group">
+						<label class="control-label col-sm-2" for="description"><?=$lang['edit']['description'];?></label>
+						<div class="col-sm-10">
+							<input type="text" class="form-control" name="description" id="description" value="<?=htmlspecialchars($result['description']);?>">
+						</div>
+					</div>
+					<?php
+					if ($_SESSION['mailcow_cc_role'] == "admin") {
+					?>
+					<div class="form-group">
+						<label class="control-label col-sm-2" for="aliases"><?=$lang['edit']['max_aliases'];?></label>
+						<div class="col-sm-10">
+							<input type="number" class="form-control" name="aliases" id="aliases" value="<?=intval($result['max_num_aliases_for_domain']);?>">
+						</div>
+					</div>
+					<div class="form-group">
+						<label class="control-label col-sm-2" for="mailboxes"><?=$lang['edit']['max_mailboxes'];?></label>
+						<div class="col-sm-10">
+							<input type="number" class="form-control" name="mailboxes" id="mailboxes" value="<?=intval($result['max_num_mboxes_for_domain']);?>">
+						</div>
+					</div>
+					<div class="form-group">
+						<label class="control-label col-sm-2" for="maxquota"><?=$lang['edit']['max_quota'];?></label>
+						<div class="col-sm-10">
+							<input type="number" class="form-control" name="maxquota" id="maxquota" value="<?=intval($result['max_new_mailbox_quota'] / 1048576);?>">
+						</div>
+					</div>
+					<div class="form-group">
+						<label class="control-label col-sm-2" for="quota"><?=$lang['edit']['domain_quota'];?></label>
+						<div class="col-sm-10">
+							<input type="number" class="form-control" name="quota" id="quota" value="<?=intval($result['max_quota_for_domain'] / 1048576);?>">
+						</div>
+					</div>
+					<div class="form-group">
+						<label class="control-label col-sm-2"><?=$lang['edit']['backup_mx_options'];?></label>
+						<div class="col-sm-10">
+							<div class="checkbox">
+								<label><input type="checkbox" name="backupmx" <?=(isset($result['backupmx_int']) && $result['backupmx_int']=="1") ? "checked" : null;?>> <?=$lang['edit']['relay_domain'];?></label>
+								<br />
+								<label><input type="checkbox" name="relay_all_recipients" <?=(isset($result['relay_all_recipients_int']) && $result['relay_all_recipients_int']=="1") ? "checked" : null;?>> <?=$lang['edit']['relay_all'];?></label>
+								<p><?=$lang['edit']['relay_all_info'];?></p>
+							</div>
+						</div>
+					</div>
+					<?php
+					}
+					?>
+					<div class="form-group">
+						<div class="col-sm-offset-2 col-sm-10">
+							<div class="checkbox">
+								<label><input type="checkbox" name="active" <?=(isset($result['active_int']) && $result['active_int']=="1") ? "checked" : null;?> <?=($_SESSION['mailcow_cc_role'] == "admin") ? null : "disabled";?>> <?=$lang['edit']['active'];?></label>
+							</div>
+						</div>
+					</div>
+					<div class="form-group">
+						<div class="col-sm-offset-2 col-sm-10">
+							<button type="submit" name="mailbox_edit_domain" class="btn btn-success btn-sm"><?=$lang['edit']['save'];?></button>
+						</div>
+					</div>
+				</form>
+				<?php
+        if (!empty($dkim = dkim_get_key_details($domain))) {
+				?>
+        <hr>
+        <div class="row">
+          <div class="col-xs-2">
+            <p>Domain: <strong><?=htmlspecialchars($result['domain_name']);?></strong> (dkim._domainkey)</p>
+          </div>
+          <div class="col-xs-10">
+            <pre><?=$dkim['dkim_txt'];?></pre>
+          </div>
+        </div>
+				<?php
+				}
+        ?>
+		<hr>
+		<div class="row">
+			<div class="col-sm-6">
+				<h4><span class="glyphicon glyphicon-thumbs-up" aria-hidden="true"></span> <?=$lang['user']['spamfilter_wl'];?></h4>
+				<p><?=$lang['user']['spamfilter_wl_desc'];?></p>
+				<div class="row">
+					<div class="col-sm-6"><b><?=$lang['user']['spamfilter_table_rule'];?></b></div>
+					<div class="col-sm-6"><b><?=$lang['user']['spamfilter_table_action'];?></b></div>
+				</div>
+				<?php
+        $get_policy_list = get_policy_list($domain);
+				if (empty($get_policy_list['whitelist'])):
+				?>
+					<div class="row">
+						<div class="col-sm-12"><i><?=$lang['user']['spamfilter_table_empty'];?></i></div>
+					</div>
+				<?php
+				else:
+          foreach($get_policy_list['whitelist'] as $wl):
+          ?>
+          <div class="row striped">
+            <form class="form-inline" method="post">
+            <div class="col-xs-6"><code><?=$wl['value'];?></code></div>
+            <div class="col-xs-6">
+              <?php
+              if ($wl['object'] == $domain):
+              ?>
+                <input type="hidden" name="delete_prefid" value="<?=$wl['prefid'];?>">
+                <input type="hidden" name="delete_policy_list_item">
+                <input type="hidden" name="domain" value="<?=$domain;?>">
+                <a href="#" onclick="$(this).closest('form').submit()" data-toggle="tooltip" data-placement="left" title="<?=$lang['user']['delete_now'];?>"><span class="glyphicon glyphicon-remove"></span></a>
+              <?php
+              else:
+              ?>
+                <span style="cursor:not-allowed"><?=$lang['user']['spamfilter_table_domain_policy'];?></span>
+              <?php
+              endif;
+              ?>
+            </div>
+            </form>
+          </div>
+          <?php
+          endforeach;
+        endif;
+				?>
+				<hr style="margin:5px 0px 7px 0px">
+				<div class="row">
+					<form class="form-inline" method="post">
+					<div class="col-xs-6">
+						<input type="text" class="form-control input-sm" name="object_from" id="object_from" placeholder="*@example.org" required>
+						<input type="hidden" name="object_list" value="wl">
+						<input type="hidden" name="domain" value="<?=$domain;?>">
+					</div>
+					<div class="col-xs-6">
+						<button type="submit" name="add_policy_list_item" class="btn btn-xs btn-default"><?=$lang['user']['spamfilter_table_add'];?></button>
+					</div>
+					</form>
+				</div>
+			</div>
+			<div class="col-sm-6">
+				<h4><span class="glyphicon glyphicon-thumbs-down" aria-hidden="true"></span> <?=$lang['user']['spamfilter_bl'];?></h4>
+				<p><?=$lang['user']['spamfilter_bl_desc'];?></p>
+				<div class="row">
+					<div class="col-sm-6"><b><?=$lang['user']['spamfilter_table_rule'];?></b></div>
+					<div class="col-sm-6"><b><?=$lang['user']['spamfilter_table_action'];?></b></div>
+				</div>
+				<?php
+				if (empty($get_policy_list['blacklist'])):
+				?>
+					<div class="row">
+						<div class="col-sm-12"><i><?=$lang['user']['spamfilter_table_empty'];?></i></div>
+					</div>
+				<?php
+				else:
+          foreach($get_policy_list['blacklist'] as $bl):
+          ?>
+          <div class="row striped">
+            <form class="form-inline" method="post">
+            <div class="col-xs-6"><code><?=$bl['value'];?></code></div>
+            <div class="col-xs-6">
+              <input type="hidden" name="delete_prefid" value="<?=$bl['prefid'];?>">
+              <?php
+              if ($bl['object'] == $domain):
+              ?>
+                <input type="hidden" name="delete_policy_list_item">
+                <input type="hidden" name="domain" value="<?=$domain;?>">
+                <a href="#" onclick="$(this).closest('form').submit()" data-toggle="tooltip" data-placement="left" title="<?=$lang['user']['delete_now'];?>"><span class="glyphicon glyphicon-remove"></span></a>
+              <?php
+              else:
+              ?>
+                <span style="cursor:not-allowed"><?=$lang['user']['spamfilter_table_domain_policy'];?></span>
+              <?php
+              endif;
+              ?>
+            </div>
+            </form>
+          </div>
+          <?php
+          endforeach;
+        endif;
+				?>
+				<hr style="margin:5px 0px 7px 0px">
+				<div class="row">
+					<form class="form-inline" method="post">
+					<div class="col-xs-6">
+						<input type="text" class="form-control input-sm" name="object_from" id="object_from" placeholder="*@example.org" required>
+						<input type="hidden" name="object_list" value="bl">
+						<input type="hidden" name="domain" value="<?=$domain;?>">
+					</div>
+					<div class="col-xs-6">
+						<button type="submit" name="add_policy_list_item" class="btn btn-xs btn-default"><?=$lang['user']['spamfilter_table_add'];?></button>
+					</div>
+					</form>
+				</div>
+			</div>
+		</div>
+        <?php
+			}
+			else {
+			?>
+				<div class="alert alert-info" role="alert"><?=$lang['info']['no_action'];?></div>
+			<?php
+			}
+	}
+	elseif (isset($_GET['aliasdomain']) &&
+		is_valid_domain_name($_GET["aliasdomain"]) &&
+		!empty($_GET["aliasdomain"])) {
+			$alias_domain = $_GET["aliasdomain"];
+      $result = mailbox_get_alias_domain_details($alias_domain);
+      if (!empty($result)) {
+			?>
+				<h4><?=$lang['edit']['edit_alias_domain'];?></h4>
+				<form class="form-horizontal" role="form" method="post" action="<?=($FORM_ACTION == "previous") ? $_SESSION['return_to'] : null;?>">
+					<input type="hidden" name="alias_domain_now" value="<?=htmlspecialchars($alias_domain);?>">
+					<div class="form-group">
+						<label class="control-label col-sm-2" for="alias_domain"><?=$lang['edit']['alias_domain'];?></label>
+						<div class="col-sm-10">
+							<input type="text" class="form-control" name="alias_domain" id="alias_domain" value="<?=htmlspecialchars($result['alias_domain']);?>">
+						</div>
+					</div>
+					<div class="form-group">
+						<div class="col-sm-offset-2 col-sm-10">
+							<div class="checkbox">
+								<label><input type="checkbox" name="active" <?=(isset($result['active_int']) && $result['active_int']=="1") ?  "checked" : null ?>> <?=$lang['edit']['active'];?></label>
+							</div>
+						</div>
+					</div>
+					<div class="form-group">
+						<div class="col-sm-offset-2 col-sm-10">
+							<button type="submit" name="mailbox_edit_alias_domain" class="btn btn-success btn-sm"><?=$lang['edit']['save'];?></button>
+						</div>
+					</div>
+				</form>
+				<?php
+        if (!empty($dkim = dkim_get_key_details($alias_domain))) {
+				?>
+        <hr>
+        <div class="row">
+          <div class="col-xs-2">
+            <p>Domain: <strong><?=htmlspecialchars($result['alias_domain']);?></strong> (dkim._domainkey)</p>
+          </div>
+          <div class="col-xs-10">
+          <pre><?=$dkim['dkim_txt'];?></pre>
+          </div>
+        </div>
+				<?php
+				}
+			}
+			else {
+			?>
+				<div class="alert alert-info" role="alert"><?=$lang['info']['no_action'];?></div>
+			<?php
+			}
+	}
+	elseif (isset($_GET['mailbox']) && filter_var($_GET["mailbox"], FILTER_VALIDATE_EMAIL) && !empty($_GET["mailbox"])) {
+    $mailbox = $_GET["mailbox"];
+    $result = mailbox_get_mailbox_details($mailbox);
+    if (!empty($result)) {
+      ?>
+      <h4><?=$lang['edit']['mailbox'];?></h4>
+      <form class="form-horizontal" role="form" method="post" action="<?=($FORM_ACTION == "previous") ? $_SESSION['return_to'] : null;?>">
+      <input type="hidden" name="username" value="<?=htmlspecialchars($result['username']);?>">
+        <div class="form-group">
+          <label class="control-label col-sm-2" for="name"><?=$lang['edit']['full_name'];?>:</label>
+          <div class="col-sm-10">
+          <input type="text" class="form-control" name="name" id="name" value="<?=htmlspecialchars($result['name'], ENT_QUOTES, 'UTF-8');?>">
+          </div>
+        </div>
+        <div class="form-group">
+          <label class="control-label col-sm-2" for="quota"><?=$lang['edit']['quota_mb'];?>:
+            <br /><span id="quotaBadge" class="badge">max. <?=intval($result['max_new_quota'] / 1048576)?> MiB</span>
+          </label>
+          <div class="col-sm-10">
+            <input type="number" name="quota" id="quota" id="destroyable" style="width:100%" min="1" max="<?=intval($result['max_new_quota'] / 1048576);?>" value="<?=intval($result['quota']) / 1048576;?>" class="form-control">
+          </div>
+        </div>
+        <div class="form-group">
+          <label class="control-label col-sm-2" for="sender_acl"><?=$lang['edit']['sender_acl'];?>:</label>
+          <div class="col-sm-10">
+            <select data-width="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;?>">
+          <input type="hidden" name="name" value="<?=htmlspecialchars($result['name']);?>">
+					<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" value="<?=htmlspecialchars($result['description'], ENT_QUOTES, 'UTF-8');?>" required>
+						</div>
+					</div>
+					<div class="form-group">
+						<label class="control-label col-sm-2" for="domain"><?=$lang['edit']['kind'];?>:</label>
+						<div class="col-sm-10">
+							<select name="kind" id="kind" title="<?=$lang['edit']['select'];?>" required>
+								<option value="location" <?=($result['kind'] == "location") ? "selected" : null;?>>Location</option>
+								<option value="group" <?=($result['kind'] == "group") ? "selected" : null;?>>Group</option>
+								<option value="thing" <?=($result['kind'] == "thing") ? "selected" : null;?>>Thing</option>
+							</select>
+						</div>
+					</div>
+					<div class="form-group">
+						<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">
+							<div class="checkbox">
+							<label><input type="checkbox" name="multiple_bookings" <?=($result['multiple_bookings_int']=="1") ? "checked" : null;?>> <?=$lang['edit']['multiple_bookings'];?></label>
+							</div>
+						</div>
+					</div>
+					<div class="form-group">
+						<div class="col-sm-offset-2 col-sm-10">
+							<button type="submit" name="mailbox_edit_resource" class="btn btn-success btn-sm"><?=$lang['edit']['save'];?></button>
+						</div>
+					</div>
+				</form>
+			<?php
+			}
+			else {
+			?>
+				<div class="alert alert-info" role="alert"><?=$lang['info']['no_action'];?></div>
+			<?php
+			}
+	}
+	else {
+	?>
+		<div class="alert alert-info" role="alert"><?=$lang['info']['no_action'];?></div>
+	<?php
+	}
+}
+elseif (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "user")) {
+	if (isset($_GET['syncjob']) &&
+    is_numeric($_GET['syncjob'])) {
+			$id = $_GET["syncjob"];
+      $result = get_syncjob_details($id);
+      if (!empty($result)) {
+			?>
+				<h4><?=$lang['edit']['syncjob'];?></h4>
+				<form class="form-horizontal" role="form" method="post" action="<?=($FORM_ACTION == "previous") ? $_SESSION['return_to'] : null;?>">
+				<input type="hidden" name="id" value="<?=htmlspecialchars($result['id']);?>">
+					<div class="form-group">
+						<label class="control-label col-sm-2" for="host1"><?=$lang['edit']['hostname'];?></label>
+						<div class="col-sm-10">
+						<input type="text" class="form-control" name="host1" id="host1" value="<?=htmlspecialchars($result['host1'], ENT_QUOTES, 'UTF-8');?>">
+						</div>
+					</div>
+					<div class="form-group">
+						<label class="control-label col-sm-2" for="port1">Port</label>
+						<div class="col-sm-10">
+						<input type="number" class="form-control" name="port1" id="port1" min="1" max="65535" value="<?=htmlspecialchars($result['port1'], ENT_QUOTES, 'UTF-8');?>">
+						</div>
+					</div>
+					<div class="form-group">
+						<label class="control-label col-sm-2" for="user1"><?=$lang['edit']['username'];?></label>
+						<div class="col-sm-10">
+						<input type="text" class="form-control" name="user1" id="user1" value="<?=htmlspecialchars($result['user1'], ENT_QUOTES, 'UTF-8');?>">
+						</div>
+					</div>
+					<div class="form-group">
+						<label class="control-label col-sm-2" for="password1"><?=$lang['edit']['password'];?></label>
+						<div class="col-sm-10">
+						<input type="text" class="form-control" name="password1" id="password1" value="<?=htmlspecialchars($result['password1'], ENT_QUOTES, 'UTF-8');?>">
+						</div>
+					</div>
+					<div class="form-group">
+						<label class="control-label col-sm-2" for="enc1"><?=$lang['edit']['encryption'];?>:</label>
+						<div class="col-sm-10">
+							<select id="enc1" name="enc1">
+								<option <?=($result['enc1'] == "TLS") ? "selected" : null;?>>TLS</option>
+								<option <?=($result['enc1'] == "SSL") ? "selected" : null;?>>SSL</option>
+								<option <?=($result['enc1'] == "PLAIN") ? "selected" : null;?>>PLAIN</option>
+							</select>
+						</div>
+					</div>
+					<div class="form-group">
+						<label class="control-label col-sm-2" for="mins_interval"><?=$lang['edit']['mins_interval'];?></label>
+						<div class="col-sm-10">
+              <input type="number" class="form-control" name="mins_interval" min="10" max="3600" value="<?=htmlspecialchars($result['mins_interval'], ENT_QUOTES, 'UTF-8');?>" required>
+						</div>
+					</div>
+					<div class="form-group">
+						<label class="control-label col-sm-2" for="subfolder2"><?=$lang['edit']['subfolder2'];?></label>
+						<div class="col-sm-10">
+						<input type="text" class="form-control" name="subfolder2" id="subfolder2" value="<?=htmlspecialchars($result['subfolder2'], ENT_QUOTES, 'UTF-8');?>">
+						</div>
+					</div>
+					<div class="form-group">
+						<label class="control-label col-sm-2" for="maxage"><?=$lang['edit']['maxage'];?></label>
+						<div class="col-sm-10">
+						<input type="number" class="form-control" name="maxage" id="maxage" value="<?=htmlspecialchars($result['maxage'], ENT_QUOTES, 'UTF-8');?>">
+						</div>
+					</div>
+					<div class="form-group">
+						<label class="control-label col-sm-2" for="exclude"><?=$lang['edit']['exclude'];?></label>
+						<div class="col-sm-10">
+						<input type="text" class="form-control" name="exclude" id="exclude" value="<?=htmlspecialchars($result['exclude'], ENT_QUOTES, 'UTF-8');?>">
+						</div>
+					</div>
+					<div class="form-group">
+						<div class="col-sm-offset-2 col-sm-10">
+							<div class="checkbox">
+							<label><input type="checkbox" name="active" <?=($result['active']=="1") ? "checked" : "";?>> <?=$lang['edit']['active'];?></label>
+							</div>
+						</div>
+					</div>
+					<div class="form-group">
+						<div class="col-sm-offset-2 col-sm-10">
+							<button type="submit" name="edit_syncjob" value="1" 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
+	}
+}
+else {
+?>
+	<div class="alert alert-danger" role="alert"><?=$lang['danger']['access_denied'];?></div>
+<?php
+}
+?>
+				</div>
+			</div>
+		</div>
+	</div>
+<a href="<?=$_SESSION['return_to'];?>">&#8592; <?=$lang['edit']['previous'];?></a>
+</div> <!-- /container -->
+<?php
+require_once("inc/footer.inc.php");
+?>
diff --git a/data/web/favicon.png b/data/web/favicon.png
new file mode 100644
index 00000000..6390041d
Binary files /dev/null and b/data/web/favicon.png differ
diff --git a/data/web/img/cow_mailcow.svg b/data/web/img/cow_mailcow.svg
new file mode 100644
index 00000000..bea3f826
--- /dev/null
+++ b/data/web/img/cow_mailcow.svg
@@ -0,0 +1,197 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Generator: Adobe Illustrator 17.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   version="1.1"
+   id="Layer_1"
+   x="0px"
+   y="0px"
+   width="446.22601"
+   height="396.02499"
+   viewBox="0 0 446.226 396.02499"
+   enable-background="new 0 0 1600 1200"
+   xml:space="preserve"
+   inkscape:version="0.91 r13725"
+   sodipodi:docname="cow_mailcow.svg"><metadata
+     id="metadata144"><rdf:RDF><cc:Work
+         rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title /></cc:Work></rdf:RDF></metadata><defs
+     id="defs142" /><sodipodi:namedview
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1"
+     objecttolerance="10"
+     gridtolerance="10"
+     guidetolerance="10"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:window-width="1097"
+     inkscape:window-height="1138"
+     id="namedview140"
+     showgrid="false"
+     inkscape:zoom="1.1125147"
+     inkscape:cx="261.00704"
+     inkscape:cy="233.97883"
+     inkscape:window-x="814"
+     inkscape:window-y="0"
+     inkscape:window-maximized="0"
+     inkscape:current-layer="Layer_1"
+     fit-margin-top="0"
+     fit-margin-left="0"
+     fit-margin-right="0"
+     fit-margin-bottom="0" /><g
+     id="g3"
+     transform="translate(-576.88698,-401.988)"><g
+       id="g5"><g
+         id="g7"><g
+           id="email"><path
+             d="m 890.306,557.81 29.26,11.373 0,172.027 c 0,9.753 -7.895,17.649 -17.638,17.649 l -235.998,0 c -9.743,0 -17.638,-7.896 -17.638,-17.649 l 0,-172.026 29.259,-8.937"
+             id="path10"
+             inkscape:connector-curvature="0"
+             style="fill:#5a3620" /><path
+             d="M 758.871,656.221 649.49,747.45 c 2.507,6.648 8.901,11.409 16.44,11.409 l 235.998,0 c 7.536,0 13.933,-4.761 16.444,-11.409 l -107.402,-91.229 -52.099,0 z"
+             id="path12"
+             inkscape:connector-curvature="0"
+             style="fill:#fee70f;fill-opacity:0.89499998" /><g
+             id="g14"><path
+               d="m 810.391,656.686 107.981,90.764 c -0.331,0.881 -0.744,1.726 -1.205,2.536 l 0.028,0.035 c 1.501,-2.596 2.371,-5.594 2.371,-8.81 l 0,-172.004 -109.175,87.479 z"
+               id="path16"
+               inkscape:connector-curvature="0"
+               style="fill:#f9e82d;fill-opacity:1" /><path
+               d="m 649.49,747.45 108.864,-90.764 -110.061,-87.479 0,172.003 c 0,3.216 0.876,6.214 2.367,8.81 l 0.039,-0.035 c -0.466,-0.809 -0.877,-1.654 -1.209,-2.535 z"
+               id="path18"
+               inkscape:connector-curvature="0"
+               style="fill:#f9e82d;fill-opacity:1" /></g></g><path
+           d="m 961.81,681.214 c 0,0 -15.232,16.783 -42.244,14.73 l 0,28.14 c 13.328,-5.185 47.061,-20.036 56.854,-40.809 l -14.61,-2.061 z"
+           id="path20"
+           inkscape:connector-curvature="0"
+           style="fill:#b58765" /><path
+           d="m 984.594,658.413 c 3.59,-9.156 7.701,-11 9.346,-11.346 -49.276,4.542 -32.99,38.693 -32.99,38.693 0,0 6.229,14.728 26.532,13.892 27.063,0.461 35.631,-50.166 35.631,-50.166 -6.654,11.655 -26.404,9.876 -38.519,8.927 z"
+           id="path22"
+           inkscape:connector-curvature="0"
+           style="fill:#fef3df" /><ellipse
+           cx="787.75098"
+           cy="788.54498"
+           rx="210.864"
+           ry="9.4680004"
+           id="ellipse24"
+           style="fill:#f1f2f2" /><path
+           d="m 783.931,446.247 c -66.396,0 -120.223,53.827 -120.223,120.223 0,66.396 53.827,120.221 120.223,120.221 66.397,0 120.222,-53.825 120.222,-120.221 0,-66.395 -53.825,-120.223 -120.222,-120.223 z m -11.96,215.702 c -53.009,0 -95.982,-43.855 -95.982,-97.953 0,-54.098 42.973,-97.952 95.982,-97.952 53.007,0 95.98,43.855 95.98,97.952 -10e-4,54.098 -42.973,97.953 -95.98,97.953 z"
+           id="path26"
+           inkscape:connector-curvature="0"
+           style="opacity:0.1;fill:#3d5263" /><g
+           id="g28"><g
+             id="g30"><polyline
+               points="691.144,492.5 673.257,540.276 686.55,605.582 712.496,631.852      "
+               id="polyline32"
+               style="fill:#3d5263" /><g
+               id="g34"><g
+                 id="g36"><polyline
+                   points="658.248,450.81 673.501,487.076 693.836,496.903 724.04,458.731        "
+                   id="polyline38"
+                   style="fill:#fef3df" /><g
+                   id="g40"><path
+                     d="m 710.634,473.205 c 0,0 -22.482,-25.556 -49.793,-18.975 0,0 4.667,34.118 46.349,44.019 l 2.61,8.533 c 0,0 -65.612,-9.689 -59.339,-67.593 0,0 49.008,-19.884 72.598,15.106"
+                     id="path42"
+                     inkscape:connector-curvature="0"
+                     style="fill:#b58765" /><polyline
+                     points="909.697,450.81 894.447,487.076 874.114,496.903 843.907,458.731         "
+                     id="polyline44"
+                     style="fill:#fef3df" /><path
+                     d="m 857.314,473.205 c 0,0 22.48,-25.556 49.79,-18.975 0,0 -4.664,34.118 -46.347,44.019 l -2.613,8.533 c 0,0 65.611,-9.689 59.339,-67.593 0,0 -49.006,-19.884 -72.6,15.106"
+                     id="path46"
+                     inkscape:connector-curvature="0"
+                     style="fill:#b58765" /></g></g><path
+                 d="m 726.619,647.067 55.945,0 16.40428,-204.81407 c -55.814,0 -112.41728,30.01707 -112.41728,77.85207 0,1.454 0.085,2.787 0.121,4.175 0.127,3.934 0.448,7.585 0.856,11.135 1.689,14.816 5.451,27.177 8.461,43.383 1.452,7.831 5.002,23.374 5.002,23.374 0.056,0.408 0.165,0.804 0.224,1.211 2.535,16.546 11.832,32.027 25.404,43.684 z"
+                 id="path48"
+                 inkscape:connector-curvature="0"
+                 style="fill:#b58765"
+                 sodipodi:nodetypes="cccscccccc" /><path
+                 d="m 781.992,433.489 0,213.577 55.944,0 c 13.572,-11.657 22.867,-27.138 25.406,-43.684 0.058,-0.407 0.163,-0.803 0.221,-1.211 0,0 3.549,-15.543 5.002,-23.374 3.011,-16.206 6.774,-28.567 8.464,-43.381 0.405,-3.552 0.724,-7.203 0.846,-11.137 0.042,-1.388 0.126,-2.721 0.126,-4.175 0,-47.834 -40.191,-86.615 -96.009,-86.615 z"
+                 id="path50"
+                 inkscape:connector-curvature="0"
+                 style="fill:#b58765" /><g
+                 id="g52"><g
+                   id="g54"><path
+                     d="m 860.944,613.502 c 0,28.321 -35.091,51.289 -78.383,51.289 -43.299,0 -78.388,-22.968 -78.388,-51.289 0,-28.325 35.089,-51.289 78.388,-51.289 43.292,0 78.383,22.964 78.383,51.289 z"
+                     id="path56"
+                     inkscape:connector-curvature="0"
+                     style="fill:#fef3df" /></g></g><g
+                 id="g58"><g
+                   id="g60"><g
+                     id="g62"><path
+                       d="m 747.044,605.582 c 0,6.215 -5.04,11.256 -11.261,11.256 -6.21,0 -11.253,-5.041 -11.253,-11.256 0,-6.223 5.043,-11.257 11.253,-11.257 6.22,0 11.261,5.034 11.261,11.257 z"
+                       id="path64"
+                       inkscape:connector-curvature="0"
+                       style="fill:#5a3620" /></g></g><g
+                   id="g66"><g
+                     id="g68"><path
+                       d="m 840.856,605.582 c 0,6.215 -5.037,11.256 -11.257,11.256 -6.218,0 -11.259,-5.041 -11.259,-11.256 0,-6.223 5.041,-11.257 11.259,-11.257 6.22,0 11.257,5.034 11.257,11.257 z"
+                       id="path70"
+                       inkscape:connector-curvature="0"
+                       style="fill:#5a3620" /></g></g></g><g
+                 id="g72"><path
+                   d="m 875.228,525.835 c 0.354,-3.113 0.634,-6.311 0.743,-9.754 0.035,-1.218 0.109,-2.384 0.109,-3.661 0,-40.785 -33.369,-74.043 -80.237,-75.775 l -7.335,0.005 c -0.003,0 -0.003,0 -0.006,0 -0.007,0.018 -28.632,88.422 76.583,140.268 0.946,-4.317 2.078,-9.585 2.73,-13.088 2.64,-14.196 5.934,-25.021 7.413,-37.995 z"
+                   id="path74"
+                   inkscape:connector-curvature="0"
+                   style="fill:#87654a" /></g><g
+                 id="g76"><g
+                   id="g78"><g
+                     id="g80"><g
+                       id="g82"><path
+                         d="m 843.907,519.681 c 0,6.964 -5.65,12.611 -12.618,12.611 -6.963,0 -12.614,-5.646 -12.614,-12.611 0,-6.97 5.651,-12.614 12.614,-12.614 6.968,0 12.618,5.644 12.618,12.614 z"
+                         id="path84"
+                         inkscape:connector-curvature="0"
+                         style="fill:#5a3620" /></g></g></g><g
+                   id="g86"><g
+                     id="g88"><g
+                       id="g90"><path
+                         d="m 752.028,519.681 c 0,6.964 -5.649,12.611 -12.612,12.611 -6.969,0 -12.612,-5.646 -12.612,-12.611 0,-6.97 5.642,-12.614 12.612,-12.614 6.964,0 12.612,5.644 12.612,12.614 z"
+                         id="path92"
+                         inkscape:connector-curvature="0"
+                         style="fill:#5a3620" /></g></g></g><g
+                   id="g94"><g
+                     id="g96"><path
+                       d="m 748.75,515.894 c 0,2.558 -2.071,4.629 -4.63,4.629 -2.558,0 -4.633,-2.072 -4.633,-4.629 0,-2.552 2.076,-4.626 4.633,-4.626 2.559,0 4.63,2.073 4.63,4.626 z"
+                       id="path98"
+                       inkscape:connector-curvature="0"
+                       style="fill:#ffffff" /></g></g><g
+                   id="g100"><g
+                     id="g102"><path
+                       d="m 839.771,515.894 c 0,2.558 -2.073,4.629 -4.629,4.629 -2.558,0 -4.631,-2.072 -4.631,-4.629 0,-2.552 2.072,-4.626 4.631,-4.626 2.555,0 4.629,2.073 4.629,4.626 z"
+                       id="path104"
+                       inkscape:connector-curvature="0"
+                       style="fill:#ffffff" /></g></g></g></g><path
+               d="m 734.557,443.625 c 0,0 -18.236,-25.199 0,-41.637 0,0 13.125,32.012 40.242,31.502"
+               id="path106"
+               inkscape:connector-curvature="0"
+               style="fill:#fef3df" /><path
+               d="m 834.496,443.625 c 0,0 18.236,-25.199 0,-41.637 0,0 -13.126,32.012 -40.242,31.502"
+               id="path108"
+               inkscape:connector-curvature="0"
+               style="fill:#fef3df" /><path
+               d="m 786.264,431.965 c -66.396,0 -120.223,53.827 -120.223,120.223 0,66.396 53.827,120.221 120.223,120.221 66.397,0 120.222,-53.825 120.222,-120.221 10e-4,-66.395 -53.825,-120.223 -120.222,-120.223 z m -11.96,215.702 c -53.009,0 -95.982,-43.855 -95.982,-97.953 0,-54.098 42.973,-97.952 95.982,-97.952 53.007,0 95.979,43.855 95.979,97.952 0,54.098 -42.972,97.953 -95.979,97.953 z"
+               id="path110"
+               inkscape:connector-curvature="0"
+               style="fill:#f1f2f2" /></g><g
+             id="g112"><path
+               d="m 781.737,436.751 c 66.396,0 120.221,53.827 120.221,120.223 0,30.718 -11.526,58.74 -30.482,79.991 21.636,-21.74 35.01,-51.708 35.01,-84.803 0,-66.395 -53.825,-120.222 -120.222,-120.222 -35.678,0 -67.721,15.549 -89.739,40.233 21.772,-21.879 51.91,-35.422 85.212,-35.422 z"
+               id="path114"
+               inkscape:connector-curvature="0"
+               style="fill:#ffffff" /></g></g><path
+           d="m 919.566,695.944 c 0,0 7.562,0.712 13.317,-0.502 l 13.013,16.12 c 0,0 -17.639,9.525 -26.33,12.523 l 0,-28.141 z"
+           id="path116"
+           inkscape:connector-curvature="0"
+           style="opacity:0.1;fill:#3d5263" /></g><path
+         d="m 648.292,659.614 0,81.645 c 0,9.72 7.88,17.6 17.6,17.6 l 236.073,0 c 9.72,0 17.6,-7.88 17.6,-17.6 l 0,-24.902 c 10e-4,0 -175.814,35.524 -271.273,-56.743 z"
+         id="path124"
+         inkscape:connector-curvature="0"
+         style="opacity:0.1;fill:#3d5263" /></g><g
+       id="g126" /></g></svg>
\ No newline at end of file
diff --git a/data/web/img/yubi.ico b/data/web/img/yubi.ico
new file mode 100644
index 00000000..126c2b17
Binary files /dev/null and b/data/web/img/yubi.ico differ
diff --git a/data/web/inc/footer.inc.php b/data/web/inc/footer.inc.php
new file mode 100644
index 00000000..5523ec58
--- /dev/null
+++ b/data/web/inc/footer.inc.php
@@ -0,0 +1,225 @@
+<?php
+include("inc/tfa_modals.php");
+if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == "admin"):
+?>
+<div id="RestartSOGo" class="modal fade" role="dialog">
+	<div class="modal-dialog">
+		<div class="modal-content">
+		<div class="modal-header">
+			<button type="button" class="close" data-dismiss="modal">&times;</button>
+			<h4 class="modal-title"><?=$lang['footer']['restart_sogo'];?></h4>
+		</div>
+		<div class="modal-body">
+			<p><?=$lang['footer']['restart_sogo_info'];?></p>
+			<hr />
+			<button class="btn btn-md btn-primary" id="triggerRestartSogo"><?=$lang['footer']['restart_now'];?></button>
+			<br /><br />
+			<div id="statusTriggerRestartSogo"></div>
+		</div>
+		</div>
+	</div>
+</div>
+<?php
+endif;
+?>
+<div style="margin-bottom:100px"></div>
+<script src="//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.6/js/bootstrap.min.js"></script>
+<script src="/js/bootstrap-switch.min.js"></script>
+<script src="/js/bootstrap-slider.min.js"></script>
+<script src="/js/bootstrap-select.min.js"></script>
+<script src="/js/u2f-api.js"></script>
+<script>
+// Select language and reopen active URL without POST
+function setLang(sel) {
+	$.post( "<?=$_SERVER['REQUEST_URI'];?>", {lang: sel} );
+	window.location.href = window.location.pathname + window.location.search;
+}
+
+$(document).ready(function() {
+  // Confirm TFA modal
+  <?php if (isset($_SESSION['pending_tfa_method'])):?>
+  $('#ConfirmTFAModal').modal({
+    backdrop: 'static',
+    keyboard: false
+  }); 
+  $('#ConfirmTFAModal').on('shown.bs.modal', function(){
+      $(this).find('#token').focus();
+      // If U2F
+      if(document.getElementById("u2f_auth_data") !== null) {
+        $.ajax({
+          type: "GET",
+          cache: false,
+          dataType: 'script',
+          url: "json_api.php",
+          data: {
+            'action':'get_u2f_auth_challenge',
+            'object':'<?=(isset($_SESSION['pending_mailcow_cc_username'])) ? $_SESSION['pending_mailcow_cc_username'] : null;?>',
+          },
+          success: function(data){
+            data;
+          }
+        });
+        setTimeout(function() {
+          console.log("sign: ", req);
+          u2f.sign(req, function(data) {
+            var form = document.getElementById('u2f_auth_form');
+            var auth = document.getElementById('u2f_auth_data');
+            console.log("Authenticate callback", data);
+            auth.value = JSON.stringify(data);
+            form.submit();
+          });
+        }, 1000);
+      }
+  });
+  <?php endif; ?>
+
+  // Set TFA modals
+
+  $('#selectTFA').change(function () {
+    if ($(this).val() == "yubi_otp") {
+      $('#YubiOTPModal').modal('show');
+      $("option:selected").prop("selected", false);
+    }
+    if ($(this).val() == "u2f") {
+      $('#U2FModal').modal('show');
+      $("option:selected").prop("selected", false);
+      $.ajax({
+        type: "GET",
+        cache: false,
+        dataType: 'script',
+        url: "json_api.php",
+        data: {
+          'action':'get_u2f_reg_challenge',
+          'object':'<?=(isset($_SESSION['mailcow_cc_username'])) ? $_SESSION['mailcow_cc_username'] : null;?>',
+        },
+        success: function(data){
+          data;
+        }
+      });
+      setTimeout(function() {
+        console.log("Register: ", req);
+        u2f.register([req], sigs, function(data) {
+          var form  = document.getElementById('u2f_reg_form');
+          var reg   = document.getElementById('u2f_register_data');
+          console.log("Register callback", data);
+          if (data.errorCode && data.errorCode != 0) {
+            var u2f_return_code = document.getElementById('u2f_return_code');
+            u2f_return_code.style.display = u2f_return_code.style.display === 'none' ? '' : null;
+            if (data.errorCode == "4") { data.errorCode = "4 - The presented device is not eligible for this request. For a registration request this may mean that the token is already registered, and for a sign request it may mean that the token does not know the presented key handle"; }
+            u2f_return_code.innerHTML = 'Error code: ' + data.errorCode;
+            return;
+          }
+          reg.value = JSON.stringify(data);
+          form.submit();
+        });
+      }, 1000);
+    }
+    if ($(this).val() == "none") {
+      $('#DisableTFAModal').modal('show');
+      $("option:selected").prop("selected", false);
+    }
+  });
+
+  // Activate tooltips
+  $(function () {
+    $('[data-toggle="tooltip"]').tooltip()
+  })
+	// Hide alerts after n seconds
+	$("#alert-fade").fadeTo(7000, 500).slideUp(500, function(){
+		$("#alert-fade").alert('close');
+	});
+
+	// Remember last navigation pill
+	(function () {
+		'use strict';
+		$('a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
+			var id = $(this).parents('[role="tablist"]').attr('id');
+			var key = 'lastTag';
+			if (id) {
+				key += ':' + id;
+			}
+			localStorage.setItem(key, $(e.target).attr('href'));
+		});
+		$('[role="tablist"]').each(function (idx, elem) {
+			var id = $(elem).attr('id');
+			var key = 'lastTag';
+			if (id) {
+				key += ':' + id;
+			}
+			var lastTab = localStorage.getItem(key);
+			if (lastTab) {
+				$('[href="' + lastTab + '"]').tab('show');
+			}
+		});
+	})();
+
+	// Disable submit after submitting form
+	$('form').submit(function() {
+		if ($('form button[type="submit"]').data('submitted') == '1') {
+			return false;
+		} else {
+			$(this).find('button[type="submit"]').first().text('<?=$lang['footer']['loading'];?>');
+			$('form button[type="submit"]').attr('data-submitted', '1');
+			function disableF5(e) { if ((e.which || e.keyCode) == 116 || (e.which || e.keyCode) == 82) e.preventDefault(); };
+			$(document).on("keydown", disableF5);
+		}
+	});
+
+	// IE fix to hide scrollbars when table body is empty
+	$('tbody').filter(function (index) {
+		return $(this).children().length < 1;
+	}).remove();
+
+	// Init Bootstrap Selectpicker
+	$('select').selectpicker();
+
+	// Trigger SOGo restart
+	$('#triggerRestartSogo').click(function(){
+		$(this).prop("disabled",true);
+		$(this).html('<span class="glyphicon glyphicon-refresh glyphicon-spin"></span> ');
+		$('#statusTriggerRestartSogo').text('Stopping SOGo workers, this may take a while... ');
+		$.ajax({
+			method: 'get',
+			url: 'call_sogo_ctrl.php',
+			data: {
+				'ajax': true,
+				'ACTION': 'stop'
+			},
+			success: function(data) {
+				$('#statusTriggerRestartSogo').append(data);
+				$('#statusTriggerRestartSogo').append('<br />Starting SOGo... ');
+				$.ajax({
+					method: 'get',
+					url: 'call_sogo_ctrl.php',
+					data: {
+						'ajax': true,
+						'ACTION': 'start'
+					},
+					success: function(data) {
+						$('#statusTriggerRestartSogo').append(data);
+						$('#triggerRestartSogo').html('<span class="glyphicon glyphicon-ok"></span> ');
+					}
+				});
+			}
+		});
+	});
+});
+</script>
+<?php
+if (isset($_SESSION['return'])):
+?>
+<div class="container">
+	<div style="position:fixed;bottom:8px;right:25px;min-width:300px;max-width:350px;z-index:2000">
+		<div <?=($_SESSION['return']['type'] == 'danger') ? null : 'id="alert-fade"'?> class="alert alert-<?=$_SESSION['return']['type'];?>" role="alert">
+		<a href="#" class="close" data-dismiss="alert"> &times;</a>
+		<?=htmlspecialchars($_SESSION['return']['msg']);?>
+		</div>
+	</div>
+</div>
+<?php
+unset($_SESSION['return']);
+endif;
+?>
+</body>
+</html>
+<?php $stmt = null; $pdo = null; ?>
diff --git a/data/web/inc/functions.inc.php b/data/web/inc/functions.inc.php
new file mode 100644
index 00000000..fef6760e
--- /dev/null
+++ b/data/web/inc/functions.inc.php
@@ -0,0 +1,4922 @@
+<?php
+function hash_password($password) {
+	$salt_str = bin2hex(openssl_random_pseudo_bytes(8));
+	return "{SSHA256}".base64_encode(hash('sha256', $password . $salt_str, true) . $salt_str);
+}
+function hasDomainAccess($username, $role, $domain) {
+	global $pdo;
+	if (!filter_var($username, FILTER_VALIDATE_EMAIL) && !ctype_alnum(str_replace(array('_', '.', '-'), '', $username))) {
+		return false;
+	}
+	if (empty($domain) || !is_valid_domain_name($domain)) {
+		return false;
+	}
+	if ($role != 'admin' && $role != 'domainadmin' && $role != 'user') {
+		return false;
+	}
+	try {
+		$stmt = $pdo->prepare("SELECT `domain` FROM `domain_admins`
+		WHERE (
+			`active`='1'
+			AND `username` = :username
+			AND (`domain` = :domain1 OR `domain` = (SELECT `target_domain` FROM `alias_domain` WHERE `alias_domain` = :domain2))
+		)
+    OR 'admin' = :role");
+		$stmt->execute(array(':username' => $username, ':domain1' => $domain, ':domain2' => $domain, ':role' => $role));
+		$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
+	}
+  catch(PDOException $e) {
+    $_SESSION['return'] = array(
+      'type' => 'danger',
+      'msg' => 'MySQL: '.$e
+    );
+    return false;
+  }
+	if (!empty($num_results)) {
+		return true;
+	}
+	return false;
+}
+function hasMailboxObjectAccess($username, $role, $object) {
+	global $pdo;
+	if (!filter_var($username, FILTER_VALIDATE_EMAIL) && !ctype_alnum(str_replace(array('_', '.', '-'), '', $username))) {
+		return false;
+	}
+	if ($role != 'admin' && $role != 'domainadmin' && $role != 'user') {
+		return false;
+	}
+	if ($username == $object) {
+		return true;
+	}
+	try {
+		$stmt = $pdo->prepare("SELECT `domain` FROM `mailbox` WHERE `username` = :object");
+		$stmt->execute(array(':object' => $object));
+		$row = $stmt->fetch(PDO::FETCH_ASSOC);
+    if (isset($row['domain']) && hasDomainAccess($username, $role, $row['domain'])) {
+      return true;
+    }
+	}
+  catch(PDOException $e) {
+		error_log($e);
+		return false;
+	}
+	return false;
+}
+function init_db_schema() {
+  // This will be much better in future releases...
+	global $pdo;
+	try {
+		$stmt = $pdo->prepare("SELECT NULL FROM `admin`, `imapsync`, `tfa`");
+		$stmt->execute();
+	}
+	catch (Exception $e) {
+		$lines = file('/web/inc/init.sql');
+		$data = '';
+		foreach ($lines as $line) {
+			if (substr($line, 0, 2) == '--' || $line == '') {
+				continue;
+			}
+			$data .= $line;
+			if (substr(trim($line), -1, 1) == ';') {
+				$pdo->query($data);
+				$data = '';
+			}
+		}
+    // Create index if not exists
+		$stmt = $pdo->query("SHOW INDEX FROM sogo_acl WHERE KEY_NAME = 'sogo_acl_c_folder_id_idx'");
+		$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
+		if ($num_results == 0) {
+			$pdo->query("CREATE INDEX sogo_acl_c_folder_id_idx ON sogo_acl(c_folder_id)");
+		}
+		$stmt = $pdo->query("SHOW INDEX FROM sogo_acl WHERE KEY_NAME = 'sogo_acl_c_uid_idx'");
+		$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
+		if ($num_results == 0) {
+			$pdo->query("CREATE INDEX sogo_acl_c_uid_idx ON sogo_acl(c_uid)");
+		}
+		$_SESSION['return'] = array(
+			'type' => 'success',
+			'msg' => 'Database initialization completed.'
+		);
+	}
+  // Add newly added columns
+  $stmt = $pdo->query("SHOW COLUMNS FROM `mailbox` LIKE 'kind'");
+  $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
+  if ($num_results == 0) {
+    $pdo->query("ALTER TABLE `mailbox` ADD `kind` VARCHAR(100) NOT NULL DEFAULT ''");
+  }
+  $stmt = $pdo->query("SHOW COLUMNS FROM `mailbox` LIKE 'multiple_bookings'");
+  $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
+  if ($num_results == 0) {
+    $pdo->query("ALTER TABLE `mailbox` ADD `multiple_bookings` tinyint(1) NOT NULL DEFAULT '0'");
+  }
+  $stmt = $pdo->query("SHOW COLUMNS FROM `mailbox` LIKE 'wants_tagged_subject'");
+  $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
+  if ($num_results == 0) {
+    $pdo->query("ALTER TABLE `mailbox` ADD `wants_tagged_subject` tinyint(1) NOT NULL DEFAULT '0'");
+  }
+  $stmt = $pdo->query("SHOW COLUMNS FROM `tfa` LIKE 'key_id'");
+  $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
+  if ($num_results == 0) {
+    $pdo->query("ALTER TABLE `tfa` ADD `key_id` VARCHAR(255) DEFAULT 'unidentified'");
+  }
+}
+function verify_ssha256($hash, $password) {
+	// Remove tag if any
+	$hash = ltrim($hash, '{SSHA256}');
+	// Decode hash
+	$dhash = base64_decode($hash);
+	// Get first 32 bytes of binary which equals a SHA256 hash
+	$ohash = substr($dhash, 0, 32);
+	// Remove SHA256 hash from decoded hash to get original salt string
+	$osalt = str_replace($ohash, '', $dhash);
+	// Check single salted SHA256 hash against extracted hash
+	if (hash('sha256', $password . $osalt, true) == $ohash) {
+		return true;
+	}
+	else {
+		return false;
+	}
+}
+function doveadm_authenticate($hash, $algorithm, $password) {
+	$descr = array(0 => array('pipe', 'r'), 1 => array('pipe', 'w'), 2 => array('pipe', 'w'));
+	$pipes = array();
+	$process = proc_open("/usr/bin/doveadm pw -s ".$algorithm." -t '".$hash."'", $descr, $pipes);
+	if (is_resource($process)) {
+		fputs($pipes[0], $password);
+		fclose($pipes[0]);
+		while ($f = fgets($pipes[1])) {
+			if (preg_match('/(verified)/', $f)) {
+				proc_close($process);
+				return true;
+			}
+			return false;
+		}
+		fclose($pipes[1]);
+		while ($f = fgets($pipes[2])) {
+			proc_close($process);
+			return false;
+		}
+		fclose($pipes[2]);
+		proc_close($process);
+	}
+	return false;
+}
+function check_login($user, $pass) {
+	global $pdo;
+	if (!filter_var($user, FILTER_VALIDATE_EMAIL) && !ctype_alnum(str_replace(array('_', '.', '-'), '', $user))) {
+		return false;
+	}
+	$user = strtolower(trim($user));
+	$stmt = $pdo->prepare("SELECT `password` FROM `admin`
+			WHERE `superadmin` = '1'
+			AND `username` = :user");
+	$stmt->execute(array(':user' => $user));
+	$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
+	foreach ($rows as $row) {
+		if (verify_ssha256($row['password'], $pass)) {
+      if (get_tfa($user)['name'] != "none") {
+        $_SESSION['pending_mailcow_cc_username'] = $user;
+        $_SESSION['pending_mailcow_cc_role'] = "admin";
+        $_SESSION['pending_tfa_method'] = get_tfa($user)['name'];
+        unset($_SESSION['ldelay']);
+        return "pending";
+      }
+      else {
+        unset($_SESSION['ldelay']);
+        return "admin";
+      }
+		}
+	}
+	$stmt = $pdo->prepare("SELECT `password` FROM `admin`
+			WHERE `superadmin` = '0'
+			AND `active`='1'
+			AND `username` = :user");
+	$stmt->execute(array(':user' => $user));
+	$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
+	foreach ($rows as $row) {
+		if (verify_ssha256($row['password'], $pass) !== false) {
+      if (get_tfa($user)['name'] != "none") {
+        $_SESSION['pending_mailcow_cc_username'] = $user;
+        $_SESSION['pending_mailcow_cc_role'] = "domainadmin";
+        $_SESSION['pending_tfa_method'] = get_tfa($user)['name'];
+        unset($_SESSION['ldelay']);
+        return "pending";
+      }
+      else {
+        unset($_SESSION['ldelay']);
+        $stmt = $pdo->prepare("UPDATE `tfa` SET `active`='1' WHERE `username` = :user");
+        $stmt->execute(array(':user' => $user));
+        return "domainadmin";
+      }
+		}
+	}
+	$stmt = $pdo->prepare("SELECT `password` FROM `mailbox`
+			WHERE `kind` NOT REGEXP 'location|thing|group'
+        AND `active`='1'
+        AND `username` = :user");
+	$stmt->execute(array(':user' => $user));
+	$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
+	foreach ($rows as $row) {
+		if (verify_ssha256($row['password'], $pass) !== false) {
+			unset($_SESSION['ldelay']);
+			return "user";
+		}
+	}
+	if (!isset($_SESSION['ldelay'])) {
+		$_SESSION['ldelay'] = "0";
+	}
+	elseif (!isset($_SESSION['mailcow_cc_username'])) {
+		$_SESSION['ldelay'] = $_SESSION['ldelay']+0.5;
+	}
+	sleep($_SESSION['ldelay']);
+}
+function formatBytes($size, $precision = 2) {
+	if(!is_numeric($size)) {
+		return "0";
+	}
+	$base = log($size, 1024);
+	$suffixes = array(' Byte', ' KiB', ' MiB', ' GiB', ' TiB');
+	if ($size == "0") {
+		return "0";
+	}
+	return round(pow(1024, $base - floor($base)), $precision) . $suffixes[floor($base)];
+}
+function edit_admin_account($postarray) {
+	global $lang;
+	global $pdo;
+	if ($_SESSION['mailcow_cc_role'] != "admin") {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => sprintf($lang['danger']['access_denied'])
+		);
+		return false;
+	}
+	$username       = $postarray['admin_user'];
+	$username_now   = $_SESSION['mailcow_cc_username'];
+
+	if (!ctype_alnum(str_replace(array('_', '.', '-'), '', $username)) || empty ($username)) {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => sprintf($lang['danger']['username_invalid'])
+		);
+		return false;
+	}
+
+	if (!empty($postarray['admin_pass']) && !empty($postarray['admin_pass2'])) {
+		if ($postarray['admin_pass'] != $postarray['admin_pass2']) {
+			$_SESSION['return'] = array(
+				'type' => 'danger',
+				'msg' => sprintf($lang['danger']['password_mismatch'])
+			);
+			return false;
+		}
+		$password_hashed = hash_password($postarray['admin_pass']);
+		try {
+			$stmt = $pdo->prepare("UPDATE `admin` SET 
+				`modified` = :modified,
+				`password` = :password_hashed,
+				`username` = :username1
+					WHERE `username` = :username2");
+			$stmt->execute(array(
+				':password_hashed' => $password_hashed,
+				':modified' => date('Y-m-d H:i:s'),
+				':username1' => $username,
+				':username2' => $username_now
+			));
+		}
+		catch (PDOException $e) {
+			$_SESSION['return'] = array(
+				'type' => 'danger',
+				'msg' => 'MySQL: '.$e
+			);
+			return false;
+		}
+	}
+	else {
+		try {
+			$stmt = $pdo->prepare("UPDATE `admin` SET 
+				`modified` = :modified,
+				`username` = :username1
+					WHERE `username` = :username2");
+			$stmt->execute(array(
+				':username1' => $username,
+				':modified' => date('Y-m-d H:i:s'),
+				':username2' => $username_now
+			));
+		}
+		catch (PDOException $e) {
+			$_SESSION['return'] = array(
+				'type' => 'danger',
+				'msg' => 'MySQL: '.$e
+			);
+			return false;
+		}
+	}
+	try {
+		$stmt = $pdo->prepare("UPDATE `domain_admins` SET `domain` = 'ALL', `username` = :username1 WHERE `username` = :username2");
+		$stmt->execute(array(':username1' => $username, ':username2' => $username_now));
+		$stmt = $pdo->prepare("UPDATE `tfa` SET `username` = :username1 WHERE `username` = :username2");
+		$stmt->execute(array(':username1' => $username, ':username2' => $username_now));
+	}
+	catch (PDOException $e) {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => 'MySQL: '.$e
+		);
+		return false;
+	}
+  $_SESSION['mailcow_cc_username'] = $username;
+	$_SESSION['return'] = array(
+		'type' => 'success',
+		'msg' => sprintf($lang['success']['admin_modified'])
+	);
+}
+function set_time_limited_aliases($postarray) {
+	global $lang;
+	global $pdo;
+  (isset($postarray['username'])) ? $username = $postarray['username'] : $username = $_SESSION['mailcow_cc_username'];
+
+  if ($_SESSION['mailcow_cc_role'] != "user" &&
+    $_SESSION['mailcow_cc_role'] != "admin") {
+      $_SESSION['return'] = array(
+        'type' => 'danger',
+        'msg' => sprintf($lang['danger']['access_denied'])
+      );
+      return false;
+  }
+  if (filter_var($username, FILTER_VALIDATE_EMAIL)) {
+    if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $username)) {
+      $_SESSION['return'] = array(
+        'type' => 'danger',
+        'msg' => sprintf($lang['danger']['access_denied'])
+      );
+      return false;
+    }
+  }
+
+	try {
+    $stmt = $pdo->prepare("SELECT `domain` FROM `mailbox` WHERE `username` = :username");
+    $stmt->execute(array(':username' => $username));
+    $domain = $stmt->fetch(PDO::FETCH_ASSOC)['domain'];
+  }
+  catch (PDOException $e) {
+    $_SESSION['return'] = array(
+      'type' => 'danger',
+      'msg' => 'MySQL: '.$e
+    );
+    return false;
+  }
+
+	switch ($postarray["set_time_limited_aliases"]) {
+		case "generate":
+			if (!is_numeric($postarray["validity"]) || $postarray["validity"] > 672) {
+				$_SESSION['return'] = array(
+					'type' => 'danger',
+					'msg' => sprintf($lang['danger']['validity_missing'])
+				);
+				return false;
+			}
+			$validity = strtotime("+".$postarray["validity"]." hour"); 
+			$letters = 'abcefghijklmnopqrstuvwxyz1234567890';
+			$random_name = substr(str_shuffle($letters), 0, 24);
+			try {
+				$stmt = $pdo->prepare("INSERT INTO `spamalias` (`address`, `goto`, `validity`) VALUES
+					(:address, :goto, :validity)");
+				$stmt->execute(array(
+					':address' => $random_name . '@' . $domain,
+					':goto' => $username,
+					':validity' => $validity
+				));
+			}
+			catch (PDOException $e) {
+				$_SESSION['return'] = array(
+					'type' => 'danger',
+					'msg' => 'MySQL: '.$e
+				);
+				return false;
+			}
+			$_SESSION['return'] = array(
+				'type' => 'success',
+				'msg' => sprintf($lang['success']['mailbox_modified'], htmlspecialchars($username))
+			);
+		break;
+		case "deleteall":
+			try {
+				$stmt = $pdo->prepare("DELETE FROM `spamalias` WHERE `goto` = :username");
+				$stmt->execute(array(
+					':username' => $username
+				));
+			}
+			catch (PDOException $e) {
+				$_SESSION['return'] = array(
+					'type' => 'danger',
+					'msg' => 'MySQL: '.$e
+				);
+				return false;
+			}	
+			$_SESSION['return'] = array(
+				'type' => 'success',
+				'msg' => sprintf($lang['success']['mailbox_modified'], htmlspecialchars($username))
+			);
+		break;
+		case "delete":
+			if (empty($postarray['item']) || !filter_var($postarray['item'], FILTER_VALIDATE_EMAIL)) {
+				$_SESSION['return'] = array(
+					'type' => 'danger',
+					'msg' => sprintf($lang['danger']['access_denied'])
+				);
+				return false;
+			}
+      $item	= $postarray['item'];
+			try {
+				$stmt = $pdo->prepare("DELETE FROM `spamalias` WHERE `goto` = :username AND `address` = :item");
+				$stmt->execute(array(
+					':username' => $username,
+					':item' => $item
+				));
+			}
+			catch (PDOException $e) {
+				$_SESSION['return'] = array(
+					'type' => 'danger',
+					'msg' => 'MySQL: '.$e
+				);
+				return false;
+			}	
+			$_SESSION['return'] = array(
+				'type' => 'success',
+				'msg' => sprintf($lang['success']['mailbox_modified'], htmlspecialchars($username))
+			);
+		break;
+		case "extendall":
+			try {
+				$stmt = $pdo->prepare("UPDATE `spamalias` SET `validity` = (`validity` + 3600) WHERE
+          `goto` = :username AND
+					`validity` >= :validity");
+				$stmt->execute(array(
+					':username' => $username,
+					':validity' => time(),
+				));
+			}
+			catch (PDOException $e) {
+				$_SESSION['return'] = array(
+					'type' => 'danger',
+					'msg' => 'MySQL: '.$e
+				);
+				return false;
+			}
+			$_SESSION['return'] = array(
+				'type' => 'success',
+				'msg' => sprintf($lang['success']['mailbox_modified'], htmlspecialchars($username))
+			);
+		break;
+		case "extend":
+			if (empty($postarray['item']) || !filter_var($postarray['item'], FILTER_VALIDATE_EMAIL)) {
+				$_SESSION['return'] = array(
+					'type' => 'danger',
+					'msg' => sprintf($lang['danger']['access_denied'])
+				);
+				return false;
+			}
+      $item	= $postarray['item'];
+			try {
+				$stmt = $pdo->prepare("UPDATE `spamalias` SET `validity` = (`validity` + 3600) WHERE 
+          `goto` = :username AND
+					`address` = :item AND
+					`validity` >= :validity");
+				$stmt->execute(array(
+					':username' => $username,
+					':item' => $item,
+					':validity' => time(),
+				));
+			}
+			catch (PDOException $e) {
+				$_SESSION['return'] = array(
+					'type' => 'danger',
+					'msg' => 'MySQL: '.$e
+				);
+				return false;
+			}
+			$_SESSION['return'] = array(
+				'type' => 'success',
+				'msg' => sprintf($lang['success']['mailbox_modified'], htmlspecialchars($username))
+			);
+		break;
+	}
+}
+function get_time_limited_aliases($username = null) {
+  // 'username' can be be set, if not, default to mailcow_cc_username
+  global $lang;
+	global $pdo;
+  $data = array();
+  if (isset($username) && filter_var($username, FILTER_VALIDATE_EMAIL)) {
+    if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $username)) {
+      $_SESSION['return'] = array(
+        'type' => 'danger',
+        'msg' => sprintf($lang['danger']['access_denied'])
+      );
+      return false;
+    }
+  }
+  else {
+    $username = $_SESSION['mailcow_cc_username'];
+  }
+  try {
+    $stmt = $pdo->prepare("SELECT `address`,
+      `goto`,
+      `validity`
+        FROM `spamalias`
+          WHERE `goto` = :username
+            AND `validity` >= :unixnow");
+    $stmt->execute(array(':username' => $username, ':unixnow' => time()));
+    $data = $stmt->fetchAll(PDO::FETCH_ASSOC);
+  }
+  catch(PDOException $e) {
+    $_SESSION['return'] = array(
+      'type' => 'danger',
+      'msg' => 'MySQL: '.$e
+    );
+  }
+  return $data;
+}
+function edit_user_account($postarray) {
+	global $lang;
+	global $pdo;
+  if (isset($postarray['username']) && filter_var($postarray['username'], FILTER_VALIDATE_EMAIL)) {
+    if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $postarray['username'])) {
+      $_SESSION['return'] = array(
+        'type' => 'danger',
+        'msg' => sprintf($lang['danger']['access_denied'])
+      );
+      return false;
+    }
+    else {
+      $username = $postarray['username'];
+    }
+  }
+  else {
+    $username = $_SESSION['mailcow_cc_username'];
+  }
+	$password_old		= $postarray['user_old_pass'];
+
+	if (isset($postarray['user_new_pass']) && isset($postarray['user_new_pass2'])) {
+		$password_new	= $postarray['user_new_pass'];
+		$password_new2	= $postarray['user_new_pass2'];
+	}
+
+	$stmt = $pdo->prepare("SELECT `password` FROM `mailbox`
+			WHERE `kind` NOT REGEXP 'location|thing|group'
+        AND `username` = :user");
+	$stmt->execute(array(':user' => $username));
+	$row = $stmt->fetch(PDO::FETCH_ASSOC);
+  if (!verify_ssha256($row['password'], $password_old)) {
+    $_SESSION['return'] = array(
+      'type' => 'danger',
+      'msg' => sprintf($lang['danger']['access_denied'])
+    );
+    return false;
+  }
+
+	if (isset($password_new) && isset($password_new2)) {
+		if (!empty($password_new2) && !empty($password_new)) {
+			if ($password_new2 != $password_new) {
+				$_SESSION['return'] = array(
+					'type' => 'danger',
+					'msg' => sprintf($lang['danger']['password_mismatch'])
+				);
+				return false;
+			}
+			if (strlen($password_new) < "6" ||
+				!preg_match('/[A-Za-z]/', $password_new) ||
+				!preg_match('/[0-9]/', $password_new)) {
+					$_SESSION['return'] = array(
+						'type' => 'danger',
+						'msg' => sprintf($lang['danger']['password_complexity'])
+					);
+					return false;
+			}
+			$password_hashed = hash_password($password_new);
+			try {
+				$stmt = $pdo->prepare("UPDATE `mailbox` SET `modified` = :modified, `password` = :password_hashed WHERE `username` = :username");
+				$stmt->execute(array(
+					':password_hashed' => $password_hashed,
+					':modified' => date('Y-m-d H:i:s'),
+					':username' => $username
+				));
+			}
+			catch (PDOException $e) {
+				$_SESSION['return'] = array(
+					'type' => 'danger',
+					'msg' => 'MySQL: '.$e
+				);
+				return false;
+			}
+		}
+	}
+	$_SESSION['return'] = array(
+		'type' => 'success',
+		'msg' => sprintf($lang['success']['mailbox_modified'], htmlspecialchars($username))
+	);
+}
+function get_spam_score($username = null) {
+	global $pdo;
+	$default = "5, 15";
+  if (isset($username) && filter_var($username, FILTER_VALIDATE_EMAIL)) {
+    if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $username)) {
+      return false;
+    }
+  }
+  else {
+    $username = $_SESSION['mailcow_cc_username'];
+  }
+	try {
+		$stmt = $pdo->prepare("SELECT `value` FROM `filterconf` WHERE `object` = :username AND
+			(`option` = 'lowspamlevel' OR `option` = 'highspamlevel')");
+		$stmt->execute(array(':username' => $username));
+		$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
+	}
+	catch(PDOException $e) {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => 'MySQL: '.$e
+		);
+		return false;
+	}
+	if (empty($num_results)) {
+		return $default;
+	}
+	else {
+		try {
+			$stmt = $pdo->prepare("SELECT `value` FROM `filterconf` WHERE `option` = 'highspamlevel' AND `object` = :username");
+			$stmt->execute(array(':username' => $username));
+			$highspamlevel = $stmt->fetch(PDO::FETCH_ASSOC);
+
+			$stmt = $pdo->prepare("SELECT `value` FROM `filterconf` WHERE `option` = 'lowspamlevel' AND `object` = :username");
+			$stmt->execute(array(':username' => $username));
+			$lowspamlevel = $stmt->fetch(PDO::FETCH_ASSOC);
+
+			return $lowspamlevel['value'].', '.$highspamlevel['value'];
+		}
+		catch(PDOException $e) {
+			$_SESSION['return'] = array(
+				'type' => 'danger',
+				'msg' => 'MySQL: '.$e
+			);
+			return false;
+		}
+	}
+}
+function edit_spam_score($postarray) {
+  // Array items
+  // 'username' can be set, defaults to mailcow_cc_username
+  // 'lowspamlevel'
+  // 'highspamlevel'
+	global $lang;
+	global $pdo;
+  if (isset($postarray['username']) && filter_var($postarray['username'], FILTER_VALIDATE_EMAIL)) {
+    if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $postarray['username'])) {
+      $_SESSION['return'] = array(
+        'type' => 'danger',
+        'msg' => sprintf($lang['danger']['access_denied'])
+      );
+      return false;
+    }
+    else {
+      $username = $postarray['username'];
+    }
+  }
+  else {
+    $username = $_SESSION['mailcow_cc_username'];
+  }
+	$lowspamlevel	= explode(',', $postarray['score'])[0];
+	$highspamlevel	= explode(',', $postarray['score'])[1];
+
+	if (!is_numeric($lowspamlevel) || !is_numeric($highspamlevel)) {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => sprintf($lang['danger']['access_denied'])
+		);
+		return false;
+	}
+	try {
+		$stmt = $pdo->prepare("DELETE FROM `filterconf` WHERE `object` = :username
+			AND (`option` = 'lowspamlevel' OR `option` = 'highspamlevel')");
+		$stmt->execute(array(
+			':username' => $username
+		));
+
+		$stmt = $pdo->prepare("INSERT INTO `filterconf` (`object`, `option`, `value`)
+			VALUES (:username, 'highspamlevel', :highspamlevel)");
+		$stmt->execute(array(
+			':username' => $username,
+			':highspamlevel' => $highspamlevel
+		));
+
+		$stmt = $pdo->prepare("INSERT INTO `filterconf` (`object`, `option`, `value`)
+			VALUES (:username, 'lowspamlevel', :lowspamlevel)");
+		$stmt->execute(array(
+			':username' => $username,
+			':lowspamlevel' => $lowspamlevel
+		));
+	}
+	catch (PDOException $e) {
+		$stmt = $pdo->prepare("DELETE FROM `filterconf` WHERE `object` = :username
+			AND (`option` = 'lowspamlevel' OR `option` = 'highspamlevel')");
+		$stmt->execute(array(
+			':username' => $username
+		));
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => 'MySQL: '.$e
+		);
+		return false;
+	}
+	$_SESSION['return'] = array(
+		'type' => 'success',
+		'msg' => sprintf($lang['success']['mailbox_modified'], $username)
+	);
+}
+function get_policy_list($object = null) {
+  // 'object' can be be set, if not, default to mailcow_cc_username
+	global $lang;
+	global $pdo;
+  if (isset($object)) {
+    if (!filter_var($object, FILTER_VALIDATE_EMAIL) && is_valid_domain_name($object)) {
+      $object = idn_to_ascii(strtolower(trim($object)));
+      if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $object)) {
+        $_SESSION['return'] = array(
+          'type' => 'danger',
+          'msg' => sprintf($lang['danger']['access_denied'])
+        );
+        return false;
+      }
+    }
+    elseif (filter_var($object, FILTER_VALIDATE_EMAIL)) {
+      if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $object)) {
+        $_SESSION['return'] = array(
+          'type' => 'danger',
+          'msg' => sprintf($lang['danger']['access_denied'])
+        );
+        return false;
+      }
+    }
+  }
+  else {
+     $object = $_SESSION['mailcow_cc_username'];
+  }
+  try {
+    // WHITELIST
+    $stmt = $pdo->prepare("SELECT `object`, `value`, `prefid` FROM `filterconf` WHERE `option`='whitelist_from' AND (`object` = :username OR `object` = SUBSTRING_INDEX(:username_domain, '@' ,-1))");
+    $stmt->execute(array(':username' => $object, ':username_domain' => $object));
+    $rows['whitelist'] = $stmt->fetchAll(PDO::FETCH_ASSOC);
+    // BLACKLIST
+    $stmt = $pdo->prepare("SELECT `object`, `value`, `prefid` FROM `filterconf` WHERE `option`='blacklist_from' AND (`object` = :username OR `object` = SUBSTRING_INDEX(:username_domain, '@' ,-1))");
+    $stmt->execute(array(':username' => $object, ':username_domain' => $object));
+    $rows['blacklist'] = $stmt->fetchAll(PDO::FETCH_ASSOC);
+  }
+  catch(PDOException $e) {
+    $_SESSION['return'] = array(
+      'type' => 'danger',
+      'msg' => 'MySQL: '.$e
+    );
+  }
+  return $rows;
+}
+function add_policy_list_item($postarray) {
+  // Array data
+  // Either 'domain' or 'username' can be be set
+  // If none of the above is set, default to mailcow_cc_username
+  //
+  // If 'delete_prefid' then delete item id
+	global $lang;
+	global $pdo;
+  (isset($postarray['username'])) ? $object = $postarray['username'] : null;
+  (isset($postarray['domain']))   ? $object = $postarray['domain'] : null;
+  (!isset($object))               ? $object = $_SESSION['mailcow_cc_username'] : null;
+
+  if (is_valid_domain_name($object)) {
+		if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $object)) {
+			$_SESSION['return'] = array(
+				'type' => 'danger',
+				'msg' => sprintf($lang['danger']['access_denied'])
+			);
+			return false;
+		}
+    $object = idn_to_ascii(strtolower(trim($object)));
+  }
+  else {
+		if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $object)) {
+			$_SESSION['return'] = array(
+				'type' => 'danger',
+				'msg' => sprintf($lang['danger']['access_denied'])
+			);
+			return false;
+		}
+  }
+
+	($postarray['object_list'] == "bl") ? $object_list = "blacklist_from" : null;
+	($postarray['object_list'] == "wl") ? $object_list = "whitelist_from" : null;
+	$object_from = preg_replace('/\.+/', '.', rtrim(preg_replace("/\.\*/", "*", trim(strtolower($postarray['object_from']))), '.'));
+  if (!ctype_alnum(str_replace(array('@', '.', '-', '*'), '', $object_from))) {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => sprintf($lang['danger']['policy_list_from_invalid'])
+		);
+		return false;
+	}
+	if ($object_list != "blacklist_from" && $object_list != "whitelist_from") {
+    $_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => sprintf($lang['danger']['access_denied'])
+		);
+		return false;
+	}
+	try {
+		$stmt = $pdo->prepare("SELECT `object` FROM `filterconf`
+			WHERE (`option` = 'whitelist_from'  OR `option` = 'blacklist_from')
+				AND `object` = :object
+				AND `value` = :object_from");
+		$stmt->execute(array(':object' => $object, ':object_from' => $object_from));
+		$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
+    if ($num_results != 0) {
+      $_SESSION['return'] = array(
+        'type' => 'danger',
+        'msg' => sprintf($lang['danger']['policy_list_from_exists'])
+      );
+      return false;
+    }
+	}
+	catch(PDOException $e) {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => 'MySQL: '.$e
+		);
+		return false;
+	}
+
+	try {
+		$stmt = $pdo->prepare("INSERT INTO `filterconf` (`object`, `option` ,`value`)
+			VALUES (:object, :object_list, :object_from)");
+		$stmt->execute(array(
+			':object' => $object,
+			':object_list' => $object_list,
+			':object_from' => $object_from
+		));
+	}
+	catch (PDOException $e) {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => 'MySQL: '.$e
+		);
+		return false;
+	}
+	$_SESSION['return'] = array(
+		'type' => 'success',
+		'msg' => sprintf($lang['success']['object_modified'], $object)
+	);
+}
+function delete_policy_list_item($postarray) {
+  // Array data
+  // Either 'domain' or 'username' can be be set
+  // If none of the above is set, default to mailcow_cc_username
+  //
+  // 'delete_prefid' is item to be deleted
+	global $lang;
+	global $pdo;
+  (isset($postarray['username'])) ? $object = $postarray['username'] : null;
+  (isset($postarray['domain']))   ? $object = $postarray['domain'] : null;
+  (!isset($object))               ? $object = $_SESSION['mailcow_cc_username'] : null;
+
+  if (is_valid_domain_name($object)) {
+		if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $object)) {
+			$_SESSION['return'] = array(
+				'type' => 'danger',
+				'msg' => sprintf($lang['danger']['access_denied'])
+			);
+			return false;
+		}
+    $object = idn_to_ascii(strtolower(trim($object)));
+  }
+  else {
+    if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $object)) {
+      $_SESSION['return'] = array(
+        'type' => 'danger',
+        'msg' => sprintf($lang['danger']['access_denied'])
+      );
+      return false;
+    }
+  }
+
+  if (!is_numeric($postarray['delete_prefid'])) {
+    $_SESSION['return'] = array(
+      'type' => 'danger',
+      'msg' => sprintf($lang['danger']['access_denied'])
+    );
+    return false;
+  }
+
+  try {
+    $stmt = $pdo->prepare("DELETE FROM `filterconf` WHERE `object` = :object AND `prefid` = :prefid");
+    $stmt->execute(array(
+      ':object' => $object,
+      ':prefid' => $postarray['delete_prefid']
+    ));
+  }
+  catch (PDOException $e) {
+    $_SESSION['return'] = array(
+      'type' => 'danger',
+      'msg' => 'MySQL: '.$e
+    );
+    return false;
+  }
+  $_SESSION['return'] = array(
+    'type' => 'success',
+    'msg' => sprintf($lang['success']['object_modified'], $object)
+  );
+  return true;
+}
+function get_syncjobs($username = null) {
+  // 'username' can be be set, if not, default to mailcow_cc_username
+	global $lang;
+	global $pdo;
+  $data = array();
+  if (isset($username) && filter_var($username, FILTER_VALIDATE_EMAIL)) {
+    if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $username)) {
+      $_SESSION['return'] = array(
+        'type' => 'danger',
+        'msg' => sprintf($lang['danger']['access_denied'])
+      );
+      return false;
+    }
+  }
+  else {
+    $username = $_SESSION['mailcow_cc_username'];
+  }
+  try {
+    $stmt = $pdo->prepare("SELECT *, CONCAT(LEFT(`password1`, 3), '…') as `password1_short`
+        FROM `imapsync`
+          WHERE `user2` = :username");
+    $stmt->execute(array(':username' => $username));
+    $data = $stmt->fetchAll(PDO::FETCH_ASSOC);
+  }
+  catch(PDOException $e) {
+    $_SESSION['return'] = array(
+      'type' => 'danger',
+      'msg' => 'MySQL: '.$e
+    );
+  }
+  return $data;
+}
+function get_syncjob_details($id) {
+	global $lang;
+	global $pdo;
+  $syncjobdetails = array();
+	if ($_SESSION['mailcow_cc_role'] != "user" &&
+		$_SESSION['mailcow_cc_role'] != "admin") {
+			$_SESSION['return'] = array(
+				'type' => 'danger',
+				'msg' => sprintf($lang['danger']['access_denied'])
+			);
+			return false;
+	}
+  if (!is_numeric($id)) {
+    $_SESSION['return'] = array(
+      'type' => 'danger',
+      'msg' => sprintf($lang['danger']['access_denied'])
+    );
+    return false;
+  }
+  try {
+    $stmt = $pdo->prepare("SELECT * FROM `imapsync` WHERE (`user2` = :username OR 'admin' = :role) AND id = :id");
+    $stmt->execute(array(':id' => $id, ':role' => $_SESSION['mailcow_cc_role'], ':username' => $_SESSION['mailcow_cc_username']));
+    $syncjobdetails = $stmt->fetch(PDO::FETCH_ASSOC);
+  }
+  catch(PDOException $e) {
+    $_SESSION['return'] = array(
+      'type' => 'danger',
+      'msg' => 'MySQL: '.$e
+    );
+  }
+  return $syncjobdetails;
+}
+function delete_syncjob($postarray) {
+  // Array items
+  // 'username' can be set, defaults to mailcow_cc_username
+	global $lang;
+	global $pdo;
+  if (isset($postarray['username']) && filter_var($postarray['username'], FILTER_VALIDATE_EMAIL)) {
+    if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $postarray['username'])) {
+      $_SESSION['return'] = array(
+        'type' => 'danger',
+        'msg' => sprintf($lang['danger']['access_denied'])
+      );
+      return false;
+    }
+    else {
+      $username = $postarray['username'];
+    }
+  }
+  else {
+    $username = $_SESSION['mailcow_cc_username'];
+  }
+  $id = $postarray['id'];
+  if (!is_numeric($id)) {
+    $_SESSION['return'] = array(
+      'type' => 'danger',
+      'msg' => sprintf($lang['danger']['access_denied'])
+    );
+    return false;
+  }
+  try {
+    $stmt = $pdo->prepare("DELETE FROM `imapsync` WHERE `user2` = :username AND `id`= :id");
+    $stmt->execute(array(
+      ':username' => $username,
+      ':id' => $id,
+    ));
+  }
+  catch (PDOException $e) {
+    $_SESSION['return'] = array(
+      'type' => 'danger',
+      'msg' => 'MySQL: '.$e
+    );
+    return false;
+  }
+  $_SESSION['return'] = array(
+    'type' => 'success',
+    'msg' => sprintf($lang['success']['mailbox_modified'], htmlspecialchars($username))
+  );
+  return true;
+}
+function add_syncjob($postarray) {
+  // Array items
+  // 'username' can be set, defaults to mailcow_cc_username
+	global $lang;
+	global $pdo;
+  if (isset($postarray['username']) && filter_var($postarray['username'], FILTER_VALIDATE_EMAIL)) {
+    if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $postarray['username'])) {
+      $_SESSION['return'] = array(
+        'type' => 'danger',
+        'msg' => sprintf($lang['danger']['access_denied'])
+      );
+      return false;
+    }
+    else {
+      $username = $postarray['username'];
+    }
+  }
+  else {
+    $username = $_SESSION['mailcow_cc_username'];
+  }
+  isset($postarray['active']) ? $active = '1' : $active = '0';
+  isset($postarray['delete2duplicates']) ? $delete2duplicates = '1' : $delete2duplicates = '0';
+  $port1            = $postarray['port1'];
+  $host1            = $postarray['host1'];
+  $password1        = $postarray['password1'];
+  $exclude          = $postarray['exclude'];
+  $maxage           = $postarray['maxage'];
+  $subfolder2       = $postarray['subfolder2'];
+  $user1            = $postarray['user1'];
+  $mins_interval    = $postarray['mins_interval'];
+  $enc1             = $postarray['enc1'];
+
+  if (empty($subfolder2)) {
+    $subfolder2 = "";
+  }
+  if (!isset($maxage) || !filter_var($maxage, FILTER_VALIDATE_INT, array('options' => array('min_range' => 1, 'max_range' => 32767)))) {
+    $maxage = "0";
+  }
+  if (!filter_var($port1, FILTER_VALIDATE_INT, array('options' => array('min_range' => 1, 'max_range' => 65535)))) {
+    $_SESSION['return'] = array(
+      'type' => 'danger',
+      'msg' => sprintf($lang['danger']['access_denied'])
+    );
+    return false;
+  }
+  if (!filter_var($mins_interval, FILTER_VALIDATE_INT, array('options' => array('min_range' => 10, 'max_range' => 3600)))) {
+    $_SESSION['return'] = array(
+      'type' => 'danger',
+      'msg' => sprintf($lang['danger']['access_denied'])
+    );
+    return false;
+  }
+  if (!is_valid_domain_name($host1)) {
+    $_SESSION['return'] = array(
+      'type' => 'danger',
+      'msg' => sprintf($lang['danger']['access_denied'])
+    );
+    return false;
+  }
+  if ($enc1 != "TLS" && $enc1 != "SSL" && $enc1 != "PLAIN") {
+    $_SESSION['return'] = array(
+      'type' => 'danger',
+      'msg' => sprintf($lang['danger']['access_denied'])
+    );
+    return false;
+  }
+  if (@preg_match("/" . $exclude . "/", null) === false) {
+    $_SESSION['return'] = array(
+      'type' => 'danger',
+      'msg' => sprintf($lang['danger']['access_denied'])
+    );
+    return false;
+  }
+  try {
+    $stmt = $pdo->prepare("SELECT `user2`, `user1` FROM `imapsync`
+      WHERE `user2` = :user2 AND `user1` = :user1");
+    $stmt->execute(array(':user1' => $user1, ':user2' => $username));
+    $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
+  }
+  catch(PDOException $e) {
+    $_SESSION['return'] = array(
+      'type' => 'danger',
+      'msg' => 'MySQL: '.$e
+    );
+    return false;
+  }
+  if ($num_results != 0) {
+    $_SESSION['return'] = array(
+      'type' => 'danger',
+      'msg' => sprintf($lang['danger']['object_exists'], htmlspecialchars($host1 . ' / ' . $user1))
+    );
+    return false;
+  }
+  try {
+    $stmt = $pdo->prepare("INSERT INTO `imapsync` (`user2`, `exclude`, `maxage`, `subfolder2`, `host1`, `authmech1`, `user1`, `password1`, `mins_interval`, `port1`, `enc1`, `delete2duplicates`, `active`)
+      VALUES (:user2, :exclude, :maxage, :subfolder2, :host1, :authmech1, :user1, :password1, :mins_interval, :port1, :enc1, :delete2duplicates, :active)");
+    $stmt->execute(array(
+      ':user2' => $username,
+      ':exclude' => $exclude,
+      ':maxage' => $maxage,
+      ':subfolder2' => $subfolder2,
+      ':host1' => $host1,
+      ':authmech1' => 'PLAIN',
+      ':user1' => $user1,
+      ':password1' => $password1,
+      ':mins_interval' => $mins_interval,
+      ':port1' => $port1,
+      ':enc1' => $enc1,
+      ':delete2duplicates' => $delete2duplicates,
+      ':active' => $active,
+    ));
+  }
+  catch(PDOException $e) {
+    $_SESSION['return'] = array(
+      'type' => 'danger',
+      'msg' => 'MySQL: '.$e
+    );
+    return false;
+  }
+  $_SESSION['return'] = array(
+    'type' => 'success',
+    'msg' => sprintf($lang['success']['mailbox_modified'], $username)
+  );
+  return true;
+}
+function edit_syncjob($postarray) {
+  // Array items
+  // 'username' can be set, defaults to mailcow_cc_username
+	global $lang;
+	global $pdo;
+  if (isset($postarray['username']) && filter_var($postarray['username'], FILTER_VALIDATE_EMAIL)) {
+    if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $postarray['username'])) {
+      $_SESSION['return'] = array(
+        'type' => 'danger',
+        'msg' => sprintf($lang['danger']['access_denied'])
+      );
+      return false;
+    }
+    else {
+      $username = $postarray['username'];
+    }
+  }
+  else {
+    $username = $_SESSION['mailcow_cc_username'];
+  }
+  isset($postarray['active']) ? $active = '1' : $active = '0';
+  isset($postarray['delete2duplicates']) ? $delete2duplicates = '1' : $delete2duplicates = '0';
+  $id               = $postarray['id'];
+  $port1            = $postarray['port1'];
+  $host1            = $postarray['host1'];
+  $password1        = $postarray['password1'];
+  $exclude          = $postarray['exclude'];
+  $maxage           = $postarray['maxage'];
+  $subfolder2       = $postarray['subfolder2'];
+  $user1            = $postarray['user1'];
+  $mins_interval    = $postarray['mins_interval'];
+  $enc1             = $postarray['enc1'];
+
+  if (empty($subfolder2)) {
+    $subfolder2 = "";
+  }
+  if (!isset($maxage) || !filter_var($maxage, FILTER_VALIDATE_INT, array('options' => array('min_range' => 1, 'max_range' => 32767)))) {
+    $maxage = "0";
+  }
+  if (!filter_var($port1, FILTER_VALIDATE_INT, array('options' => array('min_range' => 1, 'max_range' => 65535)))) {
+    $_SESSION['return'] = array(
+      'type' => 'danger',
+      'msg' => sprintf($lang['danger']['access_denied'])
+    );
+    return false;
+  }
+  if (!filter_var($mins_interval, FILTER_VALIDATE_INT, array('options' => array('min_range' => 10, 'max_range' => 3600)))) {
+    $_SESSION['return'] = array(
+      'type' => 'danger',
+      'msg' => sprintf($lang['danger']['access_denied'])
+    );
+    return false;
+  }
+  if (!is_valid_domain_name($host1)) {
+    $_SESSION['return'] = array(
+      'type' => 'danger',
+      'msg' => sprintf($lang['danger']['access_denied'])
+    );
+    return false;
+  }
+  if ($enc1 != "TLS" && $enc1 != "SSL" && $enc1 != "PLAIN") {
+    $_SESSION['return'] = array(
+      'type' => 'danger',
+      'msg' => sprintf($lang['danger']['access_denied'])
+    );
+    return false;
+  }
+  if (@preg_match("/" . $exclude . "/", null) === false) {
+    $_SESSION['return'] = array(
+      'type' => 'danger',
+      'msg' => sprintf($lang['danger']['access_denied'])
+    );
+    return false;
+  }
+  try {
+    $stmt = $pdo->prepare("SELECT `user2` FROM `imapsync`
+      WHERE `user2` = :user2 AND `id` = :id");
+    $stmt->execute(array(':user2' => $username, ':id' => $id));
+    $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
+  }
+  catch(PDOException $e) {
+    $_SESSION['return'] = array(
+      'type' => 'danger',
+      'msg' => 'MySQL: '.$e
+    );
+    return false;
+  }
+  if (empty($num_results)) {
+    $_SESSION['return'] = array(
+      'type' => 'danger',
+      'msg' => sprintf($lang['danger']['access_denied'])
+    );
+    return false;
+  }
+  try {
+    $stmt = $pdo->prepare("UPDATE `imapsync` set `maxage` = :maxage, `subfolder2` = :subfolder2, `exclude` = :exclude, `host1` = :host1, `user1` = :user1, `password1` = :password1, `mins_interval` = :mins_interval, `port1` = :port1, `enc1` = :enc1, `delete2duplicates` = :delete2duplicates, `active` = :active
+      WHERE `user2` = :user2 AND `id` = :id");
+    $stmt->execute(array(
+      ':user2' => $username,
+      ':id' => $id,
+      ':exclude' => $exclude,
+      ':maxage' => $maxage,
+      ':subfolder2' => $subfolder2,
+      ':host1' => $host1,
+      ':user1' => $user1,
+      ':password1' => $password1,
+      ':mins_interval' => $mins_interval,
+      ':port1' => $port1,
+      ':enc1' => $enc1,
+      ':delete2duplicates' => $delete2duplicates,
+      ':active' => $active,
+    ));
+  }
+  catch(PDOException $e) {
+    $_SESSION['return'] = array(
+      'type' => 'danger',
+      'msg' => 'MySQL: '.$e
+    );
+    return false;
+  }
+  $_SESSION['return'] = array(
+    'type' => 'success',
+    'msg' => sprintf($lang['success']['mailbox_modified'], $username)
+  );
+  return true;
+}
+function edit_tls_policy($postarray) {
+	global $lang;
+	global $pdo;
+  if (isset($postarray['username']) && filter_var($postarray['username'], FILTER_VALIDATE_EMAIL)) {
+    if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $postarray['username'])) {
+      $_SESSION['return'] = array(
+        'type' => 'danger',
+        'msg' => sprintf($lang['danger']['access_denied'])
+      );
+      return false;
+    }
+    else {
+      $username = $postarray['username'];
+    }
+  }
+  else {
+    $username = $_SESSION['mailcow_cc_username'];
+  }
+	isset($postarray['tls_in']) ? $tls_in = '1' : $tls_in = '0';
+	isset($postarray['tls_out']) ? $tls_out = '1' : $tls_out = '0';
+	$username = $_SESSION['mailcow_cc_username'];
+	if (!filter_var($username, FILTER_VALIDATE_EMAIL)) {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => sprintf($lang['danger']['username_invalid'])
+		);
+		return false;
+	}
+	try {
+		$stmt = $pdo->prepare("UPDATE `mailbox` SET `tls_enforce_out` = :tls_out, `tls_enforce_in` = :tls_in WHERE `username` = :username");
+		$stmt->execute(array(
+			':tls_out' => $tls_out,
+			':tls_in' => $tls_in,
+			':username' => $username
+		));
+	}
+	catch (PDOException $e) {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => 'MySQL: '.$e
+		);
+		return false;
+	}
+	$_SESSION['return'] = array(
+		'type' => 'success',
+		'msg' => sprintf($lang['success']['mailbox_modified'], $username)
+	);
+}
+function get_tls_policy($username = null) {
+	global $lang;
+	global $pdo;
+  $data = array();
+  if (isset($username) && filter_var($username, FILTER_VALIDATE_EMAIL)) {
+    if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $username)) {
+      $_SESSION['return'] = array(
+        'type' => 'danger',
+        'msg' => sprintf($lang['danger']['access_denied'])
+      );
+      return false;
+    }
+  }
+  else {
+    $username = $_SESSION['mailcow_cc_username'];
+  }
+	try {
+		$stmt = $pdo->prepare("SELECT `tls_enforce_out`, `tls_enforce_in` FROM `mailbox` WHERE `username` = :username");
+		$stmt->execute(array(':username' => $username));
+		$data = $stmt->fetch(PDO::FETCH_ASSOC);
+	}
+	catch(PDOException $e) {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => 'MySQL: '.$e
+		);
+		return false;
+	}
+	return $data;
+}
+function edit_delimiter_action($postarray) {
+  // Array items
+  // 'username' can be set, defaults to mailcow_cc_username
+	global $lang;
+	global $pdo;
+  if (isset($postarray['username']) && filter_var($postarray['username'], FILTER_VALIDATE_EMAIL)) {
+    if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $postarray['username'])) {
+      $_SESSION['return'] = array(
+        'type' => 'danger',
+        'msg' => sprintf($lang['danger']['access_denied'])
+      );
+      return false;
+    }
+    else {
+      $username = $postarray['username'];
+    }
+  }
+  else {
+    $username = $_SESSION['mailcow_cc_username'];
+  }
+  ($postarray['tagged_mail_handler'] == "subject") ? $wants_tagged_subject = '1' : $wants_tagged_subject = '0';
+  if (!filter_var($username, FILTER_VALIDATE_EMAIL)) {
+    $_SESSION['return'] = array(
+      'type' => 'danger',
+      'msg' => sprintf($lang['danger']['username_invalid'])
+    );
+    return false;
+  }
+  try {
+    $stmt = $pdo->prepare("UPDATE `mailbox` SET `wants_tagged_subject` = :wants_tagged_subject WHERE `username` = :username");
+    $stmt->execute(array(':username' => $username, ':wants_tagged_subject' => $wants_tagged_subject));
+    $SelectData = $stmt->fetch(PDO::FETCH_ASSOC);
+  }
+  catch(PDOException $e) {
+    $_SESSION['return'] = array(
+      'type' => 'danger',
+      'msg' => 'MySQL: '.$e
+    );
+    return false;
+  }
+  $_SESSION['return'] = array(
+    'type' => 'success',
+    'msg' => sprintf($lang['success']['mailbox_modified'], $username)
+  );
+  return true;
+}
+function get_delimiter_action($username = null) {
+  // 'username' can be set, defaults to mailcow_cc_username
+	global $lang;
+	global $pdo;
+	$data = array();
+  if (isset($username) && filter_var($username, FILTER_VALIDATE_EMAIL)) {
+    if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $username)) {
+      return false;
+    }
+  }
+  else {
+    $username = $_SESSION['mailcow_cc_username'];
+  }
+  try {
+    $stmt = $pdo->prepare("SELECT `wants_tagged_subject` FROM `mailbox` WHERE `username` = :username");
+    $stmt->execute(array(':username' => $username));
+    $data = $stmt->fetch(PDO::FETCH_ASSOC);
+  }
+  catch(PDOException $e) {
+    $_SESSION['return'] = array(
+      'type' => 'danger',
+      'msg' => 'MySQL: '.$e
+    );
+    return false;
+  }
+  return $data;
+}
+function user_get_alias_details($username) {
+	global $lang;
+	global $pdo;
+  if ($_SESSION['mailcow_cc_role'] == "user") {
+    $username	= $_SESSION['mailcow_cc_username'];
+  }
+  if (!filter_var($username, FILTER_VALIDATE_EMAIL)) {
+    return false;
+  }
+  try {
+    $data['address'] = $username;
+    $stmt = $pdo->prepare("SELECT IFNULL(GROUP_CONCAT(`address` SEPARATOR ', '), '&#10008;') AS `aliases` FROM `alias` WHERE `goto` = :username_goto AND `address` NOT LIKE '@%' AND `address` != :username_address");
+    $stmt->execute(array(':username_goto' => $username, ':username_address' => $username));
+    $run = $stmt->fetchAll(PDO::FETCH_ASSOC);
+    while ($row = array_shift($run)) {
+      $data['aliases'] = $row['aliases'];
+    }
+    $stmt = $pdo->prepare("SELECT IFNULL(GROUP_CONCAT(local_part, '@', alias_domain SEPARATOR ', '), '&#10008;') AS `ad_alias` FROM `mailbox`
+      LEFT OUTER JOIN `alias_domain` on `target_domain` = `domain`
+        WHERE `username` = :username ;");
+    $stmt->execute(array(':username' => $username));
+    $run = $stmt->fetchAll(PDO::FETCH_ASSOC);
+    while ($row = array_shift($run)) {
+      $data['ad_alias'] = $row['ad_alias'];
+    }
+    $stmt = $pdo->prepare("SELECT IFNULL(GROUP_CONCAT(`send_as` SEPARATOR ', '), '&#10008;') AS `send_as` FROM `sender_acl` WHERE `logged_in_as` = :username AND `send_as` NOT LIKE '@%';");
+    $stmt->execute(array(':username' => $username));
+    $run = $stmt->fetchAll(PDO::FETCH_ASSOC);
+    while ($row = array_shift($run)) {
+      $data['aliases_also_send_as'] = $row['send_as'];
+    }
+    $stmt = $pdo->prepare("SELECT IFNULL(GROUP_CONCAT(`send_as` SEPARATOR ', '), '&#10008;') AS `send_as` FROM `sender_acl` WHERE `logged_in_as` = :username AND `send_as` LIKE '@%';");
+    $stmt->execute(array(':username' => $username));
+    $run = $stmt->fetchAll(PDO::FETCH_ASSOC);
+    while ($row = array_shift($run)) {
+      $data['aliases_send_as_all'] = $row['send_as'];
+    }
+    $stmt = $pdo->prepare("SELECT IFNULL(GROUP_CONCAT(`address` SEPARATOR ', '), '&#10008;') as `address` FROM `alias` WHERE `goto` = :username AND `address` LIKE '@%';");
+    $stmt->execute(array(':username' => $username));
+    $run = $stmt->fetchAll(PDO::FETCH_ASSOC);
+    while ($row = array_shift($run)) {
+      $data['is_catch_all'] = $row['address'];
+    }
+    return $data;
+  }
+  catch(PDOException $e) {
+    $_SESSION['return'] = array(
+      'type' => 'danger',
+      'msg' => 'MySQL: '.$e
+    );
+    return false;
+  }
+}
+function is_valid_domain_name($domain_name) { 
+	if (empty($domain_name)) {
+		return false;
+	}
+	$domain_name = idn_to_ascii($domain_name);
+	return (preg_match("/^([a-z\d](-*[a-z\d])*)(\.([a-z\d](-*[a-z\d])*))*$/i", $domain_name)
+		   && preg_match("/^.{1,253}$/", $domain_name)
+		   && preg_match("/^[^\.]{1,63}(\.[^\.]{1,63})*$/", $domain_name));
+}
+function add_domain_admin($postarray) {
+	global $lang;
+	global $pdo;
+	$username		= strtolower(trim($postarray['username']));
+	$password		= $postarray['password'];
+	$password2		= $postarray['password2'];
+	isset($postarray['active']) ? $active = '1' : $active = '0';
+	if ($_SESSION['mailcow_cc_role'] != "admin") {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => sprintf($lang['danger']['access_denied'])
+		);
+		return false;
+	}
+	if (empty($postarray['domain'])) {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => sprintf($lang['danger']['domain_invalid'])
+		);
+		return false;
+	}
+	if (!ctype_alnum(str_replace(array('_', '.', '-'), '', $username)) || empty ($username)) {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => sprintf($lang['danger']['username_invalid'])
+		);
+		return false;
+	}
+	try {
+		$stmt = $pdo->prepare("SELECT `username` FROM `mailbox`
+			WHERE `username` = :username");
+		$stmt->execute(array(':username' => $username));
+		$num_results[] = count($stmt->fetchAll(PDO::FETCH_ASSOC));
+		
+		$stmt = $pdo->prepare("SELECT `username` FROM `admin`
+			WHERE `username` = :username");
+		$stmt->execute(array(':username' => $username));
+		$num_results[] = count($stmt->fetchAll(PDO::FETCH_ASSOC));
+		
+		$stmt = $pdo->prepare("SELECT `username` FROM `domain_admins`
+			WHERE `username` = :username");
+		$stmt->execute(array(':username' => $username));
+		$num_results[] = count($stmt->fetchAll(PDO::FETCH_ASSOC));
+	}
+	catch(PDOException $e) {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => 'MySQL: '.$e
+		);
+		return false;
+	}
+	foreach ($num_results as $num_results_each) {
+		if ($num_results_each != 0) {
+			$_SESSION['return'] = array(
+				'type' => 'danger',
+				'msg' => sprintf($lang['danger']['object_exists'], htmlspecialchars($username))
+			);
+			return false;
+		}
+	}
+	if (!empty($password) && !empty($password2)) {
+		if ($password != $password2) {
+			$_SESSION['return'] = array(
+				'type' => 'danger',
+				'msg' => sprintf($lang['danger']['password_mismatch'])
+			);
+			return false;
+		}
+		$password_hashed = hash_password($password);
+		foreach ($postarray['domain'] as $domain) {
+			if (!is_valid_domain_name($domain)) {
+				$_SESSION['return'] = array(
+					'type' => 'danger',
+					'msg' => sprintf($lang['danger']['domain_invalid'])
+				);
+				return false;
+			}
+			try {
+				$stmt = $pdo->prepare("INSERT INTO `domain_admins` (`username`, `domain`, `created`, `active`)
+						VALUES (:username, :domain, :created, :active)");
+				$stmt->execute(array(
+					':username' => $username,
+					':domain' => $domain,
+					':created' => date('Y-m-d H:i:s'),
+					':active' => $active
+				));
+			}
+			catch (PDOException $e) {
+        delete_domain_admin(array('username' => $username));
+				$_SESSION['return'] = array(
+					'type' => 'danger',
+					'msg' => 'MySQL: '.$e
+				);
+				return false;
+			}
+		}
+		try {
+			$stmt = $pdo->prepare("INSERT INTO `admin` (`username`, `password`, `superadmin`, `created`, `modified`, `active`)
+				VALUES (:username, :password_hashed, '0', :created, :modified, :active)");
+			$stmt->execute(array(
+				':username' => $username,
+				':password_hashed' => $password_hashed,
+				':created' => date('Y-m-d H:i:s'),
+				':modified' => date('Y-m-d H:i:s'),
+				':active' => $active
+			));
+		}
+		catch (PDOException $e) {
+			$_SESSION['return'] = array(
+				'type' => 'danger',
+				'msg' => 'MySQL: '.$e
+			);
+			return false;
+		}
+	}
+	else {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => sprintf($lang['danger']['password_empty'])
+		);
+		return false;
+	}
+	$_SESSION['return'] = array(
+		'type' => 'success',
+		'msg' => sprintf($lang['success']['domain_admin_added'], htmlspecialchars($username))
+	);
+}
+function delete_domain_admin($postarray) {
+	global $pdo;
+	global $lang;
+	if ($_SESSION['mailcow_cc_role'] != "admin") {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => sprintf($lang['danger']['access_denied'])
+		);
+		return false;
+	}
+	$username = $postarray['username'];
+	if (!ctype_alnum(str_replace(array('_', '.', '-'), '', $username))) {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => sprintf($lang['danger']['username_invalid'])
+		);
+		return false;
+	}
+	try {
+		$stmt = $pdo->prepare("DELETE FROM `domain_admins` WHERE `username` = :username");
+		$stmt->execute(array(
+			':username' => $username,
+		));
+		$stmt = $pdo->prepare("DELETE FROM `admin` WHERE `username` = :username");
+		$stmt->execute(array(
+			':username' => $username,
+		));
+	}
+	catch (PDOException $e) {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => 'MySQL: '.$e
+		);
+		return false;
+	}
+	$_SESSION['return'] = array(
+		'type' => 'success',
+		'msg' => sprintf($lang['success']['domain_admin_removed'], htmlspecialchars($username))
+	);
+}
+function get_domain_admins() {
+	global $pdo;
+	global $lang;
+  $domainadmins = array();
+	if ($_SESSION['mailcow_cc_role'] != "admin") {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => sprintf($lang['danger']['access_denied'])
+		);
+		return false;
+	}
+  try {
+    $stmt = $pdo->query("SELECT DISTINCT
+      `username`
+        FROM `domain_admins` 
+          WHERE `username` IN (
+            SELECT `username` FROM `admin`
+              WHERE `superadmin`!='1'
+          )");
+    $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
+    while ($row = array_shift($rows)) {
+      $domainadmins[] = $row['username'];
+    }
+  }
+  catch(PDOException $e) {
+    $_SESSION['return'] = array(
+      'type' => 'danger',
+      'msg' => 'MySQL: '.$e
+    );
+  }
+  return $domainadmins;
+}
+function get_domain_admin_details($domain_admin) {
+	global $pdo;
+	global $lang;
+  $domainadmindata = array();
+	if (isset($domain_admin) && $_SESSION['mailcow_cc_role'] != "admin") {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => sprintf($lang['danger']['access_denied'])
+		);
+		return false;
+	}
+  if (!isset($domain_admin) && $_SESSION['mailcow_cc_role'] != "domainadmin") {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => sprintf($lang['danger']['access_denied'])
+		);
+		return false;
+	}
+  (!isset($domain_admin)) ? $domain_admin = $_SESSION['mailcow_cc_username'] : null;
+  
+  if (!ctype_alnum(str_replace(array('_', '.', '-'), '', $domain_admin))) {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => sprintf($lang['danger']['username_invalid'])
+		);
+		return false;
+	}
+  try {
+    $stmt = $pdo->prepare("SELECT
+      `tfa`.`active` AS `tfa_active_int`,
+      `domain_admins`.`username`,
+      `domain_admins`.`created`,
+      `domain_admins`.`active` AS `active_int`,
+      CASE `domain_admins`.`active` WHEN 1 THEN '".$lang['mailbox']['yes']."' ELSE '".$lang['mailbox']['no']."' END AS `active`
+        FROM `domain_admins`
+        LEFT OUTER JOIN `tfa` ON `tfa`.`username`=`domain_admins`.`username`
+          WHERE `domain_admins`.`username`= :domain_admin");
+    $stmt->execute(array(
+      ':domain_admin' => $domain_admin
+    ));
+    $row = $stmt->fetch(PDO::FETCH_ASSOC);
+    $domainadmindata['username'] = $row['username'];
+    $domainadmindata['active'] = $row['active'];
+    $domainadmindata['active_int'] = $row['active_int'];
+    $domainadmindata['tfa_active_int'] = $row['tfa_active_int'];
+    $domainadmindata['created'] = $row['created'];
+    // GET SELECTED
+    $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);
+    while($row = array_shift($rows)) {
+      $domainadmindata['selected_domains'][] = $row['domain'];
+    }
+    // GET UNSELECTED
+    $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);
+    while($row = array_shift($rows)) {
+      $domainadmindata['unselected_domains'][] = $row['domain'];
+    }
+  }
+  catch(PDOException $e) {
+    $_SESSION['return'] = array(
+      'type' => 'danger',
+      'msg' => 'MySQL: '.$e
+    );
+  }
+  return $domainadmindata;
+}
+function set_tfa($postarray) {
+	global $lang;
+	global $pdo;
+	global $yubi;
+	global $u2f;
+
+  if ($_SESSION['mailcow_cc_role'] != "domainadmin" &&
+    $_SESSION['mailcow_cc_role'] != "admin") {
+      $_SESSION['return'] = array(
+        'type' => 'danger',
+        'msg' => sprintf($lang['danger']['access_denied'])
+      );
+      return false;
+  }
+  $username = $_SESSION['mailcow_cc_username'];
+  
+  $stmt = $pdo->prepare("SELECT `password` FROM `admin`
+      WHERE `username` = :user");
+  $stmt->execute(array(':user' => $username));
+  $row = $stmt->fetch(PDO::FETCH_ASSOC);
+  if (!verify_ssha256($row['password'], $postarray["confirm_password"])) {
+    $_SESSION['return'] = array(
+      'type' => 'danger',
+      'msg' => sprintf($lang['danger']['access_denied'])
+    );
+    return false;
+  }
+  
+	switch ($postarray["tfa_method"]) {
+		case "yubi_otp":
+      (!isset($postarray["key_id"])) ? $key_id = 'unidentified' : $key_id = $postarray["key_id"];
+      $yubico_id = $postarray['yubico_id'];
+      $yubico_key = $postarray['yubico_key'];
+      $yubi = new Auth_Yubico($yubico_id, $yubico_key);
+      if (!$yubi) {
+        $_SESSION['return'] = array(
+          'type' => 'danger',
+          'msg' => sprintf($lang['danger']['access_denied'])
+        );
+        return false;
+      }
+			if (!ctype_alnum($postarray["otp_token"]) || strlen($postarray["otp_token"]) != 44) {
+				$_SESSION['return'] = array(
+					'type' => 'danger',
+					'msg' => sprintf($lang['danger']['tfa_token_invalid'])
+				);
+				return false;
+			}
+      $yauth = $yubi->verify($postarray["otp_token"]);
+      if (PEAR::isError($yauth)) {
+				$_SESSION['return'] = array(
+					'type' => 'danger',
+					'msg' => 'Yubico API: ' . $yauth->getMessage()
+				);
+				return false;
+      }
+			try {
+        // We could also do a modhex translation here
+        $yubico_modhex_id = substr($postarray["otp_token"], 0, 12);
+        $stmt = $pdo->prepare("DELETE FROM `tfa` 
+          WHERE `username` = :username
+            AND (`authmech` != 'yubi_otp')
+            OR (`authmech` = 'yubi_otp' AND `secret` LIKE :modhex)");
+				$stmt->execute(array(':username' => $username, ':modhex' => '%' . $yubico_modhex_id));
+				$stmt = $pdo->prepare("INSERT INTO `tfa` (`key_id`, `username`, `authmech`, `active`, `secret`) VALUES
+					(:key_id, :username, 'yubi_otp', '1', :secret)");
+				$stmt->execute(array(':key_id' => $key_id, ':username' => $username, ':secret' => $yubico_id . ':' . $yubico_key . ':' . $yubico_modhex_id));
+			}
+			catch (PDOException $e) {
+				$_SESSION['return'] = array(
+					'type' => 'danger',
+					'msg' => 'MySQL: '.$e
+				);
+				return false;
+			}
+			$_SESSION['return'] = array(
+				'type' => 'success',
+				'msg' => sprintf($lang['success']['object_modified'], htmlspecialchars($username))
+			);
+		break;
+
+		case "u2f":
+      try {
+        (!isset($postarray["key_id"])) ? $key_id = 'unidentified' : $key_id = $postarray["key_id"];
+        $reg = $u2f->doRegister(json_decode($_SESSION['regReq']), json_decode($postarray['token']));
+        $stmt = $pdo->prepare("DELETE FROM `tfa` WHERE `username` = :username AND `authmech` != 'u2f'");
+				$stmt->execute(array(':username' => $username));
+        $stmt = $pdo->prepare("INSERT INTO `tfa` (`username`, `key_id`, `authmech`, `keyHandle`, `publicKey`, `certificate`, `counter`, `active`) VALUES (?, ?, 'u2f', ?, ?, ?, ?, '1')");
+        $stmt->execute(array($username, $key_id, $reg->keyHandle, $reg->publicKey, $reg->certificate, $reg->counter));
+        $_SESSION['return'] = array(
+          'type' => 'success',
+          'msg' => sprintf($lang['success']['object_modified'], $username)
+        );
+        $_SESSION['regReq'] = null;
+      }
+      catch (Exception $e) {
+        $_SESSION['return'] = array(
+          'type' => 'danger',
+          'msg' => "U2F: " . $e->getMessage()
+        );
+        $_SESSION['regReq'] = null;
+      }
+		break;
+
+		case "none":
+			try {
+				$stmt = $pdo->prepare("DELETE FROM `tfa` WHERE `username` = :username");
+				$stmt->execute(array(':username' => $username));
+			}
+			catch (PDOException $e) {
+				$_SESSION['return'] = array(
+					'type' => 'danger',
+					'msg' => 'MySQL: '.$e
+				);
+				return false;
+			}
+			$_SESSION['return'] = array(
+				'type' => 'success',
+				'msg' => sprintf($lang['success']['object_modified'], htmlspecialchars($username))
+			);
+		break;
+	}
+}
+function unset_tfa_key($postarray) {
+  // Can only unset own keys
+  // Needs at least one key left
+  global $pdo;
+  global $lang;
+  $id = intval($postarray['unset_tfa_key']);
+  if ($_SESSION['mailcow_cc_role'] != "domainadmin" &&
+    $_SESSION['mailcow_cc_role'] != "admin") {
+      $_SESSION['return'] = array(
+        'type' => 'danger',
+        'msg' => sprintf($lang['danger']['access_denied'])
+      );
+      return false;
+  }
+  $username = $_SESSION['mailcow_cc_username'];
+  try {
+    if (!is_numeric($id)) {
+      $_SESSION['return'] = array(
+        'type' => 'danger',
+        'msg' => sprintf($lang['danger']['access_denied'])
+      );
+      return false;
+    }
+    $stmt = $pdo->prepare("SELECT COUNT(*) AS `keys` FROM `tfa`
+      WHERE `username` = :username AND `active` = '1'");
+    $stmt->execute(array(':username' => $username));
+    $row = $stmt->fetch(PDO::FETCH_ASSOC);
+    if ($row['keys'] == "1") {
+      $_SESSION['return'] = array(
+        'type' => 'danger',
+        'msg' => sprintf($lang['danger']['last_key'])
+      );
+      return false;
+    }
+    $stmt = $pdo->prepare("DELETE FROM `tfa` WHERE `username` = :username AND `id` = :id");
+    $stmt->execute(array(':username' => $username, ':id' => $id));
+    $_SESSION['return'] = array(
+      'type' => 'success',
+      'msg' => sprintf($lang['success']['object_modified'], $username)
+    );
+  }
+  catch (PDOException $e) {
+    $_SESSION['return'] = array(
+      'type' => 'danger',
+      'msg' => 'MySQL: '.$e
+    );
+    return false;
+  }
+}
+function get_tfa($username = null) {
+	global $pdo;
+  if (isset($_SESSION['mailcow_cc_username'])) {
+    $username = $_SESSION['mailcow_cc_username'];
+  }
+  elseif (empty($username)) {
+    return false;
+  }
+
+  $stmt = $pdo->prepare("SELECT * FROM `tfa`
+      WHERE `username` = :username AND `active` = '1'");
+  $stmt->execute(array(':username' => $username));
+  $row = $stmt->fetch(PDO::FETCH_ASSOC);
+  
+	switch ($row["authmech"]) {
+		case "yubi_otp":
+      $data['name'] = "yubi_otp";
+      $data['pretty'] = "Yubico OTP";
+      $stmt = $pdo->prepare("SELECT `id`, `key_id`, RIGHT(`secret`, 12) AS 'modhex' FROM `tfa` WHERE `authmech` = 'yubi_otp' AND `username` = :username");
+      $stmt->execute(array(
+        ':username' => $username,
+      ));
+      $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
+      while($row = array_shift($rows)) {
+        $data['additional'][] = $row;
+      }
+      return $data;
+    break;
+		case "u2f":
+      $data['name'] = "u2f";
+      $data['pretty'] = "Fido U2F";
+      $stmt = $pdo->prepare("SELECT `id`, `key_id` FROM `tfa` WHERE `authmech` = 'u2f' AND `username` = :username");
+      $stmt->execute(array(
+        ':username' => $username,
+      ));
+      $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
+      while($row = array_shift($rows)) {
+        $data['additional'][] = $row;
+      }
+      return $data;
+    break;
+		case "hotp":
+      $data['name'] = "hotp";
+      $data['pretty'] = "HMAC-based OTP";
+      return $data;
+		break;
+ 		case "totp":
+      $data['name'] = "totp";
+      $data['pretty'] = "Time-based OTP";
+      return $data;
+		break;
+    default:
+      $data['name'] = 'none';
+      $data['pretty'] = "-";
+      return $data;
+    break;
+	}
+}
+function verify_tfa_login($username, $token) {
+	global $pdo;
+	global $lang;
+	global $yubi;
+
+  $stmt = $pdo->prepare("SELECT `authmech` FROM `tfa`
+      WHERE `username` = :username AND `active` = '1'");
+  $stmt->execute(array(':username' => $username));
+  $row = $stmt->fetch(PDO::FETCH_ASSOC);
+  
+	switch ($row["authmech"]) {
+		case "yubi_otp":
+			if (!ctype_alnum($token) || strlen($token) != 44) {
+        return false;
+      }
+      $yubico_modhex_id = substr($token, 0, 12);
+      $stmt = $pdo->prepare("SELECT `id`, `secret` FROM `tfa`
+          WHERE `username` = :username
+          AND `authmech` = 'yubi_otp'
+          AND `active`='1'
+          AND `secret` LIKE :modhex");
+      $stmt->execute(array(':username' => $username, ':modhex' => '%' . $yubico_modhex_id));
+      $row = $stmt->fetch(PDO::FETCH_ASSOC);
+      $yubico_auth = explode(':', $row['secret']);
+      $yubi = new Auth_Yubico($yubico_auth[0], $yubico_auth[1]);
+      $yauth = $yubi->verify($token);
+      if (PEAR::isError($yauth)) {
+				$_SESSION['return'] = array(
+					'type' => 'danger',
+					'msg' => 'Yubico Authentication error: ' . $yauth->getMessage()
+				);
+				return false;
+      }
+      else {
+        $_SESSION['tfa_id'] = $row['id'];
+        return true;
+      }
+    return false;
+  break;
+  case "u2f":
+    try {
+      global $u2f;
+      $reg = $u2f->doAuthenticate(json_decode($_SESSION['authReq']), get_u2f_registrations($username), json_decode($token));
+      $stmt = $pdo->prepare("UPDATE `tfa` SET `counter` = ? WHERE `id` = ?");
+      $stmt->execute(array($reg->counter, $reg->id));
+      $_SESSION['tfa_id'] = $reg->id;
+      $_SESSION['authReq'] = null;
+      return true;
+    }
+    catch (Exception $e) {
+      $_SESSION['return'] = array(
+        'type' => 'danger',
+        'msg' => "U2F: " . $e->getMessage()
+      );
+      $_SESSION['regReq'] = null;
+      return false;
+    }
+    return false;
+  break;
+  case "hotp":
+      return false;
+  break;
+  case "totp":
+      return false;
+  break;
+  default:
+      return false;
+  break;
+	}
+  return false;
+}
+function edit_domain_admin($postarray) {
+	global $lang;
+	global $pdo;
+
+	if ($_SESSION['mailcow_cc_role'] != "admin" && $_SESSION['mailcow_cc_role'] != "domainadmin") {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => sprintf($lang['danger']['access_denied'])
+		);
+		return false;
+	}
+	// Administrator
+  if ($_SESSION['mailcow_cc_role'] == "admin") {
+    $username     = $postarray['username'];
+    $username_now = $postarray['username_now'];
+    $password     = $postarray['password'];
+    $password2    = $postarray['password2'];
+    isset($postarray['active']) ? $active = '1' : $active = '0';
+
+    if(isset($postarray['domain'])) {
+      foreach ($postarray['domain'] as $domain) {
+        if (!is_valid_domain_name($domain)) {
+          $_SESSION['return'] = array(
+            'type' => 'danger',
+            'msg' => sprintf($lang['danger']['domain_invalid'])
+          );
+          return false;
+        }
+      }
+    }
+
+    if (!ctype_alnum(str_replace(array('_', '.', '-'), '', $username))) {
+      $_SESSION['return'] = array(
+        'type' => 'danger',
+        'msg' => sprintf($lang['danger']['username_invalid'])
+      );
+      return false;
+    }
+    if ($username != $username_now) {
+      if (empty(get_domain_admin_details($username_now)['username']) || !empty(get_domain_admin_details($username)['username'])) {
+        $_SESSION['return'] = array(
+          'type' => 'danger',
+          'msg' => sprintf($lang['danger']['username_invalid'])
+        );
+        return false;
+      }
+    }
+    try {
+      $stmt = $pdo->prepare("DELETE FROM `domain_admins` WHERE `username` = :username");
+      $stmt->execute(array(
+        ':username' => $username_now,
+      ));
+    }
+    catch (PDOException $e) {
+      $_SESSION['return'] = array(
+        'type' => 'danger',
+        'msg' => 'MySQL: '.$e
+      );
+      return false;
+    }
+
+    if(isset($postarray['domain'])) {
+      foreach ($postarray['domain'] as $domain) {
+        try {
+          $stmt = $pdo->prepare("INSERT INTO `domain_admins` (`username`, `domain`, `created`, `active`)
+            VALUES (:username, :domain, :created, :active)");
+          $stmt->execute(array(
+            ':username' => $username,
+            ':domain' => $domain,
+            ':created' => date('Y-m-d H:i:s'),
+            ':active' => $active
+          ));
+        }
+        catch (PDOException $e) {
+          $_SESSION['return'] = array(
+            'type' => 'danger',
+            'msg' => 'MySQL: '.$e
+          );
+          return false;
+        }
+      }
+    }
+
+    if (!empty($password) && !empty($password2)) {
+      if ($password != $password2) {
+        $_SESSION['return'] = array(
+          'type' => 'danger',
+          'msg' => sprintf($lang['danger']['password_mismatch'])
+        );
+        return false;
+      }
+      $password_hashed = hash_password($password);
+      try {
+        $stmt = $pdo->prepare("UPDATE `admin` SET `username` = :username1, `modified` = :modified, `active` = :active, `password` = :password_hashed WHERE `username` = :username2");
+        $stmt->execute(array(
+          ':password_hashed' => $password_hashed,
+          ':username1' => $username,
+          ':username2' => $username_now,
+          ':modified' => date('Y-m-d H:i:s'),
+          ':active' => $active
+        ));
+        if (isset($postarray['disable_tfa'])) {
+          $stmt = $pdo->prepare("UPDATE `tfa` SET `active` = '0' WHERE `username` = :username");
+          $stmt->execute(array(':username' => $username_now));
+        }
+        else {
+          $stmt = $pdo->prepare("UPDATE `tfa` SET `username` = :username WHERE `username` = :username_now");
+          $stmt->execute(array(':username' => $username, ':username_now' => $username_now));
+        }
+      }
+      catch (PDOException $e) {
+        $_SESSION['return'] = array(
+          'type' => 'danger',
+          'msg' => 'MySQL: '.$e
+        );
+        return false;
+      }
+    }
+    else {
+      try {
+        $stmt = $pdo->prepare("UPDATE `admin` SET `username` = :username1, `modified` = :modified, `active` = :active WHERE `username` = :username2");
+        $stmt->execute(array(
+          ':username1' => $username,
+          ':username2' => $username_now,
+          ':modified' => date('Y-m-d H:i:s'),
+          ':active' => $active
+        ));
+        if (isset($postarray['disable_tfa'])) {
+          $stmt = $pdo->prepare("UPDATE `tfa` SET `active` = '0' WHERE `username` = :username");
+          $stmt->execute(array(':username' => $username));
+        }
+        else {
+          $stmt = $pdo->prepare("UPDATE `tfa` SET `username` = :username WHERE `username` = :username_now");
+          $stmt->execute(array(':username' => $username, ':username_now' => $username_now));
+        }
+      }
+      catch (PDOException $e) {
+        $_SESSION['return'] = array(
+          'type' => 'danger',
+          'msg' => 'MySQL: '.$e
+        );
+        return false;
+      }
+    }
+    $_SESSION['return'] = array(
+      'type' => 'success',
+      'msg' => sprintf($lang['success']['domain_admin_modified'], htmlspecialchars($username))
+    );
+  }
+  // Domain administrator
+  // Can only edit itself
+  elseif ($_SESSION['mailcow_cc_role'] == "domainadmin") {
+    $username = $_SESSION['mailcow_cc_username'];
+    $password_old		= $postarray['user_old_pass'];
+    $password_new	= $postarray['user_new_pass'];
+    $password_new2	= $postarray['user_new_pass2'];
+
+    $stmt = $pdo->prepare("SELECT `password` FROM `admin`
+        WHERE `username` = :user");
+    $stmt->execute(array(':user' => $username));
+    $row = $stmt->fetch(PDO::FETCH_ASSOC);
+    if (!verify_ssha256($row['password'], $password_old)) {
+      $_SESSION['return'] = array(
+        'type' => 'danger',
+        'msg' => sprintf($lang['danger']['access_denied'])
+      );
+      return false;
+    }
+
+    if (!empty($password_new2) && !empty($password_new)) {
+      if ($password_new2 != $password_new) {
+        $_SESSION['return'] = array(
+          'type' => 'danger',
+          'msg' => sprintf($lang['danger']['password_mismatch'])
+        );
+        return false;
+      }
+      if (strlen($password_new) < "6" ||
+        !preg_match('/[A-Za-z]/', $password_new) ||
+        !preg_match('/[0-9]/', $password_new)) {
+          $_SESSION['return'] = array(
+            'type' => 'danger',
+            'msg' => sprintf($lang['danger']['password_complexity'])
+          );
+          return false;
+      }
+      $password_hashed = hash_password($password_new);
+      try {
+        $stmt = $pdo->prepare("UPDATE `admin` SET `modified` = :modified, `password` = :password_hashed WHERE `username` = :username");
+        $stmt->execute(array(
+          ':password_hashed' => $password_hashed,
+          ':modified' => date('Y-m-d H:i:s'),
+          ':username' => $username
+        ));
+      }
+      catch (PDOException $e) {
+        $_SESSION['return'] = array(
+          'type' => 'danger',
+          'msg' => 'MySQL: '.$e
+        );
+        return false;
+      }
+    }
+    
+    $_SESSION['return'] = array(
+      'type' => 'success',
+      'msg' => sprintf($lang['success']['domain_admin_modified'], htmlspecialchars($username))
+    );
+  }
+}
+function get_admin_details() {
+  // No parameter to be given, only one admin should exist
+	global $pdo;
+	global $lang;
+  $data = array();
+  if ($_SESSION['mailcow_cc_role'] != 'admin') {
+    $_SESSION['return'] = array(
+      'type' => 'danger',
+      'msg' => sprintf($lang['danger']['access_denied'])
+    );
+    return false;
+  }
+  try {
+    $stmt = $pdo->prepare("SELECT `username`, `modified`, `created` FROM `admin`WHERE `superadmin`='1' AND active='1'");
+    $stmt->execute();
+    $data = $stmt->fetch(PDO::FETCH_ASSOC);
+  }
+  catch(PDOException $e) {
+    $_SESSION['return'] = array(
+      'type' => 'danger',
+      'msg' => 'MySQL: '.$e
+    );
+  }
+  return $data;
+}
+function dkim_add_key($postarray) {
+	global $lang;
+	global $pdo;
+  if ($_SESSION['mailcow_cc_role'] != "admin") {
+    $_SESSION['return'] = array(
+      'type' => 'danger',
+      'msg' => sprintf($lang['danger']['access_denied'])
+    );
+    return false;
+  }
+  // if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $domain)) {
+    // $_SESSION['return'] = array(
+      // 'type' => 'danger',
+      // 'msg' => sprintf($lang['danger']['access_denied'])
+    // );
+    // return false;
+  // }
+  $key_length	= intval($postarray['key_size']);
+  $domain	= $postarray['domain'];
+  if (!is_valid_domain_name($domain) || !is_numeric($key_length)) {
+    $_SESSION['return'] = array(
+      'type' => 'danger',
+      'msg' => sprintf($lang['danger']['dkim_domain_or_sel_invalid'])
+    );
+    return false;
+  }
+
+  if (!empty(glob($GLOBALS['MC_DKIM_TXTS'] . '/' . $domain . '.dkim'))) {
+    $_SESSION['return'] = array(
+      'type' => 'danger',
+      'msg' => sprintf($lang['danger']['dkim_domain_or_sel_invalid'])
+    );
+    return false;
+  }
+
+  $config = array(
+    "digest_alg" => "sha256",
+    "private_key_bits" => $key_length,
+    "private_key_type" => OPENSSL_KEYTYPE_RSA,
+  );
+  if ($keypair_ressource = openssl_pkey_new($config)) {
+    $key_details = openssl_pkey_get_details($keypair_ressource);
+    $pubKey = implode(array_slice(
+        array_filter(
+          explode(PHP_EOL, $key_details['key'])
+        ), 1, -1)
+      );
+    // Save public key to file
+    file_put_contents($GLOBALS['MC_DKIM_TXTS'] . '/' . $domain . '.dkim', $pubKey);
+    // Save private key to file
+    openssl_pkey_export_to_file($keypair_ressource, $GLOBALS['MC_DKIM_KEYS'] . '/' . $domain . '.dkim');
+    $_SESSION['return'] = array(
+      'type' => 'success',
+      'msg' => sprintf($lang['success']['dkim_added'])
+    );
+    return true;
+  }
+  else {
+    $_SESSION['return'] = array(
+      'type' => 'danger',
+      'msg' => sprintf($lang['danger']['dkim_domain_or_sel_invalid'])
+    );
+    return false;
+  }
+}
+function dkim_get_key_details($domain) {
+  $data = array();
+  if (hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $domain)) {
+    $dkim_pubkey_file = escapeshellarg($GLOBALS["MC_DKIM_TXTS"]. "/" . $domain . "." . "dkim");
+    if (file_exists(substr($dkim_pubkey_file, 1, -1))) {
+      $data['pubkey'] = file_get_contents($GLOBALS["MC_DKIM_TXTS"]. "/" . $domain . "." . "dkim");
+      $data['length'] = (strlen($data['pubkey']) < 391) ? 1024 : 2048;
+      $data['dkim_txt'] = 'v=DKIM1;k=rsa;t=s;s=email;p=' . file_get_contents($GLOBALS["MC_DKIM_TXTS"]. "/" . $domain . "." . "dkim");
+    }
+  }
+  return $data;
+}
+function dkim_get_blind_keys() {
+	global $lang;
+  if ($_SESSION['mailcow_cc_role'] != "admin") {
+    $_SESSION['return'] = array(
+      'type' => 'danger',
+      'msg' => sprintf($lang['danger']['access_denied'])
+    );
+    return false;
+  }
+  $domains = array();
+  $dnstxt_folder = scandir($GLOBALS["MC_DKIM_TXTS"]);
+  $dnstxt_files = array_diff($dnstxt_folder, array('.', '..'));
+  foreach($dnstxt_files as $file) {
+    $domains[] = substr($file, 0, -5);
+  }
+  return array_diff($domains, array_merge(mailbox_get_domains(), mailbox_get_alias_domains()));
+}
+function dkim_delete_key($postarray) {
+	global $lang;
+  $domain	= $postarray['domain'];
+
+  if ($_SESSION['mailcow_cc_role'] != "admin") {
+    $_SESSION['return'] = array(
+      'type' => 'danger',
+      'msg' => sprintf($lang['danger']['access_denied'])
+    );
+    return false;
+  }
+  // if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $domain)) {
+    // $_SESSION['return'] = array(
+      // 'type' => 'danger',
+      // 'msg' => sprintf($lang['danger']['access_denied'])
+    // );
+    // return false;
+  // }
+  if (!is_valid_domain_name($domain)) {
+    $_SESSION['return'] = array(
+      'type' => 'danger',
+      'msg' => sprintf($lang['danger']['dkim_domain_or_sel_invalid'])
+    );
+    return false;
+  }
+  exec('rm ' . escapeshellarg($GLOBALS['MC_DKIM_TXTS'] . '/' . $domain . '.dkim'), $out, $return);
+  if ($return != "0") {
+    $_SESSION['return'] = array(
+      'type' => 'danger',
+      'msg' => sprintf($lang['danger']['dkim_remove_failed'])
+    );
+    return false;
+  }
+  exec('rm ' . escapeshellarg($GLOBALS['MC_DKIM_KEYS'] . '/' . $domain . '.dkim'), $out, $return);
+  if ($return != "0") {
+    $_SESSION['return'] = array(
+      'type' => 'danger',
+      'msg' => sprintf($lang['danger']['dkim_remove_failed'])
+    );
+    return false;
+  }
+  $_SESSION['return'] = array(
+    'type' => 'success',
+    'msg' => sprintf($lang['success']['dkim_removed'])
+  );
+  return true;
+}
+function mailbox_add_domain($postarray) {
+  // Array elements
+  // domain                 string
+  // description            string
+  // aliases                int
+  // mailboxes              int
+  // maxquota               int
+  // quota                  int
+  // active                 int
+  // relay_all_recipients   int
+  // backupmx               int
+	global $pdo;
+	global $lang;
+	if ($_SESSION['mailcow_cc_role'] != "admin") {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => sprintf($lang['danger']['access_denied'])
+		);
+		return false;
+	}
+	$domain				= idn_to_ascii(strtolower(trim($postarray['domain'])));
+	$description  = $postarray['description'];
+	$aliases			= $postarray['aliases'];
+	$mailboxes    = $postarray['mailboxes'];
+	$maxquota			= $postarray['maxquota'];
+	$quota				= $postarray['quota'];
+
+	if ($maxquota > $quota) {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => sprintf($lang['danger']['mailbox_quota_exceeds_domain_quota'])
+		);
+		return false;
+	}
+
+	isset($postarray['active'])               ? $active = '1'                 : $active = '0';
+	isset($postarray['relay_all_recipients'])	? $relay_all_recipients = '1'   : $relay_all_recipients = '0';
+	isset($postarray['backupmx'])             ? $backupmx = '1'               : $backupmx = '0';
+	isset($postarray['relay_all_recipients']) ? $backupmx = '1'               : true;
+
+	if (!is_valid_domain_name($domain)) {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => sprintf($lang['danger']['domain_invalid'])
+		);
+		return false;
+	}
+
+	foreach (array($quota, $maxquota, $mailboxes, $aliases) as $data) {
+		if (!is_numeric($data)) {
+			$_SESSION['return'] = array(
+				'type' => 'danger',
+				'msg' => sprintf($lang['danger']['object_is_not_numeric'], htmlspecialchars($data))
+			);
+			return false;
+		}
+	}
+
+	try {
+		$stmt = $pdo->prepare("SELECT `domain` FROM `domain`
+			WHERE `domain` = :domain");
+		$stmt->execute(array(':domain' => $domain));
+		$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
+		$stmt = $pdo->prepare("SELECT `alias_domain` FROM `alias_domain`
+			WHERE `alias_domain` = :domain");
+		$stmt->execute(array(':domain' => $domain));
+		$num_results = $num_results + count($stmt->fetchAll(PDO::FETCH_ASSOC));
+	}
+	catch(PDOException $e) {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => 'MySQL: '.$e
+		);
+		return false;
+	}
+	if ($num_results != 0) {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => sprintf($lang['danger']['domain_exists'], htmlspecialchars($domain))
+		);
+		return false;
+	}
+
+	try {
+		$stmt = $pdo->prepare("INSERT INTO `domain` (`domain`, `description`, `aliases`, `mailboxes`, `maxquota`, `quota`, `transport`, `backupmx`, `created`, `modified`, `active`, `relay_all_recipients`)
+			VALUES (:domain, :description, :aliases, :mailboxes, :maxquota, :quota, 'virtual', :backupmx, :created, :modified, :active, :relay_all_recipients)");
+		$stmt->execute(array(
+			':domain' => $domain,
+			':description' => $description,
+			':aliases' => $aliases,
+			':mailboxes' => $mailboxes,
+			':maxquota' => $maxquota,
+			':quota' => $quota,
+			':backupmx' => $backupmx,
+			':active' => $active,
+			':created' => date('Y-m-d H:i:s'),
+			':modified' => date('Y-m-d H:i:s'),
+			':relay_all_recipients' => $relay_all_recipients
+		));
+		$_SESSION['return'] = array(
+			'type' => 'success',
+			'msg' => sprintf($lang['success']['domain_added'], htmlspecialchars($domain))
+		);
+	}
+	catch (PDOException $e) {
+    mailbox_delete_domain(array('domain' => $domain));
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => 'MySQL: '.$e
+		);
+		return false;
+	}
+}
+function mailbox_add_alias($postarray) {
+  // Array elements
+  // address  string  (separated by " ", "," ";" "\n") - email address or domain
+  // goto     string  (separated by " ", "," ";" "\n")
+  // active   int
+	global $lang;
+	global $pdo;
+	$addresses  = array_map('trim', preg_split( "/( |,|;|\n)/", $postarray['address']));
+	$gotos      = array_map('trim', preg_split( "/( |,|;|\n)/", $postarray['goto']));
+	isset($postarray['active']) ? $active = '1' : $active = '0';
+	if (empty($addresses[0])) {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => sprintf($lang['danger']['alias_empty'])
+		);
+		return false;
+	}
+
+	if (empty($gotos[0])) {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => sprintf($lang['danger']['goto_empty'])
+		);
+		return false;
+	}
+
+	foreach ($addresses as $address) {
+		if (empty($address)) {
+			continue;
+		}
+
+		$domain       = idn_to_ascii(substr(strstr($address, '@'), 1));
+		$local_part   = strstr($address, '@', true);
+		$address      = $local_part.'@'.$domain;
+
+		try {
+			$stmt = $pdo->prepare("SELECT `domain` FROM `domain`
+				WHERE `domain`= :domain1 OR `domain` = (SELECT `target_domain` FROM `alias_domain` WHERE `alias_domain` = :domain2)");
+			$stmt->execute(array(':domain1' => $domain, ':domain2' => $domain));
+			$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
+      if ($num_results == 0) {
+        $_SESSION['return'] = array(
+          'type' => 'danger',
+          'msg' => sprintf($lang['danger']['domain_not_found'], $domain)
+        );
+        return false;
+      }
+
+			$stmt = $pdo->prepare("SELECT `address` FROM `alias`
+				WHERE `address`= :address");
+			$stmt->execute(array(':address' => $address));
+			$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
+      if ($num_results != 0) {
+        $_SESSION['return'] = array(
+          'type' => 'danger',
+          'msg' => sprintf($lang['danger']['is_alias_or_mailbox'], htmlspecialchars($address))
+        );
+        return false;
+      }
+
+			$stmt = $pdo->prepare("SELECT `address` FROM `spamalias`
+				WHERE `address`= :address");
+			$stmt->execute(array(':address' => $address));
+			$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
+      if ($num_results != 0) {
+        $_SESSION['return'] = array(
+          'type' => 'danger',
+          'msg' => sprintf($lang['danger']['is_spam_alias'], htmlspecialchars($address))
+        );
+        return false;
+      }
+		}
+		catch(PDOException $e) {
+			$_SESSION['return'] = array(
+				'type' => 'danger',
+				'msg' => 'MySQL: '.$e
+			);
+			return false;
+		}
+
+		if ((!filter_var($address, FILTER_VALIDATE_EMAIL) === true) && !empty($local_part)) {
+			$_SESSION['return'] = array(
+				'type' => 'danger',
+				'msg' => sprintf($lang['danger']['alias_invalid'])
+			);
+			return false;
+		}
+
+		if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $domain)) {
+			$_SESSION['return'] = array(
+				'type' => 'danger',
+				'msg' => sprintf($lang['danger']['access_denied'])
+			);
+			return false;
+		}
+
+		foreach ($gotos as &$goto) {
+			if (empty($goto)) {
+				continue;
+			}
+
+			$goto_domain		= idn_to_ascii(substr(strstr($goto, '@'), 1));
+			$goto_local_part	= strstr($goto, '@', true);
+			$goto				= $goto_local_part.'@'.$goto_domain;
+
+			$stmt = $pdo->prepare("SELECT `username` FROM `mailbox`
+				WHERE `kind` REGEXP 'location|thing|group'
+          AND `username`= :goto");
+			$stmt->execute(array(':goto' => $goto));
+			$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
+      if ($num_results != 0) {
+				$_SESSION['return'] = array(
+					'type' => 'danger',
+					'msg' => sprintf($lang['danger']['goto_invalid'])
+				);
+				return false;
+      }
+
+			if (!filter_var($goto, FILTER_VALIDATE_EMAIL) === true) {
+				$_SESSION['return'] = array(
+					'type' => 'danger',
+					'msg' => sprintf($lang['danger']['goto_invalid'])
+				);
+				return false;
+			}
+			if ($goto == $address) {
+				$_SESSION['return'] = array(
+					'type' => 'danger',
+					'msg' => sprintf($lang['danger']['alias_goto_identical'])
+				);
+				return false;
+			}
+		}
+
+		$gotos = array_filter($gotos);
+		$goto = implode(",", $gotos);
+
+		try {
+			$stmt = $pdo->prepare("INSERT INTO `alias` (`address`, `goto`, `domain`, `created`, `modified`, `active`)
+				VALUES (:address, :goto, :domain, :created, :modified, :active)");
+
+			if (!filter_var($address, FILTER_VALIDATE_EMAIL) === true) {
+				$stmt->execute(array(
+					':address' => '@'.$domain,
+					':goto' => $goto,
+					':domain' => $domain,
+					':created' => date('Y-m-d H:i:s'),
+					':modified' => date('Y-m-d H:i:s'),
+					':active' => $active
+				));
+			}
+			else {
+				$stmt->execute(array(
+					':address' => $address,
+					':goto' => $goto,
+					':domain' => $domain,
+					':created' => date('Y-m-d H:i:s'),
+					':modified' => date('Y-m-d H:i:s'),
+					':active' => $active
+				));
+			}
+			$_SESSION['return'] = array(
+				'type' => 'success',
+				'msg' => sprintf($lang['success']['alias_added'])
+			);
+		}
+		catch (PDOException $e) {
+      mailbox_delete_alias(array('address' => $address));
+			$_SESSION['return'] = array(
+				'type' => 'danger',
+				'msg' => 'MySQL: '.$e
+			);
+			return false;
+		}
+	}
+	$_SESSION['return'] = array(
+		'type' => 'success',
+		'msg' => sprintf($lang['success']['alias_added'])
+	);
+}
+function mailbox_add_alias_domain($postarray) {
+  // Array elements
+  // active         int
+  // alias_domain   string
+  // target_domain  string
+	global $lang;
+	global $pdo;
+	isset($postarray['active']) ? $active = '1' : $active = '0';
+	$alias_domain     = idn_to_ascii(strtolower(trim($postarray['alias_domain'])));
+	$target_domain    = idn_to_ascii(strtolower(trim($postarray['target_domain'])));
+
+	if (!is_valid_domain_name($alias_domain)) {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => sprintf($lang['danger']['alias_domain_invalid'])
+		);
+		return false;
+	}
+
+	if (!is_valid_domain_name($target_domain)) {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => sprintf($lang['danger']['target_domain_invalid'])
+		);
+		return false;
+	}
+
+	if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $target_domain)) {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => sprintf($lang['danger']['access_denied'])
+		);
+		return false;
+	}
+
+	if ($alias_domain == $target_domain) {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => sprintf($lang['danger']['aliasd_targetd_identical'])
+		);
+		return false;
+	}
+
+	try {
+		$stmt = $pdo->prepare("SELECT `domain` FROM `domain`
+			WHERE `domain`= :target_domain");
+		$stmt->execute(array(':target_domain' => $target_domain));
+		$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
+    if ($num_results == 0) {
+      $_SESSION['return'] = array(
+        'type' => 'danger',
+        'msg' => sprintf($lang['danger']['targetd_not_found'])
+      );
+      return false;
+    }
+
+		$stmt = $pdo->prepare("SELECT `alias_domain` FROM `alias_domain` WHERE `alias_domain`= :alias_domain
+			UNION
+			SELECT `alias_domain` FROM `alias_domain` WHERE `alias_domain`= :alias_domain_in_domain");
+		$stmt->execute(array(':alias_domain' => $alias_domain, ':alias_domain_in_domain' => $alias_domain));
+		$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
+    if ($num_results != 0) {
+      $_SESSION['return'] = array(
+        'type' => 'danger',
+        'msg' => sprintf($lang['danger']['aliasd_exists'])
+      );
+      return false;
+    }
+  }
+	catch(PDOException $e) {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => 'MySQL: '.$e
+		);
+		return false;
+	}
+
+	try {
+		$stmt = $pdo->prepare("INSERT INTO `alias_domain` (`alias_domain`, `target_domain`, `created`, `modified`, `active`)
+			VALUES (:alias_domain, :target_domain, :created, :modified, :active)");
+		$stmt->execute(array(
+			':alias_domain' => $alias_domain,
+			':target_domain' => $target_domain,
+			':created' => date('Y-m-d H:i:s'),
+			':modified' => date('Y-m-d H:i:s'),
+			':active' => $active
+		));
+		$_SESSION['return'] = array(
+			'type' => 'success',
+			'msg' => sprintf($lang['success']['aliasd_added'], htmlspecialchars($alias_domain))
+		);
+	}
+	catch (PDOException $e) {
+    mailbox_delete_alias_domain(array('alias_domain' => $alias_domain));
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => 'MySQL: '.$e
+		);
+		return false;
+	}
+}
+function mailbox_add_mailbox($postarray) {
+  // Array elements
+  // active             int
+  // local_part         string
+  // domain             string
+  // name               string    (username if empty)
+  // password           string
+  // password2          string
+  // quota              int       (MiB)
+  // active             int
+
+	global $pdo;
+	global $lang;
+	$local_part   = strtolower(trim($postarray['local_part']));
+	$domain       = idn_to_ascii(strtolower(trim($postarray['domain'])));
+  $username     = $local_part . '@' . $domain;
+	if (!filter_var($username, FILTER_VALIDATE_EMAIL)) {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => sprintf($lang['danger']['mailbox_invalid'])
+		);
+		return false;
+	}
+	if (empty($postarray['local_part'])) {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => sprintf($lang['danger']['mailbox_invalid'])
+		);
+		return false;
+	}
+	$password     = $postarray['password'];
+	$password2    = $postarray['password2'];
+	$name         = $postarray['name'];
+  $quota_m			= filter_var($postarray['quota'], FILTER_SANITIZE_NUMBER_FLOAT);
+
+	if (empty($name)) {
+		$name = $local_part;
+	}
+
+	isset($postarray['active']) ? $active = '1' : $active = '0';
+
+	$quota_b		= ($quota_m * 1048576);
+	$maildir		= $domain."/".$local_part."/";
+
+	if (!is_valid_domain_name($domain)) {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => sprintf($lang['danger']['domain_invalid'])
+		);
+		return false;
+	}
+
+	if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $domain)) {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => sprintf($lang['danger']['access_denied'])
+		);
+		return false;
+	}
+
+	try {
+		$stmt = $pdo->prepare("SELECT `mailboxes`, `maxquota`, `quota` FROM `domain`
+			WHERE `domain` = :domain");
+		$stmt->execute(array(':domain' => $domain));
+		$DomainData = $stmt->fetch(PDO::FETCH_ASSOC);
+
+		$stmt = $pdo->prepare("SELECT 
+			COUNT(*) as count,
+			COALESCE(ROUND(SUM(`quota`)/1048576), 0) as `quota`
+				FROM `mailbox`
+					WHERE `kind` NOT REGEXP 'location|thing|group'
+            AND `domain` = :domain");
+		$stmt->execute(array(':domain' => $domain));
+		$MailboxData = $stmt->fetch(PDO::FETCH_ASSOC);
+
+		$stmt = $pdo->prepare("SELECT `local_part` FROM `mailbox` WHERE `local_part` = :local_part and `domain`= :domain");
+		$stmt->execute(array(':local_part' => $local_part, ':domain' => $domain));
+		$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
+    if ($num_results != 0) {
+      $_SESSION['return'] = array(
+        'type' => 'danger',
+        'msg' => sprintf($lang['danger']['object_exists'], htmlspecialchars($username))
+      );
+      return false;
+    }
+
+		$stmt = $pdo->prepare("SELECT `address` FROM `alias` WHERE address= :username");
+		$stmt->execute(array(':username' => $username));
+		$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
+    if ($num_results != 0) {
+      $_SESSION['return'] = array(
+        'type' => 'danger',
+        'msg' => sprintf($lang['danger']['is_alias'], htmlspecialchars($username))
+      );
+      return false;
+    }
+
+		$stmt = $pdo->prepare("SELECT `address` FROM `spamalias` WHERE `address`= :username");
+		$stmt->execute(array(':username' => $username));
+		$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
+    if ($num_results != 0) {
+      $_SESSION['return'] = array(
+        'type' => 'danger',
+        'msg' => sprintf($lang['danger']['is_spam_alias'], htmlspecialchars($username))
+      );
+      return false;
+    }
+
+		$stmt = $pdo->prepare("SELECT `domain` FROM `domain` WHERE `domain`= :domain");
+		$stmt->execute(array(':domain' => $domain));
+		$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
+    if ($num_results == 0) {
+      $_SESSION['return'] = array(
+        'type' => 'danger',
+        'msg' => sprintf($lang['danger']['domain_not_found'], $domain)
+      );
+      return false;
+    }
+  }
+	catch(PDOException $e) {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => 'MySQL: '.$e
+		);
+		return false;
+	}
+
+	if (!is_numeric($quota_m) || $quota_m == "0") {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => sprintf($lang['danger']['quota_not_0_not_numeric'])
+		);
+		return false;
+	}
+
+	if (!empty($password) && !empty($password2)) {
+		if ($password != $password2) {
+			$_SESSION['return'] = array(
+				'type' => 'danger',
+				'msg' => sprintf($lang['danger']['password_mismatch'])
+			);
+			return false;
+		}
+		$password_hashed = hash_password($password);
+	}
+	else {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => sprintf($lang['danger']['password_empty'])
+		);
+		return false;
+	}
+
+	if ($MailboxData['count'] >= $DomainData['mailboxes']) {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => sprintf($lang['danger']['max_mailbox_exceeded'], $MailboxData['count'], $DomainData['mailboxes'])
+		);
+		return false;
+	}
+
+	if ($quota_m > $DomainData['maxquota']) {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => sprintf($lang['danger']['mailbox_quota_exceeded'], $DomainData['maxquota'])
+		);
+		return false;
+	}
+
+	if (($MailboxData['quota'] + $quota_m) > $DomainData['quota']) {
+		$quota_left_m = ($DomainData['quota'] - $MailboxData['quota']);
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => sprintf($lang['danger']['mailbox_quota_left_exceeded'], $quota_left_m)
+		);
+		return false;
+	}
+
+	try {
+		$stmt = $pdo->prepare("INSERT INTO `mailbox` (`username`, `password`, `name`, `maildir`, `quota`, `local_part`, `domain`, `created`, `modified`, `active`) 
+			VALUES (:username, :password_hashed, :name, :maildir, :quota_b, :local_part, :domain, :created, :modified, :active)");
+		$stmt->execute(array(
+			':username' => $username,
+			':password_hashed' => $password_hashed,
+			':name' => $name,
+			':maildir' => $maildir,
+			':quota_b' => $quota_b,
+			':local_part' => $local_part,
+			':domain' => $domain,
+			':created' => date('Y-m-d H:i:s'),
+			':modified' => date('Y-m-d H:i:s'),
+			':active' => $active
+		));
+
+		$stmt = $pdo->prepare("INSERT INTO `quota2` (`username`, `bytes`, `messages`)
+			VALUES (:username, '0', '0')");
+		$stmt->execute(array(':username' => $username));
+
+		$stmt = $pdo->prepare("INSERT INTO `alias` (`address`, `goto`, `domain`, `created`, `modified`, `active`)
+			VALUES (:username1, :username2, :domain, :created, :modified, :active)");
+		$stmt->execute(array(
+			':username1' => $username,
+			':username2' => $username,
+			':domain' => $domain,
+			':created' => date('Y-m-d H:i:s'),
+			':modified' => date('Y-m-d H:i:s'),
+			':active' => $active
+		));
+
+		$_SESSION['return'] = array(
+			'type' => 'success',
+			'msg' => sprintf($lang['success']['mailbox_added'], htmlspecialchars($username))
+		);
+	}
+	catch (PDOException $e) {
+    mailbox_delete_mailbox(array('username' => $username));
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => 'MySQL: '.$e
+		);
+		return false;
+	}
+}
+function mailbox_add_resource($postarray) {
+  // Array elements
+  // active             int
+  // domain             string
+  // description        string
+  // multiple_bookings  int
+  // kind               string
+
+	global $pdo;
+	global $lang;
+	$domain             = idn_to_ascii(strtolower(trim($postarray['domain'])));
+  $description        = $postarray['description'];
+  $local_part         = preg_replace('/[^\da-z]/i', '', preg_quote($description, '/'));
+  $name               = $local_part . '@' . $domain;
+  $kind               = $postarray['kind'];
+	isset($postarray['active']) ? $active = '1' : $active = '0';
+	isset($postarray['multiple_bookings']) ? $multiple_bookings = '1' : $multiple_bookings = '0';
+
+	if (!filter_var($name, FILTER_VALIDATE_EMAIL)) {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => sprintf($lang['danger']['resource_invalid'])
+		);
+		return false;
+	}
+
+	if (empty($description)) {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => sprintf($lang['danger']['description_invalid'])
+		);
+		return false;
+  }
+  
+	if ($kind != 'location' && $kind != 'group' && $kind != 'thing') {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => sprintf($lang['danger']['resource_invalid'])
+		);
+		return false;
+	}
+
+	if (!is_valid_domain_name($domain)) {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => sprintf($lang['danger']['domain_invalid'])
+		);
+		return false;
+	}
+
+	if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $domain)) {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => sprintf($lang['danger']['access_denied'])
+		);
+		return false;
+	}
+
+	try {
+		$stmt = $pdo->prepare("SELECT `username` FROM `mailbox` WHERE `username` = :name");
+		$stmt->execute(array(':name' => $name));
+		$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
+    if ($num_results != 0) {
+      $_SESSION['return'] = array(
+        'type' => 'danger',
+        'msg' => sprintf($lang['danger']['object_exists'], htmlspecialchars($name))
+      );
+      return false;
+    }
+
+		$stmt = $pdo->prepare("SELECT `address` FROM `alias` WHERE address= :name");
+		$stmt->execute(array(':name' => $name));
+		$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
+    if ($num_results != 0) {
+      $_SESSION['return'] = array(
+        'type' => 'danger',
+        'msg' => sprintf($lang['danger']['is_alias'], htmlspecialchars($name))
+      );
+      return false;
+    }
+
+		$stmt = $pdo->prepare("SELECT `address` FROM `spamalias` WHERE `address`= :name");
+		$stmt->execute(array(':name' => $name));
+		$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
+    if ($num_results != 0) {
+      $_SESSION['return'] = array(
+        'type' => 'danger',
+        'msg' => sprintf($lang['danger']['is_spam_alias'], htmlspecialchars($name))
+      );
+      return false;
+    }
+
+		$stmt = $pdo->prepare("SELECT `domain` FROM `domain` WHERE `domain`= :domain");
+		$stmt->execute(array(':domain' => $domain));
+		$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
+    if ($num_results == 0) {
+      $_SESSION['return'] = array(
+        'type' => 'danger',
+        'msg' => sprintf($lang['danger']['domain_not_found'], $domain)
+      );
+      return false;
+    }
+  }
+	catch(PDOException $e) {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => 'MySQL: '.$e
+		);
+		return false;
+	}
+
+	try {
+		$stmt = $pdo->prepare("INSERT INTO `mailbox` (`username`, `password`, `name`, `maildir`, `quota`, `local_part`, `domain`, `created`, `modified`, `active`, `multiple_bookings`, `kind`) 
+			VALUES (:name, 'RESOURCE', :description, 'RESOURCE', 0, :local_part, :domain, :created, :modified, :active, :multiple_bookings, :kind)");
+		$stmt->execute(array(
+			':name' => $name,
+			':description' => $description,
+			':local_part' => $local_part,
+			':domain' => $domain,
+			':created' => date('Y-m-d H:i:s'),
+			':modified' => date('Y-m-d H:i:s'),
+			':active' => $active,
+			':kind' => $kind,
+			':multiple_bookings' => $multiple_bookings
+		));
+
+		$_SESSION['return'] = array(
+			'type' => 'success',
+			'msg' => sprintf($lang['success']['resource_added'], htmlspecialchars($name))
+		);
+	}
+	catch (PDOException $e) {
+    mailbox_delete_resource(array('name' => $name));
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => 'MySQL: '.$e
+		);
+		return false;
+	}
+}
+function mailbox_edit_alias_domain($postarray) {
+  // Array elements
+  // active             int
+  // alias_domain_now   string
+  // alias_domain       string
+	global $lang;
+	global $pdo;
+	isset($postarray['active']) ? $active = '1' : $active = '0';
+	$alias_domain       = idn_to_ascii(strtolower(trim($postarray['alias_domain'])));
+	$alias_domain_now   = strtolower(trim($postarray['alias_domain_now']));
+	if (!is_valid_domain_name($alias_domain)) {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => sprintf($lang['danger']['alias_domain_invalid'])
+		);
+		return false;
+	}
+
+	if (!is_valid_domain_name($alias_domain_now)) {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => sprintf($lang['danger']['alias_domain_invalid'])
+		);
+		return false;
+	}
+
+	try {
+		$stmt = $pdo->prepare("SELECT `target_domain` FROM `alias_domain`
+				WHERE `alias_domain`= :alias_domain_now");
+		$stmt->execute(array(':alias_domain_now' => $alias_domain_now));
+		$DomainData = $stmt->fetch(PDO::FETCH_ASSOC);
+	}
+	catch(PDOException $e) {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => 'MySQL: '.$e
+		);
+		return false;
+	}
+	if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $DomainData['target_domain'])) {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => sprintf($lang['danger']['access_denied'])
+		);
+		return false;
+	}
+
+	try {
+		$stmt = $pdo->prepare("SELECT `target_domain` FROM `alias_domain`
+		WHERE `target_domain`= :alias_domain");
+		$stmt->execute(array(':alias_domain' => $alias_domain));
+		$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
+	}
+	catch(PDOException $e) {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => 'MySQL: '.$e
+		);
+		return false;
+	}
+	if ($num_results != 0) {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => sprintf($lang['danger']['aliasd_targetd_identical'])
+		);
+		return false;
+	}
+
+	try {
+		$stmt = $pdo->prepare("UPDATE `alias_domain` SET
+      `alias_domain` = :alias_domain,
+      `active` = :active,
+      `modified` = :modified,
+        WHERE `alias_domain` = :alias_domain_now");
+		$stmt->execute(array(
+			':alias_domain' => $alias_domain,
+      ':modified' => date('Y-m-d H:i:s'),
+			':alias_domain_now' => $alias_domain_now,
+			':active' => $active
+		));
+	}
+	catch (PDOException $e) {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => 'MySQL: '.$e
+		);
+		return false;
+	}
+
+	$_SESSION['return'] = array(
+		'type' => 'success',
+		'msg' => sprintf($lang['success']['aliasd_modified'], htmlspecialchars($alias_domain))
+	);
+}
+function mailbox_edit_alias($postarray) {
+  // Array elements
+  // address            string
+  // goto               string    (separated by " ", "," ";" "\n") - email address or domain
+  // active             int
+	global $lang;
+	global $pdo;
+	$address      = $postarray['address'];
+	$domain       = idn_to_ascii(substr(strstr($address, '@'), 1));
+	$local_part   = strstr($address, '@', true);
+	if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $domain)) {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => sprintf($lang['danger']['access_denied'])
+		);
+		return false;
+	}
+	if (empty($postarray['goto'])) {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => sprintf($lang['danger']['goto_empty'])
+		);
+		return false;
+	}
+	$gotos = array_map('trim', preg_split( "/( |,|;|\n)/", $postarray['goto']));
+	foreach ($gotos as &$goto) {
+		if (empty($goto)) {
+			continue;
+		}
+		if (!filter_var($goto, FILTER_VALIDATE_EMAIL)) {
+			$_SESSION['return'] = array(
+				'type' => 'danger',
+				'msg' =>sprintf($lang['danger']['goto_invalid'])
+			);
+			return false;
+		}
+		if ($goto == $address) {
+			$_SESSION['return'] = array(
+				'type' => 'danger',
+				'msg' => sprintf($lang['danger']['alias_goto_identical'])
+			);
+			return false;
+		}
+	}
+	$gotos = array_filter($gotos);
+	$goto = implode(",", $gotos);
+	isset($postarray['active']) ? $active = '1' : $active = '0';
+	if ((!filter_var($address, FILTER_VALIDATE_EMAIL) === true) && !empty($local_part)) {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => sprintf($lang['danger']['alias_invalid'])
+		);
+		return false;
+	}
+
+	try {
+		$stmt = $pdo->prepare("UPDATE `alias` SET
+      `goto` = :goto,
+      `active`= :active,
+      `modified` = :modified
+        WHERE `address` = :address");
+		$stmt->execute(array(
+			':goto' => $goto,
+			':active' => $active,
+			':address' => $address,
+      ':modified' => date('Y-m-d H:i:s'),
+		));
+		$_SESSION['return'] = array(
+			'type' => 'success',
+		'msg' => sprintf($lang['success']['alias_modified'], htmlspecialchars($address))
+		);
+	}
+	catch (PDOException $e) {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => 'MySQL: '.$e
+		);
+		return false;
+	}
+}
+function mailbox_edit_domain($postarray) {
+  // Array elements
+  // domain                 string
+  // description            string
+  // active                 int
+  // relay_all_recipients   int
+  // backupmx               int
+  // aliases                float
+  // mailboxes              float
+  // maxquota               float
+  // quota                  float     (Byte)
+  // active                 int
+
+	global $lang;
+	global $pdo;
+  
+  $domain       = idn_to_ascii($postarray['domain']);
+	if (!is_valid_domain_name($domain)) {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => sprintf($lang['danger']['domain_invalid'])
+		);
+		return false;
+	}
+
+	if ($_SESSION['mailcow_cc_role'] == "domainadmin" && 	hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $domain)) {
+    $description  = $postarray['description'];
+    isset($postarray['active']) ? $active = '1' : $active = '0';
+    try {
+      $stmt = $pdo->prepare("UPDATE `domain` SET 
+      `modified`= :modified,
+      `description` = :description
+        WHERE `domain` = :domain");
+      $stmt->execute(array(
+        ':modified' => date('Y-m-d H:i:s'),
+        ':description' => $description,
+        ':domain' => $domain
+      ));
+      $_SESSION['return'] = array(
+        'type' => 'success',
+        'msg' => sprintf($lang['success']['domain_modified'], htmlspecialchars($domain))
+      );
+    }
+    catch (PDOException $e) {
+      $_SESSION['return'] = array(
+        'type' => 'danger',
+        'msg' => 'MySQL: '.$e
+      );
+      return false;
+    }
+  }
+  elseif ($_SESSION['mailcow_cc_role'] == "admin") {
+    $description  = $postarray['description'];
+    isset($postarray['active']) ? $active = '1' : $active = '0';
+    $aliases		= filter_var($postarray['aliases'], FILTER_SANITIZE_NUMBER_FLOAT);
+    $mailboxes  = filter_var($postarray['mailboxes'], FILTER_SANITIZE_NUMBER_FLOAT);
+    $maxquota		= filter_var($postarray['maxquota'], FILTER_SANITIZE_NUMBER_FLOAT);
+    $quota			= filter_var($postarray['quota'], FILTER_SANITIZE_NUMBER_FLOAT);
+    isset($postarray['relay_all_recipients']) ? $relay_all_recipients = '1' : $relay_all_recipients = '0';
+    isset($postarray['backupmx']) ? $backupmx = '1' : $backupmx = '0';
+    isset($postarray['relay_all_recipients']) ? $backupmx = '1' : true;
+    try {
+      // GET MAILBOX DATA
+      $stmt = $pdo->prepare("SELECT 
+          COUNT(*) AS count,
+          MAX(COALESCE(ROUND(`quota`/1048576), 0)) AS `maxquota`,
+          COALESCE(ROUND(SUM(`quota`)/1048576), 0) AS `quota`
+            FROM `mailbox`
+              WHERE `kind` NOT REGEXP 'location|thing|group'
+                AND domain = :domain");
+      $stmt->execute(array(':domain' => $domain));
+      $MailboxData = $stmt->fetch(PDO::FETCH_ASSOC);
+      // GET ALIAS DATA
+      $stmt = $pdo->prepare("SELECT COUNT(*) AS `count` FROM `alias`
+          WHERE domain = :domain
+          AND address NOT IN (
+            SELECT `username` FROM `mailbox`
+          )");
+      $stmt->execute(array(':domain' => $domain));
+      $AliasData = $stmt->fetch(PDO::FETCH_ASSOC);
+    }
+    catch(PDOException $e) {
+      $_SESSION['return'] = array(
+        'type' => 'danger',
+        'msg' => 'MySQL: '.$e
+      );
+      return false;
+    }
+
+    if ($maxquota > $quota) {
+      $_SESSION['return'] = array(
+        'type' => 'danger',
+        'msg' => sprintf($lang['danger']['mailbox_quota_exceeds_domain_quota'])
+      );
+      return false;
+    }
+
+    if ($MailboxData['maxquota'] > $maxquota) {
+      $_SESSION['return'] = array(
+        'type' => 'danger',
+        'msg' => sprintf($lang['danger']['max_quota_in_use'], $MailboxData['maxquota'])
+      );
+      return false;
+    }
+
+    if ($MailboxData['quota'] > $quota) {
+      $_SESSION['return'] = array(
+        'type' => 'danger',
+        'msg' => sprintf($lang['danger']['domain_quota_m_in_use'], $MailboxData['quota'])
+      );
+      return false;
+    }
+
+    if ($MailboxData['count'] > $mailboxes) {
+      $_SESSION['return'] = array(
+        'type' => 'danger',
+        'msg' => sprintf($lang['danger']['mailboxes_in_use'], $MailboxData['count'])
+      );
+      return false;
+    }
+
+    if ($AliasData['count'] > $aliases) {
+      $_SESSION['return'] = array(
+        'type' => 'danger',
+        'msg' => sprintf($lang['danger']['aliases_in_use'], $AliasData['count'])
+      );
+      return false;
+    }
+    try {
+      $stmt = $pdo->prepare("UPDATE `domain` SET 
+      `modified`= :modified,
+      `relay_all_recipients` = :relay_all_recipients,
+      `backupmx` = :backupmx,
+      `active` = :active,
+      `quota` = :quota,
+      `maxquota` = :maxquota,
+      `mailboxes` = :mailboxes,
+      `aliases` = :aliases,
+      `description` = :description
+        WHERE `domain` = :domain");
+      $stmt->execute(array(
+        ':relay_all_recipients' => $relay_all_recipients,
+        ':backupmx' => $backupmx,
+        ':active' => $active,
+        ':quota' => $quota,
+        ':maxquota' => $maxquota,
+        ':mailboxes' => $mailboxes,
+        ':aliases' => $aliases,
+        ':modified' => date('Y-m-d H:i:s'),
+        ':description' => $description,
+        ':domain' => $domain
+      ));
+      $_SESSION['return'] = array(
+        'type' => 'success',
+        'msg' => sprintf($lang['success']['domain_modified'], htmlspecialchars($domain))
+      );
+    }
+    catch (PDOException $e) {
+      $_SESSION['return'] = array(
+        'type' => 'danger',
+        'msg' => 'MySQL: '.$e
+      );
+      return false;
+    }
+  }
+}
+function mailbox_edit_mailbox($postarray) {
+	global $lang;
+	global $pdo;
+	isset($postarray['active']) ? $active = '1' : $active = '0';
+	if (!filter_var($postarray['username'], FILTER_VALIDATE_EMAIL)) {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => sprintf($lang['danger']['username_invalid'])
+		);
+		return false;
+	}
+	$quota_m      = $postarray['quota'];
+	$quota_b      = $quota_m*1048576;
+	$username     = $postarray['username'];
+	$name         = $postarray['name'];
+	$password     = $postarray['password'];
+	$password2    = $postarray['password2'];
+
+	try {
+		$stmt = $pdo->prepare("SELECT `domain`
+			FROM `mailbox`
+				WHERE username = :username");
+		$stmt->execute(array(':username' => $username));
+		$MailboxData1 = $stmt->fetch(PDO::FETCH_ASSOC);
+
+		$stmt = $pdo->prepare("SELECT 
+			COALESCE(ROUND(SUM(`quota`)/1048576), 0) as `quota_m_now`
+				FROM `mailbox`
+					WHERE `username` = :username");
+		$stmt->execute(array(':username' => $username));
+		$MailboxData2 = $stmt->fetch(PDO::FETCH_ASSOC);
+
+		$stmt = $pdo->prepare("SELECT 
+			COALESCE(ROUND(SUM(`quota`)/1048576), 0) as `quota_m_in_use`
+				FROM `mailbox`
+					WHERE `domain` = :domain");
+		$stmt->execute(array(':domain' => $MailboxData1['domain']));
+		$MailboxData3 = $stmt->fetch(PDO::FETCH_ASSOC);
+
+		$stmt = $pdo->prepare("SELECT `quota`, `maxquota`
+			FROM `domain`
+				WHERE `domain` = :domain");
+		$stmt->execute(array(':domain' => $MailboxData1['domain']));
+		$DomainData = $stmt->fetch(PDO::FETCH_ASSOC);
+	}
+	catch(PDOException $e) {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => 'MySQL: '.$e
+		);
+		return false;
+	}
+
+	if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $MailboxData1['domain'])) {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => sprintf($lang['danger']['access_denied'])
+		);
+		return false;
+	}
+	if (!is_numeric($quota_m) || $quota_m == "0") {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => sprintf($lang['danger']['quota_not_0_not_numeric'], htmlspecialchars($quota_m))
+		);
+		return false;
+	}
+	if ($quota_m > $DomainData['maxquota']) {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => sprintf($lang['danger']['mailbox_quota_exceeded'], $DomainData['maxquota'])
+		);
+		return false;
+	}
+	if (($MailboxData3['quota_m_in_use'] - $MailboxData2['quota_m_now'] + $quota_m) > $DomainData['quota']) {
+		$quota_left_m = ($DomainData['quota'] - $MailboxData3['quota_m_in_use'] + $MailboxData2['quota_m_now']);
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => sprintf($lang['danger']['mailbox_quota_left_exceeded'], $quota_left_m)
+		);
+		return false;
+	}
+
+  // Get sender_acl items set by admin
+  $sender_acl_admin = array_merge(
+    mailbox_get_sender_acl_handles($username)['sender_acl_domains']['ro'],
+    mailbox_get_sender_acl_handles($username)['sender_acl_addresses']['ro']
+  );
+
+  // Get sender_acl items from POST array
+  (isset($postarray['sender_acl'])) ? $sender_acl_domain_admin = $postarray['sender_acl'] : $sender_acl_domain_admin = array();
+
+	if (!empty($sender_acl_domain_admin) || !empty($sender_acl_admin)) {
+    // Check items in POST array
+		foreach ($sender_acl_domain_admin as $sender_acl) {
+			if (!filter_var($sender_acl, FILTER_VALIDATE_EMAIL) && !is_valid_domain_name(ltrim($sender_acl, '@'))) {
+					$_SESSION['return'] = array(
+						'type' => 'danger',
+						'msg' => sprintf($lang['danger']['sender_acl_invalid'])
+					);
+					return false;
+			}
+      if (is_valid_domain_name(ltrim($sender_acl, '@'))) {
+        if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], ltrim($sender_acl, '@'))) {
+					$_SESSION['return'] = array(
+						'type' => 'danger',
+						'msg' => sprintf($lang['danger']['sender_acl_invalid'])
+					);
+					return false;
+        }
+      }
+			if (filter_var($sender_acl, FILTER_VALIDATE_EMAIL)) {
+        if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $sender_acl)) {
+					$_SESSION['return'] = array(
+						'type' => 'danger',
+						'msg' => sprintf($lang['danger']['sender_acl_invalid'])
+					);
+					return false;
+        }
+      }
+    }
+
+    // Merge both arrays
+    $sender_acl_merged = array_merge($sender_acl_domain_admin, $sender_acl_admin);
+
+    try {
+      $stmt = $pdo->prepare("DELETE FROM `sender_acl` WHERE `logged_in_as` = :username");
+      $stmt->execute(array(
+        ':username' => $username
+      ));
+    }
+    catch (PDOException $e) {
+      $_SESSION['return'] = array(
+        'type' => 'danger',
+        'msg' => 'MySQL: '.$e
+      );
+      return false;
+    }
+
+		foreach ($sender_acl_merged as $sender_acl) {
+      $domain = ltrim($sender_acl, '@');
+      if (is_valid_domain_name($domain)) {
+        $sender_acl = '@' . $domain;
+      }
+			try {
+				$stmt = $pdo->prepare("INSERT INTO `sender_acl` (`send_as`, `logged_in_as`)
+					VALUES (:sender_acl, :username)");
+				$stmt->execute(array(
+					':sender_acl' => $sender_acl,
+					':username' => $username
+				));
+			}
+			catch (PDOException $e) {
+				$_SESSION['return'] = array(
+					'type' => 'danger',
+					'msg' => 'MySQL: '.$e
+				);
+				return false;
+			}
+		}
+	}
+  else {
+    try {
+      $stmt = $pdo->prepare("DELETE FROM `sender_acl` WHERE `logged_in_as` = :username");
+      $stmt->execute(array(
+        ':username' => $username
+      ));
+    }
+    catch (PDOException $e) {
+      $_SESSION['return'] = array(
+        'type' => 'danger',
+        'msg' => 'MySQL: '.$e
+      );
+      return false;
+    }
+  }
+	if (!empty($password) && !empty($password2)) {
+		if ($password != $password2) {
+			$_SESSION['return'] = array(
+				'type' => 'danger',
+				'msg' => sprintf($lang['danger']['password_mismatch'])
+			);
+			return false;
+		}
+		$password_hashed = hash_password($password);
+		try {
+			$stmt = $pdo->prepare("UPDATE `alias` SET
+					`modified` = :modified,
+					`active` = :active
+						WHERE `address` = :address");
+			$stmt->execute(array(
+				':address' => $username,
+				':modified' => date('Y-m-d H:i:s'),
+				':active' => $active
+			));
+			$stmt = $pdo->prepare("UPDATE `mailbox` SET
+					`modified` = :modified,
+					`active` = :active,
+					`password` = :password_hashed,
+					`name`= :name,
+					`quota` = :quota_b
+						WHERE `username` = :username");
+			$stmt->execute(array(
+				':modified' => date('Y-m-d H:i:s'),
+				':password_hashed' => $password_hashed,
+				':active' => $active,
+				':name' => $name,
+				':quota_b' => $quota_b,
+				':username' => $username
+			));
+			$_SESSION['return'] = array(
+				'type' => 'success',
+				'msg' => sprintf($lang['success']['mailbox_modified'], $username)
+			);
+			return true;
+		}
+		catch (PDOException $e) {
+			$_SESSION['return'] = array(
+				'type' => 'danger',
+				'msg' => 'MySQL: '.$e
+			);
+			return false;
+		}
+	}
+	try {
+		$stmt = $pdo->prepare("UPDATE `alias` SET
+				`modified` = :modified,
+				`active` = :active
+					WHERE `address` = :address");
+		$stmt->execute(array(
+			':address' => $username,
+			':modified' => date('Y-m-d H:i:s'),
+			':active' => $active
+		));
+		$stmt = $pdo->prepare("UPDATE `mailbox` SET
+				`modified` = :modified,
+				`active` = :active,
+				`name`= :name,
+				`quota` = :quota_b
+					WHERE `username` = :username");
+		$stmt->execute(array(
+			':active' => $active,
+			':modified' => date('Y-m-d H:i:s'),
+			':name' => $name,
+			':quota_b' => $quota_b,
+			':username' => $username
+		));
+		$_SESSION['return'] = array(
+			'type' => 'success',
+			'msg' => sprintf($lang['success']['mailbox_modified'], $username)
+		);
+		return true;
+	}
+	catch (PDOException $e) {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => 'MySQL: '.$e
+		);
+		return false;
+	}
+}
+function mailbox_edit_resource($postarray) {
+	global $lang;
+	global $pdo;
+
+	isset($postarray['active']) ? $active = '1' : $active = '0';
+	isset($postarray['multiple_bookings']) ? $multiple_bookings = '1' : $multiple_bookings = '0';
+	$name               = $postarray['name'];
+	$kind               = $postarray['kind'];
+	$description        = $postarray['description'];
+
+	if (!filter_var($name, FILTER_VALIDATE_EMAIL)) {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => sprintf($lang['danger']['resource_invalid'])
+		);
+		return false;
+	}
+
+	if (empty($description)) {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => sprintf($lang['danger']['description_invalid'])
+		);
+		return false;
+  }
+
+	if ($kind != 'location' && $kind != 'group' && $kind != 'thing') {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => sprintf($lang['danger']['resource_invalid'])
+		);
+		return false;
+	}
+
+  if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $name)) {
+    $_SESSION['return'] = array(
+      'type' => 'danger',
+      'msg' => sprintf($lang['danger']['access_denied'])
+    );
+    return false;
+  }
+
+	try {
+		$stmt = $pdo->prepare("UPDATE `mailbox` SET
+				`modified` = :modified,
+				`active` = :active,
+				`name`= :description,
+				`kind`= :kind,
+				`multiple_bookings`= :multiple_bookings
+          WHERE `username` = :name");
+		$stmt->execute(array(
+			':active' => $active,
+			':modified' => date('Y-m-d H:i:s'),
+			':description' => $description,
+			':multiple_bookings' => $multiple_bookings,
+			':kind' => $kind,
+			':name' => $name
+		));
+		$_SESSION['return'] = array(
+			'type' => 'success',
+			'msg' => sprintf($lang['success']['resource_modified'], $name)
+		);
+		return true;
+	}
+	catch (PDOException $e) {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => 'MySQL: '.$e
+		);
+		return false;
+	}
+}
+function mailbox_get_mailboxes($domain = null) {
+	global $lang;
+	global $pdo;
+  $mailboxes = array();
+	if (isset($domain) && !hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $domain)) {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => sprintf($lang['danger']['access_denied'])
+		);
+		return false;
+	}
+  elseif (isset($domain) && hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $domain)) {
+    try {
+      $stmt = $pdo->prepare("SELECT `username` FROM `mailbox` WHERE `kind` NOT REGEXP 'location|thing|group' AND `domain` != 'ALL' AND `domain` = :domain");
+      $stmt->execute(array(
+        ':domain' => $domain,
+      ));
+      $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
+      while($row = array_shift($rows)) {
+        $mailboxes[] = $row['username'];
+      }
+    }
+    catch (PDOException $e) {
+      $_SESSION['return'] = array(
+        'type' => 'danger',
+        'msg' => 'MySQL: '.$e
+      );
+      return false;
+    }
+  }
+  else {
+    try {
+      $stmt = $pdo->prepare("SELECT `username` FROM `mailbox` WHERE `kind` NOT REGEXP 'location|thing|group' AND `domain` IN (SELECT `domain` FROM `domain_admins` WHERE `active` = '1' AND `username` = :username) OR 'admin' = :role");
+      $stmt->execute(array(
+        ':username' => $_SESSION['mailcow_cc_username'],
+        ':role' => $_SESSION['mailcow_cc_role'],
+      ));
+      $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
+      while($row = array_shift($rows)) {
+        $mailboxes[] = $row['username'];
+      }
+    }
+    catch (PDOException $e) {
+      $_SESSION['return'] = array(
+        'type' => 'danger',
+        'msg' => 'MySQL: '.$e
+      );
+      return false;
+    }
+  }
+  return $mailboxes;
+}
+function mailbox_get_resources($domain = null) {
+	global $lang;
+	global $pdo;
+  $resources = array();
+	if (isset($domain) && !hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $domain)) {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => sprintf($lang['danger']['access_denied'])
+		);
+		return false;
+	}
+  elseif (isset($domain) && hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $domain)) {
+    try {
+      $stmt = $pdo->prepare("SELECT `username` FROM `mailbox` WHERE `kind` REGEXP 'location|thing|group' AND `domain` != 'ALL' AND `domain` = :domain");
+      $stmt->execute(array(
+        ':domain' => $domain,
+      ));
+      $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
+      while($row = array_shift($rows)) {
+        $resources[] = $row['username'];
+      }
+    }
+    catch (PDOException $e) {
+      $_SESSION['return'] = array(
+        'type' => 'danger',
+        'msg' => 'MySQL: '.$e
+      );
+      return false;
+    }
+  }
+  else {
+    try {
+      $stmt = $pdo->prepare("SELECT `username` FROM `mailbox` WHERE `kind` REGEXP 'location|thing|group' AND `domain` IN (SELECT `domain` FROM `domain_admins` WHERE `active` = '1' AND `username` = :username) OR 'admin' = :role");
+      $stmt->execute(array(
+        ':username' => $_SESSION['mailcow_cc_username'],
+        ':role' => $_SESSION['mailcow_cc_role'],
+      ));
+      $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
+      while($row = array_shift($rows)) {
+        $resources[] = $row['username'];
+      }
+    }
+    catch (PDOException $e) {
+      $_SESSION['return'] = array(
+        'type' => 'danger',
+        'msg' => 'MySQL: '.$e
+      );
+      return false;
+    }
+  }
+  return $resources;
+}
+function mailbox_get_alias_domains($domain = null) {
+  // Get all domains assigned to mailcow_cc_username or domain, if set
+  // Domain admin needs to be active
+  // Domain does not need to be active
+	global $lang;
+	global $pdo;
+  $aliasdomains = array();
+	if (isset($domain) && !hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $domain)) {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => sprintf($lang['danger']['access_denied'])
+		);
+		return false;
+  }
+  elseif (isset($domain) && hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $domain)) {
+    try {
+      $stmt = $pdo->prepare("SELECT `alias_domain` FROM `alias_domain` WHERE `target_domain` = :domain");
+      $stmt->execute(array(
+        ':domain' => $domain,
+      ));
+      $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
+      while($row = array_shift($rows)) {
+        $aliasdomains[] = $row['alias_domain'];
+      }
+    }
+    catch (PDOException $e) {
+      $_SESSION['return'] = array(
+        'type' => 'danger',
+        'msg' => 'MySQL: '.$e
+      );
+      return false;
+    }
+  }
+	else {
+    try {
+      $stmt = $pdo->prepare("SELECT `alias_domain` FROM `alias_domain` WHERE `target_domain` IN (SELECT `domain` FROM `domain_admins` WHERE `active` = '1' AND `username` = :username) OR 'admin' = :role");
+      $stmt->execute(array(
+        ':username' => $_SESSION['mailcow_cc_username'],
+        ':role' => $_SESSION['mailcow_cc_role'],
+      ));
+      $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
+      while($row = array_shift($rows)) {
+        $aliasdomains[] = $row['alias_domain'];
+      }
+    }
+    catch (PDOException $e) {
+      $_SESSION['return'] = array(
+        'type' => 'danger',
+        'msg' => 'MySQL: '.$e
+      );
+      return false;
+    }
+  }
+  return $aliasdomains;
+}
+function mailbox_get_aliases($domain) {
+	global $lang;
+	global $pdo;
+  $aliases = array();
+	if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $domain)) {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => sprintf($lang['danger']['access_denied'])
+		);
+		return false;
+	}
+
+  try {
+    $stmt = $pdo->prepare("SELECT `address` FROM `alias` WHERE `address` != `goto` AND `domain` = :domain");
+    $stmt->execute(array(
+      ':domain' => $domain,
+    ));
+    $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
+    while($row = array_shift($rows)) {
+      $aliases[] = $row['address'];
+    }
+  }
+  catch (PDOException $e) {
+    $_SESSION['return'] = array(
+      'type' => 'danger',
+      'msg' => 'MySQL: '.$e
+    );
+    return false;
+  }
+  return $aliases;
+}
+function mailbox_get_alias_details($address) {
+	global $lang;
+	global $pdo;
+  $aliasdata = array();
+  try {
+    $stmt = $pdo->prepare("SELECT
+      `domain`,
+      `goto`,
+      `address`,
+      `active` as `active_int`,
+      CASE `active` WHEN 1 THEN '".$lang['mailbox']['yes']."' ELSE '".$lang['mailbox']['no']."' END AS `active`,
+      `created`,
+      `modified`
+        FROM `alias`
+            WHERE `address` = :address AND `address` != `goto`");
+    $stmt->execute(array(
+      ':address' => $address,
+    ));
+    $row = $stmt->fetch(PDO::FETCH_ASSOC);
+    $aliasdata['domain'] = $row['domain'];
+    $aliasdata['goto'] = $row['goto'];
+    $aliasdata['address'] = $row['address'];
+    (!filter_var($aliasdata['address'], FILTER_VALIDATE_EMAIL)) ? $aliasdata['is_catch_all'] = 1 : $aliasdata['is_catch_all'] = 0;
+    $aliasdata['active'] = $row['active'];
+    $aliasdata['active_int'] = $row['active_int'];
+    $aliasdata['created'] = $row['created'];
+    $aliasdata['modified'] = $row['modified'];
+    if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $aliasdata['domain'])) {
+      $_SESSION['return'] = array(
+        'type' => 'danger',
+        'msg' => sprintf($lang['danger']['access_denied'])
+      );
+      return false;
+    }
+  }
+  catch (PDOException $e) {
+    $_SESSION['return'] = array(
+      'type' => 'danger',
+      'msg' => 'MySQL: '.$e
+    );
+    return false;
+  }
+  return $aliasdata;
+}
+function mailbox_get_alias_domain_details($aliasdomain) {
+	global $lang;
+	global $pdo;
+  $aliasdomaindata = array();
+  try {
+    $stmt = $pdo->prepare("SELECT
+      `alias_domain`,
+      `target_domain`,
+      `active` AS `active_int`,
+      CASE `active` WHEN 1 THEN '".$lang['mailbox']['yes']."' ELSE '".$lang['mailbox']['no']."' END AS `active`,
+      `created`,
+      `modified`
+        FROM `alias_domain`
+            WHERE `alias_domain` = :aliasdomain");
+    $stmt->execute(array(
+      ':aliasdomain' => $aliasdomain,
+    ));
+    $row = $stmt->fetch(PDO::FETCH_ASSOC);
+    $aliasdomaindata['alias_domain'] = $row['alias_domain'];
+    $aliasdomaindata['target_domain'] = $row['target_domain'];
+    $aliasdomaindata['active'] = $row['active'];
+    $aliasdomaindata['active_int'] = $row['active_int'];
+    $aliasdomaindata['created'] = $row['created'];
+    $aliasdomaindata['modified'] = $row['modified'];
+  }
+  catch (PDOException $e) {
+    $_SESSION['return'] = array(
+      'type' => 'danger',
+      'msg' => 'MySQL: '.$e
+    );
+    return false;
+  }
+  if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $aliasdomaindata['target_domain'])) {
+    $_SESSION['return'] = array(
+      'type' => 'danger',
+      'msg' => sprintf($lang['danger']['access_denied'])
+    );
+    return false;
+  }
+  return $aliasdomaindata;
+}
+function mailbox_get_domains() {
+  // Get all domains assigned to mailcow_cc_username
+  // Domain admin needs to be active
+  // Domain does not need to be active
+	global $lang;
+	global $pdo;
+
+  try {
+    $domains = array();
+    $stmt = $pdo->prepare("SELECT `domain` FROM `domain`
+      WHERE (`domain` IN (
+        SELECT `domain` from `domain_admins`
+          WHERE (`active`='1' AND `username` = :username))
+        )
+        OR ('admin'= :role)
+        AND `domain` != 'ALL'");
+    $stmt->execute(array(
+      ':username' => $_SESSION['mailcow_cc_username'],
+      ':role' => $_SESSION['mailcow_cc_role'],
+    ));
+    $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
+    while($row = array_shift($rows)) {
+      $domains[] = $row['domain'];
+    }
+  }
+  catch (PDOException $e) {
+    $_SESSION['return'] = array(
+      'type' => 'danger',
+      'msg' => 'MySQL: '.$e
+    );
+    return false;
+  }
+  return $domains;
+}
+function mailbox_get_domain_details($domain) {
+	global $lang;
+	global $pdo;
+
+  $domaindata = array();
+	$domain = idn_to_ascii(strtolower(trim($domain)));
+
+	if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $domain)) {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => sprintf($lang['danger']['access_denied'])
+		);
+		return false;
+	}
+
+  try {
+    $stmt = $pdo->prepare("SELECT 
+        `domain`,
+        `description`,
+        `aliases`,
+        `mailboxes`, 
+        `maxquota`,
+        `quota`,
+        `relay_all_recipients` as `relay_all_recipients_int`,
+        `backupmx` as `backupmx_int`,
+        `active` as `active_int`,
+        CASE `relay_all_recipients` WHEN 1 THEN '".$lang['mailbox']['yes']."' ELSE '".$lang['mailbox']['no']."' END AS `relay_all_recipients`,
+        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`= :domain");
+    $stmt->execute(array(
+      ':domain' => $domain,
+    ));
+    $row = $stmt->fetch(PDO::FETCH_ASSOC);
+    $stmt = $pdo->prepare("SELECT COUNT(*) AS `count`, COALESCE(SUM(`quota`), 0) as `in_use` FROM `mailbox` WHERE `kind` NOT REGEXP 'location|thing|group' AND `domain` = :domain");
+    $stmt->execute(array(':domain' => $row['domain']));
+    $MailboxDataDomain	= $stmt->fetch(PDO::FETCH_ASSOC);
+
+    $domaindata['max_new_mailbox_quota']	= ($row['quota'] * 1048576) - $MailboxDataDomain['in_use'];
+    if ($domaindata['max_new_mailbox_quota'] > ($row['maxquota'] * 1048576)) {
+      $domaindata['max_new_mailbox_quota'] = ($row['maxquota'] * 1048576);
+    }
+    $domaindata['quota_used_in_domain'] = $MailboxDataDomain['in_use'];
+    $domaindata['mboxes_in_domain'] = $MailboxDataDomain['count'];
+    $domaindata['mboxes_left'] = $row['mailboxes']	- $MailboxDataDomain['count'];
+    $domaindata['domain_name'] = $row['domain'];
+    $domaindata['description'] = $row['description'];
+    $domaindata['max_num_aliases_for_domain'] = $row['aliases'];
+    $domaindata['max_num_mboxes_for_domain'] = $row['mailboxes'];
+    $domaindata['max_quota_for_mbox'] = $row['maxquota'] * 1048576;
+    $domaindata['max_quota_for_domain'] = $row['quota'] * 1048576;
+    $domaindata['backupmx'] = $row['backupmx'];
+    $domaindata['backupmx_int'] = $row['backupmx_int'];
+    $domaindata['active'] = $row['active'];
+    $domaindata['active_int'] = $row['active_int'];
+    $domaindata['relay_all_recipients'] = $row['relay_all_recipients'];
+    $domaindata['relay_all_recipients_int'] = $row['relay_all_recipients_int'];
+
+    $stmt = $pdo->prepare("SELECT COUNT(*) AS `alias_count` FROM `alias`
+      WHERE `domain`= :domain
+        AND `address` NOT IN (
+          SELECT `username` FROM `mailbox`
+        )");
+    $stmt->execute(array(
+      ':domain' => $domain,
+    ));
+    $AliasData = $stmt->fetch(PDO::FETCH_ASSOC);
+    (isset($AliasData['alias_count'])) ? $domaindata['aliases_in_domain'] = $AliasData['alias_count'] : $domaindata['aliases_in_domain'] = "0";
+  }
+  catch (PDOException $e) {
+    $_SESSION['return'] = array(
+      'type' => 'danger',
+      'msg' => 'MySQL: '.$e
+    );
+    return false;
+  }
+
+  return $domaindata;
+}
+function mailbox_get_mailbox_details($mailbox) {
+	global $lang;
+	global $pdo;
+  if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $mailbox)) {
+    $_SESSION['return'] = array(
+      'type' => 'danger',
+      'msg' => sprintf($lang['danger']['access_denied'])
+    );
+    return false;
+  }
+  $mailboxdata = array();
+  try {
+    $stmt = $pdo->prepare("SELECT
+        `domain`.`backupmx`,
+        `mailbox`.`username`,
+        `mailbox`.`name`,
+        `mailbox`.`active` AS `active_int`,
+        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`.`kind` NOT REGEXP 'location|thing|group' AND `mailbox`.`username` = `quota2`.`username` AND `domain`.`domain` = `mailbox`.`domain` AND `mailbox`.`username` = :mailbox");
+    $stmt->execute(array(
+      ':mailbox' => $mailbox,
+    ));
+    $row = $stmt->fetch(PDO::FETCH_ASSOC);
+
+    $stmt = $pdo->prepare("SELECT `maxquota`, `quota` FROM  `domain` WHERE `domain` = :domain");
+    $stmt->execute(array(':domain' => $row['domain']));
+    $DomainQuota  = $stmt->fetch(PDO::FETCH_ASSOC);
+
+    $stmt = $pdo->prepare("SELECT COALESCE(SUM(`quota`), 0) as `in_use` FROM `mailbox` WHERE `kind` NOT REGEXP 'location|thing|group' AND `domain` = :domain AND `username` != :username");
+    $stmt->execute(array(':domain' => $row['domain'], ':username' => $row['username']));
+    $MailboxUsage	= $stmt->fetch(PDO::FETCH_ASSOC);
+
+    $mailboxdata['max_new_quota'] = ($DomainQuota['quota'] * 1048576) - $MailboxUsage['in_use'];
+    if ($mailboxdata['max_new_quota'] > ($DomainQuota['maxquota'] * 1048576)) {
+      $mailboxdata['max_new_quota'] = ($DomainQuota['maxquota'] * 1048576);
+    }
+
+    $mailboxdata['username'] = $row['username'];
+    $mailboxdata['is_relayed'] = $row['backupmx'];
+    $mailboxdata['name'] = $row['name'];
+    $mailboxdata['active'] = $row['active'];
+    $mailboxdata['active_int'] = $row['active_int'];
+    $mailboxdata['domain'] = $row['domain'];
+    $mailboxdata['quota'] = $row['quota'];
+    $mailboxdata['quota_used'] = intval($row['bytes']);
+    $mailboxdata['percent_in_use'] = round((intval($row['bytes']) / intval($row['quota'])) * 100);
+    $mailboxdata['messages'] = $row['messages'];
+    if ($mailboxdata['percent_in_use'] >= 90) {
+      $mailboxdata['percent_class'] = "danger";
+    }
+    elseif ($mailboxdata['percent_in_use'] >= 75) {
+      $mailboxdata['percent_class'] = "warning";
+    }
+    else {
+      $mailboxdata['percent_class'] = "success";
+    }
+  }
+  catch (PDOException $e) {
+    $_SESSION['return'] = array(
+      'type' => 'danger',
+      'msg' => 'MySQL: '.$e
+    );
+    return false;
+  }
+  return $mailboxdata;
+}
+function mailbox_get_resource_details($resource) {
+	global $lang;
+	global $pdo;
+  $resourcedata = array();
+  if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $resource)) {
+    $_SESSION['return'] = array(
+      'type' => 'danger',
+      'msg' => sprintf($lang['danger']['access_denied'])
+    );
+    return false;
+  }
+  try {
+    $stmt = $pdo->prepare("SELECT
+        `username`,
+        `name`,
+        `kind`,
+        `multiple_bookings` AS `multiple_bookings_int`,
+        `local_part`,
+        `active` AS `active_int`,
+        CASE `multiple_bookings` WHEN 1 THEN '".$lang['mailbox']['yes']."' ELSE '".$lang['mailbox']['no']."' END AS `multiple_bookings`,
+        CASE `active` WHEN 1 THEN '".$lang['mailbox']['yes']."' ELSE '".$lang['mailbox']['no']."' END AS `active`,
+        `domain`
+          FROM `mailbox` WHERE `kind` REGEXP 'location|thing|group' AND `username` = :resource");
+    $stmt->execute(array(
+      ':resource' => $resource,
+    ));
+    $row = $stmt->fetch(PDO::FETCH_ASSOC);
+    $resourcedata['name'] = $row['username'];
+    $resourcedata['kind'] = $row['kind'];
+    $resourcedata['multiple_bookings'] = $row['multiple_bookings'];
+    $resourcedata['multiple_bookings_int'] = $row['multiple_bookings'];
+    $resourcedata['description'] = $row['name'];
+    $resourcedata['active'] = $row['active'];
+    $resourcedata['active_int'] = $row['active_int'];
+    $resourcedata['domain'] = $row['domain'];
+    $resourcedata['local_part'] = $row['local_part'];
+  }
+  catch (PDOException $e) {
+    $_SESSION['return'] = array(
+      'type' => 'danger',
+      'msg' => 'MySQL: '.$e
+    );
+    return false;
+  }
+  if (!isset($resourcedata['domain']) ||
+    (isset($resourcedata['domain']) && !hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $resourcedata['domain']))) {
+    $_SESSION['return'] = array(
+      'type' => 'danger',
+      'msg' => sprintf($lang['danger']['access_denied'])
+    );
+    return false;
+  }
+  
+  return $resourcedata;
+}
+function mailbox_delete_domain($postarray) {
+	global $lang;
+	global $pdo;
+	$domain = $postarray['domain'];
+	if ($_SESSION['mailcow_cc_role'] != "admin") {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => sprintf($lang['danger']['access_denied'])
+		);
+		return false;
+	}
+	if (!is_valid_domain_name($domain)) {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => sprintf($lang['danger']['domain_invalid'])
+		);
+		return false;
+	}
+	$domain	= idn_to_ascii(strtolower(trim($domain)));
+
+	try {
+		$stmt = $pdo->prepare("SELECT `username` FROM `mailbox`
+			WHERE `domain` = :domain");
+		$stmt->execute(array(':domain' => $domain));
+		$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
+	}
+	catch(PDOException $e) {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => 'MySQL: '.$e
+		);
+		return false;
+	}
+	if ($num_results != 0 || !empty($num_results)) {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => sprintf($lang['danger']['domain_not_empty'])
+		);
+		return false;
+	}
+
+	try {
+		$stmt = $pdo->prepare("DELETE FROM `domain` WHERE `domain` = :domain");
+		$stmt->execute(array(
+			':domain' => $domain,
+		));
+		$stmt = $pdo->prepare("DELETE FROM `domain_admins` WHERE `domain` = :domain");
+		$stmt->execute(array(
+			':domain' => $domain,
+		));
+		$stmt = $pdo->prepare("DELETE FROM `alias` WHERE `domain` = :domain");
+		$stmt->execute(array(
+			':domain' => $domain,
+		));
+		$stmt = $pdo->prepare("DELETE FROM `alias_domain` WHERE `target_domain` = :domain");
+		$stmt->execute(array(
+			':domain' => $domain,
+		));
+		$stmt = $pdo->prepare("DELETE FROM `mailbox` WHERE `domain` = :domain");
+		$stmt->execute(array(
+			':domain' => $domain,
+		));
+		$stmt = $pdo->prepare("DELETE FROM `sender_acl` WHERE `logged_in_as` LIKE :domain");
+		$stmt->execute(array(
+			':domain' => '%@'.$domain,
+		));
+		$stmt = $pdo->prepare("DELETE FROM `quota2` WHERE `username` = :domain");
+		$stmt->execute(array(
+			':domain' => '%@'.$domain,
+		));
+		$stmt = $pdo->prepare("DELETE FROM `spamalias` WHERE `address` = :domain");
+		$stmt->execute(array(
+			':domain' => '%@'.$domain,
+		));
+		$stmt = $pdo->prepare("DELETE FROM `filterconf` WHERE `object` = :domain");
+		$stmt->execute(array(
+			':domain' => '%@'.$domain,
+		));
+	}
+	catch (PDOException $e) {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => 'MySQL: '.$e
+		);
+		return false;
+	}
+	$_SESSION['return'] = array(
+		'type' => 'success',
+		'msg' => sprintf($lang['success']['domain_removed'], htmlspecialchars($domain))
+	);
+	return true;
+}
+function mailbox_delete_alias($postarray) {
+	global $lang;
+	global $pdo;
+	$address		= $postarray['address'];
+	$local_part		= strstr($address, '@', true);
+  $domain = mailbox_get_alias_details($address)['domain'];
+	try {
+		$stmt = $pdo->prepare("SELECT `goto` FROM `alias` WHERE `address` = :address");
+		$stmt->execute(array(':address' => $address));
+		$gotos = $stmt->fetch(PDO::FETCH_ASSOC);
+	}
+	catch(PDOException $e) {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => 'MySQL: '.$e
+		);
+		return false;
+	}
+	$goto_array = explode(',', $gotos['goto']);
+
+	if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $domain)) {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => sprintf($lang['danger']['access_denied'])
+		);
+		return false;
+	}
+	try {
+		$stmt = $pdo->prepare("DELETE FROM `alias` WHERE `address` = :address AND `address` NOT IN (SELECT `username` FROM `mailbox`)");
+		$stmt->execute(array(
+			':address' => $address
+		));
+	}
+	catch (PDOException $e) {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => 'MySQL: '.$e
+		);
+		return false;
+	}
+	$_SESSION['return'] = array(
+		'type' => 'success',
+		'msg' => sprintf($lang['success']['alias_removed'], htmlspecialchars($address))
+	);
+
+}
+function mailbox_delete_alias_domain($postarray) {
+	global $lang;
+	global $pdo;
+  $alias_domain = $postarray['alias_domain'];
+	if (!is_valid_domain_name($postarray['alias_domain'])) {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => sprintf($lang['danger']['domain_invalid'])
+		);
+		return false;
+	}
+	try {
+		$stmt = $pdo->prepare("SELECT `target_domain` FROM `alias_domain`
+			WHERE `alias_domain`= :alias_domain");
+		$stmt->execute(array(':alias_domain' => $alias_domain));
+		$DomainData = $stmt->fetch(PDO::FETCH_ASSOC);
+	}
+	catch(PDOException $e) {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => 'MySQL: '.$e
+		);
+		return false;
+	}
+
+	if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $DomainData['target_domain'])) {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => sprintf($lang['danger']['access_denied'])
+		);
+		return false;
+	}
+
+	try {
+		$stmt = $pdo->prepare("DELETE FROM `alias_domain` WHERE `alias_domain` = :alias_domain");
+		$stmt->execute(array(
+			':alias_domain' => $alias_domain,
+		));
+		$stmt = $pdo->prepare("DELETE FROM `alias` WHERE `domain` = :alias_domain");
+		$stmt->execute(array(
+			':alias_domain' => $alias_domain,
+		));
+	}
+	catch (PDOException $e) {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => 'MySQL: '.$e
+		);
+		return false;
+	}
+	$_SESSION['return'] = array(
+		'type' => 'success',
+		'msg' => sprintf($lang['success']['alias_domain_removed'], htmlspecialchars($alias_domain))
+	);
+}
+function mailbox_delete_mailbox($postarray) {
+	global $lang;
+	global $pdo;
+	$username	= $postarray['username'];
+
+	if (!filter_var($postarray['username'], FILTER_VALIDATE_EMAIL)) {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => sprintf($lang['danger']['access_denied'])
+		);
+		return false;
+	}
+
+	if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $username)) {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => sprintf($lang['danger']['access_denied'])
+		);
+		return false;
+	}
+
+	try {
+		$stmt = $pdo->prepare("DELETE FROM `alias` WHERE `goto` = :username");
+		$stmt->execute(array(
+			':username' => $username
+		));
+		$stmt = $pdo->prepare("DELETE FROM `quota2` WHERE `username` = :username");
+		$stmt->execute(array(
+			':username' => $username
+		));
+		$stmt = $pdo->prepare("DELETE FROM `mailbox` WHERE `username` = :username");
+		$stmt->execute(array(
+			':username' => $username
+		));
+		$stmt = $pdo->prepare("DELETE FROM `sender_acl` WHERE `logged_in_as` = :username");
+		$stmt->execute(array(
+			':username' => $username
+		));
+		$stmt = $pdo->prepare("DELETE FROM `spamalias` WHERE `goto` = :username");
+		$stmt->execute(array(
+			':username' => $username
+		));
+		$stmt = $pdo->prepare("DELETE FROM `imapsync` WHERE `user2` = :username");
+		$stmt->execute(array(
+			':username' => $username
+		));
+		$stmt = $pdo->prepare("DELETE FROM `filterconf` WHERE `object` = :username");
+		$stmt->execute(array(
+			':username' => $username
+		));
+    $stmt = $pdo->prepare("DELETE FROM `sogo_user_profile` WHERE `c_uid` = :username");
+    $stmt->execute(array(
+      ':username' => $username
+    ));
+    $stmt = $pdo->prepare("DELETE FROM `sogo_cache_folder` WHERE `c_uid` = :username");
+    $stmt->execute(array(
+      ':username' => $username
+    ));
+    $stmt = $pdo->prepare("DELETE FROM `sogo_acl` WHERE `c_object` LIKE '%/" . $username . "/%' OR `c_uid` = :username");
+    $stmt->execute(array(
+      ':username' => $username
+    ));
+    $stmt = $pdo->prepare("DELETE FROM `sogo_store` WHERE `c_folder_id` IN (SELECT `c_folder_id` FROM `sogo_folder_info` WHERE `c_path2` = :username)");
+    $stmt->execute(array(
+      ':username' => $username
+    ));
+    $stmt = $pdo->prepare("DELETE FROM `sogo_quick_contact` WHERE `c_folder_id` IN (SELECT `c_folder_id` FROM `sogo_folder_info` WHERE `c_path2` = :username)");
+    $stmt->execute(array(
+      ':username' => $username
+    ));
+    $stmt = $pdo->prepare("DELETE FROM `sogo_quick_appointment` WHERE `c_folder_id` IN (SELECT `c_folder_id` FROM `sogo_folder_info` WHERE `c_path2` = :username)");
+    $stmt->execute(array(
+      ':username' => $username
+    ));
+    $stmt = $pdo->prepare("DELETE FROM `sogo_folder_info` WHERE `c_path2` = :username");
+    $stmt->execute(array(
+      ':username' => $username
+    ));
+		$stmt = $pdo->prepare("SELECT `address`, `goto` FROM `alias`
+				WHERE `goto` LIKE :username");
+		$stmt->execute(array(':username' => '%'.$username.'%'));
+		$GotoData = $stmt->fetchAll(PDO::FETCH_ASSOC);
+		foreach ($GotoData as $gotos) {
+			$goto_exploded = explode(',', $gotos['goto']);
+			if (($key = array_search($username, $goto_exploded)) !== false) {
+				unset($goto_exploded[$key]);
+			}
+			$gotos_rebuild = implode(',', $goto_exploded);
+			$stmt = $pdo->prepare("UPDATE `alias` SET
+        `goto` = :goto,
+        `modified` = :modified,
+          WHERE `address` = :address");
+			$stmt->execute(array(
+				':goto' => $gotos_rebuild,
+        ':modified' => date('Y-m-d H:i:s'),
+				':address' => $gotos['address']
+			));
+		}
+	}
+	catch (PDOException $e) {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => 'MySQL: '.$e
+		);
+		return false;
+	}
+	$_SESSION['return'] = array(
+		'type' => 'success',
+		'msg' => sprintf($lang['success']['mailbox_removed'], htmlspecialchars($username))
+	);
+}
+function mailbox_reset_eas($username) {
+	global $lang;
+	global $pdo;
+
+  (isset($postarray['username'])) ? $username = $postarray['username'] : $username = $_SESSION['mailcow_cc_username'];
+
+	if (!filter_var($username, FILTER_VALIDATE_EMAIL)) {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => sprintf($lang['danger']['access_denied'])
+		);
+		return false;
+	}
+
+	if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $username)) {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => sprintf($lang['danger']['access_denied'])
+		);
+		return false;
+	}
+
+	try {
+    $stmt = $pdo->prepare("DELETE FROM `sogo_cache_folder` WHERE `c_uid` = :username");
+    $stmt->execute(array(
+      ':username' => $username
+    ));
+	}
+	catch (PDOException $e) {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => 'MySQL: '.$e
+		);
+		return false;
+	}
+	$_SESSION['return'] = array(
+		'type' => 'success',
+		'msg' => sprintf($lang['success']['eas_reset'], htmlspecialchars($username))
+	);
+}
+function mailbox_delete_resource($postarray) {
+	global $lang;
+	global $pdo;
+	$name	= $postarray['name'];
+	if (!filter_var($postarray['name'], FILTER_VALIDATE_EMAIL)) {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => sprintf($lang['danger']['access_denied'])
+		);
+		return false;
+	}
+
+	if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $name)) {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => sprintf($lang['danger']['access_denied'])
+		);
+		return false;
+	}
+
+	try {
+		$stmt = $pdo->prepare("DELETE FROM `mailbox` WHERE `username` = :username");
+		$stmt->execute(array(
+			':username' => $name
+		));
+    $stmt = $pdo->prepare("DELETE FROM `sogo_user_profile` WHERE `c_uid` = :username");
+    $stmt->execute(array(
+      ':username' => $name
+    ));
+    $stmt = $pdo->prepare("DELETE FROM `sogo_cache_folder` WHERE `c_uid` = :username");
+    $stmt->execute(array(
+      ':username' => $name
+    ));
+    $stmt = $pdo->prepare("DELETE FROM `sogo_acl` WHERE `c_object` LIKE '%/" . $name . "/%' OR `c_uid` = :username");
+    $stmt->execute(array(
+      ':username' => $name
+    ));
+    $stmt = $pdo->prepare("DELETE FROM `sogo_store` WHERE `c_folder_id` IN (SELECT `c_folder_id` FROM `sogo_folder_info` WHERE `c_path2` = :username)");
+    $stmt->execute(array(
+      ':username' => $name
+    ));
+    $stmt = $pdo->prepare("DELETE FROM `sogo_quick_contact` WHERE `c_folder_id` IN (SELECT `c_folder_id` FROM `sogo_folder_info` WHERE `c_path2` = :username)");
+    $stmt->execute(array(
+      ':username' => $name
+    ));
+    $stmt = $pdo->prepare("DELETE FROM `sogo_quick_appointment` WHERE `c_folder_id` IN (SELECT `c_folder_id` FROM `sogo_folder_info` WHERE `c_path2` = :username)");
+    $stmt->execute(array(
+      ':username' => $name
+    ));
+    $stmt = $pdo->prepare("DELETE FROM `sogo_folder_info` WHERE `c_path2` = :username");
+    $stmt->execute(array(
+      ':username' => $name
+    ));
+	}
+	catch (PDOException $e) {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => 'MySQL: '.$e
+		);
+		return false;
+	}
+	$_SESSION['return'] = array(
+		'type' => 'success',
+		'msg' => sprintf($lang['success']['resource_removed'], htmlspecialchars($name))
+	);
+}
+function mailbox_get_sender_acl_handles($mailbox) {
+	global $pdo;
+	global $lang;
+	if ($_SESSION['mailcow_cc_role'] != "admin" && $_SESSION['mailcow_cc_role'] != "domainadmin") {
+    $_SESSION['return'] = array(
+      'type' => 'danger',
+      'msg' => sprintf($lang['danger']['access_denied'])
+    );
+    return false;
+	}
+
+  $data['sender_acl_domains']['ro']               = array();
+  $data['sender_acl_domains']['rw']               = array();
+  $data['sender_acl_domains']['selectable']       = array();
+  $data['sender_acl_addresses']['ro']             = array();
+  $data['sender_acl_addresses']['rw']             = array();
+  $data['sender_acl_addresses']['selectable']     = array();
+  $data['fixed_sender_aliases']                   = array();
+  
+  try {
+    $stmt = $pdo->prepare("SELECT `address` FROM `alias` WHERE `goto` = :goto AND `address` NOT LIKE '@%'");
+    $stmt->execute(array(':goto' => $mailbox));
+    $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
+    while ($row = array_shift($rows)) {
+      $data['fixed_sender_aliases'][] = $row['address'];
+    }
+
+    // Return array $data['sender_acl_domains/addresses']['ro'] with read-only objects
+    // Return array $data['sender_acl_domains/addresses']['rw'] with read-write objects (can be deleted)
+    $stmt = $pdo->prepare("SELECT REPLACE(`send_as`, '@', '') AS `send_as` FROM `sender_acl` WHERE `logged_in_as` = :logged_in_as AND `send_as` LIKE '@%'");
+    $stmt->execute(array(':logged_in_as' => $mailbox));
+    $domain_rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
+    while ($domain_row = array_shift($domain_rows)) {
+      if (is_valid_domain_name($domain_row['send_as']) && !hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $domain_row['send_as'])) {
+        $data['sender_acl_domains']['ro'][] = $domain_row['send_as'];
+        continue;
+      }
+      if (is_valid_domain_name($domain_row['send_as']) && hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $domain_row['send_as'])) {
+        $data['sender_acl_domains']['rw'][] = $domain_row['send_as'];
+        continue;
+      }
+    }
+
+    $stmt = $pdo->prepare("SELECT `send_as` FROM `sender_acl` WHERE `logged_in_as` = :logged_in_as AND `send_as` NOT LIKE '@%'");
+    $stmt->execute(array(':logged_in_as' => $mailbox));
+    $address_rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
+    while ($address_row = array_shift($address_rows)) {
+      if (filter_var($address_row['send_as'], FILTER_VALIDATE_EMAIL) && !hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $address_row['send_as'])) {
+        $data['sender_acl_addresses']['ro'][] = $address_row['send_as'];
+        continue;
+      }
+      if (filter_var($address_row['send_as'], FILTER_VALIDATE_EMAIL) && hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $address_row['send_as'])) {
+        $data['sender_acl_addresses']['rw'][] = $address_row['send_as'];
+        continue;
+      }
+    }
+
+    $stmt = $pdo->prepare("SELECT `domain` FROM `domain`
+      WHERE `domain` NOT IN (
+        SELECT REPLACE(`send_as`, '@', '') FROM `sender_acl` 
+          WHERE `logged_in_as` = :logged_in_as
+            AND `send_as` LIKE '@%')");
+    $stmt->execute(array(
+      ':logged_in_as' => $mailbox,
+    ));
+    $rows_domain = $stmt->fetchAll(PDO::FETCH_ASSOC);
+    while ($row_domain = array_shift($rows_domain)) {
+      if (is_valid_domain_name($row_domain['domain']) && hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $row_domain['domain'])) {
+        $data['sender_acl_domains']['selectable'][] = $row_domain['domain'];
+      }
+    }
+
+    $stmt = $pdo->prepare("SELECT `address` FROM `alias`
+      WHERE `goto` != :goto
+        AND `address` NOT IN (
+          SELECT `send_as` FROM `sender_acl` 
+            WHERE `logged_in_as` = :logged_in_as
+              AND `send_as` NOT LIKE '@%')");
+    $stmt->execute(array(
+      ':logged_in_as' => $mailbox,
+      ':goto' => $mailbox
+    ));
+    $rows_mbox = $stmt->fetchAll(PDO::FETCH_ASSOC);
+    while ($row = array_shift($rows_mbox)) {
+      if (filter_var($row['address'], FILTER_VALIDATE_EMAIL) && hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $row['address'])) {
+        $data['sender_acl_addresses']['selectable'][] = $row['address'];
+      }
+    }
+  }
+  catch(PDOException $e) {
+    $_SESSION['return'] = array(
+      'type' => 'danger',
+      'msg' => 'MySQL: '.$e
+    );
+    return false;
+  }
+  return $data;
+}
+function get_u2f_registrations($username) {
+  global $pdo;
+  $sel = $pdo->prepare("SELECT * FROM `tfa` WHERE `authmech` = 'u2f' AND `username` = ? AND `active` = '1'");
+  $sel->execute(array($username));
+  return $sel->fetchAll(PDO::FETCH_OBJ);
+}
+?>
diff --git a/data/web/inc/header.inc.php b/data/web/inc/header.inc.php
new file mode 100644
index 00000000..a128df88
--- /dev/null
+++ b/data/web/inc/header.inc.php
@@ -0,0 +1,104 @@
+<!DOCTYPE html>
+<html lang="<?= $_SESSION['mailcow_locale'] ?>">
+<head>
+<meta charset="utf-8">
+<meta http-equiv="X-UA-Compatible" content="IE=edge">
+<meta name="viewport" content="width=device-width, initial-scale=1">
+<title>mailcow UI</title>
+<!--[if lt IE 9]>
+<script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
+<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
+<![endif]-->
+<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.0/jquery.min.js" integrity="sha384-XxcvoeNF5V0ZfksTnV+bejnCsJjOOIzN6UVwF85WBsAnU3zeYh5bloN+L4WLgeNE" crossorigin="anonymous"></script>
+<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.6/css/bootstrap.min.css">
+<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/bootswatch/3.3.6/<?=strtolower(trim($DEFAULT_THEME));?>/bootstrap.min.css">
+<link rel="stylesheet" href="/css/bootstrap-select.min.css">
+<link rel="stylesheet" href="/css/bootstrap-slider.min.css">
+<link rel="stylesheet" href="/css/bootstrap-switch.min.css">
+<link rel="stylesheet" href="//fonts.googleapis.com/css?family=Source+Sans+Pro:400,600,700&subset=latin,latin-ext">
+<link rel="stylesheet" href="/inc/languages.min.css">
+<link rel="stylesheet" href="/css/mailcow.css">
+<link rel="stylesheet" href="/css/tables.css">
+<?=(preg_match("/mailbox.php/i", $_SERVER['REQUEST_URI'])) ? '<link rel="stylesheet" href="/css/mailbox.css">' : null;?>
+<link rel="shortcut icon" href="/favicon.png" type="image/png">
+<link rel="icon" href="/favicon.png" type="image/png">
+</head>
+<body style="padding-top:70px">
+<nav class="navbar navbar-default navbar-fixed-top"  role="navigation">
+	<div class="container-fluid">
+		<div class="navbar-header">
+			<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
+				<span class="sr-only">Toggle navigation</span>
+				<span class="icon-bar"></span>
+				<span class="icon-bar"></span>
+				<span class="icon-bar"></span>
+			</button>
+			<a class="navbar-brand" href="/"><img height="32" alt="mailcow-logo" style="margin-top:-5px;" src="/img/cow_mailcow.svg" /></a>
+		</div>
+		<div id="navbar" class="navbar-collapse collapse">
+			<ul class="nav navbar-nav navbar-right">
+				<?php
+				if (isset($_SESSION['mailcow_locale'])) {
+				?>
+				<li class="dropdown">
+					<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false"><span class="lang-sm lang-lbl" lang="<?=$_SESSION['mailcow_locale'];?>"></span><span class="caret"></span></a>
+					<ul class="dropdown-menu" role="menu">
+						<li <?=($_SESSION['mailcow_locale'] == 'de') ? 'class="active"' : ''?>> <a href="?<?= http_build_query(array_merge($_GET, array("lang" => "de"))) ?>"><span class="lang-xs lang-lbl-full" lang="de"></span></a></li>
+						<li <?=($_SESSION['mailcow_locale'] == 'en') ? 'class="active"' : ''?>> <a href="?<?= http_build_query(array_merge($_GET, array("lang" => "en"))) ?>"><span class="lang-xs lang-lbl-full" lang="en"></span></a></li>
+						<li <?=($_SESSION['mailcow_locale'] == 'es') ? 'class="active"' : ''?>> <a href="?<?= http_build_query(array_merge($_GET, array("lang" => "es"))) ?>"><span class="lang-xs lang-lbl-full" lang="es"></span></a></li>
+						<li <?=($_SESSION['mailcow_locale'] == 'nl') ? 'class="active"' : ''?>> <a href="?<?= http_build_query(array_merge($_GET, array("lang" => "nl"))) ?>"><span class="lang-xs lang-lbl-full" lang="nl"></span></a></li>
+						<li <?=($_SESSION['mailcow_locale'] == 'pt') ? 'class="active"' : ''?>> <a href="?<?= http_build_query(array_merge($_GET, array("lang" => "pt"))) ?>"><span class="lang-xs lang-lbl-full" lang="pt"></span></a></li>
+					</ul>
+				</li>
+				<?php
+				}
+				if (isset($_SESSION['mailcow_cc_role'])) {
+				?>
+				<li class="dropdown">
+					<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false"><?=$lang['header']['mailcow_settings'];?><span class="caret"></span></a>
+					<ul class="dropdown-menu" role="menu">
+					<?php
+						if (isset($_SESSION['mailcow_cc_role'])) {
+							if ($_SESSION['mailcow_cc_role'] == "admin") {
+							?>
+								<li <?=(preg_match("/admin/i", $_SERVER['REQUEST_URI'])) ? 'class="active"' : ''?>><a href="/admin.php"><?=$lang['header']['administration'];?></a></li>
+							<?php
+							}
+							if ($_SESSION['mailcow_cc_role'] == "admin" || $_SESSION['mailcow_cc_role'] == "domainadmin") {
+							?>
+								<li <?=(preg_match("/mailbox/i", $_SERVER['REQUEST_URI'])) ? 'class="active"' : ''?>><a href="/mailbox.php"><?=$lang['header']['mailboxes'];?></a></li>
+							<?php
+							}
+							if ($_SESSION['mailcow_cc_role'] != "admin") {
+							?>
+								<li <?=(preg_match("/user/i", $_SERVER['REQUEST_URI'])) ? 'class="active"' : ''?>><a href="/user.php"><?=$lang['header']['user_settings'];?></a></li>
+							<?php
+              }
+						}
+						?>
+					</ul>
+				</li>
+				<?php
+				if ($_SESSION['mailcow_cc_role'] == "admin"):
+				?>
+				<li><a href data-toggle="modal" data-target="#RestartSOGo"><span style="font-size:12px" class="glyphicon glyphicon-refresh" aria-hidden="true"></span> <?=$lang['header']['restart_sogo'];?></a></li>
+				<?php
+				endif;
+				?>
+					<?php
+				}
+				if (!isset($_SESSION["dual-login"]) && isset($_SESSION['mailcow_cc_username'])):
+				?>
+					<li><a style="border-left:1px solid #E7E7E7" href="#" onclick="logout.submit()"><?=sprintf($lang['header']['logged_in_as_logout'], $_SESSION['mailcow_cc_username']);?></a></li>
+				<?php
+				elseif (isset($_SESSION["dual-login"])):
+				?>
+					<li><a style="border-left:1px solid #E7E7E7" href="#" onclick="logout.submit()"><?=sprintf($lang['header']['logged_in_as_logout_dual'], $_SESSION['mailcow_cc_username'], $_SESSION["dual-login"]["username"]);?></a></li>
+				<?php
+				endif;
+				?>
+			</ul>
+		</div><!--/.nav-collapse -->
+	</div><!--/.container-fluid -->
+</nav>
+<form action="/" method="post" id="logout"><input type="hidden" name="logout"></form>
diff --git a/data/web/inc/init.sql b/data/web/inc/init.sql
new file mode 100644
index 00000000..84e19f74
--- /dev/null
+++ b/data/web/inc/init.sql
@@ -0,0 +1,281 @@
+CREATE TABLE IF NOT EXISTS `admin` (
+	`username` VARCHAR(255) NOT NULL,
+	`password` VARCHAR(255) NOT NULL,
+	`superadmin` TINYINT(1) NOT NULL DEFAULT '0',
+	`created` DATETIME NOT NULL DEFAULT '2016-01-01 00:00:00',
+	`modified` DATETIME NOT NULL DEFAULT '2016-01-01 00:00:00',
+	`active` TINYINT(1) NOT NULL DEFAULT '1',
+	PRIMARY KEY (`username`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;
+
+CREATE TABLE IF NOT EXISTS `alias` (
+	`address` VARCHAR(255) NOT NULL,
+	`goto` TEXT NOT NULL,
+	`domain` VARCHAR(255) NOT NULL,
+	`created` DATETIME NOT NULL DEFAULT '2016-01-01 00:00:00',
+	`modified` DATETIME NOT NULL DEFAULT '2016-01-01 00:00:00',
+	`active` TINYINT(1) NOT NULL DEFAULT '1',
+	PRIMARY KEY (`address`),
+	KEY `domain` (`domain`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;
+
+CREATE TABLE IF NOT EXISTS `sender_acl` (
+	`logged_in_as` VARCHAR(255) NOT NULL,
+	`send_as` VARCHAR(255) NOT NULL
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;
+
+CREATE TABLE IF NOT EXISTS `spamalias` (
+	`address` VARCHAR(255) NOT NULL,
+	`goto` TEXT NOT NULL,
+	`validity` INT(11) NOT NULL,
+	PRIMARY KEY (`address`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;
+
+CREATE TABLE IF NOT EXISTS `alias_domain` (
+	`alias_domain` VARCHAR(255) NOT NULL,
+	`target_domain` VARCHAR(255) NOT NULL,
+	`created` DATETIME NOT NULL DEFAULT '2016-01-01 00:00:00',
+	`modified` DATETIME NOT NULL DEFAULT '2016-01-01 00:00:00',
+	`active` TINYINT(1) NOT NULL DEFAULT '1',
+	PRIMARY KEY (`alias_domain`),
+	KEY `active` (`active`),
+	KEY `target_domain` (`target_domain`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;
+
+CREATE TABLE IF NOT EXISTS `domain` (
+	`domain` VARCHAR(255) NOT NULL,
+	`description` VARCHAR(255),
+	`aliases` INT(10) NOT NULL DEFAULT '0',
+	`mailboxes` INT(10) NOT NULL DEFAULT '0',
+	`maxquota` BIGINT(20) NOT NULL DEFAULT '0',
+	`quota` BIGINT(20) NOT NULL DEFAULT '0',
+	`transport` VARCHAR(255) NOT NULL,
+	`backupmx` TINYINT(1) NOT NULL DEFAULT '0',
+	`relay_all_recipients` TINYINT(1) NOT NULL DEFAULT '0',
+	`created` DATETIME NOT NULL DEFAULT '2016-01-01 00:00:00',
+	`modified` DATETIME NOT NULL DEFAULT '2016-01-01 00:00:00',
+	`active` TINYINT(1) NOT NULL DEFAULT '1',
+	PRIMARY KEY (`domain`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;
+
+CREATE TABLE IF NOT EXISTS `domain_admins` (
+	`username` VARCHAR(255) NOT NULL,
+	`domain` VARCHAR(255) NOT NULL,
+	`created` DATETIME NOT NULL DEFAULT '2016-01-01 00:00:00',
+	`active` TINYINT(1) NOT NULL DEFAULT '1',
+	KEY `username` (`username`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;
+
+CREATE TABLE IF NOT EXISTS `mailbox` (
+	`username` VARCHAR(255) NOT NULL,
+	`password` VARCHAR(255) NOT NULL,
+	`name` VARCHAR(255),
+	`maildir` VARCHAR(255) NOT NULL,
+	`quota` BIGINT(20) NOT NULL DEFAULT '0',
+	`local_part` VARCHAR(255) NOT NULL,
+	`domain` VARCHAR(255) NOT NULL,
+	`created` DATETIME NOT NULL DEFAULT '2016-01-01 00:00:00',
+	`modified` DATETIME NOT NULL DEFAULT '2016-01-01 00:00:00',
+	`tls_enforce_in` TINYINT(1) NOT NULL DEFAULT '0',
+	`tls_enforce_out` TINYINT(1) NOT NULL DEFAULT '0',
+	`kind` VARCHAR(100) NOT NULL DEFAULT '',
+	`multiple_bookings` TINYINT(1) NOT NULL DEFAULT '0',
+	`wants_tagged_subject` TINYINT(1) NOT NULL DEFAULT '0',
+	`active` TINYINT(1) NOT NULL DEFAULT '1',
+	PRIMARY KEY (`username`),
+	KEY `domain` (`domain`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;
+
+CREATE TABLE IF NOT EXISTS `quota2` (
+	`username` VARCHAR(100) NOT NULL,
+	`bytes` BIGINT(20) NOT NULL DEFAULT '0',
+	`messages` INT(11) NOT NULL DEFAULT '0',
+	PRIMARY KEY (`username`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;
+
+CREATE TABLE IF NOT EXISTS `filterconf` (
+	`object` VARCHAR(100) NOT NULL DEFAULT '',
+	`option` VARCHAR(50) NOT NULL DEFAULT '',
+	`value` VARCHAR(100) NOT NULL DEFAULT '',
+	`prefid` INT(11) NOT NULL AUTO_INCREMENT,
+	PRIMARY KEY (`prefid`),
+	KEY `object` (`object`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;
+
+CREATE TABLE IF NOT EXISTS `imapsync` (
+  `id` INT NOT NULL AUTO_INCREMENT,
+  `user2` VARCHAR(255) NOT NULL,
+  `host1` VARCHAR(255) NOT NULL,
+  `authmech1` ENUM('PLAIN','LOGIN','CRAM-MD5') DEFAULT 'PLAIN',
+  `regextrans2` VARCHAR(255) DEFAULT '',
+  `authmd51` TINYINT(1) NOT NULL DEFAULT 0,
+  `domain2` VARCHAR(255) NOT NULL DEFAULT '',
+  `subfolder2` VARCHAR(255) NOT NULL DEFAULT '',
+  `user1` VARCHAR(255) NOT NULL,
+  `password1` VARCHAR(255) NOT NULL,
+  `exclude` VARCHAR(500) NOT NULL DEFAULT '',
+  `maxage` SMALLINT NOT NULL DEFAULT '0',
+  `mins_interval` VARCHAR(50) NOT NULL,
+  `port1` SMALLINT NOT NULL,
+  `enc1` ENUM('TLS','SSL','PLAIN') DEFAULT 'TLS',
+  `delete2duplicates` TINYINT(1) NOT NULL DEFAULT '1',
+  `returned_text` TEXT,
+  `last_run` TIMESTAMP NULL DEFAULT NULL,
+  `created` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
+  `modified` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
+  `active` TINYINT(1) NOT NULL DEFAULT '0',
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;
+
+CREATE TABLE IF NOT EXISTS `tfa` (
+  `id` INT NOT NULL AUTO_INCREMENT,
+  `username` VARCHAR(255) NOT NULL,
+  `authmech` ENUM('yubi_otp', 'u2f', 'hotp', 'totp'),
+  `secret` VARCHAR(255) DEFAULT NULL,
+  `keyHandle` VARCHAR(255) DEFAULT NULL,
+  `publicKey` VARCHAR(255) DEFAULT NULL,
+  `counter` INT NOT NULL DEFAULT '0',
+  `certificate` TEXT,
+  `active` TINYINT(1) NOT NULL DEFAULT '0',
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;
+
+DROP VIEW IF EXISTS grouped_mail_aliases;
+DROP VIEW IF EXISTS grouped_sender_acl;
+DROP VIEW IF EXISTS grouped_domain_alias_address;
+
+CREATE VIEW grouped_mail_aliases (username, aliases) AS
+SELECT goto, IFNULL(GROUP_CONCAT(address SEPARATOR ' '), '') AS address FROM alias
+WHERE address!=goto
+AND active = '1'
+AND address NOT LIKE '@%'
+GROUP BY goto;
+
+CREATE VIEW grouped_sender_acl (username, send_as) AS
+SELECT logged_in_as, IFNULL(GROUP_CONCAT(send_as SEPARATOR ' '), '') AS send_as FROM sender_acl
+WHERE send_as NOT LIKE '@%'
+GROUP BY logged_in_as;
+
+CREATE VIEW grouped_domain_alias_address (username, ad_alias) AS
+SELECT username, IFNULL(GROUP_CONCAT(local_part, '@', alias_domain SEPARATOR ' '), '') AS ad_alias FROM mailbox
+LEFT OUTER JOIN alias_domain on target_domain=domain GROUP BY username;
+
+CREATE TABLE IF NOT EXISTS sogo_acl (
+	c_folder_id INTEGER NOT NULL,
+	c_object character varying(255) NOT NULL,
+	c_uid character varying(255) NOT NULL,
+	c_role character varying(80) NOT NULL
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;
+
+CREATE TABLE IF NOT EXISTS sogo_alarms_folder (
+	c_path          VARCHAR(255) NOT NULL,
+	c_name          VARCHAR(255) NOT NULL,
+	c_uid           VARCHAR(255) NOT NULL,
+	c_recurrence_id INT(11)      DEFAULT NULL,
+	c_alarm_number  INT(11)      NOT NULL,
+	c_alarm_date    INT(11)      NOT NULL
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;
+
+CREATE TABLE IF NOT EXISTS sogo_cache_folder (
+	c_uid          VARCHAR(255) NOT NULL,
+	c_path         VARCHAR(255) NOT NULL,
+	c_parent_path  VARCHAR(255) DEFAULT NULL,
+	c_type         TINYINT(3)   unsigned NOT NULL,
+	c_creationdate INT(11)      NOT NULL,
+	c_lastmodified INT(11)      NOT NULL,
+	c_version      INT(11)      NOT NULL DEFAULT '0',
+	c_deleted      TINYINT(4)   NOT NULL DEFAULT '0',
+	c_content      longTEXT,
+	PRIMARY KEY (c_uid,c_path)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;
+
+CREATE TABLE IF NOT EXISTS sogo_folder_info (
+	c_folder_id      BIGINT(20)    unsigned NOT NULL AUTO_INCREMENT,
+	c_path           VARCHAR(255)  NOT NULL,
+	c_path1          VARCHAR(255)  NOT NULL,
+	c_path2          VARCHAR(255)  DEFAULT NULL,
+	c_path3          VARCHAR(255)  DEFAULT NULL,
+	c_path4          VARCHAR(255)  DEFAULT NULL,
+	c_foldername     VARCHAR(255)  NOT NULL,
+	c_location       INTeger NULL,
+	c_quick_location VARCHAR(2048) DEFAULT NULL,
+	c_acl_location   VARCHAR(2048) DEFAULT NULL,
+	c_folder_type    VARCHAR(255)  NOT NULL,
+	PRIMARY KEY (c_path),
+	UNIQUE KEY c_folder_id (c_folder_id)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;
+
+CREATE TABLE IF NOT EXISTS sogo_quick_appointment (
+	c_folder_id INTeger NOT NULL,
+	c_name character varying(255) NOT NULL,
+	c_uid character varying(255) NOT NULL,
+	c_startdate INTeger,
+	c_enddate INTeger,
+	c_cycleenddate INTeger,
+	c_title character varying(1000) NOT NULL,
+	c_participants TEXT,
+	c_isallday INTeger,
+	c_iscycle INTeger,
+	c_cycleinfo TEXT,
+	c_classification INTeger NOT NULL,
+	c_isopaque INTeger NOT NULL,
+	c_status INTeger NOT NULL,
+	c_priority INTeger,
+	c_location character varying(255),
+	c_orgmail character varying(255),
+	c_partmails TEXT,
+	c_partstates TEXT,
+	c_category character varying(255),
+	c_sequence INTeger,
+	c_component character varying(10) NOT NULL,
+	c_nextalarm INTeger,
+	c_description TEXT,
+	CONSTRAINT sogo_quick_appointment_pkey PRIMARY KEY (c_folder_id, c_name)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;
+
+CREATE TABLE IF NOT EXISTS sogo_quick_contact (
+	c_folder_id INTeger NOT NULL,
+	c_name character varying(255) NOT NULL,
+	c_givenname character varying(255),
+	c_cn character varying(255),
+	c_sn character varying(255),
+	c_screenname character varying(255),
+	c_l character varying(255),
+	c_mail character varying(255),
+	c_o character varying(255),
+	c_ou character varying(255),
+	c_telephonenumber character varying(255),
+	c_categories character varying(255),
+	c_component character varying(10) NOT NULL,
+	CONSTRAINT sogo_quick_contact_pkey PRIMARY KEY (c_folder_id, c_name)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;
+
+CREATE TABLE IF NOT EXISTS sogo_sessions_folder (
+	c_id           VARCHAR(255) NOT NULL,
+	c_value        VARCHAR(255) NOT NULL,
+	c_creationdate INT(11)      NOT NULL,
+	c_lastseen     INT(11)      NOT NULL,
+	PRIMARY KEY (c_id)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;
+
+CREATE TABLE IF NOT EXISTS sogo_store (
+	c_folder_id INTeger NOT NULL,
+	c_name character varying(255) NOT NULL,
+	c_content mediumTEXT NOT NULL,
+	c_creationdate INTeger NOT NULL,
+	c_lastmodified INTeger NOT NULL,
+	c_version INTeger NOT NULL,
+	c_deleted INTeger,
+	CONSTRAINT sogo_store_pkey PRIMARY KEY (c_folder_id, c_name)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;
+
+CREATE TABLE IF NOT EXISTS sogo_user_profile (
+	c_uid      VARCHAR(255) NOT NULL,
+	c_defaults TEXT,
+	c_settings TEXT,
+	PRIMARY KEY (c_uid)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;
+
+INSERT INTO `admin` (username, password, superadmin, created, modified, active) SELECT 'admin', '{SSHA256}K8eVJ6YsZbQCfuJvSUbaQRLr0HPLz5rC9IAp0PAFl0tmNDBkMDc0NDAyOTAxN2Rk', 1, NOW(), NOW(), 1 WHERE NOT EXISTS (SELECT * FROM `admin`);
+DELETE FROM `domain_admins`;
+INSERT INTO `domain_admins` (username, domain, created, active) SELECT `username`, 'ALL', NOW(), 1 FROM `admin` WHERE superadmin='1' AND `username` NOT IN (SELECT `username` FROM `domain_admins`);
diff --git a/data/web/inc/languages.min.css b/data/web/inc/languages.min.css
new file mode 100644
index 00000000..79b740c3
--- /dev/null
+++ b/data/web/inc/languages.min.css
@@ -0,0 +1 @@
+.lang-xs{background-position:0 -473px;min-width:14px;height:11px;min-height:11px;max-height:11px;background-repeat:no-repeat;display:inline-block;background-image:url(languages.png)}.lang-sm{background-position:0 -1172px;min-width:22px;height:16px;min-height:16px;max-height:16px;background-repeat:no-repeat;display:inline-block;background-image:url(languages.png)}.lang-lg{background-position:0 -2134px;min-width:30px;height:22px;min-height:22px;max-height:22px;background-repeat:no-repeat;display:inline-block;background-image:url(languages.png)}.lang-xs[lang=ar]{background-position:0 0}.lang-xs[lang=be]{background-position:0 -11px}.lang-xs[lang=bg]{background-position:0 -22px}.lang-xs[lang=cs]{background-position:0 -33px}.lang-xs[lang=da]{background-position:0 -44px}.lang-xs[lang=de]{background-position:0 -55px}.lang-xs[lang=el]{background-position:0 -66px}.lang-xs[lang=en]{background-position:0 -77px}.lang-xs[lang=es]{background-position:0 -88px}.lang-xs[lang=et]{background-position:0 -99px}.lang-xs[lang=fi]{background-position:0 -110px}.lang-xs[lang=fr]{background-position:0 -121px}.lang-xs[lang=ga]{background-position:0 -132px}.lang-xs[lang=hi]{background-position:0 -143px}.lang-xs[lang=hr]{background-position:0 -154px}.lang-xs[lang=hu]{background-position:0 -165px}.lang-xs[lang=in]{background-position:0 -176px}.lang-xs[lang=is]{background-position:0 -187px}.lang-xs[lang=it]{background-position:0 -198px}.lang-xs[lang=iw]{background-position:0 -209px}.lang-xs[lang=ja]{background-position:0 -220px}.lang-xs[lang=ko]{background-position:0 -231px}.lang-xs[lang=lt]{background-position:0 -242px}.lang-xs[lang=lv]{background-position:0 -253px}.lang-xs[lang=mk]{background-position:0 -264px}.lang-xs[lang=ms]{background-position:0 -275px}.lang-xs[lang=mt]{background-position:0 -286px}.lang-xs[lang=nl]{background-position:0 -297px}.lang-xs[lang=no]{background-position:0 -308px}.lang-xs[lang=pl]{background-position:0 -319px}.lang-xs[lang=pt]{background-position:0 -330px}.lang-xs[lang=ro]{background-position:0 -341px}.lang-xs[lang=ru]{background-position:0 -352px}.lang-xs[lang=sk]{background-position:0 -363px}.lang-xs[lang=sl]{background-position:0 -374px}.lang-xs[lang=sq]{background-position:0 -385px}.lang-xs[lang=sr]{background-position:0 -396px}.lang-xs[lang=sv]{background-position:0 -407px}.lang-xs[lang=th]{background-position:0 -418px}.lang-xs[lang=tr]{background-position:0 -429px}.lang-xs[lang=uk]{background-position:0 -440px}.lang-xs[lang=vi]{background-position:0 -451px}.lang-xs[lang=zh]{background-position:0 -462px}.lang-sm[lang=ar]{background-position:0 -484px}.lang-sm[lang=be]{background-position:0 -500px}.lang-sm[lang=bg]{background-position:0 -516px}.lang-sm[lang=cs]{background-position:0 -532px}.lang-sm[lang=da]{background-position:0 -548px}.lang-sm[lang=de]{background-position:0 -564px}.lang-sm[lang=el]{background-position:0 -580px}.lang-sm[lang=en]{background-position:0 -596px}.lang-sm[lang=es]{background-position:0 -612px}.lang-sm[lang=et]{background-position:0 -628px}.lang-sm[lang=fi]{background-position:0 -644px}.lang-sm[lang=fr]{background-position:0 -660px}.lang-sm[lang=ga]{background-position:0 -676px}.lang-sm[lang=hi]{background-position:0 -692px}.lang-sm[lang=hr]{background-position:0 -708px}.lang-sm[lang=hu]{background-position:0 -724px}.lang-sm[lang=in]{background-position:0 -740px}.lang-sm[lang=is]{background-position:0 -756px}.lang-sm[lang=it]{background-position:0 -772px}.lang-sm[lang=iw]{background-position:0 -788px}.lang-sm[lang=ja]{background-position:0 -804px}.lang-sm[lang=ko]{background-position:0 -820px}.lang-sm[lang=lt]{background-position:0 -836px}.lang-sm[lang=lv]{background-position:0 -852px}.lang-sm[lang=mk]{background-position:0 -868px}.lang-sm[lang=ms]{background-position:0 -884px}.lang-sm[lang=mt]{background-position:0 -900px}.lang-sm[lang=nl]{background-position:0 -916px}.lang-sm[lang=no]{background-position:0 -932px}.lang-sm[lang=pl]{background-position:0 -948px}.lang-sm[lang=pt]{background-position:0 -964px}.lang-sm[lang=ro]{background-position:0 -980px}.lang-sm[lang=ru]{background-position:0 -996px}.lang-sm[lang=sk]{background-position:0 -1012px}.lang-sm[lang=sl]{background-position:0 -1028px}.lang-sm[lang=sq]{background-position:0 -1044px}.lang-sm[lang=sr]{background-position:0 -1060px}.lang-sm[lang=sv]{background-position:0 -1076px}.lang-sm[lang=th]{background-position:0 -1092px}.lang-sm[lang=tr]{background-position:0 -1108px}.lang-sm[lang=uk]{background-position:0 -1124px}.lang-sm[lang=vi]{background-position:0 -1140px}.lang-sm[lang=zh]{background-position:0 -1156px}.lang-lg[lang=ar]{background-position:0 -1188px}.lang-lg[lang=be]{background-position:0 -1210px}.lang-lg[lang=bg]{background-position:0 -1232px}.lang-lg[lang=cs]{background-position:0 -1254px}.lang-lg[lang=da]{background-position:0 -1276px}.lang-lg[lang=de]{background-position:0 -1298px}.lang-lg[lang=el]{background-position:0 -1320px}.lang-lg[lang=en]{background-position:0 -1342px}.lang-lg[lang=es]{background-position:0 -1364px}.lang-lg[lang=et]{background-position:0 -1386px}.lang-lg[lang=fi]{background-position:0 -1408px}.lang-lg[lang=fr]{background-position:0 -1430px}.lang-lg[lang=ga]{background-position:0 -1452px}.lang-lg[lang=hi]{background-position:0 -1474px}.lang-lg[lang=hr]{background-position:0 -1496px}.lang-lg[lang=hu]{background-position:0 -1518px}.lang-lg[lang=in]{background-position:0 -1540px}.lang-lg[lang=is]{background-position:0 -1562px}.lang-lg[lang=it]{background-position:0 -1584px}.lang-lg[lang=iw]{background-position:0 -1606px}.lang-lg[lang=ja]{background-position:0 -1628px}.lang-lg[lang=ko]{background-position:0 -1650px}.lang-lg[lang=lt]{background-position:0 -1672px}.lang-lg[lang=lv]{background-position:0 -1694px}.lang-lg[lang=mk]{background-position:0 -1716px}.lang-lg[lang=ms]{background-position:0 -1738px}.lang-lg[lang=mt]{background-position:0 -1760px}.lang-lg[lang=nl]{background-position:0 -1782px}.lang-lg[lang=no]{background-position:0 -1804px}.lang-lg[lang=pl]{background-position:0 -1826px}.lang-lg[lang=pt]{background-position:0 -1848px}.lang-lg[lang=ro]{background-position:0 -1870px}.lang-lg[lang=ru]{background-position:0 -1892px}.lang-lg[lang=sk]{background-position:0 -1914px}.lang-lg[lang=sl]{background-position:0 -1936px}.lang-lg[lang=sq]{background-position:0 -1958px}.lang-lg[lang=sr]{background-position:0 -1980px}.lang-lg[lang=sv]{background-position:0 -2002px}.lang-lg[lang=th]{background-position:0 -2024px}.lang-lg[lang=tr]{background-position:0 -2046px}.lang-lg[lang=uk]{background-position:0 -2068px}.lang-lg[lang=vi]{background-position:0 -2090px}.lang-lg[lang=zh]{background-position:0 -2112px}.lang-lbl-en:after,.lang-lbl-full:after,.lang-lbl:after{content:"Unknown language"}.lang-lbl[lang=ar]:after{content:"\000627\000644\000639\000631\000628\00064A\000629"}.lang-lbl[lang=be]:after{content:"\000411\000435\00043B\000430\000440\000443\000441\00043A\000456"}.lang-lbl[lang=bg]:after{content:"\000411\00044A\00043B\000433\000430\000440\000441\00043A\000438"}.lang-lbl[lang=cs]:after{content:"\00010Ce\000161tina"}.lang-lbl[lang=da]:after{content:"Dansk"}.lang-lbl[lang=de]:after{content:"Deutsch"}.lang-lbl[lang=el]:after{content:"\000395\0003BB\0003BB\0003B7\0003BD\0003B9\0003BA\0003AC"}.lang-lbl[lang=en]:after{content:"English"}.lang-lbl[lang=es]:after{content:"Espa\0000F1ol"}.lang-lbl[lang=et]:after{content:"Eesti"}.lang-lbl[lang=fi]:after{content:"Suomi"}.lang-lbl[lang=fr]:after{content:"Fran\0000E7ais"}.lang-lbl[lang=ga]:after{content:"Gaeilge"}.lang-lbl[lang=hi]:after{content:"\000939\00093F\000902\000926\000940"}.lang-lbl[lang=hr]:after{content:"Hrvatski"}.lang-lbl[lang=hu]:after{content:"Magyar"}.lang-lbl[lang=in]:after{content:"Bahasa\000020indonesia"}.lang-lbl[lang=is]:after{content:"\0000CDslenska"}.lang-lbl[lang=it]:after{content:"Italiano"}.lang-lbl[lang=iw]:after{content:"\0005E2\0005D1\0005E8\0005D9\0005EA"}.lang-lbl[lang=ja]:after{content:"\0065E5\00672C\008A9E"}.lang-lbl[lang=ko]:after{content:"\00D55C\00AD6D\00C5B4"}.lang-lbl[lang=lt]:after{content:"Lietuvi\000173"}.lang-lbl[lang=lv]:after{content:"Latvie\000161u"}.lang-lbl[lang=mk]:after{content:"\00041C\000430\00043A\000435\000434\00043E\00043D\000441\00043A\000438"}.lang-lbl[lang=ms]:after{content:"Bahasa\000020melayu"}.lang-lbl[lang=mt]:after{content:"Malti"}.lang-lbl[lang=nl]:after{content:"Nederlands"}.lang-lbl[lang=no]:after{content:"Norsk"}.lang-lbl[lang=pl]:after{content:"Polski"}.lang-lbl[lang=pt]:after{content:"Portugu\0000EAs"}.lang-lbl[lang=ro]:after{content:"Rom\0000E2n\000103"}.lang-lbl[lang=ru]:after{content:"\000420\000443\000441\000441\00043A\000438\000439"}.lang-lbl[lang=sk]:after{content:"Sloven\00010Dina"}.lang-lbl[lang=sl]:after{content:"Sloven\000161\00010Dina"}.lang-lbl[lang=sq]:after{content:"Shqipe"}.lang-lbl[lang=sr]:after{content:"\000421\000440\00043F\000441\00043A\000438"}.lang-lbl[lang=sv]:after{content:"Svenska"}.lang-lbl[lang=th]:after{content:"\000E44\000E17\000E22"}.lang-lbl[lang=tr]:after{content:"T\0000FCrk\0000E7e"}.lang-lbl[lang=uk]:after{content:"\000423\00043A\000440\000430\000457\00043D\000441\00044C\00043A\000430"}.lang-lbl[lang=vi]:after{content:"Ti\001EBFng\000020vi\001EC7t"}.lang-lbl[lang=zh]:after{content:"\004E2D\006587"}.lang-lbl-en[lang=ar]:after{content:"Arabic"}.lang-lbl-en[lang=be]:after{content:"Belarusian"}.lang-lbl-en[lang=bg]:after{content:"Bulgarian"}.lang-lbl-en[lang=cs]:after{content:"Czech"}.lang-lbl-en[lang=da]:after{content:"Danish"}.lang-lbl-en[lang=de]:after{content:"German"}.lang-lbl-en[lang=el]:after{content:"Greek"}.lang-lbl-en[lang=en]:after{content:"English"}.lang-lbl-en[lang=es]:after{content:"Spanish"}.lang-lbl-en[lang=et]:after{content:"Estonian"}.lang-lbl-en[lang=fi]:after{content:"Finnish"}.lang-lbl-en[lang=fr]:after{content:"French"}.lang-lbl-en[lang=ga]:after{content:"Irish"}.lang-lbl-en[lang=hi]:after{content:"Hindi"}.lang-lbl-en[lang=hr]:after{content:"Croatian"}.lang-lbl-en[lang=hu]:after{content:"Hungarian"}.lang-lbl-en[lang=in]:after{content:"Indonesian"}.lang-lbl-en[lang=is]:after{content:"Icelandic"}.lang-lbl-en[lang=it]:after{content:"Italian"}.lang-lbl-en[lang=iw]:after{content:"Hebrew"}.lang-lbl-en[lang=ja]:after{content:"Japanese"}.lang-lbl-en[lang=ko]:after{content:"Korean"}.lang-lbl-en[lang=lt]:after{content:"Lithuanian"}.lang-lbl-en[lang=lv]:after{content:"Latvian"}.lang-lbl-en[lang=mk]:after{content:"Macedonian"}.lang-lbl-en[lang=ms]:after{content:"Malay"}.lang-lbl-en[lang=mt]:after{content:"Maltese"}.lang-lbl-en[lang=nl]:after{content:"Dutch"}.lang-lbl-en[lang=no]:after{content:"Norwegian"}.lang-lbl-en[lang=pl]:after{content:"Polish"}.lang-lbl-en[lang=pt]:after{content:"Portuguese"}.lang-lbl-en[lang=ro]:after{content:"Romanian"}.lang-lbl-en[lang=ru]:after{content:"Russian"}.lang-lbl-en[lang=sk]:after{content:"Slovak"}.lang-lbl-en[lang=sl]:after{content:"Slovenian"}.lang-lbl-en[lang=sq]:after{content:"Albanian"}.lang-lbl-en[lang=sr]:after{content:"Serbian"}.lang-lbl-en[lang=sv]:after{content:"Swedish"}.lang-lbl-en[lang=th]:after{content:"Thai"}.lang-lbl-en[lang=tr]:after{content:"Turkish"}.lang-lbl-en[lang=uk]:after{content:"Ukrainian"}.lang-lbl-en[lang=vi]:after{content:"Vietnamese"}.lang-lbl-en[lang=zh]:after{content:"Chinese"}.lang-lbl-full[lang=ar]:after{content:"\000627\000644\000639\000631\000628\00064A\000629\0000A0/\0000A0Arabic"}.lang-lbl-full[lang=be]:after{content:"\000411\000435\00043B\000430\000440\000443\000441\00043A\000456\0000A0/\0000A0Belarusian"}.lang-lbl-full[lang=bg]:after{content:"\000411\00044A\00043B\000433\000430\000440\000441\00043A\000438\0000A0/\0000A0Bulgarian"}.lang-lbl-full[lang=cs]:after{content:"\00010Ce\000161tina\0000A0/\0000A0Czech"}.lang-lbl-full[lang=da]:after{content:"Dansk\0000A0/\0000A0Danish"}.lang-lbl-full[lang=de]:after{content:"Deutsch\0000A0/\0000A0German"}.lang-lbl-full[lang=el]:after{content:"\000395\0003BB\0003BB\0003B7\0003BD\0003B9\0003BA\0003AC\0000A0/\0000A0Greek"}.lang-lbl-full[lang=en]:after{content:"English\0000A0/\0000A0English"}.lang-lbl-full[lang=es]:after{content:"Espa\0000F1ol\0000A0/\0000A0Spanish"}.lang-lbl-full[lang=et]:after{content:"Eesti\0000A0/\0000A0Estonian"}.lang-lbl-full[lang=fi]:after{content:"Suomi\0000A0/\0000A0Finnish"}.lang-lbl-full[lang=fr]:after{content:"Fran\0000E7ais\0000A0/\0000A0French"}.lang-lbl-full[lang=ga]:after{content:"Gaeilge\0000A0/\0000A0Irish"}.lang-lbl-full[lang=hi]:after{content:"\000939\00093F\000902\000926\000940\0000A0/\0000A0Hindi"}.lang-lbl-full[lang=hr]:after{content:"Hrvatski\0000A0/\0000A0Croatian"}.lang-lbl-full[lang=hu]:after{content:"Magyar\0000A0/\0000A0Hungarian"}.lang-lbl-full[lang=in]:after{content:"Bahasa\000020indonesia\0000A0/\0000A0Indonesian"}.lang-lbl-full[lang=is]:after{content:"\0000CDslenska\0000A0/\0000A0Icelandic"}.lang-lbl-full[lang=it]:after{content:"Italiano\0000A0/\0000A0Italian"}.lang-lbl-full[lang=iw]:after{content:"\0005E2\0005D1\0005E8\0005D9\0005EA\0000A0/\0000A0Hebrew"}.lang-lbl-full[lang=ja]:after{content:"\0065E5\00672C\008A9E\0000A0/\0000A0Japanese"}.lang-lbl-full[lang=ko]:after{content:"\00D55C\00AD6D\00C5B4\0000A0/\0000A0Korean"}.lang-lbl-full[lang=lt]:after{content:"Lietuvi\000173\0000A0/\0000A0Lithuanian"}.lang-lbl-full[lang=lv]:after{content:"Latvie\000161u\0000A0/\0000A0Latvian"}.lang-lbl-full[lang=mk]:after{content:"\00041C\000430\00043A\000435\000434\00043E\00043D\000441\00043A\000438\0000A0/\0000A0Macedonian"}.lang-lbl-full[lang=ms]:after{content:"Bahasa\000020melayu\0000A0/\0000A0Malay"}.lang-lbl-full[lang=mt]:after{content:"Malti\0000A0/\0000A0Maltese"}.lang-lbl-full[lang=nl]:after{content:"Nederlands\0000A0/\0000A0Dutch"}.lang-lbl-full[lang=no]:after{content:"Norsk\0000A0/\0000A0Norwegian"}.lang-lbl-full[lang=pl]:after{content:"Polski\0000A0/\0000A0Polish"}.lang-lbl-full[lang=pt]:after{content:"Portugu\0000EAs\0000A0/\0000A0Portuguese"}.lang-lbl-full[lang=ro]:after{content:"Rom\0000E2n\000103\0000A0/\0000A0Romanian"}.lang-lbl-full[lang=ru]:after{content:"\000420\000443\000441\000441\00043A\000438\000439\0000A0/\0000A0Russian"}.lang-lbl-full[lang=sk]:after{content:"Sloven\00010Dina\0000A0/\0000A0Slovak"}.lang-lbl-full[lang=sl]:after{content:"Sloven\000161\00010Dina\0000A0/\0000A0Slovenian"}.lang-lbl-full[lang=sq]:after{content:"Shqipe\0000A0/\0000A0Albanian"}.lang-lbl-full[lang=sr]:after{content:"\000421\000440\00043F\000441\00043A\000438\0000A0/\0000A0Serbian"}.lang-lbl-full[lang=sv]:after{content:"Svenska\0000A0/\0000A0Swedish"}.lang-lbl-full[lang=th]:after{content:"\000E44\000E17\000E22\0000A0/\0000A0Thai"}.lang-lbl-full[lang=tr]:after{content:"T\0000FCrk\0000E7e\0000A0/\0000A0Turkish"}.lang-lbl-full[lang=uk]:after{content:"\000423\00043A\000440\000430\000457\00043D\000441\00044C\00043A\000430\0000A0/\0000A0Ukrainian"}.lang-lbl-full[lang=vi]:after{content:"Ti\001EBFng\000020vi\001EC7t\0000A0/\0000A0Vietnamese"}.lang-lbl-full[lang=zh]:after{content:"\004E2D\006587\0000A0/\0000A0Chinese"}.lang-lg:before,.lang-sm:before,.lang-xs:before{content:'\0000A0'}.lang-xs.lang-lbl,.lang-xs.lang-lbl-en,.lang-xs.lang-lbl-full{padding-left:16px}.lang-sm.lang-lbl,.lang-sm.lang-lbl-en,.lang-sm.lang-lbl-full{padding-left:24px}.lang-lg.lang-lbl,.lang-lg.lang-lbl-en,.lang-lg.lang-lbl-full{padding-left:32px}.lang-lg.lang-lbl-en:before,.lang-lg.lang-lbl-full:before,.lang-lg.lang-lbl:before,.lang-sm.lang-lbl-en:before,.lang-sm.lang-lbl-full:before,.lang-sm.lang-lbl:before,.lang-xs.lang-lbl-en:before,.lang-xs.lang-lbl-full:before,.lang-xs.lang-lbl:before{content:''}.lang-lg,.lang-lg:after{top:0;position:relative}.lang-sm{top:1px;position:relative}.lang-sm:after{top:-1px;position:relative}.lang-xs{top:4px;position:relative}.lang-xs:after{top:-4px;position:relative}.lead>.lang-lg{top:2px}.lead>.lang-lg:after{top:-2px}.lead>.lang-sm{top:6px}.lead>.lang-sm:after{top:-6px}.lead>.lang-xs{top:8px}.lead>.lang-xs:after{top:-8px}small>.lang-sm{top:-1px}small>.lang-sm:after{top:1px}small>.lang-xs{top:2px}small>.lang-xs:after{top:-2px}h1>.lang-lg{top:9px}h1>.lang-lg:after{top:-9px}h1>.lang-sm{top:12px}h1>.lang-sm:after{top:-12px}h1>.lang-xs{top:14px}h1>.lang-xs:after{top:-14px}h2>.lang-lg{top:5px}h2>.lang-lg:after{top:-5px}h2>.lang-sm{top:8px}h2>.lang-sm:after{top:-8px}h2>.lang-xs{top:10px}h2>.lang-xs:after{top:-10px}h3>.lang-lg{top:1px}h3>.lang-lg:after{top:-1px}h3>.lang-sm{top:5px}h3>.lang-sm:after{top:-5px}h3>.lang-xs{top:8px}h3>.lang-xs:after{top:-8px}h4>.lang-lg{top:-1px}h4>.lang-lg:after,h4>.lang-sm{top:1px}h4>.lang-sm:after{top:-1px}h4>.lang-xs{top:4px}h4>.lang-xs:after{top:-4px}h5>.lang-sm,h5>.lang-sm:after{top:0}h5>.lang-xs{top:2px}h5>.lang-xs:after{top:-2px}h6>.lang-sm,h6>.lang-sm:after{top:0}h6>.lang-xs{top:1px}h6>.lang-xs:after{top:-1px}.btn>.lang-sm{top:2px}.btn>.lang-sm:after{top:-2px}.btn>.lang-xs{top:4px}.btn>.lang-xs:after{top:-4px}.btn.btn-xs>.lang-sm,.btn.btn-xs>.lang-sm:after{top:0}.btn.btn-xs>.lang-xs{top:3px}.btn.btn-xs>.lang-xs:after{top:-3px}.btn.btn-sm>.lang-sm,.btn.btn-sm>.lang-sm:after{top:0}.btn.btn-sm>.lang-xs{top:3px}.btn.btn-sm>.lang-xs:after{top:-3px}.btn.btn-lg>.lang-lg{top:1px}.btn.btn-lg>.lang-lg:after{top:-1px}.btn.btn-lg>.lang-sm{top:3px}.btn.btn-lg>.lang-sm:after{top:-3px}.btn.btn-lg>.lang-xs{top:6px}.btn.btn-lg>.lang-xs:after{top:-6px}
\ No newline at end of file
diff --git a/data/web/inc/languages.png b/data/web/inc/languages.png
new file mode 100644
index 00000000..c88e039b
Binary files /dev/null and b/data/web/inc/languages.png differ
diff --git a/data/web/inc/lib/U2F.php b/data/web/inc/lib/U2F.php
new file mode 100644
index 00000000..b79d7fac
--- /dev/null
+++ b/data/web/inc/lib/U2F.php
@@ -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);
+    }
+}
diff --git a/data/web/inc/lib/Yubico.php b/data/web/inc/lib/Yubico.php
new file mode 100644
index 00000000..68a3087b
--- /dev/null
+++ b/data/web/inc/lib/Yubico.php
@@ -0,0 +1,475 @@
+<?php
+  /**
+   * Class for verifying Yubico One-Time-Passcodes
+   *
+   * @category    Auth
+   * @package     Auth_Yubico
+   * @author      Simon Josefsson <[email protected]>, Olov Danielson <[email protected]>
+   * @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 &timestamp=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');
+	}
+}
+?>
diff --git a/data/web/inc/prerequisites.inc.php b/data/web/inc/prerequisites.inc.php
new file mode 100644
index 00000000..ffa1eac6
--- /dev/null
+++ b/data/web/inc/prerequisites.inc.php
@@ -0,0 +1,103 @@
+<?php
+//ini_set("session.cookie_secure", 1);
+//ini_set("session.cookie_httponly", 1);
+session_start();
+if (isset($_POST["logout"])) {
+  if (isset($_SESSION["dual-login"])) {
+    $_SESSION["mailcow_cc_username"] = $_SESSION["dual-login"]["username"];
+    $_SESSION["mailcow_cc_role"] = $_SESSION["dual-login"]["role"];
+    unset($_SESSION["dual-login"]);
+  }
+  else {
+    session_unset();
+    session_destroy();
+    session_write_close();
+    setcookie(session_name(),'',0,'/');
+  }
+}
+
+require_once 'inc/vars.inc.php';
+if (file_exists('./inc/vars.local.inc.php')) {
+	include_once 'inc/vars.local.inc.php';
+}
+
+// Yubi OTP API
+require_once 'inc/lib/Yubico.php';
+
+// U2F API
+require_once 'inc/lib/U2F.php';
+$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";
+$opt = [
+    PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
+    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
+    PDO::ATTR_EMULATE_PREPARES   => false,
+];
+try {
+	$pdo = new PDO($dsn, $database_user, $database_pass, $opt);
+}
+catch (PDOException $e) {
+?>
+<center style='font-family: "Lucida Sans Unicode", "Lucida Grande", Verdana, Arial, Helvetica, sans-serif;'>🐮 Connection failed, database may be in warm-up state, please try again later.<br /><br />The following error was reported:<br/>  <?=$e->getMessage();?></center>
+<?php
+exit;
+}
+
+$_SESSION['mailcow_locale'] = strtolower(trim($DEFAULT_LANG));
+setcookie('language', $DEFAULT_LANG);
+if (isset($_COOKIE['language'])) {
+	switch ($_COOKIE['language']) {
+		case "de":
+			$_SESSION['mailcow_locale'] = 'de';
+			setcookie('language', 'de');
+		break;
+		case "en":
+			$_SESSION['mailcow_locale'] = 'en';
+			setcookie('language', 'en');
+		break;
+		case "es":
+			$_SESSION['mailcow_locale'] = 'es';
+			setcookie('language', 'es');
+		break;
+		case "nl":
+			$_SESSION['mailcow_locale'] = 'nl';
+			setcookie('language', 'nl');
+		break;
+		case "pt":
+			$_SESSION['mailcow_locale'] = 'pt';
+			setcookie('language', 'pt');
+		break;
+	}
+}
+if (isset($_GET['lang'])) {
+	switch ($_GET['lang']) {
+		case "de":
+			$_SESSION['mailcow_locale'] = 'de';
+			setcookie('language', 'de');
+		break;
+		case "en":
+			$_SESSION['mailcow_locale'] = 'en';
+			setcookie('language', 'en');
+		break;
+		case "es":
+			$_SESSION['mailcow_locale'] = 'es';
+			setcookie('language', 'es');
+		break;
+		case "nl":
+			$_SESSION['mailcow_locale'] = 'nl';
+			setcookie('language', 'nl');
+		break;
+		case "pt":
+			$_SESSION['mailcow_locale'] = 'pt';
+			setcookie('language', 'pt');
+		break;
+	}
+}
+require_once 'lang/lang.en.php';
+include 'lang/lang.'.$_SESSION['mailcow_locale'].'.php';
+require_once 'inc/functions.inc.php';
+require_once 'inc/triggers.inc.php';
+(!isset($_SESSION['mailcow_cc_username'])) ? init_db_schema() : null;
diff --git a/data/web/inc/tfa_modals.php b/data/web/inc/tfa_modals.php
new file mode 100644
index 00000000..d6956724
--- /dev/null
+++ b/data/web/inc/tfa_modals.php
@@ -0,0 +1,133 @@
+<div class="modal fade" id="YubiOTPModal" tabindex="-1" role="dialog" aria-labelledby="YubiOTPModalLabel">
+  <div class="modal-dialog" role="document">
+    <div class="modal-content">
+      <div class="modal-header"><b><?=$lang['tfa']['yubi_otp'];?></b></div>
+      <div class="modal-body">
+      <form role="form" method="post">
+        <div class="form-group">
+          <input type="text" class="form-control" name="key_id" id="key_id" placeholder="<?=$lang['tfa']['key_id'];?>" autocomplete="off" required>
+        </div>
+        <hr>
+        <p class="help-block"><?=$lang['tfa']['api_register'];?></p>
+        <div class="form-group">
+          <input type="text" class="form-control" name="yubico_id" id="yubico_id" placeholder="Yubico API ID" autocomplete="off" required>
+        </div>
+        <div class="form-group">
+          <input type="text" class="form-control" name="yubico_key" id="yubico_key" placeholder="Yubico API Key" autocomplete="off" required>
+        </div>
+        <hr>
+        <div class="form-group">
+          <input type="password" class="form-control" name="confirm_password" id="confirm_password" placeholder="<?=$lang['user']['password_now'];?>" autocomplete="off" required>
+        </div>
+        <div class="form-group">
+          <div class="input-group">
+            <span class="input-group-addon" id="yubi-addon"><img alt="Yubicon Icon" src="/img/yubi.ico"></span>
+            <input type="text" name="otp_token" class="form-control" placeholder="Touch Yubikey" aria-describedby="yubi-addon">
+            <input type="hidden" name="tfa_method" value="yubi_otp">
+          </div>
+        </div>
+        <button class="btn btn-sm btn-default" type="submit" name="set_tfa"><?=$lang['user']['save_changes'];?></button>
+      </form>
+      </div>
+    </div>
+  </div>
+</div>
+
+<div class="modal fade" id="U2FModal" tabindex="-1" role="dialog" aria-labelledby="U2FModalLabel">
+  <div class="modal-dialog" role="document">
+    <div class="modal-content">
+      <div class="modal-header"><b><?=$lang['tfa']['u2f'];?></b></div>
+      <div class="modal-body">
+        <form role="form" method="post" id="u2f_reg_form">
+          <div class="form-group">
+            <input type="text" class="form-control" name="key_id" id="key_id" placeholder="<?=$lang['tfa']['key_id'];?>" autocomplete="off" required>
+          </div>
+          <div class="form-group">
+            <input type="password" class="form-control" name="confirm_password" id="confirm_password" placeholder="<?=$lang['user']['password_now'];?>" autocomplete="off" required>
+          </div>
+          <hr>
+          <p><?=$lang['tfa']['waiting_usb_register'];?></p>
+          <div class="alert alert-danger" style="display:none" id="u2f_return_code"></div>
+          <input type="hidden" name="token" id="u2f_register_data"/>
+          <input type="hidden" name="tfa_method" value="u2f">
+          <input type="hidden" name="set_tfa"/><br/>
+        </form>
+      </div>
+    </div>
+  </div>
+</div>
+
+<div class="modal fade" id="DisableTFAModal" tabindex="-1" role="dialog" aria-labelledby="DisableTFAModalLabel">
+  <div class="modal-dialog" role="document">
+    <div class="modal-content">
+      <div class="modal-header"><b><?=$lang['tfa']['delete_tfa'];?></b></div>
+      <div class="modal-body">
+        <form role="form" method="post">
+          <div class="input-group">
+            <input type="password" class="form-control" name="confirm_password" id="confirm_password" placeholder="<?=$lang['user']['password_now'];?>" autocomplete="off" required>
+            <span class="input-group-btn">
+              <input type="hidden" name="tfa_method" value="none">
+              <button class="btn btn-danger" type="submit" name="set_tfa"><?=$lang['tfa']['delete_tfa'];?></button>
+            </span>
+          </div>
+        </form>
+      </div>
+    </div>
+  </div>
+</div>
+
+<?php
+if (isset($_SESSION['pending_tfa_method'])):
+  $tfa_method = $_SESSION['pending_tfa_method'];
+?>
+<div class="modal fade" id="ConfirmTFAModal" tabindex="-1" role="dialog" aria-labelledby="ConfirmTFAModalLabel">
+  <div class="modal-dialog" role="document">
+    <div class="modal-content">
+      <div class="modal-header"><button type="button" class="close" data-dismiss="modal">&times;</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;
+?>
\ No newline at end of file
diff --git a/data/web/inc/triggers.inc.php b/data/web/inc/triggers.inc.php
new file mode 100644
index 00000000..2895420d
--- /dev/null
+++ b/data/web/inc/triggers.inc.php
@@ -0,0 +1,173 @@
+<?php
+if (isset($_POST["verify_tfa_login"])) {
+  if (verify_tfa_login($_SESSION['pending_mailcow_cc_username'], $_POST["token"])) {
+    $_SESSION['mailcow_cc_username'] = $_SESSION['pending_mailcow_cc_username'];
+    $_SESSION['mailcow_cc_role'] = $_SESSION['pending_mailcow_cc_role'];
+    unset($_SESSION['pending_mailcow_cc_username']);
+    unset($_SESSION['pending_mailcow_cc_role']);
+    unset($_SESSION['pending_tfa_method']);
+		header("Location: /user.php");
+  }
+}
+
+if (isset($_POST["login_user"]) && isset($_POST["pass_user"])) {
+	$login_user = strtolower(trim($_POST["login_user"]));
+	$as = check_login($login_user, $_POST["pass_user"]);
+	if ($as == "admin") {
+		$_SESSION['mailcow_cc_username'] = $login_user;
+		$_SESSION['mailcow_cc_role'] = "admin";
+		header("Location: /admin.php");
+	}
+	elseif ($as == "domainadmin") {
+		$_SESSION['mailcow_cc_username'] = $login_user;
+		$_SESSION['mailcow_cc_role'] = "domainadmin";
+		header("Location: /mailbox.php");
+	}
+	elseif ($as == "user") {
+		$_SESSION['mailcow_cc_username'] = $login_user;
+		$_SESSION['mailcow_cc_role'] = "user";
+		header("Location: /user.php");
+	}
+	elseif ($as != "pending") {
+    unset($_SESSION['pending_mailcow_cc_username']);
+    unset($_SESSION['pending_mailcow_cc_role']);
+    unset($_SESSION['pending_tfa_method']);
+		unset($_SESSION['mailcow_cc_username']);
+		unset($_SESSION['mailcow_cc_role']);
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => $lang['danger']['login_failed']
+		);
+	}
+}
+
+if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == "admin") {
+	if (isset($_GET["duallogin"])) {
+    if (filter_var($_GET["duallogin"], FILTER_VALIDATE_EMAIL)) {
+      $stmt = $pdo->prepare("SELECT `username` FROM `mailbox` WHERE `username` = :duallogin");
+      $stmt->execute(array(':duallogin' => $_GET["duallogin"]));
+      $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
+      if ($num_results != 0) {
+        $_SESSION["dual-login"]["username"] = $_SESSION['mailcow_cc_username'];
+        $_SESSION["dual-login"]["role"]     = $_SESSION['mailcow_cc_role'];
+        $_SESSION['mailcow_cc_username']    = $_GET["duallogin"];
+        $_SESSION['mailcow_cc_role']        = "user";
+        header("Location: /user.php");
+      }
+    }
+  }
+
+	if (isset($_POST["edit_admin_account"])) {
+		edit_admin_account($_POST);
+	}
+	if (isset($_POST["dkim_delete_key"])) {
+		dkim_delete_key($_POST);
+	}
+	if (isset($_POST["dkim_add_key"])) {
+		dkim_add_key($_POST);
+	}
+	if (isset($_POST["add_domain_admin"])) {
+		add_domain_admin($_POST);
+	}
+	if (isset($_POST["delete_domain_admin"])) {
+		delete_domain_admin($_POST);
+	}
+}
+if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == "user") {
+	if (isset($_POST["edit_user_account"])) {
+		edit_user_account($_POST);
+	}
+	if (isset($_POST["mailbox_reset_eas"])) {
+		mailbox_reset_eas($_POST);
+	}
+	if (isset($_POST["edit_spam_score"])) {
+		edit_spam_score($_POST);
+	}
+	if (isset($_POST["edit_delimiter_action"])) {
+		edit_delimiter_action($_POST);
+	}
+	if (isset($_POST["add_policy_list_item"])) {
+		add_policy_list_item($_POST);
+	}
+	if (isset($_POST["delete_policy_list_item"])) {
+		delete_policy_list_item($_POST);
+	}
+	if (isset($_POST["edit_tls_policy"])) {
+		edit_tls_policy($_POST);
+	}
+	if (isset($_POST["add_syncjob"])) {
+		add_syncjob($_POST);
+	}
+	if (isset($_POST["edit_syncjob"])) {
+		edit_syncjob($_POST);
+	}
+	if (isset($_POST["delete_syncjob"])) {
+		delete_syncjob($_POST);
+	}
+	if (isset($_POST["set_time_limited_aliases"])) {
+		set_time_limited_aliases($_POST);
+	}
+}
+if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "admin" || $_SESSION['mailcow_cc_role'] == "domainadmin")) {
+	if (isset($_POST["edit_domain_admin"])) {
+		edit_domain_admin($_POST);
+	}
+	if (isset($_POST["set_tfa"])) {
+		set_tfa($_POST);
+	}
+	if (isset($_POST["unset_tfa_key"])) {
+		unset_tfa_key($_POST);
+	}
+	if (isset($_POST["add_policy_list_item"])) {
+		add_policy_list_item($_POST);
+	}
+	if (isset($_POST["delete_policy_list_item"])) {
+		delete_policy_list_item($_POST);
+	}
+	if (isset($_POST["mailbox_add_domain"])) {
+		mailbox_add_domain($_POST);
+	}
+	if (isset($_POST["mailbox_add_alias"])) {
+		mailbox_add_alias($_POST);
+	}
+	if (isset($_POST["mailbox_add_alias_domain"])) {
+		mailbox_add_alias_domain($_POST);
+	}
+	if (isset($_POST["mailbox_add_mailbox"])) {
+		mailbox_add_mailbox($_POST);
+	}
+	if (isset($_POST["mailbox_add_resource"])) {
+		mailbox_add_resource($_POST);
+	}
+	if (isset($_POST["mailbox_edit_alias"])) {
+		mailbox_edit_alias($_POST);
+	}
+	if (isset($_POST["mailbox_edit_domain"])) {
+		mailbox_edit_domain($_POST);
+	}
+	if (isset($_POST["mailbox_edit_mailbox"])) {
+		mailbox_edit_mailbox($_POST);
+	}
+	if (isset($_POST["mailbox_edit_alias_domain"])) {
+		mailbox_edit_alias_domain($_POST);
+	}
+	if (isset($_POST["mailbox_edit_resource"])) {
+		mailbox_edit_resource($_POST);
+	}
+	if (isset($_POST["mailbox_delete_domain"])) {
+		mailbox_delete_domain($_POST);
+	}
+	if (isset($_POST["mailbox_delete_alias"])) {
+		mailbox_delete_alias($_POST);
+	}
+	if (isset($_POST["mailbox_delete_alias_domain"])) {
+		mailbox_delete_alias_domain($_POST);
+	}
+	if (isset($_POST["mailbox_delete_mailbox"])) {
+		mailbox_delete_mailbox($_POST);
+	}
+	if (isset($_POST["mailbox_delete_resource"])) {
+		mailbox_delete_resource($_POST);
+	}
+}
+?>
diff --git a/data/web/inc/vars.inc.php b/data/web/inc/vars.inc.php
new file mode 100644
index 00000000..b8270ba7
--- /dev/null
+++ b/data/web/inc/vars.inc.php
@@ -0,0 +1,38 @@
+<?php
+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!
+This file will be reset on upgrades.
+*/
+
+// SQL database connection variables
+$database_type = "mysql";
+$database_host = "mysql";
+$database_user = getenv('DBUSER');
+$database_pass = getenv('DBPASS');
+$database_name = getenv('DBNAME');
+
+// Other variables
+$mailcow_hostname = getenv('MAILCOW_HOSTNAME');
+
+// Where to go after adding and editing objects
+// Can be "form" or "previous"
+// "form" will stay in the current form, "previous" will redirect to previous page
+$FORM_ACTION = "previous";
+
+// File locations should not be changed
+$MC_DKIM_TXTS = "/data/dkim/txt";
+$MC_DKIM_KEYS = "/data/dkim/keys";
+
+// Change default language, "en", "pt", "de" or "nl"
+$DEFAULT_LANG = "en";
+
+// Change theme (default: lumen)
+// Needs to be one of those: cerulean, cosmo, cyborg, darkly, flatly, journal, lumen, paper, readable, sandstone,
+// simplex, slate, spacelab, superhero, united, yeti
+// See https://bootswatch.com/
+$DEFAULT_THEME = "lumen";
+
+?>
diff --git a/data/web/index.php b/data/web/index.php
new file mode 100644
index 00000000..31bafda6
--- /dev/null
+++ b/data/web/index.php
@@ -0,0 +1,90 @@
+<?php
+require_once("inc/prerequisites.inc.php");
+
+if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == "admin") {
+	header('Location: /admin.php');
+	exit();
+}
+elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == "domainadmin") {
+	header('Location: /mailbox.php');
+	exit();
+}
+elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == "user") {
+	header('Location: /user.php');
+	exit();
+}
+require_once("inc/header.inc.php");
+$_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
+?>
+<div class="container">
+	<div class="row">
+		<div class="col-md-offset-3 col-md-6">
+			<div class="panel panel-default">
+				<div class="panel-heading"><span class="glyphicon glyphicon-user" aria-hidden="true"></span> <?=$lang['login']['login'];?></div>
+				<div class="panel-body">
+				  <center><img style="max-width:250px" src="/img/cow_mailcow.svg" alt="mailcow"></center>
+					<legend>mailcow UI</legend>
+						<form method="post" autofill="off">
+						<div class="form-group">
+							<label class="sr-only" for="login_user"><?=$lang['login']['username'];?></label>
+							<div class="input-group">
+								<div class="input-group-addon"><i class="glyphicon glyphicon-user"></i></div>
+								<input name="login_user" autocorrect="off" autocapitalize="none" type="name" id="login_user" class="form-control" placeholder="<?=$lang['login']['username'];?>" required="" autofocus="">
+							</div>
+						</div>
+						<div class="form-group">
+							<label class="sr-only" for="pass_user"><?=$lang['login']['password'];?></label>
+							<div class="input-group">
+								<div class="input-group-addon"><i class="glyphicon glyphicon-lock"></i></div>
+								<input name="pass_user" type="password" id="pass_user" class="form-control" placeholder="<?=$lang['login']['password'];?>" required="">
+							</div>
+						</div>
+						<div class="form-group">
+							<button type="submit" class="btn btn-success" value="Login"><?=$lang['login']['login'];?></button>
+							<div class="btn-group pull-right">
+								<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
+									<span class="lang-sm lang-lbl" lang="<?=$_SESSION['mailcow_locale'];?>"></span> <span class="caret"></span>
+								</button>
+								<ul class="dropdown-menu">
+									<li <?=($_SESSION['mailcow_locale'] == 'de') ? 'class="active"' : ''?>><a href="?<?= http_build_query(array_merge($_GET, array("lang" => "de"))) ?>"><span class="lang-xs lang-lbl-full" lang="de"></span></a></li>
+									<li <?=($_SESSION['mailcow_locale'] == 'en') ? 'class="active"' : ''?>><a href="?<?= http_build_query(array_merge($_GET, array("lang" => "en"))) ?>"><span class="lang-xs lang-lbl-full" lang="en"></span></a></li>
+									<li <?=($_SESSION['mailcow_locale'] == 'es') ? 'class="active"' : ''?>><a href="?<?= http_build_query(array_merge($_GET, array("lang" => "es"))) ?>"><span class="lang-xs lang-lbl-full" lang="es"></span></a></li>
+									<li <?=($_SESSION['mailcow_locale'] == 'nl') ? 'class="active"' : ''?>><a href="?<?= http_build_query(array_merge($_GET, array("lang" => "nl"))) ?>"><span class="lang-xs lang-lbl-full" lang="nl"></span></a></li>
+									<li <?=($_SESSION['mailcow_locale'] == 'pt') ? 'class="active"' : ''?>><a href="?<?= http_build_query(array_merge($_GET, array("lang" => "pt"))) ?>"><span class="lang-xs lang-lbl-full" lang="pt"></span></a></li>
+								</ul>
+							</div>
+						</div>
+						</form>
+						<?php
+						if (isset($_SESSION['ldelay']) && $_SESSION['ldelay'] != "0"):
+						?>
+						<p><div class="alert alert-info"><?=sprintf($lang['login']['delayed'], $_SESSION['ldelay']);?></b></div></p>
+						<?php
+						endif;
+						?>
+					<legend>mailcow Apps</legend>
+					<a href="/SOGo/" role="button" class="btn btn-lg btn-default"><?=$lang['start']['start_sogo'];?></a>
+				</div>
+			</div>
+		</div>
+		<div class="col-md-offset-3 col-md-6">
+			<div class="panel panel-default" style="">
+				<div class="panel-heading">
+					<a data-toggle="collapse" href="#collapse1"><span class="glyphicon glyphicon-question-sign" aria-hidden="true"></span> <?=$lang['start']['help'];?></a>
+				</div>
+				<div id="collapse1" class="panel-collapse collapse">
+					<div class="panel-body">
+						<p><span style="border-bottom: 1px dotted #999">mailcow UI</span></p>
+						<p><?=$lang['start']['mailcow_panel_detail'];?></p>
+						<p><span style="border-bottom: 1px dotted #999">mailcow Apps</span></p>
+						<p><?=$lang['start']['mailcow_apps_detail'];?></p>
+					</div>
+				</div>
+			</div>
+		</div>
+	</div>
+</div> <!-- /container -->
+<script src="js/index.js"></script>
+<?php
+require_once("inc/footer.inc.php");
+?>
diff --git a/data/web/js/add.js b/data/web/js/add.js
new file mode 100644
index 00000000..1ebcdbb2
--- /dev/null
+++ b/data/web/js/add.js
@@ -0,0 +1,18 @@
+$(document).ready(function() {
+	// add.php
+	// Get max. possible quota for a domain when domain field changes
+	$('#addSelectDomain').on('change', function() {
+		$.get("json_api.php", { action:"get_domain_details", object:this.value }, function(data){
+      var result = jQuery.parseJSON( data );
+      max_new_mailbox_quota = ( result.max_new_mailbox_quota / 1048576);
+			if (max_new_mailbox_quota != '0') {
+				$("#quotaBadge").html('max. ' +  max_new_mailbox_quota + ' MiB');
+				$('#addInputQuota').attr({"disabled": false, "value": "", "type": "number", "max": max_new_mailbox_quota});
+			}
+			else {
+				$("#quotaBadge").html('max. ' + max_new_mailbox_quota + ' MiB');
+				$('#addInputQuota').attr({"disabled": true, "value": "", "type": "text", "value": "n/a"});
+			}
+		});
+	});
+});
diff --git a/data/web/js/admin.js b/data/web/js/admin.js
new file mode 100644
index 00000000..a235a422
--- /dev/null
+++ b/data/web/js/admin.js
@@ -0,0 +1,31 @@
+$(document).ready(function() {
+	// Postfix restrictions, drag and drop functions
+	$( "[id*=srr-sortable]" ).sortable({
+		items: "li:not(.list-heading)",
+		cancel: ".ui-state-disabled",
+		connectWith: "[id*=srr-sortable]",
+		dropOnEmpty: true,
+		placeholder: "ui-state-highlight"
+	});
+	$( "[id*=ssr-sortable]" ).sortable({
+		items: "li:not(.list-heading)",
+		cancel: ".ui-state-disabled",
+		connectWith: "[id*=ssr-sortable]",
+		dropOnEmpty: true,
+		placeholder: "ui-state-highlight"
+	});
+	$('#srr_form').submit(function(){
+		var srr_joined_vals = $("[id^=srr-sortable-active] li").map(function() {
+			return $(this).data("value");
+		}).get().join(', ');
+		var input = $("<input>").attr("type", "hidden").attr("name", "srr_value").val(srr_joined_vals);
+		$('#srr_form').append($(input));
+	});
+	$('#ssr_form').submit(function(){
+		var ssr_joined_vals = $("[id^=ssr-sortable-active] li").map(function() {
+			return $(this).data("value");
+		}).get().join(', ');
+		var input = $("<input>").attr("type", "hidden").attr("name", "ssr_value").val(ssr_joined_vals);
+		$('#ssr_form').append($(input));
+	});
+});
\ No newline at end of file
diff --git a/data/web/js/bootstrap-select.min.js b/data/web/js/bootstrap-select.min.js
new file mode 100644
index 00000000..d2a33149
--- /dev/null
+++ b/data/web/js/bootstrap-select.min.js
@@ -0,0 +1,9 @@
+/*!
+ * Bootstrap-select v1.12.2 (http://silviomoreto.github.io/bootstrap-select)
+ *
+ * Copyright 2013-2017 bootstrap-select
+ * Licensed under MIT (https://github.com/silviomoreto/bootstrap-select/blob/master/LICENSE)
+ */
+!function(a,b){"function"==typeof define&&define.amd?define(["jquery"],function(a){return b(a)}):"object"==typeof module&&module.exports?module.exports=b(require("jquery")):b(a.jQuery)}(this,function(a){!function(a){"use strict";function b(b){var c=[{re:/[\xC0-\xC6]/g,ch:"A"},{re:/[\xE0-\xE6]/g,ch:"a"},{re:/[\xC8-\xCB]/g,ch:"E"},{re:/[\xE8-\xEB]/g,ch:"e"},{re:/[\xCC-\xCF]/g,ch:"I"},{re:/[\xEC-\xEF]/g,ch:"i"},{re:/[\xD2-\xD6]/g,ch:"O"},{re:/[\xF2-\xF6]/g,ch:"o"},{re:/[\xD9-\xDC]/g,ch:"U"},{re:/[\xF9-\xFC]/g,ch:"u"},{re:/[\xC7-\xE7]/g,ch:"c"},{re:/[\xD1]/g,ch:"N"},{re:/[\xF1]/g,ch:"n"}];return a.each(c,function(){b=b?b.replace(this.re,this.ch):""}),b}function c(b){var c=arguments,d=b;[].shift.apply(c);var e,f=this.each(function(){var b=a(this);if(b.is("select")){var f=b.data("selectpicker"),g="object"==typeof d&&d;if(f){if(g)for(var h in g)g.hasOwnProperty(h)&&(f.options[h]=g[h])}else{var i=a.extend({},k.DEFAULTS,a.fn.selectpicker.defaults||{},b.data(),g);i.template=a.extend({},k.DEFAULTS.template,a.fn.selectpicker.defaults?a.fn.selectpicker.defaults.template:{},b.data().template,g.template),b.data("selectpicker",f=new k(this,i))}"string"==typeof d&&(e=f[d]instanceof Function?f[d].apply(f,c):f.options[d])}});return"undefined"!=typeof e?e:f}String.prototype.includes||!function(){var a={}.toString,b=function(){try{var a={},b=Object.defineProperty,c=b(a,a,a)&&b}catch(a){}return c}(),c="".indexOf,d=function(b){if(null==this)throw new TypeError;var d=String(this);if(b&&"[object RegExp]"==a.call(b))throw new TypeError;var e=d.length,f=String(b),g=f.length,h=arguments.length>1?arguments[1]:void 0,i=h?Number(h):0;i!=i&&(i=0);var j=Math.min(Math.max(i,0),e);return!(g+j>e)&&c.call(d,f,i)!=-1};b?b(String.prototype,"includes",{value:d,configurable:!0,writable:!0}):String.prototype.includes=d}(),String.prototype.startsWith||!function(){var a=function(){try{var a={},b=Object.defineProperty,c=b(a,a,a)&&b}catch(a){}return c}(),b={}.toString,c=function(a){if(null==this)throw new TypeError;var c=String(this);if(a&&"[object RegExp]"==b.call(a))throw new TypeError;var d=c.length,e=String(a),f=e.length,g=arguments.length>1?arguments[1]:void 0,h=g?Number(g):0;h!=h&&(h=0);var i=Math.min(Math.max(h,0),d);if(f+i>d)return!1;for(var j=-1;++j<f;)if(c.charCodeAt(i+j)!=e.charCodeAt(j))return!1;return!0};a?a(String.prototype,"startsWith",{value:c,configurable:!0,writable:!0}):String.prototype.startsWith=c}(),Object.keys||(Object.keys=function(a,b,c){c=[];for(b in a)c.hasOwnProperty.call(a,b)&&c.push(b);return c});var d={useDefault:!1,_set:a.valHooks.select.set};a.valHooks.select.set=function(b,c){return c&&!d.useDefault&&a(b).data("selected",!0),d._set.apply(this,arguments)};var e=null;a.fn.triggerNative=function(a){var b,c=this[0];c.dispatchEvent?("function"==typeof Event?b=new Event(a,{bubbles:!0}):(b=document.createEvent("Event"),b.initEvent(a,!0,!1)),c.dispatchEvent(b)):c.fireEvent?(b=document.createEventObject(),b.eventType=a,c.fireEvent("on"+a,b)):this.trigger(a)},a.expr.pseudos.icontains=function(b,c,d){var e=a(b),f=(e.data("tokens")||e.text()).toString().toUpperCase();return f.includes(d[3].toUpperCase())},a.expr.pseudos.ibegins=function(b,c,d){var e=a(b),f=(e.data("tokens")||e.text()).toString().toUpperCase();return f.startsWith(d[3].toUpperCase())},a.expr.pseudos.aicontains=function(b,c,d){var e=a(b),f=(e.data("tokens")||e.data("normalizedText")||e.text()).toString().toUpperCase();return f.includes(d[3].toUpperCase())},a.expr.pseudos.aibegins=function(b,c,d){var e=a(b),f=(e.data("tokens")||e.data("normalizedText")||e.text()).toString().toUpperCase();return f.startsWith(d[3].toUpperCase())};var f={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#x27;","`":"&#x60;"},g={"&amp;":"&","&lt;":"<","&gt;":">","&quot;":'"',"&#x27;":"'","&#x60;":"`"},h=function(a){var b=function(b){return a[b]},c="(?:"+Object.keys(a).join("|")+")",d=RegExp(c),e=RegExp(c,"g");return function(a){return a=null==a?"":""+a,d.test(a)?a.replace(e,b):a}},i=h(f),j=h(g),k=function(b,c){d.useDefault||(a.valHooks.select.set=d._set,d.useDefault=!0),this.$element=a(b),this.$newElement=null,this.$button=null,this.$menu=null,this.$lis=null,this.options=c,null===this.options.title&&(this.options.title=this.$element.attr("title"));var e=this.options.windowPadding;"number"==typeof e&&(this.options.windowPadding=[e,e,e,e]),this.val=k.prototype.val,this.render=k.prototype.render,this.refresh=k.prototype.refresh,this.setStyle=k.prototype.setStyle,this.selectAll=k.prototype.selectAll,this.deselectAll=k.prototype.deselectAll,this.destroy=k.prototype.destroy,this.remove=k.prototype.remove,this.show=k.prototype.show,this.hide=k.prototype.hide,this.init()};k.VERSION="1.12.2",k.DEFAULTS={noneSelectedText:"Nothing selected",noneResultsText:"No results matched {0}",countSelectedText:function(a,b){return 1==a?"{0} item selected":"{0} items selected"},maxOptionsText:function(a,b){return[1==a?"Limit reached ({n} item max)":"Limit reached ({n} items max)",1==b?"Group limit reached ({n} item max)":"Group limit reached ({n} items max)"]},selectAllText:"Select All",deselectAllText:"Deselect All",doneButton:!1,doneButtonText:"Close",multipleSeparator:", ",styleBase:"btn",style:"btn-default",size:"auto",title:null,selectedTextFormat:"values",width:!1,container:!1,hideDisabled:!1,showSubtext:!1,showIcon:!0,showContent:!0,dropupAuto:!0,header:!1,liveSearch:!1,liveSearchPlaceholder:null,liveSearchNormalize:!1,liveSearchStyle:"contains",actionsBox:!1,iconBase:"glyphicon",tickIcon:"glyphicon-ok",showTick:!1,template:{caret:'<span class="caret"></span>'},maxOptions:!1,mobile:!1,selectOnTab:!1,dropdownAlignRight:!1,windowPadding:0},k.prototype={constructor:k,init:function(){var b=this,c=this.$element.attr("id");this.$element.addClass("bs-select-hidden"),this.liObj={},this.multiple=this.$element.prop("multiple"),this.autofocus=this.$element.prop("autofocus"),this.$newElement=this.createView(),this.$element.after(this.$newElement).appendTo(this.$newElement),this.$button=this.$newElement.children("button"),this.$menu=this.$newElement.children(".dropdown-menu"),this.$menuInner=this.$menu.children(".inner"),this.$searchbox=this.$menu.find("input"),this.$element.removeClass("bs-select-hidden"),this.options.dropdownAlignRight===!0&&this.$menu.addClass("dropdown-menu-right"),"undefined"!=typeof c&&(this.$button.attr("data-id",c),a('label[for="'+c+'"]').click(function(a){a.preventDefault(),b.$button.focus()})),this.checkDisabled(),this.clickListener(),this.options.liveSearch&&this.liveSearchListener(),this.render(),this.setStyle(),this.setWidth(),this.options.container&&this.selectPosition(),this.$menu.data("this",this),this.$newElement.data("this",this),this.options.mobile&&this.mobile(),this.$newElement.on({"hide.bs.dropdown":function(a){b.$menuInner.attr("aria-expanded",!1),b.$element.trigger("hide.bs.select",a)},"hidden.bs.dropdown":function(a){b.$element.trigger("hidden.bs.select",a)},"show.bs.dropdown":function(a){b.$menuInner.attr("aria-expanded",!0),b.$element.trigger("show.bs.select",a)},"shown.bs.dropdown":function(a){b.$element.trigger("shown.bs.select",a)}}),b.$element[0].hasAttribute("required")&&this.$element.on("invalid",function(){b.$button.addClass("bs-invalid").focus(),b.$element.on({"focus.bs.select":function(){b.$button.focus(),b.$element.off("focus.bs.select")},"shown.bs.select":function(){b.$element.val(b.$element.val()).off("shown.bs.select")},"rendered.bs.select":function(){this.validity.valid&&b.$button.removeClass("bs-invalid"),b.$element.off("rendered.bs.select")}})}),setTimeout(function(){b.$element.trigger("loaded.bs.select")})},createDropdown:function(){var b=this.multiple||this.options.showTick?" show-tick":"",c=this.$element.parent().hasClass("input-group")?" input-group-btn":"",d=this.autofocus?" autofocus":"",e=this.options.header?'<div class="popover-title"><button type="button" class="close" aria-hidden="true">&times;</button>'+this.options.header+"</div>":"",f=this.options.liveSearch?'<div class="bs-searchbox"><input type="text" class="form-control" autocomplete="off"'+(null===this.options.liveSearchPlaceholder?"":' placeholder="'+i(this.options.liveSearchPlaceholder)+'"')+' role="textbox" aria-label="Search"></div>':"",g=this.multiple&&this.options.actionsBox?'<div class="bs-actionsbox"><div class="btn-group btn-group-sm btn-block"><button type="button" class="actions-btn bs-select-all btn btn-default">'+this.options.selectAllText+'</button><button type="button" class="actions-btn bs-deselect-all btn btn-default">'+this.options.deselectAllText+"</button></div></div>":"",h=this.multiple&&this.options.doneButton?'<div class="bs-donebutton"><div class="btn-group btn-block"><button type="button" class="btn btn-sm btn-default">'+this.options.doneButtonText+"</button></div></div>":"",j='<div class="btn-group bootstrap-select'+b+c+'"><button type="button" class="'+this.options.styleBase+' dropdown-toggle" data-toggle="dropdown"'+d+' role="button"><span class="filter-option pull-left"></span>&nbsp;<span class="bs-caret">'+this.options.template.caret+'</span></button><div class="dropdown-menu open" role="combobox">'+e+f+g+'<ul class="dropdown-menu inner" role="listbox" aria-expanded="false"></ul>'+h+"</div></div>";return a(j)},createView:function(){var a=this.createDropdown(),b=this.createLi();return a.find("ul")[0].innerHTML=b,a},reloadLi:function(){var a=this.createLi();this.$menuInner[0].innerHTML=a},createLi:function(){var c=this,d=[],e=0,f=document.createElement("option"),g=-1,h=function(a,b,c,d){return"<li"+("undefined"!=typeof c&""!==c?' class="'+c+'"':"")+("undefined"!=typeof b&null!==b?' data-original-index="'+b+'"':"")+("undefined"!=typeof d&null!==d?'data-optgroup="'+d+'"':"")+">"+a+"</li>"},j=function(d,e,f,g){return'<a tabindex="0"'+("undefined"!=typeof e?' class="'+e+'"':"")+(f?' style="'+f+'"':"")+(c.options.liveSearchNormalize?' data-normalized-text="'+b(i(a(d).html()))+'"':"")+("undefined"!=typeof g||null!==g?' data-tokens="'+g+'"':"")+' role="option">'+d+'<span class="'+c.options.iconBase+" "+c.options.tickIcon+' check-mark"></span></a>'};if(this.options.title&&!this.multiple&&(g--,!this.$element.find(".bs-title-option").length)){var k=this.$element[0];f.className="bs-title-option",f.innerHTML=this.options.title,f.value="",k.insertBefore(f,k.firstChild);var l=a(k.options[k.selectedIndex]);void 0===l.attr("selected")&&void 0===this.$element.data("selected")&&(f.selected=!0)}return this.$element.find("option").each(function(b){var f=a(this);if(g++,!f.hasClass("bs-title-option")){var k=this.className||"",l=this.style.cssText,m=f.data("content")?f.data("content"):f.html(),n=f.data("tokens")?f.data("tokens"):null,o="undefined"!=typeof f.data("subtext")?'<small class="text-muted">'+f.data("subtext")+"</small>":"",p="undefined"!=typeof f.data("icon")?'<span class="'+c.options.iconBase+" "+f.data("icon")+'"></span> ':"",q=f.parent(),r="OPTGROUP"===q[0].tagName,s=r&&q[0].disabled,t=this.disabled||s;if(""!==p&&t&&(p="<span>"+p+"</span>"),c.options.hideDisabled&&(t&&!r||s))return void g--;if(f.data("content")||(m=p+'<span class="text">'+m+o+"</span>"),r&&f.data("divider")!==!0){if(c.options.hideDisabled&&t){if(void 0===q.data("allOptionsDisabled")){var u=q.children();q.data("allOptionsDisabled",u.filter(":disabled").length===u.length)}if(q.data("allOptionsDisabled"))return void g--}var v=" "+q[0].className||"";if(0===f.index()){e+=1;var w=q[0].label,x="undefined"!=typeof q.data("subtext")?'<small class="text-muted">'+q.data("subtext")+"</small>":"",y=q.data("icon")?'<span class="'+c.options.iconBase+" "+q.data("icon")+'"></span> ':"";w=y+'<span class="text">'+i(w)+x+"</span>",0!==b&&d.length>0&&(g++,d.push(h("",null,"divider",e+"div"))),g++,d.push(h(w,null,"dropdown-header"+v,e))}if(c.options.hideDisabled&&t)return void g--;d.push(h(j(m,"opt "+k+v,l,n),b,"",e))}else if(f.data("divider")===!0)d.push(h("",b,"divider"));else if(f.data("hidden")===!0)d.push(h(j(m,k,l,n),b,"hidden is-hidden"));else{var z=this.previousElementSibling&&"OPTGROUP"===this.previousElementSibling.tagName;if(!z&&c.options.hideDisabled)for(var A=a(this).prevAll(),B=0;B<A.length;B++)if("OPTGROUP"===A[B].tagName){for(var C=0,D=0;D<B;D++){var E=A[D];(E.disabled||a(E).data("hidden")===!0)&&C++}C===B&&(z=!0);break}z&&(g++,d.push(h("",null,"divider",e+"div"))),d.push(h(j(m,k,l,n),b))}c.liObj[b]=g}}),this.multiple||0!==this.$element.find("option:selected").length||this.options.title||this.$element.find("option").eq(0).prop("selected",!0).attr("selected","selected"),d.join("")},findLis:function(){return null==this.$lis&&(this.$lis=this.$menu.find("li")),this.$lis},render:function(b){var c,d=this;b!==!1&&this.$element.find("option").each(function(a){var b=d.findLis().eq(d.liObj[a]);d.setDisabled(a,this.disabled||"OPTGROUP"===this.parentNode.tagName&&this.parentNode.disabled,b),d.setSelected(a,this.selected,b)}),this.togglePlaceholder(),this.tabIndex();var e=this.$element.find("option").map(function(){if(this.selected){if(d.options.hideDisabled&&(this.disabled||"OPTGROUP"===this.parentNode.tagName&&this.parentNode.disabled))return;var b,c=a(this),e=c.data("icon")&&d.options.showIcon?'<i class="'+d.options.iconBase+" "+c.data("icon")+'"></i> ':"";return b=d.options.showSubtext&&c.data("subtext")&&!d.multiple?' <small class="text-muted">'+c.data("subtext")+"</small>":"","undefined"!=typeof c.attr("title")?c.attr("title"):c.data("content")&&d.options.showContent?c.data("content").toString():e+c.html()+b}}).toArray(),f=this.multiple?e.join(this.options.multipleSeparator):e[0];if(this.multiple&&this.options.selectedTextFormat.indexOf("count")>-1){var g=this.options.selectedTextFormat.split(">");if(g.length>1&&e.length>g[1]||1==g.length&&e.length>=2){c=this.options.hideDisabled?", [disabled]":"";var h=this.$element.find("option").not('[data-divider="true"], [data-hidden="true"]'+c).length,i="function"==typeof this.options.countSelectedText?this.options.countSelectedText(e.length,h):this.options.countSelectedText;f=i.replace("{0}",e.length.toString()).replace("{1}",h.toString())}}void 0==this.options.title&&(this.options.title=this.$element.attr("title")),"static"==this.options.selectedTextFormat&&(f=this.options.title),f||(f="undefined"!=typeof this.options.title?this.options.title:this.options.noneSelectedText),this.$button.attr("title",j(a.trim(f.replace(/<[^>]*>?/g,"")))),this.$button.children(".filter-option").html(f),this.$element.trigger("rendered.bs.select")},setStyle:function(a,b){this.$element.attr("class")&&this.$newElement.addClass(this.$element.attr("class").replace(/selectpicker|mobile-device|bs-select-hidden|validate\[.*\]/gi,""));var c=a?a:this.options.style;"add"==b?this.$button.addClass(c):"remove"==b?this.$button.removeClass(c):(this.$button.removeClass(this.options.style),this.$button.addClass(c))},liHeight:function(b){if(b||this.options.size!==!1&&!this.sizeInfo){var c=document.createElement("div"),d=document.createElement("div"),e=document.createElement("ul"),f=document.createElement("li"),g=document.createElement("li"),h=document.createElement("a"),i=document.createElement("span"),j=this.options.header&&this.$menu.find(".popover-title").length>0?this.$menu.find(".popover-title")[0].cloneNode(!0):null,k=this.options.liveSearch?document.createElement("div"):null,l=this.options.actionsBox&&this.multiple&&this.$menu.find(".bs-actionsbox").length>0?this.$menu.find(".bs-actionsbox")[0].cloneNode(!0):null,m=this.options.doneButton&&this.multiple&&this.$menu.find(".bs-donebutton").length>0?this.$menu.find(".bs-donebutton")[0].cloneNode(!0):null;if(i.className="text",c.className=this.$menu[0].parentNode.className+" open",d.className="dropdown-menu open",e.className="dropdown-menu inner",f.className="divider",i.appendChild(document.createTextNode("Inner text")),h.appendChild(i),g.appendChild(h),e.appendChild(g),e.appendChild(f),j&&d.appendChild(j),k){var n=document.createElement("input");k.className="bs-searchbox",n.className="form-control",k.appendChild(n),d.appendChild(k)}l&&d.appendChild(l),d.appendChild(e),m&&d.appendChild(m),c.appendChild(d),document.body.appendChild(c);var o=h.offsetHeight,p=j?j.offsetHeight:0,q=k?k.offsetHeight:0,r=l?l.offsetHeight:0,s=m?m.offsetHeight:0,t=a(f).outerHeight(!0),u="function"==typeof getComputedStyle&&getComputedStyle(d),v=u?null:a(d),w={vert:parseInt(u?u.paddingTop:v.css("paddingTop"))+parseInt(u?u.paddingBottom:v.css("paddingBottom"))+parseInt(u?u.borderTopWidth:v.css("borderTopWidth"))+parseInt(u?u.borderBottomWidth:v.css("borderBottomWidth")),horiz:parseInt(u?u.paddingLeft:v.css("paddingLeft"))+parseInt(u?u.paddingRight:v.css("paddingRight"))+parseInt(u?u.borderLeftWidth:v.css("borderLeftWidth"))+parseInt(u?u.borderRightWidth:v.css("borderRightWidth"))},x={vert:w.vert+parseInt(u?u.marginTop:v.css("marginTop"))+parseInt(u?u.marginBottom:v.css("marginBottom"))+2,horiz:w.horiz+parseInt(u?u.marginLeft:v.css("marginLeft"))+parseInt(u?u.marginRight:v.css("marginRight"))+2};document.body.removeChild(c),this.sizeInfo={liHeight:o,headerHeight:p,searchHeight:q,actionsHeight:r,doneButtonHeight:s,dividerHeight:t,menuPadding:w,menuExtras:x}}},setSize:function(){if(this.findLis(),this.liHeight(),this.options.header&&this.$menu.css("padding-top",0),this.options.size!==!1){var b,c,d,e,f,g,h,i,j=this,k=this.$menu,l=this.$menuInner,m=a(window),n=this.$newElement[0].offsetHeight,o=this.$newElement[0].offsetWidth,p=this.sizeInfo.liHeight,q=this.sizeInfo.headerHeight,r=this.sizeInfo.searchHeight,s=this.sizeInfo.actionsHeight,t=this.sizeInfo.doneButtonHeight,u=this.sizeInfo.dividerHeight,v=this.sizeInfo.menuPadding,w=this.sizeInfo.menuExtras,x=this.options.hideDisabled?".disabled":"",y=function(){var b,c=j.$newElement.offset(),d=a(j.options.container);j.options.container&&!d.is("body")?(b=d.offset(),b.top+=parseInt(d.css("borderTopWidth")),b.left+=parseInt(d.css("borderLeftWidth"))):b={top:0,left:0};var e=j.options.windowPadding;f=c.top-b.top-m.scrollTop(),g=m.height()-f-n-b.top-e[2],h=c.left-b.left-m.scrollLeft(),i=m.width()-h-o-b.left-e[1],f-=e[0],h-=e[3]};if(y(),"auto"===this.options.size){var z=function(){var m,n=function(b,c){return function(d){return c?d.classList?d.classList.contains(b):a(d).hasClass(b):!(d.classList?d.classList.contains(b):a(d).hasClass(b))}},u=j.$menuInner[0].getElementsByTagName("li"),x=Array.prototype.filter?Array.prototype.filter.call(u,n("hidden",!1)):j.$lis.not(".hidden"),z=Array.prototype.filter?Array.prototype.filter.call(x,n("dropdown-header",!0)):x.filter(".dropdown-header");y(),b=g-w.vert,c=i-w.horiz,j.options.container?(k.data("height")||k.data("height",k.height()),d=k.data("height"),k.data("width")||k.data("width",k.width()),e=k.data("width")):(d=k.height(),e=k.width()),j.options.dropupAuto&&j.$newElement.toggleClass("dropup",f>g&&b-w.vert<d),j.$newElement.hasClass("dropup")&&(b=f-w.vert),"auto"===j.options.dropdownAlignRight&&k.toggleClass("dropdown-menu-right",h>i&&c-w.horiz<e-o),m=x.length+z.length>3?3*p+w.vert-2:0,k.css({"max-height":b+"px",overflow:"hidden","min-height":m+q+r+s+t+"px"}),l.css({"max-height":b-q-r-s-t-v.vert+"px","overflow-y":"auto","min-height":Math.max(m-v.vert,0)+"px"})};z(),this.$searchbox.off("input.getSize propertychange.getSize").on("input.getSize propertychange.getSize",z),m.off("resize.getSize scroll.getSize").on("resize.getSize scroll.getSize",z)}else if(this.options.size&&"auto"!=this.options.size&&this.$lis.not(x).length>this.options.size){var A=this.$lis.not(".divider").not(x).children().slice(0,this.options.size).last().parent().index(),B=this.$lis.slice(0,A+1).filter(".divider").length;b=p*this.options.size+B*u+v.vert,j.options.container?(k.data("height")||k.data("height",k.height()),d=k.data("height")):d=k.height(),j.options.dropupAuto&&this.$newElement.toggleClass("dropup",f>g&&b-w.vert<d),k.css({"max-height":b+q+r+s+t+"px",overflow:"hidden","min-height":""}),l.css({"max-height":b-v.vert+"px","overflow-y":"auto","min-height":""})}}},setWidth:function(){if("auto"===this.options.width){this.$menu.css("min-width","0");var a=this.$menu.parent().clone().appendTo("body"),b=this.options.container?this.$newElement.clone().appendTo("body"):a,c=a.children(".dropdown-menu").outerWidth(),d=b.css("width","auto").children("button").outerWidth();a.remove(),b.remove(),this.$newElement.css("width",Math.max(c,d)+"px")}else"fit"===this.options.width?(this.$menu.css("min-width",""),this.$newElement.css("width","").addClass("fit-width")):this.options.width?(this.$menu.css("min-width",""),this.$newElement.css("width",this.options.width)):(this.$menu.css("min-width",""),this.$newElement.css("width",""));this.$newElement.hasClass("fit-width")&&"fit"!==this.options.width&&this.$newElement.removeClass("fit-width")},selectPosition:function(){this.$bsContainer=a('<div class="bs-container" />');var b,c,d,e=this,f=a(this.options.container),g=function(a){e.$bsContainer.addClass(a.attr("class").replace(/form-control|fit-width/gi,"")).toggleClass("dropup",a.hasClass("dropup")),b=a.offset(),f.is("body")?c={top:0,left:0}:(c=f.offset(),c.top+=parseInt(f.css("borderTopWidth"))-f.scrollTop(),c.left+=parseInt(f.css("borderLeftWidth"))-f.scrollLeft()),d=a.hasClass("dropup")?0:a[0].offsetHeight,e.$bsContainer.css({top:b.top-c.top+d,left:b.left-c.left,width:a[0].offsetWidth})};this.$button.on("click",function(){var b=a(this);e.isDisabled()||(g(e.$newElement),e.$bsContainer.appendTo(e.options.container).toggleClass("open",!b.hasClass("open")).append(e.$menu))}),a(window).on("resize scroll",function(){g(e.$newElement)}),this.$element.on("hide.bs.select",function(){e.$menu.data("height",e.$menu.height()),e.$bsContainer.detach()})},setSelected:function(a,b,c){c||(this.togglePlaceholder(),c=this.findLis().eq(this.liObj[a])),c.toggleClass("selected",b).find("a").attr("aria-selected",b)},setDisabled:function(a,b,c){c||(c=this.findLis().eq(this.liObj[a])),b?c.addClass("disabled").children("a").attr("href","#").attr("tabindex",-1).attr("aria-disabled",!0):c.removeClass("disabled").children("a").removeAttr("href").attr("tabindex",0).attr("aria-disabled",!1)},isDisabled:function(){return this.$element[0].disabled},checkDisabled:function(){var a=this;this.isDisabled()?(this.$newElement.addClass("disabled"),this.$button.addClass("disabled").attr("tabindex",-1).attr("aria-disabled",!0)):(this.$button.hasClass("disabled")&&(this.$newElement.removeClass("disabled"),this.$button.removeClass("disabled").attr("aria-disabled",!1)),this.$button.attr("tabindex")!=-1||this.$element.data("tabindex")||this.$button.removeAttr("tabindex")),this.$button.click(function(){return!a.isDisabled()})},togglePlaceholder:function(){var a=this.$element.val();this.$button.toggleClass("bs-placeholder",null===a||""===a||a.constructor===Array&&0===a.length)},tabIndex:function(){this.$element.data("tabindex")!==this.$element.attr("tabindex")&&this.$element.attr("tabindex")!==-98&&"-98"!==this.$element.attr("tabindex")&&(this.$element.data("tabindex",this.$element.attr("tabindex")),this.$button.attr("tabindex",this.$element.data("tabindex"))),this.$element.attr("tabindex",-98)},clickListener:function(){var b=this,c=a(document);c.data("spaceSelect",!1),this.$button.on("keyup",function(a){/(32)/.test(a.keyCode.toString(10))&&c.data("spaceSelect")&&(a.preventDefault(),c.data("spaceSelect",!1))}),this.$button.on("click",function(){b.setSize()}),this.$element.on("shown.bs.select",function(){if(b.options.liveSearch||b.multiple){if(!b.multiple){var a=b.liObj[b.$element[0].selectedIndex];if("number"!=typeof a||b.options.size===!1)return;var c=b.$lis.eq(a)[0].offsetTop-b.$menuInner[0].offsetTop;c=c-b.$menuInner[0].offsetHeight/2+b.sizeInfo.liHeight/2,b.$menuInner[0].scrollTop=c}}else b.$menuInner.find(".selected a").focus()}),this.$menuInner.on("click","li a",function(c){var d=a(this),f=d.parent().data("originalIndex"),g=b.$element.val(),h=b.$element.prop("selectedIndex"),i=!0;if(b.multiple&&1!==b.options.maxOptions&&c.stopPropagation(),c.preventDefault(),!b.isDisabled()&&!d.parent().hasClass("disabled")){var j=b.$element.find("option"),k=j.eq(f),l=k.prop("selected"),m=k.parent("optgroup"),n=b.options.maxOptions,o=m.data("maxOptions")||!1;if(b.multiple){if(k.prop("selected",!l),b.setSelected(f,!l),d.blur(),n!==!1||o!==!1){var p=n<j.filter(":selected").length,q=o<m.find("option:selected").length;if(n&&p||o&&q)if(n&&1==n)j.prop("selected",!1),k.prop("selected",!0),b.$menuInner.find(".selected").removeClass("selected"),b.setSelected(f,!0);else if(o&&1==o){m.find("option:selected").prop("selected",!1),k.prop("selected",!0);var r=d.parent().data("optgroup");b.$menuInner.find('[data-optgroup="'+r+'"]').removeClass("selected"),b.setSelected(f,!0)}else{var s="string"==typeof b.options.maxOptionsText?[b.options.maxOptionsText,b.options.maxOptionsText]:b.options.maxOptionsText,t="function"==typeof s?s(n,o):s,u=t[0].replace("{n}",n),v=t[1].replace("{n}",o),w=a('<div class="notify"></div>');t[2]&&(u=u.replace("{var}",t[2][n>1?0:1]),v=v.replace("{var}",t[2][o>1?0:1])),k.prop("selected",!1),b.$menu.append(w),n&&p&&(w.append(a("<div>"+u+"</div>")),i=!1,b.$element.trigger("maxReached.bs.select")),o&&q&&(w.append(a("<div>"+v+"</div>")),i=!1,b.$element.trigger("maxReachedGrp.bs.select")),setTimeout(function(){b.setSelected(f,!1)},10),w.delay(750).fadeOut(300,function(){a(this).remove()})}}}else j.prop("selected",!1),k.prop("selected",!0),b.$menuInner.find(".selected").removeClass("selected").find("a").attr("aria-selected",!1),b.setSelected(f,!0);!b.multiple||b.multiple&&1===b.options.maxOptions?b.$button.focus():b.options.liveSearch&&b.$searchbox.focus(),i&&(g!=b.$element.val()&&b.multiple||h!=b.$element.prop("selectedIndex")&&!b.multiple)&&(e=[f,k.prop("selected"),l],b.$element.triggerNative("change"))}}),this.$menu.on("click","li.disabled a, .popover-title, .popover-title :not(.close)",function(c){c.currentTarget==this&&(c.preventDefault(),c.stopPropagation(),b.options.liveSearch&&!a(c.target).hasClass("close")?b.$searchbox.focus():b.$button.focus())}),this.$menuInner.on("click",".divider, .dropdown-header",function(a){a.preventDefault(),a.stopPropagation(),b.options.liveSearch?b.$searchbox.focus():b.$button.focus()}),this.$menu.on("click",".popover-title .close",function(){b.$button.click()}),this.$searchbox.on("click",function(a){a.stopPropagation()}),this.$menu.on("click",".actions-btn",function(c){b.options.liveSearch?b.$searchbox.focus():b.$button.focus(),c.preventDefault(),c.stopPropagation(),a(this).hasClass("bs-select-all")?b.selectAll():b.deselectAll()}),this.$element.change(function(){b.render(!1),b.$element.trigger("changed.bs.select",e),e=null})},liveSearchListener:function(){var c=this,d=a('<li class="no-results"></li>');this.$button.on("click.dropdown.data-api",function(){c.$menuInner.find(".active").removeClass("active"),c.$searchbox.val()&&(c.$searchbox.val(""),c.$lis.not(".is-hidden").removeClass("hidden"),d.parent().length&&d.remove()),c.multiple||c.$menuInner.find(".selected").addClass("active"),setTimeout(function(){c.$searchbox.focus()},10)}),this.$searchbox.on("click.dropdown.data-api focus.dropdown.data-api touchend.dropdown.data-api",function(a){a.stopPropagation()}),this.$searchbox.on("input propertychange",function(){if(c.$lis.not(".is-hidden").removeClass("hidden"),c.$lis.filter(".active").removeClass("active"),d.remove(),c.$searchbox.val()){var e,f=c.$lis.not(".is-hidden, .divider, .dropdown-header");if(e=c.options.liveSearchNormalize?f.find("a").not(":a"+c._searchStyle()+'("'+b(c.$searchbox.val())+'")'):f.find("a").not(":"+c._searchStyle()+'("'+c.$searchbox.val()+'")'),e.length===f.length)d.html(c.options.noneResultsText.replace("{0}",'"'+i(c.$searchbox.val())+'"')),c.$menuInner.append(d),c.$lis.addClass("hidden");else{e.parent().addClass("hidden");var g,h=c.$lis.not(".hidden");h.each(function(b){var c=a(this);c.hasClass("divider")?void 0===g?c.addClass("hidden"):(g&&g.addClass("hidden"),g=c):c.hasClass("dropdown-header")&&h.eq(b+1).data("optgroup")!==c.data("optgroup")?c.addClass("hidden"):g=null}),g&&g.addClass("hidden"),f.not(".hidden").first().addClass("active")}}})},_searchStyle:function(){var a={begins:"ibegins",startsWith:"ibegins"};return a[this.options.liveSearchStyle]||"icontains"},val:function(a){return"undefined"!=typeof a?(this.$element.val(a),this.render(),this.$element):this.$element.val()},changeAll:function(b){if(this.multiple){"undefined"==typeof b&&(b=!0),this.findLis();var c=this.$element.find("option"),d=this.$lis.not(".divider, .dropdown-header, .disabled, .hidden"),e=d.length,f=[];if(b){if(d.filter(".selected").length===d.length)return}else if(0===d.filter(".selected").length)return;d.toggleClass("selected",b);for(var g=0;g<e;g++){var h=d[g].getAttribute("data-original-index");f[f.length]=c.eq(h)[0]}a(f).prop("selected",b),this.render(!1),this.togglePlaceholder(),this.$element.triggerNative("change")}},selectAll:function(){return this.changeAll(!0)},deselectAll:function(){return this.changeAll(!1)},toggle:function(a){a=a||window.event,a&&a.stopPropagation(),this.$button.trigger("click")},keydown:function(c){var d,e,f,g,h,i,j,k,l,m=a(this),n=m.is("input")?m.parent().parent():m.parent(),o=n.data("this"),p=":not(.disabled, .hidden, .dropdown-header, .divider)",q={32:" ",48:"0",49:"1",50:"2",51:"3",52:"4",53:"5",54:"6",55:"7",56:"8",57:"9",59:";",65:"a",66:"b",67:"c",68:"d",69:"e",70:"f",71:"g",72:"h",73:"i",74:"j",75:"k",76:"l",77:"m",78:"n",79:"o",80:"p",81:"q",82:"r",83:"s",84:"t",85:"u",86:"v",87:"w",88:"x",89:"y",90:"z",96:"0",97:"1",98:"2",99:"3",100:"4",101:"5",102:"6",103:"7",104:"8",105:"9"};if(o.options.liveSearch&&(n=m.parent().parent()),o.options.container&&(n=o.$menu),d=a('[role="listbox"] li',n),l=o.$newElement.hasClass("open"),!l&&(c.keyCode>=48&&c.keyCode<=57||c.keyCode>=96&&c.keyCode<=105||c.keyCode>=65&&c.keyCode<=90))return o.options.container?o.$button.trigger("click"):(o.setSize(),o.$menu.parent().addClass("open"),l=!0),void o.$searchbox.focus();if(o.options.liveSearch&&(/(^9$|27)/.test(c.keyCode.toString(10))&&l&&(c.preventDefault(),c.stopPropagation(),o.$menuInner.click(),o.$button.focus()),d=a('[role="listbox"] li'+p,n),m.val()||/(38|40)/.test(c.keyCode.toString(10))||0===d.filter(".active").length&&(d=o.$menuInner.find("li"),d=o.options.liveSearchNormalize?d.filter(":a"+o._searchStyle()+"("+b(q[c.keyCode])+")"):d.filter(":"+o._searchStyle()+"("+q[c.keyCode]+")"))),d.length){if(/(38|40)/.test(c.keyCode.toString(10)))e=d.index(d.find("a").filter(":focus").parent()),g=d.filter(p).first().index(),h=d.filter(p).last().index(),f=d.eq(e).nextAll(p).eq(0).index(),i=d.eq(e).prevAll(p).eq(0).index(),j=d.eq(f).prevAll(p).eq(0).index(),o.options.liveSearch&&(d.each(function(b){a(this).hasClass("disabled")||a(this).data("index",b)}),e=d.index(d.filter(".active")),g=d.first().data("index"),h=d.last().data("index"),f=d.eq(e).nextAll().eq(0).data("index"),i=d.eq(e).prevAll().eq(0).data("index"),j=d.eq(f).prevAll().eq(0).data("index")),k=m.data("prevIndex"),38==c.keyCode?(o.options.liveSearch&&e--,e!=j&&e>i&&(e=i),e<g&&(e=g),e==k&&(e=h)):40==c.keyCode&&(o.options.liveSearch&&e++,e==-1&&(e=0),e!=j&&e<f&&(e=f),e>h&&(e=h),e==k&&(e=g)),m.data("prevIndex",e),o.options.liveSearch?(c.preventDefault(),m.hasClass("dropdown-toggle")||(d.removeClass("active").eq(e).addClass("active").children("a").focus(),m.focus())):d.eq(e).children("a").focus();else if(!m.is("input")){var r,s,t=[];d.each(function(){a(this).hasClass("disabled")||a.trim(a(this).children("a").text().toLowerCase()).substring(0,1)==q[c.keyCode]&&t.push(a(this).index())}),r=a(document).data("keycount"),r++,a(document).data("keycount",r),s=a.trim(a(":focus").text().toLowerCase()).substring(0,1),s!=q[c.keyCode]?(r=1,a(document).data("keycount",r)):r>=t.length&&(a(document).data("keycount",0),r>t.length&&(r=1)),d.eq(t[r-1]).children("a").focus()}if((/(13|32)/.test(c.keyCode.toString(10))||/(^9$)/.test(c.keyCode.toString(10))&&o.options.selectOnTab)&&l){if(/(32)/.test(c.keyCode.toString(10))||c.preventDefault(),o.options.liveSearch)/(32)/.test(c.keyCode.toString(10))||(o.$menuInner.find(".active a").click(),
+m.focus());else{var u=a(":focus");u.click(),u.focus(),c.preventDefault(),a(document).data("spaceSelect",!0)}a(document).data("keycount",0)}(/(^9$|27)/.test(c.keyCode.toString(10))&&l&&(o.multiple||o.options.liveSearch)||/(27)/.test(c.keyCode.toString(10))&&!l)&&(o.$menu.parent().removeClass("open"),o.options.container&&o.$newElement.removeClass("open"),o.$button.focus())}},mobile:function(){this.$element.addClass("mobile-device")},refresh:function(){this.$lis=null,this.liObj={},this.reloadLi(),this.render(),this.checkDisabled(),this.liHeight(!0),this.setStyle(),this.setWidth(),this.$lis&&this.$searchbox.trigger("propertychange"),this.$element.trigger("refreshed.bs.select")},hide:function(){this.$newElement.hide()},show:function(){this.$newElement.show()},remove:function(){this.$newElement.remove(),this.$element.remove()},destroy:function(){this.$newElement.before(this.$element).remove(),this.$bsContainer?this.$bsContainer.remove():this.$menu.remove(),this.$element.off(".bs.select").removeData("selectpicker").removeClass("bs-select-hidden selectpicker")}};var l=a.fn.selectpicker;a.fn.selectpicker=c,a.fn.selectpicker.Constructor=k,a.fn.selectpicker.noConflict=function(){return a.fn.selectpicker=l,this},a(document).data("keycount",0).on("keydown.bs.select",'.bootstrap-select [data-toggle=dropdown], .bootstrap-select [role="listbox"], .bs-searchbox input',k.prototype.keydown).on("focusin.modal",'.bootstrap-select [data-toggle=dropdown], .bootstrap-select [role="listbox"], .bs-searchbox input',function(a){a.stopPropagation()}),a(window).on("load.bs.select.data-api",function(){a(".selectpicker").each(function(){var b=a(this);c.call(b,b.data())})})}(a)});
+//# sourceMappingURL=bootstrap-select.js.map
\ No newline at end of file
diff --git a/data/web/js/bootstrap-slider.min.js b/data/web/js/bootstrap-slider.min.js
new file mode 100644
index 00000000..72363a27
--- /dev/null
+++ b/data/web/js/bootstrap-slider.min.js
@@ -0,0 +1,5 @@
+/*! =======================================================
+                      VERSION  9.7.2              
+========================================================= */
+"use strict";var _typeof="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(a){return typeof a}:function(a){return a&&"function"==typeof Symbol&&a.constructor===Symbol&&a!==Symbol.prototype?"symbol":typeof a},windowIsDefined="object"===("undefined"==typeof window?"undefined":_typeof(window));!function(a){if("function"==typeof define&&define.amd)define(["jquery"],a);else if("object"===("undefined"==typeof module?"undefined":_typeof(module))&&module.exports){var b;try{b=require("jquery")}catch(c){b=null}module.exports=a(b)}else window&&(window.Slider=a(window.jQuery))}(function(a){var b="slider",c="bootstrapSlider";windowIsDefined&&!window.console&&(window.console={}),windowIsDefined&&!window.console.log&&(window.console.log=function(){}),windowIsDefined&&!window.console.warn&&(window.console.warn=function(){});var d;return function(a){function b(){}function c(a){function c(b){b.prototype.option||(b.prototype.option=function(b){a.isPlainObject(b)&&(this.options=a.extend(!0,this.options,b))})}function e(b,c){a.fn[b]=function(e){if("string"==typeof e){for(var g=d.call(arguments,1),h=0,i=this.length;i>h;h++){var j=this[h],k=a.data(j,b);if(k)if(a.isFunction(k[e])&&"_"!==e.charAt(0)){var l=k[e].apply(k,g);if(void 0!==l&&l!==k)return l}else f("no such method '"+e+"' for "+b+" instance");else f("cannot call methods on "+b+" prior to initialization; attempted to call '"+e+"'")}return this}var m=this.map(function(){var d=a.data(this,b);return d?(d.option(e),d._init()):(d=new c(this,e),a.data(this,b,d)),a(this)});return!m||m.length>1?m:m[0]}}if(a){var f="undefined"==typeof console?b:function(a){console.error(a)};return a.bridget=function(a,b){c(b),e(a,b)},a.bridget}}var d=Array.prototype.slice;c(a)}(a),function(a){function e(b,c){function d(a,b){var c="data-slider-"+b.replace(/_/g,"-"),d=a.getAttribute(c);try{return JSON.parse(d)}catch(e){return d}}this._state={value:null,enabled:null,offset:null,size:null,percentage:null,inDrag:!1,over:!1},this.ticksCallbackMap={},this.handleCallbackMap={},"string"==typeof b?this.element=document.querySelector(b):b instanceof HTMLElement&&(this.element=b),c=c?c:{};for(var e=Object.keys(this.defaultOptions),f=0;f<e.length;f++){var h=e[f],i=c[h];i="undefined"!=typeof i?i:d(this.element,h),i=null!==i?i:this.defaultOptions[h],this.options||(this.options={}),this.options[h]=i}"auto"===this.options.rtl&&(this.options.rtl="rtl"===window.getComputedStyle(this.element).direction),"vertical"!==this.options.orientation||"top"!==this.options.tooltip_position&&"bottom"!==this.options.tooltip_position?"horizontal"!==this.options.orientation||"left"!==this.options.tooltip_position&&"right"!==this.options.tooltip_position||(this.options.tooltip_position="top"):this.options.rtl?this.options.tooltip_position="left":this.options.tooltip_position="right";var j,k,l,m,n,o=this.element.style.width,p=!1,q=this.element.parentNode;if(this.sliderElem)p=!0;else{this.sliderElem=document.createElement("div"),this.sliderElem.className="slider";var r=document.createElement("div");if(r.className="slider-track",k=document.createElement("div"),k.className="slider-track-low",j=document.createElement("div"),j.className="slider-selection",l=document.createElement("div"),l.className="slider-track-high",m=document.createElement("div"),m.className="slider-handle min-slider-handle",m.setAttribute("role","slider"),m.setAttribute("aria-valuemin",this.options.min),m.setAttribute("aria-valuemax",this.options.max),n=document.createElement("div"),n.className="slider-handle max-slider-handle",n.setAttribute("role","slider"),n.setAttribute("aria-valuemin",this.options.min),n.setAttribute("aria-valuemax",this.options.max),r.appendChild(k),r.appendChild(j),r.appendChild(l),this.rangeHighlightElements=[],Array.isArray(this.options.rangeHighlights)&&this.options.rangeHighlights.length>0)for(var s=0;s<this.options.rangeHighlights.length;s++){var t=document.createElement("div");t.className="slider-rangeHighlight slider-selection",this.rangeHighlightElements.push(t),r.appendChild(t)}var u=Array.isArray(this.options.labelledby);if(u&&this.options.labelledby[0]&&m.setAttribute("aria-labelledby",this.options.labelledby[0]),u&&this.options.labelledby[1]&&n.setAttribute("aria-labelledby",this.options.labelledby[1]),!u&&this.options.labelledby&&(m.setAttribute("aria-labelledby",this.options.labelledby),n.setAttribute("aria-labelledby",this.options.labelledby)),this.ticks=[],Array.isArray(this.options.ticks)&&this.options.ticks.length>0){for(this.ticksContainer=document.createElement("div"),this.ticksContainer.className="slider-tick-container",f=0;f<this.options.ticks.length;f++){var v=document.createElement("div");if(v.className="slider-tick",this.options.ticks_tooltip){var w=this._addTickListener(),x=w.addMouseEnter(this,v,f),y=w.addMouseLeave(this,v);this.ticksCallbackMap[f]={mouseEnter:x,mouseLeave:y}}this.ticks.push(v),this.ticksContainer.appendChild(v)}j.className+=" tick-slider-selection"}if(this.tickLabels=[],Array.isArray(this.options.ticks_labels)&&this.options.ticks_labels.length>0)for(this.tickLabelContainer=document.createElement("div"),this.tickLabelContainer.className="slider-tick-label-container",f=0;f<this.options.ticks_labels.length;f++){var z=document.createElement("div"),A=0===this.options.ticks_positions.length,B=this.options.reversed&&A?this.options.ticks_labels.length-(f+1):f;z.className="slider-tick-label",z.innerHTML=this.options.ticks_labels[B],this.tickLabels.push(z),this.tickLabelContainer.appendChild(z)}var C=function(a){var b=document.createElement("div");b.className="tooltip-arrow";var c=document.createElement("div");c.className="tooltip-inner",a.appendChild(b),a.appendChild(c)},D=document.createElement("div");D.className="tooltip tooltip-main",D.setAttribute("role","presentation"),C(D);var E=document.createElement("div");E.className="tooltip tooltip-min",E.setAttribute("role","presentation"),C(E);var F=document.createElement("div");F.className="tooltip tooltip-max",F.setAttribute("role","presentation"),C(F),this.sliderElem.appendChild(r),this.sliderElem.appendChild(D),this.sliderElem.appendChild(E),this.sliderElem.appendChild(F),this.tickLabelContainer&&this.sliderElem.appendChild(this.tickLabelContainer),this.ticksContainer&&this.sliderElem.appendChild(this.ticksContainer),this.sliderElem.appendChild(m),this.sliderElem.appendChild(n),q.insertBefore(this.sliderElem,this.element),this.element.style.display="none"}if(a&&(this.$element=a(this.element),this.$sliderElem=a(this.sliderElem)),this.eventToCallbackMap={},this.sliderElem.id=this.options.id,this.touchCapable="ontouchstart"in window||window.DocumentTouch&&document instanceof window.DocumentTouch,this.touchX=0,this.touchY=0,this.tooltip=this.sliderElem.querySelector(".tooltip-main"),this.tooltipInner=this.tooltip.querySelector(".tooltip-inner"),this.tooltip_min=this.sliderElem.querySelector(".tooltip-min"),this.tooltipInner_min=this.tooltip_min.querySelector(".tooltip-inner"),this.tooltip_max=this.sliderElem.querySelector(".tooltip-max"),this.tooltipInner_max=this.tooltip_max.querySelector(".tooltip-inner"),g[this.options.scale]&&(this.options.scale=g[this.options.scale]),p===!0&&(this._removeClass(this.sliderElem,"slider-horizontal"),this._removeClass(this.sliderElem,"slider-vertical"),this._removeClass(this.sliderElem,"slider-rtl"),this._removeClass(this.tooltip,"hide"),this._removeClass(this.tooltip_min,"hide"),this._removeClass(this.tooltip_max,"hide"),["left","right","top","width","height"].forEach(function(a){this._removeProperty(this.trackLow,a),this._removeProperty(this.trackSelection,a),this._removeProperty(this.trackHigh,a)},this),[this.handle1,this.handle2].forEach(function(a){this._removeProperty(a,"left"),this._removeProperty(a,"right"),this._removeProperty(a,"top")},this),[this.tooltip,this.tooltip_min,this.tooltip_max].forEach(function(a){this._removeProperty(a,"left"),this._removeProperty(a,"right"),this._removeProperty(a,"top"),this._removeProperty(a,"margin-left"),this._removeProperty(a,"margin-right"),this._removeProperty(a,"margin-top"),this._removeClass(a,"right"),this._removeClass(a,"left"),this._removeClass(a,"top")},this)),"vertical"===this.options.orientation?(this._addClass(this.sliderElem,"slider-vertical"),this.stylePos="top",this.mousePos="pageY",this.sizePos="offsetHeight"):(this._addClass(this.sliderElem,"slider-horizontal"),this.sliderElem.style.width=o,this.options.orientation="horizontal",this.options.rtl?this.stylePos="right":this.stylePos="left",this.mousePos="pageX",this.sizePos="offsetWidth"),this.options.rtl&&this._addClass(this.sliderElem,"slider-rtl"),this._setTooltipPosition(),Array.isArray(this.options.ticks)&&this.options.ticks.length>0&&(this.options.max=Math.max.apply(Math,this.options.ticks),this.options.min=Math.min.apply(Math,this.options.ticks)),Array.isArray(this.options.value)?(this.options.range=!0,this._state.value=this.options.value):this.options.range?this._state.value=[this.options.value,this.options.max]:this._state.value=this.options.value,this.trackLow=k||this.trackLow,this.trackSelection=j||this.trackSelection,this.trackHigh=l||this.trackHigh,"none"===this.options.selection?(this._addClass(this.trackLow,"hide"),this._addClass(this.trackSelection,"hide"),this._addClass(this.trackHigh,"hide")):("after"===this.options.selection||"before"===this.options.selection)&&(this._removeClass(this.trackLow,"hide"),this._removeClass(this.trackSelection,"hide"),this._removeClass(this.trackHigh,"hide")),this.handle1=m||this.handle1,this.handle2=n||this.handle2,p===!0)for(this._removeClass(this.handle1,"round triangle"),this._removeClass(this.handle2,"round triangle hide"),f=0;f<this.ticks.length;f++)this._removeClass(this.ticks[f],"round triangle hide");var G=["round","triangle","custom"],H=-1!==G.indexOf(this.options.handle);if(H)for(this._addClass(this.handle1,this.options.handle),this._addClass(this.handle2,this.options.handle),f=0;f<this.ticks.length;f++)this._addClass(this.ticks[f],this.options.handle);if(this._state.offset=this._offset(this.sliderElem),this._state.size=this.sliderElem[this.sizePos],this.setValue(this._state.value),this.handle1Keydown=this._keydown.bind(this,0),this.handle1.addEventListener("keydown",this.handle1Keydown,!1),this.handle2Keydown=this._keydown.bind(this,1),this.handle2.addEventListener("keydown",this.handle2Keydown,!1),this.mousedown=this._mousedown.bind(this),this.touchstart=this._touchstart.bind(this),this.touchmove=this._touchmove.bind(this),this.touchCapable){var I=!1;try{var J=Object.defineProperty({},"passive",{get:function(){I=!0}});window.addEventListener("test",null,J)}catch(K){}var L=I?{passive:!0}:!1;this.sliderElem.addEventListener("touchstart",this.touchstart,L),this.sliderElem.addEventListener("touchmove",this.touchmove,L)}if(this.sliderElem.addEventListener("mousedown",this.mousedown,!1),this.resize=this._resize.bind(this),window.addEventListener("resize",this.resize,!1),"hide"===this.options.tooltip)this._addClass(this.tooltip,"hide"),this._addClass(this.tooltip_min,"hide"),this._addClass(this.tooltip_max,"hide");else if("always"===this.options.tooltip)this._showTooltip(),this._alwaysShowTooltip=!0;else{if(this.showTooltip=this._showTooltip.bind(this),this.hideTooltip=this._hideTooltip.bind(this),this.options.ticks_tooltip){var M=this._addTickListener(),N=M.addMouseEnter(this,this.handle1),O=M.addMouseLeave(this,this.handle1);this.handleCallbackMap.handle1={mouseEnter:N,mouseLeave:O},N=M.addMouseEnter(this,this.handle2),O=M.addMouseLeave(this,this.handle2),this.handleCallbackMap.handle2={mouseEnter:N,mouseLeave:O}}else this.sliderElem.addEventListener("mouseenter",this.showTooltip,!1),this.sliderElem.addEventListener("mouseleave",this.hideTooltip,!1);this.handle1.addEventListener("focus",this.showTooltip,!1),this.handle1.addEventListener("blur",this.hideTooltip,!1),this.handle2.addEventListener("focus",this.showTooltip,!1),this.handle2.addEventListener("blur",this.hideTooltip,!1)}this.options.enabled?this.enable():this.disable()}var f={formatInvalidInputErrorMsg:function(a){return"Invalid input value '"+a+"' passed in"},callingContextNotSliderInstance:"Calling context element does not have instance of Slider bound to it. Check your code to make sure the JQuery object returned from the call to the slider() initializer is calling the method"},g={linear:{toValue:function(a){var b=a/100*(this.options.max-this.options.min),c=!0;if(this.options.ticks_positions.length>0){for(var d,e,f,g=0,h=1;h<this.options.ticks_positions.length;h++)if(a<=this.options.ticks_positions[h]){d=this.options.ticks[h-1],f=this.options.ticks_positions[h-1],e=this.options.ticks[h],g=this.options.ticks_positions[h];break}var i=(a-f)/(g-f);b=d+i*(e-d),c=!1}var j=c?this.options.min:0,k=j+Math.round(b/this.options.step)*this.options.step;return k<this.options.min?this.options.min:k>this.options.max?this.options.max:k},toPercentage:function(a){if(this.options.max===this.options.min)return 0;if(this.options.ticks_positions.length>0){for(var b,c,d,e=0,f=0;f<this.options.ticks.length;f++)if(a<=this.options.ticks[f]){b=f>0?this.options.ticks[f-1]:0,d=f>0?this.options.ticks_positions[f-1]:0,c=this.options.ticks[f],e=this.options.ticks_positions[f];break}if(f>0){var g=(a-b)/(c-b);return d+g*(e-d)}}return 100*(a-this.options.min)/(this.options.max-this.options.min)}},logarithmic:{toValue:function(a){var b=0===this.options.min?0:Math.log(this.options.min),c=Math.log(this.options.max),d=Math.exp(b+(c-b)*a/100);return d=this.options.min+Math.round((d-this.options.min)/this.options.step)*this.options.step,d<this.options.min?this.options.min:d>this.options.max?this.options.max:d},toPercentage:function(a){if(this.options.max===this.options.min)return 0;var b=Math.log(this.options.max),c=0===this.options.min?0:Math.log(this.options.min),d=0===a?0:Math.log(a);return 100*(d-c)/(b-c)}}};d=function(a,b){return e.call(this,a,b),this},d.prototype={_init:function(){},constructor:d,defaultOptions:{id:"",min:0,max:10,step:1,precision:0,orientation:"horizontal",value:5,range:!1,selection:"before",tooltip:"show",tooltip_split:!1,handle:"round",reversed:!1,rtl:"auto",enabled:!0,formatter:function(a){return Array.isArray(a)?a[0]+" : "+a[1]:a},natural_arrow_keys:!1,ticks:[],ticks_positions:[],ticks_labels:[],ticks_snap_bounds:0,ticks_tooltip:!1,scale:"linear",focus:!1,tooltip_position:null,labelledby:null,rangeHighlights:[]},getElement:function(){return this.sliderElem},getValue:function(){return this.options.range?this._state.value:this._state.value[0]},setValue:function(a,b,c){a||(a=0);var d=this.getValue();this._state.value=this._validateInputValue(a);var e=this._applyPrecision.bind(this);this.options.range?(this._state.value[0]=e(this._state.value[0]),this._state.value[1]=e(this._state.value[1]),this._state.value[0]=Math.max(this.options.min,Math.min(this.options.max,this._state.value[0])),this._state.value[1]=Math.max(this.options.min,Math.min(this.options.max,this._state.value[1]))):(this._state.value=e(this._state.value),this._state.value=[Math.max(this.options.min,Math.min(this.options.max,this._state.value))],this._addClass(this.handle2,"hide"),"after"===this.options.selection?this._state.value[1]=this.options.max:this._state.value[1]=this.options.min),this.options.max>this.options.min?this._state.percentage=[this._toPercentage(this._state.value[0]),this._toPercentage(this._state.value[1]),100*this.options.step/(this.options.max-this.options.min)]:this._state.percentage=[0,0,100],this._layout();var f=this.options.range?this._state.value:this._state.value[0];return this._setDataVal(f),b===!0&&this._trigger("slide",f),d!==f&&c===!0&&this._trigger("change",{oldValue:d,newValue:f}),this},destroy:function(){this._removeSliderEventHandlers(),this.sliderElem.parentNode.removeChild(this.sliderElem),this.element.style.display="",this._cleanUpEventCallbacksMap(),this.element.removeAttribute("data"),a&&(this._unbindJQueryEventHandlers(),this.$element.removeData("slider"))},disable:function(){return this._state.enabled=!1,this.handle1.removeAttribute("tabindex"),this.handle2.removeAttribute("tabindex"),this._addClass(this.sliderElem,"slider-disabled"),this._trigger("slideDisabled"),this},enable:function(){return this._state.enabled=!0,this.handle1.setAttribute("tabindex",0),this.handle2.setAttribute("tabindex",0),this._removeClass(this.sliderElem,"slider-disabled"),this._trigger("slideEnabled"),this},toggle:function(){return this._state.enabled?this.disable():this.enable(),this},isEnabled:function(){return this._state.enabled},on:function(a,b){return this._bindNonQueryEventHandler(a,b),this},off:function(b,c){a?(this.$element.off(b,c),this.$sliderElem.off(b,c)):this._unbindNonQueryEventHandler(b,c)},getAttribute:function(a){return a?this.options[a]:this.options},setAttribute:function(a,b){return this.options[a]=b,this},refresh:function(){return this._removeSliderEventHandlers(),e.call(this,this.element,this.options),a&&a.data(this.element,"slider",this),this},relayout:function(){return this._resize(),this._layout(),this},_removeSliderEventHandlers:function(){if(this.handle1.removeEventListener("keydown",this.handle1Keydown,!1),this.handle2.removeEventListener("keydown",this.handle2Keydown,!1),this.options.ticks_tooltip){for(var a=this.ticksContainer.getElementsByClassName("slider-tick"),b=0;b<a.length;b++)a[b].removeEventListener("mouseenter",this.ticksCallbackMap[b].mouseEnter,!1),a[b].removeEventListener("mouseleave",this.ticksCallbackMap[b].mouseLeave,!1);this.handle1.removeEventListener("mouseenter",this.handleCallbackMap.handle1.mouseEnter,!1),this.handle2.removeEventListener("mouseenter",this.handleCallbackMap.handle2.mouseEnter,!1),this.handle1.removeEventListener("mouseleave",this.handleCallbackMap.handle1.mouseLeave,!1),this.handle2.removeEventListener("mouseleave",this.handleCallbackMap.handle2.mouseLeave,!1)}this.handleCallbackMap=null,this.ticksCallbackMap=null,this.showTooltip&&(this.handle1.removeEventListener("focus",this.showTooltip,!1),this.handle2.removeEventListener("focus",this.showTooltip,!1)),this.hideTooltip&&(this.handle1.removeEventListener("blur",this.hideTooltip,!1),this.handle2.removeEventListener("blur",this.hideTooltip,!1)),this.showTooltip&&this.sliderElem.removeEventListener("mouseenter",this.showTooltip,!1),this.hideTooltip&&this.sliderElem.removeEventListener("mouseleave",this.hideTooltip,!1),this.sliderElem.removeEventListener("touchstart",this.touchstart,!1),this.sliderElem.removeEventListener("touchmove",this.touchmove,!1),this.sliderElem.removeEventListener("mousedown",this.mousedown,!1),window.removeEventListener("resize",this.resize,!1)},_bindNonQueryEventHandler:function(a,b){void 0===this.eventToCallbackMap[a]&&(this.eventToCallbackMap[a]=[]),this.eventToCallbackMap[a].push(b)},_unbindNonQueryEventHandler:function(a,b){var c=this.eventToCallbackMap[a];if(void 0!==c)for(var d=0;d<c.length;d++)if(c[d]===b){c.splice(d,1);break}},_cleanUpEventCallbacksMap:function(){for(var a=Object.keys(this.eventToCallbackMap),b=0;b<a.length;b++){var c=a[b];delete this.eventToCallbackMap[c]}},_showTooltip:function(){this.options.tooltip_split===!1?(this._addClass(this.tooltip,"in"),this.tooltip_min.style.display="none",this.tooltip_max.style.display="none"):(this._addClass(this.tooltip_min,"in"),this._addClass(this.tooltip_max,"in"),this.tooltip.style.display="none"),this._state.over=!0},_hideTooltip:function(){this._state.inDrag===!1&&this.alwaysShowTooltip!==!0&&(this._removeClass(this.tooltip,"in"),this._removeClass(this.tooltip_min,"in"),this._removeClass(this.tooltip_max,"in")),this._state.over=!1},_setToolTipOnMouseOver:function(a){function b(a,b){return b?[100-a.percentage[0],this.options.range?100-a.percentage[1]:a.percentage[1]]:[a.percentage[0],a.percentage[1]]}var c=this.options.formatter(a?a.value[0]:this._state.value[0]),d=a?b(a,this.options.reversed):b(this._state,this.options.reversed);this._setText(this.tooltipInner,c),this.tooltip.style[this.stylePos]=d[0]+"%","vertical"===this.options.orientation?this._css(this.tooltip,"margin-"+this.stylePos,-this.tooltip.offsetHeight/2+"px"):this._css(this.tooltip,"margin-"+this.stylePos,-this.tooltip.offsetWidth/2+"px")},_addTickListener:function(){return{addMouseEnter:function(a,b,c){var d=function(){var b=a._state,d=c>=0?c:this.attributes["aria-valuenow"].value,e=parseInt(d,10);b.value[0]=e,b.percentage[0]=a.options.ticks_positions[e],a._setToolTipOnMouseOver(b),a._showTooltip()};return b.addEventListener("mouseenter",d,!1),d},addMouseLeave:function(a,b){var c=function(){a._hideTooltip()};return b.addEventListener("mouseleave",c,!1),c}}},_layout:function(){var a;if(a=this.options.reversed?[100-this._state.percentage[0],this.options.range?100-this._state.percentage[1]:this._state.percentage[1]]:[this._state.percentage[0],this._state.percentage[1]],this.handle1.style[this.stylePos]=a[0]+"%",this.handle1.setAttribute("aria-valuenow",this._state.value[0]),isNaN(this.options.formatter(this._state.value[0]))&&this.handle1.setAttribute("aria-valuetext",this.options.formatter(this._state.value[0])),this.handle2.style[this.stylePos]=a[1]+"%",this.handle2.setAttribute("aria-valuenow",this._state.value[1]),isNaN(this.options.formatter(this._state.value[1]))&&this.handle2.setAttribute("aria-valuetext",this.options.formatter(this._state.value[1])),this.rangeHighlightElements.length>0&&Array.isArray(this.options.rangeHighlights)&&this.options.rangeHighlights.length>0)for(var b=0;b<this.options.rangeHighlights.length;b++){var c=this._toPercentage(this.options.rangeHighlights[b].start),d=this._toPercentage(this.options.rangeHighlights[b].end);if(this.options.reversed){var e=100-d;d=100-c,c=e}var f=this._createHighlightRange(c,d);f?"vertical"===this.options.orientation?(this.rangeHighlightElements[b].style.top=f.start+"%",this.rangeHighlightElements[b].style.height=f.size+"%"):(this.options.rtl?this.rangeHighlightElements[b].style.right=f.start+"%":this.rangeHighlightElements[b].style.left=f.start+"%",this.rangeHighlightElements[b].style.width=f.size+"%"):this.rangeHighlightElements[b].style.display="none"}if(Array.isArray(this.options.ticks)&&this.options.ticks.length>0){var g,h="vertical"===this.options.orientation?"height":"width";g="vertical"===this.options.orientation?"marginTop":this.options.rtl?"marginRight":"marginLeft";var i=this._state.size/(this.options.ticks.length-1);if(this.tickLabelContainer){var j=0;if(0===this.options.ticks_positions.length)"vertical"!==this.options.orientation&&(this.tickLabelContainer.style[g]=-i/2+"px"),j=this.tickLabelContainer.offsetHeight;else for(k=0;k<this.tickLabelContainer.childNodes.length;k++)this.tickLabelContainer.childNodes[k].offsetHeight>j&&(j=this.tickLabelContainer.childNodes[k].offsetHeight);"horizontal"===this.options.orientation&&(this.sliderElem.style.marginBottom=j+"px")}for(var k=0;k<this.options.ticks.length;k++){var l=this.options.ticks_positions[k]||this._toPercentage(this.options.ticks[k]);this.options.reversed&&(l=100-l),this.ticks[k].style[this.stylePos]=l+"%",this._removeClass(this.ticks[k],"in-selection"),this.options.range?l>=a[0]&&l<=a[1]&&this._addClass(this.ticks[k],"in-selection"):"after"===this.options.selection&&l>=a[0]?this._addClass(this.ticks[k],"in-selection"):"before"===this.options.selection&&l<=a[0]&&this._addClass(this.ticks[k],"in-selection"),this.tickLabels[k]&&(this.tickLabels[k].style[h]=i+"px","vertical"!==this.options.orientation&&void 0!==this.options.ticks_positions[k]?(this.tickLabels[k].style.position="absolute",this.tickLabels[k].style[this.stylePos]=l+"%",this.tickLabels[k].style[g]=-i/2+"px"):"vertical"===this.options.orientation&&(this.options.rtl?this.tickLabels[k].style.marginRight=this.sliderElem.offsetWidth+"px":this.tickLabels[k].style.marginLeft=this.sliderElem.offsetWidth+"px",this.tickLabelContainer.style[g]=this.sliderElem.offsetWidth/2*-1+"px"))}}var m;if(this.options.range){m=this.options.formatter(this._state.value),this._setText(this.tooltipInner,m),this.tooltip.style[this.stylePos]=(a[1]+a[0])/2+"%","vertical"===this.options.orientation?this._css(this.tooltip,"margin-"+this.stylePos,-this.tooltip.offsetHeight/2+"px"):this._css(this.tooltip,"margin-"+this.stylePos,-this.tooltip.offsetWidth/2+"px");var n=this.options.formatter(this._state.value[0]);this._setText(this.tooltipInner_min,n);var o=this.options.formatter(this._state.value[1]);this._setText(this.tooltipInner_max,o),this.tooltip_min.style[this.stylePos]=a[0]+"%","vertical"===this.options.orientation?this._css(this.tooltip_min,"margin-"+this.stylePos,-this.tooltip_min.offsetHeight/2+"px"):this._css(this.tooltip_min,"margin-"+this.stylePos,-this.tooltip_min.offsetWidth/2+"px"),this.tooltip_max.style[this.stylePos]=a[1]+"%","vertical"===this.options.orientation?this._css(this.tooltip_max,"margin-"+this.stylePos,-this.tooltip_max.offsetHeight/2+"px"):this._css(this.tooltip_max,"margin-"+this.stylePos,-this.tooltip_max.offsetWidth/2+"px")}else m=this.options.formatter(this._state.value[0]),this._setText(this.tooltipInner,m),this.tooltip.style[this.stylePos]=a[0]+"%","vertical"===this.options.orientation?this._css(this.tooltip,"margin-"+this.stylePos,-this.tooltip.offsetHeight/2+"px"):this._css(this.tooltip,"margin-"+this.stylePos,-this.tooltip.offsetWidth/2+"px");if("vertical"===this.options.orientation)this.trackLow.style.top="0",this.trackLow.style.height=Math.min(a[0],a[1])+"%",this.trackSelection.style.top=Math.min(a[0],a[1])+"%",this.trackSelection.style.height=Math.abs(a[0]-a[1])+"%",this.trackHigh.style.bottom="0",this.trackHigh.style.height=100-Math.min(a[0],a[1])-Math.abs(a[0]-a[1])+"%";else{"right"===this.stylePos?this.trackLow.style.right="0":this.trackLow.style.left="0",this.trackLow.style.width=Math.min(a[0],a[1])+"%","right"===this.stylePos?this.trackSelection.style.right=Math.min(a[0],a[1])+"%":this.trackSelection.style.left=Math.min(a[0],a[1])+"%",this.trackSelection.style.width=Math.abs(a[0]-a[1])+"%","right"===this.stylePos?this.trackHigh.style.left="0":this.trackHigh.style.right="0",this.trackHigh.style.width=100-Math.min(a[0],a[1])-Math.abs(a[0]-a[1])+"%";var p=this.tooltip_min.getBoundingClientRect(),q=this.tooltip_max.getBoundingClientRect();"bottom"===this.options.tooltip_position?p.right>q.left?(this._removeClass(this.tooltip_max,"bottom"),this._addClass(this.tooltip_max,"top"),this.tooltip_max.style.top="",this.tooltip_max.style.bottom="22px"):(this._removeClass(this.tooltip_max,"top"),this._addClass(this.tooltip_max,"bottom"),this.tooltip_max.style.top=this.tooltip_min.style.top,this.tooltip_max.style.bottom=""):p.right>q.left?(this._removeClass(this.tooltip_max,"top"),this._addClass(this.tooltip_max,"bottom"),this.tooltip_max.style.top="18px"):(this._removeClass(this.tooltip_max,"bottom"),this._addClass(this.tooltip_max,"top"),this.tooltip_max.style.top=this.tooltip_min.style.top)}},_createHighlightRange:function(a,b){return this._isHighlightRange(a,b)?a>b?{start:b,size:a-b}:{start:a,size:b-a}:null},_isHighlightRange:function(a,b){return a>=0&&100>=a&&b>=0&&100>=b?!0:!1},_resize:function(a){this._state.offset=this._offset(this.sliderElem),this._state.size=this.sliderElem[this.sizePos],this._layout()},_removeProperty:function(a,b){a.style.removeProperty?a.style.removeProperty(b):a.style.removeAttribute(b)},_mousedown:function(a){if(!this._state.enabled)return!1;this._state.offset=this._offset(this.sliderElem),this._state.size=this.sliderElem[this.sizePos];var b=this._getPercentage(a);if(this.options.range){var c=Math.abs(this._state.percentage[0]-b),d=Math.abs(this._state.percentage[1]-b);this._state.dragged=d>c?0:1,this._adjustPercentageForRangeSliders(b)}else this._state.dragged=0;this._state.percentage[this._state.dragged]=b,this._layout(),this.touchCapable&&(document.removeEventListener("touchmove",this.mousemove,!1),document.removeEventListener("touchend",this.mouseup,!1)),this.mousemove&&document.removeEventListener("mousemove",this.mousemove,!1),this.mouseup&&document.removeEventListener("mouseup",this.mouseup,!1),this.mousemove=this._mousemove.bind(this),this.mouseup=this._mouseup.bind(this),this.touchCapable&&(document.addEventListener("touchmove",this.mousemove,!1),document.addEventListener("touchend",this.mouseup,!1)),document.addEventListener("mousemove",this.mousemove,!1),document.addEventListener("mouseup",this.mouseup,!1),this._state.inDrag=!0;var e=this._calculateValue();return this._trigger("slideStart",e),this._setDataVal(e),this.setValue(e,!1,!0),a.returnValue=!1,this.options.focus&&this._triggerFocusOnHandle(this._state.dragged),!0},_touchstart:function(a){if(void 0===a.changedTouches)return void this._mousedown(a);var b=a.changedTouches[0];this.touchX=b.pageX,this.touchY=b.pageY},_triggerFocusOnHandle:function(a){0===a&&this.handle1.focus(),1===a&&this.handle2.focus()},_keydown:function(a,b){if(!this._state.enabled)return!1;var c;switch(b.keyCode){case 37:case 40:c=-1;break;case 39:case 38:c=1}if(c){if(this.options.natural_arrow_keys){var d="vertical"===this.options.orientation&&!this.options.reversed,e="horizontal"===this.options.orientation&&this.options.reversed;(d||e)&&(c=-c)}var f=this._state.value[a]+c*this.options.step,g=f/this.options.max*100;if(this._state.keyCtrl=a,this.options.range){this._adjustPercentageForRangeSliders(g);var h=this._state.keyCtrl?this._state.value[0]:f,i=this._state.keyCtrl?f:this._state.value[1];f=[h,i]}return this._trigger("slideStart",f),this._setDataVal(f),this.setValue(f,!0,!0),this._setDataVal(f),this._trigger("slideStop",f),this._layout(),this._pauseEvent(b),delete this._state.keyCtrl,!1}},_pauseEvent:function(a){a.stopPropagation&&a.stopPropagation(),a.preventDefault&&a.preventDefault(),a.cancelBubble=!0,a.returnValue=!1},_mousemove:function(a){if(!this._state.enabled)return!1;var b=this._getPercentage(a);this._adjustPercentageForRangeSliders(b),this._state.percentage[this._state.dragged]=b,this._layout();var c=this._calculateValue(!0);return this.setValue(c,!0,!0),!1},_touchmove:function(a){if(void 0!==a.changedTouches){var b=a.changedTouches[0],c=b.pageX-this.touchX,d=b.pageY-this.touchY;this._state.inDrag||("vertical"===this.options.orientation&&5>=c&&c>=-5&&(d>=15||-15>=d)?this._mousedown(a):5>=d&&d>=-5&&(c>=15||-15>=c)&&this._mousedown(a))}},_adjustPercentageForRangeSliders:function(a){if(this.options.range){var b=this._getNumDigitsAfterDecimalPlace(a);b=b?b-1:0;var c=this._applyToFixedAndParseFloat(a,b);0===this._state.dragged&&this._applyToFixedAndParseFloat(this._state.percentage[1],b)<c?(this._state.percentage[0]=this._state.percentage[1],this._state.dragged=1):1===this._state.dragged&&this._applyToFixedAndParseFloat(this._state.percentage[0],b)>c?(this._state.percentage[1]=this._state.percentage[0],this._state.dragged=0):0===this._state.keyCtrl&&this._state.value[1]/this.options.max*100<a?(this._state.percentage[0]=this._state.percentage[1],this._state.keyCtrl=1,this.handle2.focus()):1===this._state.keyCtrl&&this._state.value[0]/this.options.max*100>a&&(this._state.percentage[1]=this._state.percentage[0],this._state.keyCtrl=0,this.handle1.focus())}},_mouseup:function(){if(!this._state.enabled)return!1;this.touchCapable&&(document.removeEventListener("touchmove",this.mousemove,!1),document.removeEventListener("touchend",this.mouseup,!1)),document.removeEventListener("mousemove",this.mousemove,!1),document.removeEventListener("mouseup",this.mouseup,!1),this._state.inDrag=!1,this._state.over===!1&&this._hideTooltip();var a=this._calculateValue(!0);return this._layout(),this._setDataVal(a),this._trigger("slideStop",a),!1},_calculateValue:function(a){var b;if(this.options.range?(b=[this.options.min,this.options.max],0!==this._state.percentage[0]&&(b[0]=this._toValue(this._state.percentage[0]),b[0]=this._applyPrecision(b[0])),100!==this._state.percentage[1]&&(b[1]=this._toValue(this._state.percentage[1]),b[1]=this._applyPrecision(b[1]))):(b=this._toValue(this._state.percentage[0]),b=parseFloat(b),b=this._applyPrecision(b)),a){for(var c=[b,1/0],d=0;d<this.options.ticks.length;d++){
+var e=Math.abs(this.options.ticks[d]-b);e<=c[1]&&(c=[this.options.ticks[d],e])}if(c[1]<=this.options.ticks_snap_bounds)return c[0]}return b},_applyPrecision:function(a){var b=this.options.precision||this._getNumDigitsAfterDecimalPlace(this.options.step);return this._applyToFixedAndParseFloat(a,b)},_getNumDigitsAfterDecimalPlace:function(a){var b=(""+a).match(/(?:\.(\d+))?(?:[eE]([+-]?\d+))?$/);return b?Math.max(0,(b[1]?b[1].length:0)-(b[2]?+b[2]:0)):0},_applyToFixedAndParseFloat:function(a,b){var c=a.toFixed(b);return parseFloat(c)},_getPercentage:function(a){!this.touchCapable||"touchstart"!==a.type&&"touchmove"!==a.type||(a=a.touches[0]);var b=a[this.mousePos],c=this._state.offset[this.stylePos],d=b-c;"right"===this.stylePos&&(d=-d);var e=d/this._state.size*100;return e=Math.round(e/this._state.percentage[2])*this._state.percentage[2],this.options.reversed&&(e=100-e),Math.max(0,Math.min(100,e))},_validateInputValue:function(a){if(isNaN(+a)){if(Array.isArray(a))return this._validateArray(a),a;throw new Error(f.formatInvalidInputErrorMsg(a))}return+a},_validateArray:function(a){for(var b=0;b<a.length;b++){var c=a[b];if("number"!=typeof c)throw new Error(f.formatInvalidInputErrorMsg(c))}},_setDataVal:function(a){this.element.setAttribute("data-value",a),this.element.setAttribute("value",a),this.element.value=a},_trigger:function(b,c){c=c||0===c?c:void 0;var d=this.eventToCallbackMap[b];if(d&&d.length)for(var e=0;e<d.length;e++){var f=d[e];f(c)}a&&this._triggerJQueryEvent(b,c)},_triggerJQueryEvent:function(a,b){var c={type:a,value:b};this.$element.trigger(c),this.$sliderElem.trigger(c)},_unbindJQueryEventHandlers:function(){this.$element.off(),this.$sliderElem.off()},_setText:function(a,b){"undefined"!=typeof a.textContent?a.textContent=b:"undefined"!=typeof a.innerText&&(a.innerText=b)},_removeClass:function(a,b){for(var c=b.split(" "),d=a.className,e=0;e<c.length;e++){var f=c[e],g=new RegExp("(?:\\s|^)"+f+"(?:\\s|$)");d=d.replace(g," ")}a.className=d.trim()},_addClass:function(a,b){for(var c=b.split(" "),d=a.className,e=0;e<c.length;e++){var f=c[e],g=new RegExp("(?:\\s|^)"+f+"(?:\\s|$)"),h=g.test(d);h||(d+=" "+f)}a.className=d.trim()},_offsetLeft:function(a){return a.getBoundingClientRect().left},_offsetRight:function(a){return a.getBoundingClientRect().right},_offsetTop:function(a){for(var b=a.offsetTop;(a=a.offsetParent)&&!isNaN(a.offsetTop);)b+=a.offsetTop,"BODY"!==a.tagName&&(b-=a.scrollTop);return b},_offset:function(a){return{left:this._offsetLeft(a),right:this._offsetRight(a),top:this._offsetTop(a)}},_css:function(b,c,d){if(a)a.style(b,c,d);else{var e=c.replace(/^-ms-/,"ms-").replace(/-([\da-z])/gi,function(a,b){return b.toUpperCase()});b.style[e]=d}},_toValue:function(a){return this.options.scale.toValue.apply(this,[a])},_toPercentage:function(a){return this.options.scale.toPercentage.apply(this,[a])},_setTooltipPosition:function(){var a=[this.tooltip,this.tooltip_min,this.tooltip_max];if("vertical"===this.options.orientation){var b;b=this.options.tooltip_position?this.options.tooltip_position:this.options.rtl?"left":"right";var c="left"===b?"right":"left";a.forEach(function(a){this._addClass(a,b),a.style[c]="100%"}.bind(this))}else"bottom"===this.options.tooltip_position?a.forEach(function(a){this._addClass(a,"bottom"),a.style.top="22px"}.bind(this)):a.forEach(function(a){this._addClass(a,"top"),a.style.top=-this.tooltip.outerHeight-14+"px"}.bind(this))}},a&&a.fn&&!function(){var e=void 0;a.fn.slider?(windowIsDefined&&window.console.warn("bootstrap-slider.js - WARNING: $.fn.slider namespace is already bound. Use the $.fn.bootstrapSlider namespace instead."),e=c):(a.bridget(b,d),e=b),a.bridget(c,d),a(function(){a("input[data-provide=slider]")[e]()})}()}(a),d});
\ No newline at end of file
diff --git a/data/web/js/bootstrap-switch.min.js b/data/web/js/bootstrap-switch.min.js
new file mode 100644
index 00000000..effcb6cb
--- /dev/null
+++ b/data/web/js/bootstrap-switch.min.js
@@ -0,0 +1,10 @@
+/**
+  * bootstrap-switch - Turn checkboxes and radio buttons into toggle switches.
+  *
+  * @version v3.3.3
+  * @homepage http://www.bootstrap-switch.org
+  * @author Mattia Larentis <[email protected]> (http://larentis.eu)
+  * @license Apache-2.0
+  */
+
+'use strict';var _createClass=function(){function a(b,c){for(var e,d=0;d<c.length;d++)e=c[d],e.enumerable=e.enumerable||!1,e.configurable=!0,'value'in e&&(e.writable=!0),Object.defineProperty(b,e.key,e)}return function(b,c,d){return c&&a(b.prototype,c),d&&a(b,d),b}}();function _classCallCheck(a,b){if(!(a instanceof b))throw new TypeError('Cannot call a class as a function')}(function(a,b){var c=function(){function d(e){var g=this,f=1<arguments.length&&void 0!==arguments[1]?arguments[1]:{};_classCallCheck(this,d),this.$element=a(e),this.options=a.extend({},a.fn.bootstrapSwitch.defaults,this._getElementOptions(),f),this.prevOptions={},this.$wrapper=a('<div>',{class:function _class(){var h=[];return h.push(g.options.state?'on':'off'),g.options.size&&h.push(g.options.size),g.options.disabled&&h.push('disabled'),g.options.readonly&&h.push('readonly'),g.options.indeterminate&&h.push('indeterminate'),g.options.inverse&&h.push('inverse'),g.$element.attr('id')&&h.push('id-'+g.$element.attr('id')),h.map(g._getClass.bind(g)).concat([g.options.baseClass],g._getClasses(g.options.wrapperClass)).join(' ')}}),this.$container=a('<div>',{class:this._getClass('container')}),this.$on=a('<span>',{html:this.options.onText,class:this._getClass('handle-on')+' '+this._getClass(this.options.onColor)}),this.$off=a('<span>',{html:this.options.offText,class:this._getClass('handle-off')+' '+this._getClass(this.options.offColor)}),this.$label=a('<span>',{html:this.options.labelText,class:this._getClass('label')}),this.$element.on('init.bootstrapSwitch',this.options.onInit.bind(this,e)),this.$element.on('switchChange.bootstrapSwitch',function(){for(var j=arguments.length,h=Array(j),k=0;k<j;k++)h[k]=arguments[k];!1===g.options.onSwitchChange.apply(e,h)&&(g.$element.is(':radio')?a('[name="'+g.$element.attr('name')+'"]').trigger('previousState.bootstrapSwitch',!0):g.$element.trigger('previousState.bootstrapSwitch',!0))}),this.$container=this.$element.wrap(this.$container).parent(),this.$wrapper=this.$container.wrap(this.$wrapper).parent(),this.$element.before(this.options.inverse?this.$off:this.$on).before(this.$label).before(this.options.inverse?this.$on:this.$off),this.options.indeterminate&&this.$element.prop('indeterminate',!0),this._init(),this._elementHandlers(),this._handleHandlers(),this._labelHandlers(),this._formHandler(),this._externalLabelHandler(),this.$element.trigger('init.bootstrapSwitch',this.options.state)}return _createClass(d,[{key:'setPrevOptions',value:function setPrevOptions(){this.prevOptions=Object.assign({},this.options)}},{key:'state',value:function state(e,f){return'undefined'==typeof e?this.options.state:this.options.disabled||this.options.readonly||this.options.state&&!this.options.radioAllOff&&this.$element.is(':radio')?this.$element:(this.$element.is(':radio')?a('[name="'+this.$element.attr('name')+'"]').trigger('setPreviousOptions.bootstrapSwitch'):this.$element.trigger('setPreviousOptions.bootstrapSwitch'),this.options.indeterminate&&this.indeterminate(!1),this.$element.prop('checked',!!e).trigger('change.bootstrapSwitch',f),this.$element)}},{key:'toggleState',value:function toggleState(e){return this.options.disabled||this.options.readonly?this.$element:this.options.indeterminate?(this.indeterminate(!1),this.state(!0)):this.$element.prop('checked',!this.options.state).trigger('change.bootstrapSwitch',e)}},{key:'size',value:function size(e){return'undefined'==typeof e?this.options.size:(null!=this.options.size&&this.$wrapper.removeClass(this._getClass(this.options.size)),e&&this.$wrapper.addClass(this._getClass(e)),this._width(),this._containerPosition(),this.options.size=e,this.$element)}},{key:'animate',value:function animate(e){return'undefined'==typeof e?this.options.animate:this.options.animate===!!e?this.$element:this.toggleAnimate()}},{key:'toggleAnimate',value:function toggleAnimate(){return this.options.animate=!this.options.animate,this.$wrapper.toggleClass(this._getClass('animate')),this.$element}},{key:'disabled',value:function disabled(e){return'undefined'==typeof e?this.options.disabled:this.options.disabled===!!e?this.$element:this.toggleDisabled()}},{key:'toggleDisabled',value:function toggleDisabled(){return this.options.disabled=!this.options.disabled,this.$element.prop('disabled',this.options.disabled),this.$wrapper.toggleClass(this._getClass('disabled')),this.$element}},{key:'readonly',value:function readonly(e){return'undefined'==typeof e?this.options.readonly:this.options.readonly===!!e?this.$element:this.toggleReadonly()}},{key:'toggleReadonly',value:function toggleReadonly(){return this.options.readonly=!this.options.readonly,this.$element.prop('readonly',this.options.readonly),this.$wrapper.toggleClass(this._getClass('readonly')),this.$element}},{key:'indeterminate',value:function indeterminate(e){return'undefined'==typeof e?this.options.indeterminate:this.options.indeterminate===!!e?this.$element:this.toggleIndeterminate()}},{key:'toggleIndeterminate',value:function toggleIndeterminate(){return this.options.indeterminate=!this.options.indeterminate,this.$element.prop('indeterminate',this.options.indeterminate),this.$wrapper.toggleClass(this._getClass('indeterminate')),this._containerPosition(),this.$element}},{key:'inverse',value:function inverse(e){return'undefined'==typeof e?this.options.inverse:this.options.inverse===!!e?this.$element:this.toggleInverse()}},{key:'toggleInverse',value:function toggleInverse(){this.$wrapper.toggleClass(this._getClass('inverse'));var e=this.$on.clone(!0),f=this.$off.clone(!0);return this.$on.replaceWith(f),this.$off.replaceWith(e),this.$on=f,this.$off=e,this.options.inverse=!this.options.inverse,this.$element}},{key:'onColor',value:function onColor(e){return'undefined'==typeof e?this.options.onColor:(this.options.onColor&&this.$on.removeClass(this._getClass(this.options.onColor)),this.$on.addClass(this._getClass(e)),this.options.onColor=e,this.$element)}},{key:'offColor',value:function offColor(e){return'undefined'==typeof e?this.options.offColor:(this.options.offColor&&this.$off.removeClass(this._getClass(this.options.offColor)),this.$off.addClass(this._getClass(e)),this.options.offColor=e,this.$element)}},{key:'onText',value:function onText(e){return'undefined'==typeof e?this.options.onText:(this.$on.html(e),this._width(),this._containerPosition(),this.options.onText=e,this.$element)}},{key:'offText',value:function offText(e){return'undefined'==typeof e?this.options.offText:(this.$off.html(e),this._width(),this._containerPosition(),this.options.offText=e,this.$element)}},{key:'labelText',value:function labelText(e){return'undefined'==typeof e?this.options.labelText:(this.$label.html(e),this._width(),this.options.labelText=e,this.$element)}},{key:'handleWidth',value:function handleWidth(e){return'undefined'==typeof e?this.options.handleWidth:(this.options.handleWidth=e,this._width(),this._containerPosition(),this.$element)}},{key:'labelWidth',value:function labelWidth(e){return'undefined'==typeof e?this.options.labelWidth:(this.options.labelWidth=e,this._width(),this._containerPosition(),this.$element)}},{key:'baseClass',value:function baseClass(){return this.options.baseClass}},{key:'wrapperClass',value:function wrapperClass(e){return'undefined'==typeof e?this.options.wrapperClass:(e||(e=a.fn.bootstrapSwitch.defaults.wrapperClass),this.$wrapper.removeClass(this._getClasses(this.options.wrapperClass).join(' ')),this.$wrapper.addClass(this._getClasses(e).join(' ')),this.options.wrapperClass=e,this.$element)}},{key:'radioAllOff',value:function radioAllOff(e){if('undefined'==typeof e)return this.options.radioAllOff;var f=!!e;return this.options.radioAllOff===f?this.$element:(this.options.radioAllOff=f,this.$element)}},{key:'onInit',value:function onInit(e){return'undefined'==typeof e?this.options.onInit:(e||(e=a.fn.bootstrapSwitch.defaults.onInit),this.options.onInit=e,this.$element)}},{key:'onSwitchChange',value:function onSwitchChange(e){return'undefined'==typeof e?this.options.onSwitchChange:(e||(e=a.fn.bootstrapSwitch.defaults.onSwitchChange),this.options.onSwitchChange=e,this.$element)}},{key:'destroy',value:function destroy(){var e=this.$element.closest('form');return e.length&&e.off('reset.bootstrapSwitch').removeData('bootstrap-switch'),this.$container.children().not(this.$element).remove(),this.$element.unwrap().unwrap().off('.bootstrapSwitch').removeData('bootstrap-switch'),this.$element}},{key:'_getElementOptions',value:function _getElementOptions(){return{state:this.$element.is(':checked'),size:this.$element.data('size'),animate:this.$element.data('animate'),disabled:this.$element.is(':disabled'),readonly:this.$element.is('[readonly]'),indeterminate:this.$element.data('indeterminate'),inverse:this.$element.data('inverse'),radioAllOff:this.$element.data('radio-all-off'),onColor:this.$element.data('on-color'),offColor:this.$element.data('off-color'),onText:this.$element.data('on-text'),offText:this.$element.data('off-text'),labelText:this.$element.data('label-text'),handleWidth:this.$element.data('handle-width'),labelWidth:this.$element.data('label-width'),baseClass:this.$element.data('base-class'),wrapperClass:this.$element.data('wrapper-class')}}},{key:'_width',value:function _width(){var g=this,e=this.$on.add(this.$off).add(this.$label).css('width',''),f=void 0;return f='auto'===this.options.handleWidth?Math.round(Math.max(this.$on.width(),this.$off.width())):this.options.handleWidth,e.width(f),this.$label.width(function(h,j){return'auto'===g.options.labelWidth?j<f?f:j:g.options.labelWidth}),this._handleWidth=this.$on.outerWidth(),this._labelWidth=this.$label.outerWidth(),this.$container.width(2*this._handleWidth+this._labelWidth),this.$wrapper.width(this._handleWidth+this._labelWidth)}},{key:'_containerPosition',value:function _containerPosition(){var g=this,e=0<arguments.length&&void 0!==arguments[0]?arguments[0]:this.options.state,f=arguments[1];this.$container.css('margin-left',function(){var h=[0,'-'+g._handleWidth+'px'];return g.options.indeterminate?'-'+g._handleWidth/2+'px':e?g.options.inverse?h[1]:h[0]:g.options.inverse?h[0]:h[1]})}},{key:'_init',value:function _init(){var f=this,e=function(){f.setPrevOptions(),f._width(),f._containerPosition(),setTimeout(function(){if(f.options.animate)return f.$wrapper.addClass(f._getClass('animate'))},50)};if(this.$wrapper.is(':visible'))return void e();var g=b.setInterval(function(){if(f.$wrapper.is(':visible'))return e(),b.clearInterval(g)},50)}},{key:'_elementHandlers',value:function _elementHandlers(){var e=this;return this.$element.on({'setPreviousOptions.bootstrapSwitch':this.setPrevOptions.bind(this),'previousState.bootstrapSwitch':function previousStateBootstrapSwitch(){e.options=e.prevOptions,e.options.indeterminate&&e.$wrapper.addClass(e._getClass('indeterminate')),e.$element.prop('checked',e.options.state).trigger('change.bootstrapSwitch',!0)},'change.bootstrapSwitch':function changeBootstrapSwitch(f,g){f.preventDefault(),f.stopImmediatePropagation();var h=e.$element.is(':checked');e._containerPosition(h),h===e.options.state||(e.options.state=h,e.$wrapper.toggleClass(e._getClass('off')).toggleClass(e._getClass('on')),!g&&(e.$element.is(':radio')&&a('[name="'+e.$element.attr('name')+'"]').not(e.$element).prop('checked',!1).trigger('change.bootstrapSwitch',!0),e.$element.trigger('switchChange.bootstrapSwitch',[h])))},'focus.bootstrapSwitch':function focusBootstrapSwitch(f){f.preventDefault(),e.$wrapper.addClass(e._getClass('focused'))},'blur.bootstrapSwitch':function blurBootstrapSwitch(f){f.preventDefault(),e.$wrapper.removeClass(e._getClass('focused'))},'keydown.bootstrapSwitch':function keydownBootstrapSwitch(f){!f.which||e.options.disabled||e.options.readonly||(37===f.which||39===f.which)&&(f.preventDefault(),f.stopImmediatePropagation(),e.state(39===f.which))}})}},{key:'_handleHandlers',value:function _handleHandlers(){var e=this;return this.$on.on('click.bootstrapSwitch',function(f){return f.preventDefault(),f.stopPropagation(),e.state(!1),e.$element.trigger('focus.bootstrapSwitch')}),this.$off.on('click.bootstrapSwitch',function(f){return f.preventDefault(),f.stopPropagation(),e.state(!0),e.$element.trigger('focus.bootstrapSwitch')})}},{key:'_labelHandlers',value:function _labelHandlers(){var f=this;this.$label.on({click:function click(g){g.stopPropagation()},'mousedown.bootstrapSwitch touchstart.bootstrapSwitch':function mousedownBootstrapSwitchTouchstartBootstrapSwitch(g){f._dragStart||f.options.disabled||f.options.readonly||(g.preventDefault(),g.stopPropagation(),f._dragStart=(g.pageX||g.originalEvent.touches[0].pageX)-parseInt(f.$container.css('margin-left'),10),f.options.animate&&f.$wrapper.removeClass(f._getClass('animate')),f.$element.trigger('focus.bootstrapSwitch'))},'mousemove.bootstrapSwitch touchmove.bootstrapSwitch':function mousemoveBootstrapSwitchTouchmoveBootstrapSwitch(g){if(null!=f._dragStart){var h=(g.pageX||g.originalEvent.touches[0].pageX)-f._dragStart;g.preventDefault(),h<-f._handleWidth||0<h||(f._dragEnd=h,f.$container.css('margin-left',f._dragEnd+'px'))}},'mouseup.bootstrapSwitch touchend.bootstrapSwitch':function mouseupBootstrapSwitchTouchendBootstrapSwitch(g){if(f._dragStart){if(g.preventDefault(),f.options.animate&&f.$wrapper.addClass(f._getClass('animate')),f._dragEnd){var h=f._dragEnd>-(f._handleWidth/2);f._dragEnd=!1,f.state(f.options.inverse?!h:h)}else f.state(!f.options.state);f._dragStart=!1}},'mouseleave.bootstrapSwitch':function mouseleaveBootstrapSwitch(){f.$label.trigger('mouseup.bootstrapSwitch')}})}},{key:'_externalLabelHandler',value:function _externalLabelHandler(){var f=this,e=this.$element.closest('label');e.on('click',function(g){g.preventDefault(),g.stopImmediatePropagation(),g.target===e[0]&&f.toggleState()})}},{key:'_formHandler',value:function _formHandler(){var e=this.$element.closest('form');e.data('bootstrap-switch')||e.on('reset.bootstrapSwitch',function(){b.setTimeout(function(){e.find('input').filter(function(){return a(this).data('bootstrap-switch')}).each(function(){return a(this).bootstrapSwitch('state',this.checked)})},1)}).data('bootstrap-switch',!0)}},{key:'_getClass',value:function _getClass(e){return this.options.baseClass+'-'+e}},{key:'_getClasses',value:function _getClasses(e){return a.isArray(e)?e.map(this._getClass.bind(this)):[this._getClass(e)]}}]),d}();a.fn.bootstrapSwitch=function(d){for(var f=arguments.length,e=Array(1<f?f-1:0),g=1;g<f;g++)e[g-1]=arguments[g];var h=this;return this.each(function(){var j=a(this),k=j.data('bootstrap-switch');k||(k=new c(this,d),j.data('bootstrap-switch',k)),'string'==typeof d&&(h=k[d].apply(k,e))}),h},a.fn.bootstrapSwitch.Constructor=c,a.fn.bootstrapSwitch.defaults={state:!0,size:null,animate:!0,disabled:!1,readonly:!1,indeterminate:!1,inverse:!1,radioAllOff:!1,onColor:'primary',offColor:'default',onText:'ON',offText:'OFF',labelText:'&nbsp',handleWidth:'auto',labelWidth:'auto',baseClass:'bootstrap-switch',wrapperClass:'wrapper',onInit:function onInit(){},onSwitchChange:function onSwitchChange(){}}})(window.jQuery,window);
diff --git a/data/web/js/index.js b/data/web/js/index.js
new file mode 100644
index 00000000..568bbb8a
--- /dev/null
+++ b/data/web/js/index.js
@@ -0,0 +1,3 @@
+$(document).ready(function() {
+	$('nav').hide();
+});
\ No newline at end of file
diff --git a/data/web/js/mailbox.js b/data/web/js/mailbox.js
new file mode 100644
index 00000000..d12a37c3
--- /dev/null
+++ b/data/web/js/mailbox.js
@@ -0,0 +1,54 @@
+$(document).ready(function() {
+	// Show element counter for tables
+	$('[data-toggle="tooltip"]').tooltip();
+	var rowCountDomainAlias = $('#domainaliastable >tbody >#data').length;
+	var rowCountDomain = $('#domaintable >tbody >#data').length;
+	var rowCountMailbox = $('#mailboxtable >tbody >#data').length;
+	var rowCountAlias = $('#aliastable >tbody >#data').length;
+	var rowCountResource = $('#resourcetable >tbody >#data').length;
+	$("#numRowsDomainAlias").text(rowCountDomainAlias);
+	$("#numRowsDomain").text(rowCountDomain);
+	$("#numRowsMailbox").text(rowCountMailbox);
+	$("#numRowsAlias").text(rowCountAlias);
+	$("#numRowsResource").text(rowCountResource);
+
+	// Filter table function
+	$.fn.extend({
+		filterTable: function(){
+			return this.each(function(){
+				$(this).on('keyup', function(e){
+					var $this = $(this),
+                        search = $this.val().toLowerCase(),
+                        target = $this.attr('data-filters'),
+                        $target = $(target),
+                        $rows = $target.find('tbody #data');
+					$target.find('tbody .filterTable_no_results').remove();
+					if(search == '') {
+						$target.find('tbody #no-data').show();
+						$rows.show();
+					} else {
+						$target.find('tbody #no-data').hide();
+						$rows.each(function(){
+							var $this = $(this);
+							$this.text().toLowerCase().indexOf(search) === -1 ? $this.hide() : $this.show();
+						})
+						if($target.find('tbody #data:visible').size() === 0) {
+							var col_count = $target.find('#data').first().find('td').size();
+							var no_results = $('<tr class="filterTable_no_results"><td colspan="100%">-</td></tr>')
+							$target.find('tbody').prepend(no_results);
+						}
+					}
+				});
+			});
+		}
+	});
+	$('[data-action="filter"]').filterTable();
+	$('.container').on('click', '.panel-heading span.filter', function(e){
+		var $this = $(this),
+		$panel = $this.parents('.panel');
+		$panel.find('.panel-body').slideToggle("fast");
+		if($this.css('display') != 'none') {
+			$panel.find('.panel-body input').focus();
+		}
+	});
+});
diff --git a/data/web/js/sorttable.js b/data/web/js/sorttable.js
new file mode 100644
index 00000000..cb3e293c
--- /dev/null
+++ b/data/web/js/sorttable.js
@@ -0,0 +1,236 @@
+(function() {
+  var SELECTOR, addEventListener, clickEvents, numberRegExp, sortable, touchDevice, trimRegExp;
+
+  SELECTOR = 'table[data-sortable]';
+
+  numberRegExp = /^-?[£$¤]?[\d,.]+%?$/;
+
+  trimRegExp = /^\s+|\s+$/g;
+
+  clickEvents = ['click'];
+
+  touchDevice = 'ontouchstart' in document.documentElement;
+
+  if (touchDevice) {
+    clickEvents.push('touchstart');
+  }
+
+  addEventListener = function(el, event, handler) {
+    if (el.addEventListener != null) {
+      return el.addEventListener(event, handler, false);
+    } else {
+      return el.attachEvent("on" + event, handler);
+    }
+  };
+
+  sortable = {
+    init: function(options) {
+      var table, tables, _i, _len, _results;
+      if (options == null) {
+        options = {};
+      }
+      if (options.selector == null) {
+        options.selector = SELECTOR;
+      }
+      tables = document.querySelectorAll(options.selector);
+      _results = [];
+      for (_i = 0, _len = tables.length; _i < _len; _i++) {
+        table = tables[_i];
+        _results.push(sortable.initTable(table));
+      }
+      return _results;
+    },
+    initTable: function(table) {
+      var i, th, ths, _i, _len, _ref;
+      if (((_ref = table.tHead) != null ? _ref.rows.length : void 0) !== 1) {
+        return;
+      }
+      if (table.getAttribute('data-sortable-initialized') === 'true') {
+        return;
+      }
+      table.setAttribute('data-sortable-initialized', 'true');
+      ths = table.querySelectorAll('th');
+      for (i = _i = 0, _len = ths.length; _i < _len; i = ++_i) {
+        th = ths[i];
+        if (th.getAttribute('data-sortable') !== 'false') {
+          sortable.setupClickableTH(table, th, i);
+        }
+      }
+      return table;
+    },
+    setupClickableTH: function(table, th, i) {
+      var eventName, onClick, type, _i, _len, _results;
+      type = sortable.getColumnType(table, i);
+      onClick = function(e) {
+        var compare, item, newSortedDirection, position, row, rowArray, sorted, sortedDirection, tBody, ths, value, _compare, _i, _j, _k, _l, _len, _len1, _len2, _len3, _len4, _m, _ref, _ref1;
+        if (e.handled !== true) {
+          e.handled = true;
+        } else {
+          return false;
+        }
+        sorted = this.getAttribute('data-sorted') === 'true';
+        sortedDirection = this.getAttribute('data-sorted-direction');
+        if (sorted) {
+          newSortedDirection = sortedDirection === 'ascending' ? 'descending' : 'ascending';
+        } else {
+          newSortedDirection = type.defaultSortDirection;
+        }
+        ths = this.parentNode.querySelectorAll('th');
+        for (_i = 0, _len = ths.length; _i < _len; _i++) {
+          th = ths[_i];
+          th.setAttribute('data-sorted', 'false');
+          th.removeAttribute('data-sorted-direction');
+        }
+        this.setAttribute('data-sorted', 'true');
+        this.setAttribute('data-sorted-direction', newSortedDirection);
+        tBody = table.tBodies[0];
+        rowArray = [];
+        if (!sorted) {
+          if (type.compare != null) {
+            _compare = type.compare;
+          } else {
+            _compare = function(a, b) {
+              return b - a;
+            };
+          }
+          compare = function(a, b) {
+            if (a[0] === b[0]) {
+              return a[2] - b[2];
+            }
+            if (type.reverse) {
+              return _compare(b[0], a[0]);
+            } else {
+              return _compare(a[0], b[0]);
+            }
+          };
+          _ref = tBody.rows;
+          for (position = _j = 0, _len1 = _ref.length; _j < _len1; position = ++_j) {
+            row = _ref[position];
+            value = sortable.getNodeValue(row.cells[i]);
+            if (type.comparator != null) {
+              value = type.comparator(value);
+            }
+            rowArray.push([value, row, position]);
+          }
+          rowArray.sort(compare);
+          for (_k = 0, _len2 = rowArray.length; _k < _len2; _k++) {
+            row = rowArray[_k];
+            tBody.appendChild(row[1]);
+          }
+        } else {
+          _ref1 = tBody.rows;
+          for (_l = 0, _len3 = _ref1.length; _l < _len3; _l++) {
+            item = _ref1[_l];
+            rowArray.push(item);
+          }
+          rowArray.reverse();
+          for (_m = 0, _len4 = rowArray.length; _m < _len4; _m++) {
+            row = rowArray[_m];
+            tBody.appendChild(row);
+          }
+        }
+        if (typeof window['CustomEvent'] === 'function') {
+          return typeof table.dispatchEvent === "function" ? table.dispatchEvent(new CustomEvent('Sortable.sorted', {
+            bubbles: true
+          })) : void 0;
+        }
+      };
+      _results = [];
+      for (_i = 0, _len = clickEvents.length; _i < _len; _i++) {
+        eventName = clickEvents[_i];
+        _results.push(addEventListener(th, eventName, onClick));
+      }
+      return _results;
+    },
+    getColumnType: function(table, i) {
+      var row, specified, text, type, _i, _j, _len, _len1, _ref, _ref1, _ref2;
+      specified = (_ref = table.querySelectorAll('th')[i]) != null ? _ref.getAttribute('data-sortable-type') : void 0;
+      if (specified != null) {
+        return sortable.typesObject[specified];
+      }
+      _ref1 = table.tBodies[0].rows;
+      for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
+        row = _ref1[_i];
+        text = sortable.getNodeValue(row.cells[i]);
+        _ref2 = sortable.types;
+        for (_j = 0, _len1 = _ref2.length; _j < _len1; _j++) {
+          type = _ref2[_j];
+          if (type.match(text)) {
+            return type;
+          }
+        }
+      }
+      return sortable.typesObject.alpha;
+    },
+    getNodeValue: function(node) {
+      var dataValue;
+      if (!node) {
+        return '';
+      }
+      dataValue = node.getAttribute('data-value');
+      if (dataValue !== null) {
+        return dataValue;
+      }
+      if (typeof node.innerText !== 'undefined') {
+        return node.innerText.replace(trimRegExp, '');
+      }
+      return node.textContent.replace(trimRegExp, '');
+    },
+    setupTypes: function(types) {
+      var type, _i, _len, _results;
+      sortable.types = types;
+      sortable.typesObject = {};
+      _results = [];
+      for (_i = 0, _len = types.length; _i < _len; _i++) {
+        type = types[_i];
+        _results.push(sortable.typesObject[type.name] = type);
+      }
+      return _results;
+    }
+  };
+
+  sortable.setupTypes([
+    {
+      name: 'numeric',
+      defaultSortDirection: 'descending',
+      match: function(a) {
+        return a.match(numberRegExp);
+      },
+      comparator: function(a) {
+        return parseFloat(a.replace(/[^0-9.-]/g, ''), 10) || 0;
+      }
+    }, {
+      name: 'date',
+      defaultSortDirection: 'ascending',
+      reverse: true,
+      match: function(a) {
+        return !isNaN(Date.parse(a));
+      },
+      comparator: function(a) {
+        return Date.parse(a) || 0;
+      }
+    }, {
+      name: 'alpha',
+      defaultSortDirection: 'ascending',
+      match: function() {
+        return true;
+      },
+      compare: function(a, b) {
+        return a.localeCompare(b);
+      }
+    }
+  ]);
+
+  setTimeout(sortable.init, 0);
+
+  if (typeof define === 'function' && define.amd) {
+    define(function() {
+      return sortable;
+    });
+  } else if (typeof exports !== 'undefined') {
+    module.exports = sortable;
+  } else {
+    window.Sortable = sortable;
+  }
+
+}).call(this);
diff --git a/data/web/js/u2f-api.js b/data/web/js/u2f-api.js
new file mode 100644
index 00000000..0f06f50d
--- /dev/null
+++ b/data/web/js/u2f-api.js
@@ -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);
+    });
+};
\ No newline at end of file
diff --git a/data/web/js/user.js b/data/web/js/user.js
new file mode 100644
index 00000000..8d9450be
--- /dev/null
+++ b/data/web/js/user.js
@@ -0,0 +1,33 @@
+$(document).ready(function() {
+	// Show and activate password fields after box was checked
+	// Hidden by default
+	if ( !$("#togglePwNew").is(':checked') ) {
+		$(".passFields").hide();
+	}
+	$('#togglePwNew').click(function() {
+		$("#user_new_pass").attr("disabled", !this.checked);
+		$("#user_new_pass2").attr("disabled", !this.checked);
+		var $this = $(this);
+		if ($this.is(':checked')) {
+			$(".passFields").slideDown();
+		} else {
+			$(".passFields").slideUp();
+		}
+	});
+	// Show generate button after time selection
+	$('#generate_tla').hide(); 
+	$('#validity').change(function(){
+		$('#generate_tla').show(); 
+	});
+
+	// Init Bootstrap Switch
+	$.fn.bootstrapSwitch.defaults.onColor = 'success';
+	$("[name='tls_out']").bootstrapSwitch();
+	$("[name='tls_in']").bootstrapSwitch();
+
+  // Log modal
+  $('#logModal').on('show.bs.modal', function(e) {
+  var logText = $(e.relatedTarget).data('log-text');
+  $(e.currentTarget).find('#logText').html('<pre style="background:none;font-size:11px;line-height:1.1;border:0px">' + logText + '</pre>');
+  });
+});
\ No newline at end of file
diff --git a/data/web/json_api.php b/data/web/json_api.php
new file mode 100644
index 00000000..d404b0bd
--- /dev/null
+++ b/data/web/json_api.php
@@ -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;
+    }
+  }
+}
\ No newline at end of file
diff --git a/data/web/lang/lang.de.php b/data/web/lang/lang.de.php
new file mode 100644
index 00000000..f63f1557
--- /dev/null
+++ b/data/web/lang/lang.de.php
@@ -0,0 +1,448 @@
+<?php
+/*
+//
+//  German language file
+//
+*/
+$lang['footer']['loading'] = 'Einen Moment bitte...';
+$lang['header']['restart_sogo'] = 'SOGo neustarten';
+$lang['footer']['restart_sogo'] = 'SOGo neustarten';
+$lang['footer']['restart_now'] = 'Jetzt neustarten';
+$lang['footer']['restart_sogo_info'] = 'Einige Änderungen an Domains benötigen einen Neustart SOGos. Hier können Sie SOGo neustarten.<br /><br /><b>Wichtig:</b> Ein korrekter Neustart SOGos kann eine Weile in Anspruch nehmen, bitte warten Sie, bis der Prozess vollständig beendet wurde.';
+$lang['dkim']['confirm'] = 'Sind Sie sicher?';
+$lang['danger']['dkim_not_found'] = 'DKIM-Key nicht gefunden';
+$lang['danger']['dkim_remove_failed'] = 'Kann DKIM-Key nicht entfernen';
+$lang['danger']['dkim_add_failed'] = 'Kann DKIM-Key nicht hinzufügen';
+$lang['danger']['dkim_domain_or_sel_invalid'] = 'DKIM-Domain oder -Selector nicht korrekt';
+$lang['danger']['dkim_key_length_invalid'] = 'DKIM Schlüssellänge ungültig';
+$lang['success']['dkim_removed'] = 'DKIM-Key wurde entfernt';
+$lang['success']['dkim_added'] = 'DKIM-Key wurde hinzugefügt';
+$lang['danger']['access_denied'] = 'Zugriff verweigert oder unvollständige/ungültige Daten';
+$lang['danger']['whitelist_from_invalid'] = 'Whitelist-Eintrag ist ungültig';
+$lang['danger']['domain_invalid'] = 'Domainname ist ungültig';
+$lang['danger']['mailbox_quota_exceeds_domain_quota'] = 'Maximale Größe für Mailboxen überschreitet das Domain Speicherlimit';
+$lang['danger']['object_is_not_numeric'] = 'Wert %s ist nicht numerisch';
+$lang['success']['domain_added'] = 'Domain %s wurde angelegt';
+$lang['danger']['alias_empty'] = 'Alias-Adresse darf nicht leer sein';
+$lang['danger']['goto_empty'] = 'Ziel-Adresse darf nicht leer sein';
+$lang['danger']['policy_list_from_exists'] = 'Ein Eintrag mit diesem Wert existiert bereits';
+$lang['danger']['policy_list_from_invalid'] = 'Eintrag hat ungültiges Format';
+$lang['danger']['alias_invalid'] = 'Alias-Adrese ist ungültig';
+$lang['danger']['goto_invalid'] = 'Ziel-Adrese ist ungültig';
+$lang['danger']['last_key'] = 'Letzter Key kann nicht gelöscht werden';
+$lang['danger']['alias_domain_invalid'] = 'Alias-Domain ist ungültig';
+$lang['danger']['target_domain_invalid'] = 'Ziel-Domain ist ungültig';
+$lang['danger']['object_exists'] = 'Objekt %s existiert bereits';
+$lang['danger']['domain_exists'] = 'Domain %s existiert bereits';
+$lang['danger']['alias_goto_identical'] = 'Alias- und Ziel-Adresse dürfen nicht identisch sein';
+$lang['danger']['aliasd_targetd_identical'] = 'Alias-Domain darf nicht gleich Ziel-Domain sein';
+$lang['success']['alias_added'] = 'Alias-Adresse(n) wurden angelegt';
+$lang['success']['alias_modified'] = 'Änderungen an Alias %s wurden gespeichert';
+$lang['success']['aliasd_modified'] = 'Änderungen an Alias-Domain %s wurden gespeichert';
+$lang['success']['mailbox_modified'] = 'Änderungen an Mailbox %s wurden gespeichert';
+$lang['success']['resource_modified'] = "Änderungen an Ressource %s wurden gespeichert";
+$lang['success']['object_modified'] = "Änderungen an Objekt %s wurden gespeichert";
+$lang['success']['msg_size_saved'] = 'Limit wurde gesetzt';
+$lang['danger']['aliasd_not_found'] = 'Alias-Domain nicht gefunden';
+$lang['danger']['targetd_not_found'] = 'Ziel-Domain nicht gefunden';
+$lang['danger']['aliasd_exists'] = 'Alias-Domain existiert bereits';
+$lang['success']['aliasd_added'] = 'Alias-Domain %s wurde angelegt';
+$lang['success']['aliasd_modified'] = 'Änderungen an Alias-Domain %s wurden gespeichert';
+$lang['success']['domain_modified'] = 'Änderungen an Domain %s wurden gespeichert';
+$lang['success']['domain_admin_modified'] = 'Änderungen an Domain-Administrator %s wurden gespeichert';
+$lang['success']['domain_admin_added'] = 'Domain-Administrator %s wurde angelegt';
+$lang['success']['changes_general'] = 'Änderungen wurden gespeichert';
+$lang['success']['admin_modified'] = 'Änderungen am Administrator wurden gespeichert';
+$lang['danger']['exit_code_not_null'] = 'Fehler: Exit-Code ist %d';
+$lang['danger']['mailbox_not_available'] = 'Mailbox nicht verfügbar';
+$lang['danger']['username_invalid'] = 'Benutzername kann nicht verwendet werden';
+$lang['danger']['password_mismatch'] = 'Passwort-Wiederholung stimmt nicht überein';
+$lang['danger']['password_complexity'] = 'Passwort entspricht nicht den Vorgaben (Klein- und Großschreibung und mindestens eine Ziffer, mindestens 6 Zeichen lang)';
+$lang['danger']['password_empty'] = 'Passwort darf nicht leer sein';
+$lang['danger']['login_failed'] = 'Anmeldung fehlgeschlagen';
+$lang['danger']['mailbox_invalid'] = 'Mailboxname ist ungültig';
+$lang['danger']['resource_invalid'] = 'Ressourcenname ist ungültig';
+$lang['danger']['description_invalid'] = 'Ressourcenbeschreibung ist ungültig';
+$lang['danger']['mailbox_invalid_suggest'] = 'Mailboxname ist ungültig, meinten Sie vielleicht "%s"?';
+$lang['danger']['is_alias'] = '%s lautet bereits eine Alias-Adresse';
+$lang['danger']['is_alias_or_mailbox'] = "Eine Mailbox oder ein Alias mit der Adresse %s ist bereits vorhanden";
+$lang['danger']['is_spam_alias'] = '%s lautet bereits eine Spam-Alias-Adresse';
+$lang['danger']['quota_not_0_not_numeric'] = 'Speicherplatz muss numerisch und >= 0 sein';
+$lang['danger']['domain_not_found'] = 'Domain "%s" nicht gefunden.';
+$lang['danger']['max_mailbox_exceeded'] = 'Anzahl an Mailboxen überschritten (%d von %d)';
+$lang['danger']['mailbox_quota_exceeded'] = 'Speicherplatz überschreitet das Limit (max. %d MiB)';
+$lang['danger']['mailbox_quota_left_exceeded'] = 'Nicht genügend Speicherplatz vorhanden (Speicherplatz anwendbar: %d MiB)';
+$lang['success']['mailbox_added'] = 'Mailbox %s wurde angelegt';
+$lang['success']['resource_added'] = 'Ressource %s wurde angelegt';
+$lang['success']['domain_removed'] = 'Domain %s wurde entfernt';
+$lang['success']['alias_removed'] = 'Alias-Adresse %s wurde entfernt';
+$lang['success']['alias_domain_removed'] = 'Alias-Domain %s wurde entfernt';
+$lang['success']['domain_admin_removed'] = 'Domain-Administrator %s wurde entfernt';
+$lang['success']['mailbox_removed'] = 'Mailbox %s wurde entfernt';
+$lang['success']['eas_reset'] = "ActiveSync Gerät des Benutzers %s wurden zurückgesetzt";
+$lang['success']['resource_removed'] = 'Ressource %s wurde entfernt';
+$lang['danger']['max_quota_in_use'] = 'Mailbox Speicherplatzlimit muss größer oder gleich %d MiB sein';
+$lang['danger']['domain_quota_m_in_use'] = 'Domain Speicherplatzlimit muss größer oder gleich %d MiB sein';
+$lang['danger']['mailboxes_in_use'] = 'Maximale Anzahl an Mailboxen muss größer oder gleich %d sein';
+$lang['danger']['aliases_in_use'] = 'Maximale Anzahl an Aliassen muss größer oder gleich %d sein';
+$lang['danger']['sender_acl_invalid'] = 'Sender ACL Wert muss eine Adresse oder Domain sein';
+$lang['danger']['domain_not_empty'] = 'Kann nur leere Domains entfernen';
+$lang['warning']['spam_alias_temp_error'] = 'Kann zur Zeit keinen Spam-Alias erstellen, bitte versuchen Sie es später noch einmal.';
+$lang['danger']['spam_alias_max_exceeded'] = 'Maximale Anzahl an Spam-Alias-Adressen erreicht';
+$lang['danger']['validity_missing'] = 'Bitte geben Sie eine Gültigkeitsdauer an';
+$lang['user']['on'] = 'Ein';
+$lang['user']['off'] = 'Aus';
+$lang['user']['user_change_fn'] = '';
+$lang['user']['user_settings'] = 'Benutzereinstellungen';
+$lang['user']['mailbox_settings'] = 'Mailbox-Einstellungen';
+$lang['user']['mailbox_details'] = 'Mailbox-Details';
+$lang['user']['change_password'] = 'Passwort ändern';
+$lang['user']['new_password'] = 'Neues Passwort';
+$lang['user']['save_changes'] = 'Änderungen speichern';
+$lang['user']['password_now'] = 'Aktuelles Passwort (Änderungen bestätigen)';
+$lang['user']['new_password_repeat'] = 'Neues Passwort (Wiederholung)';
+$lang['user']['new_password_description'] = 'Mindestanforderung: 6 Zeichen lang, Buchstaben und Zahlen.';
+$lang['user']['did_you_know'] = '<b>Wussten Sie schon?</b> Sie können Ihre E-Mail-Adresse mit Tags versehen, etwa "ich+<b>Privat</b>@example.com", um Nachrichten automatisch in einem Unterordner (Beispiel: "Privat") abzulegen.';
+$lang['user']['spam_aliases'] = 'Temporäre E-Mail Aliasse';
+$lang['user']['alias'] = 'Alias';
+$lang['user']['aliases'] = 'Aliasse';
+$lang['user']['domain_aliases'] = 'Domain-Alias Adressen';
+$lang['user']['is_catch_all'] = 'Ist Catch-All Adresse für Domain(s)';
+$lang['user']['aliases_also_send_as'] = 'Darf außerdem versenden als';
+$lang['user']['aliases_send_as_all'] = 'Absender für folgende Domains nicht prüfen';
+$lang['user']['alias_create_random'] = 'Zufälligen Alias generieren';
+$lang['user']['alias_extend_all'] = 'Gültigkeit +1h';
+$lang['user']['alias_valid_until'] = 'Gültig bis';
+$lang['user']['alias_remove_all'] = 'Alle entfernen';
+$lang['user']['alias_time_left'] = 'Zeit verbleibend';
+$lang['user']['alias_full_date'] = 'd.m.Y, H:i:s T';
+$lang['user']['syncjob_full_date'] = 'd.m.Y, H:i:s T';
+$lang['user']['alias_select_validity'] = 'Bitte Gültigkeit auswählen';
+$lang['user']['sync_jobs'] = 'Sync Jobs';
+$lang['user']['hour'] = 'Stunde';
+$lang['user']['hours'] = 'Stunden';
+$lang['user']['day'] = 'Tag';
+$lang['user']['week'] = 'Woche';
+$lang['user']['weeks'] = 'Wochen';
+$lang['user']['spamfilter'] = 'Spamfilter';
+$lang['user']['spamfilter_wl'] = 'Whitelist';
+$lang['user']['spamfilter_wl_desc'] = 'Für E-Mail-Adressen, die vom Spamfilter <b>nicht</b> erfasst werden sollen. Die Verwendung von Wildcards ist gestattet.';
+$lang['user']['spamfilter_bl'] = 'Blacklist';
+$lang['user']['spamfilter_bl_desc'] = 'Für E-Mail-Adressen, die vom Spamfilter <b>immer</b> als Spam erfasst und abgelehnt werden. Die Verwendung von Wildcards ist gestattet.';
+$lang['user']['spamfilter_table_rule'] = 'Regel';
+$lang['user']['spamfilter_table_action'] = 'Aktion';
+$lang['user']['spamfilter_table_empty'] = 'Keine Einträge vorhanden';
+$lang['user']['spamfilter_table_remove'] = 'entfernen';
+$lang['user']['spamfilter_table_add'] = 'Eintrag hinzufügen';
+$lang['user']['spamfilter_behavior'] = 'Bewertung';
+$lang['user']['spamfilter_default_score'] = 'Spam-Score:';
+$lang['user']['spamfilter_green'] = 'Grün: Die Nachricht ist kein Spam';
+$lang['user']['spamfilter_yellow'] = 'Gelb: Die Nachricht ist vielleicht Spam, wird als Spam markiert und in den Junk-Ordner verschoben';
+$lang['user']['spamfilter_red'] = 'Rot: Die Nachricht ist eindeutig Spam und wird vom Server abgelehnt';
+$lang['user']['spamfilter_default_score'] = 'Standardwert:';
+$lang['user']['spamfilter_hint'] = 'Der erste Wert beschreibt den "low spam score", der zweite Wert den "high spam score".';
+$lang['user']['spamfilter_table_domain_policy'] = "n.v. (Domainrichtlinie)";
+
+$lang['user']['tls_policy_warning'] = '<strong>Vorsicht:</strong> Entscheiden Sie sich unverschlüsselte Verbindungen abzulehnen, kann dies dazu führen, dass Kontakte Sie nicht mehr erreichen.<br />Nachrichten, die die Richtlinie nicht erfüllen, werden durch einen Hard-Fail im Mailsystem abgewiesen.';
+$lang['user']['tls_policy'] = 'Verschlüsselungsrichtlinie';
+$lang['user']['tls_enforce_in'] = 'TLS eingehend erzwingen';
+$lang['user']['tls_enforce_out'] = 'TLS ausgehend erzwingen';
+$lang['user']['no_record'] = 'Kein Eintrag';
+
+$lang['user']['misc_settings'] = 'Sonstige Kontoeinstellungen';
+$lang['user']['misc_delete_profile'] = 'Sonstige Kontoeinstellungen';
+
+$lang['user']['tag_handling'] = 'Umgang mit getaggten E-Mails steuern';
+$lang['user']['tag_in_subfolder'] = 'In Unterordner';
+$lang['user']['tag_in_subject'] = 'In Betreff';
+$lang['user']['tag_help_explain'] = 'Als Unterordner: Es wird ein Ordner mit dem Namen des Tags unterhalb der Inbox erstellt ("INBOX/Facebook").<br />
+In Betreff: Der Name des Tags wird dem Betreff angefügt, etwa "[Facebook] Meine Neuigkeiten".';
+$lang['user']['tag_help_example'] = 'Beispiel für eine getaggte E-Mail-Adresse: ich<b>+Facebook</b>@example.org';
+$lang['user']['eas_reset'] = 'ActiveSync Geräte-Cache zurücksetzen';
+$lang['user']['eas_reset_now'] = 'Jetzt zurücksetzen';
+$lang['user']['eas_reset_help'] = 'In vielen Fällen kann ein ActiveSync Profil durch das Zurücksetzen des Caches repariert werden.<br /><b>Vorsicht:</b> Alle Elemente werden erneut heruntergeladen!';
+
+$lang['user']['encryption'] = 'Verschlüsselung';
+$lang['user']['username'] = 'Benutzername';
+$lang['user']['password'] = 'Password';
+$lang['user']['last_run'] = 'Letzte Ausführung';
+$lang['user']['excludes'] = 'Ausschlüsse';
+$lang['user']['interval'] = 'Intervall';
+$lang['user']['active'] = 'Aktiv';
+$lang['user']['action'] = 'Aktion';
+$lang['user']['edit'] = 'Bearbeiten';
+$lang['user']['remove'] = 'Entfernen';
+$lang['user']['delete_now'] = 'Sofort löschen';
+$lang['user']['create_syncjob'] = 'Neuen Sync-Job erstellen';
+
+$lang['start']['dashboard'] = '%s - Dashboard';
+$lang['start']['start_rc'] = 'Roundcube öffnen';
+$lang['start']['start_sogo'] = 'SOGo öffnen';
+$lang['start']['mailcow_apps_detail'] = 'Verwenden Sie mailcow Apps, um E-Mails abzurufen, Kalender- und Kontakte zu verwalten und vieles mehr.';
+$lang['start']['mailcow_panel'] = 'mailcow UI starten';
+$lang['start']['mailcow_panel_description'] = 'Die mailcow Steuerung steht sowohl für Administratoren als auch Mailbox-Benutzer zur Verfügung.';
+$lang['start']['mailcow_panel_detail'] = '<b>Domain-Administratoren</b> erstellen, verändern oder löschen Mailboxen, verwalten die Domäne und sehen sonstige Einstellungen ein.<br />
+	Als <b>Mailbox-Benutzer</b> erstellen Sie hier zeitlich limitierte Aliasse, ändern das Verhalten des Spamfilters, setzen ein neues Passwort und vieles mehr.';
+$lang['start']['recommended_config'] = 'Empfohlene Software-Konfiguration (ohne ActiveSync)';
+$lang['start']['imap_smtp_server'] = 'IMAP- und SMTP-Server';
+$lang['start']['imap_smtp_server_description'] = 'Für eine optimale Verbindung empfehlen wir die Verwendung des <a href="%s" target="_blank"><b>Mozilla Thunderbirds</b></a>.';
+$lang['start']['imap_smtp_server_badge'] = 'E-Mail lesen und schreiben';
+$lang['start']['imap_smtp_server_auth_info'] = 'Bitte verwenden Sie Ihre vollständige E-Mail-Adresse sowie das PLAIN-Authentifizierungsverfahren.<br />
+Ihre Anmeldedaten werden durch die obligatorische Verschlüsselung entgegen des Begriffes "PLAIN" nicht unverschlüsselt übertragen.';
+$lang['start']['managesieve'] = 'ManageSieve';
+$lang['start']['managesieve_badge'] = 'E-Mail-Filter';
+$lang['start']['managesieve_description'] = 'Bitte verwenden Sie <b>Mozilla Thunderbirds</b> zusammen mit der <a style="text-decoration:none" target="_blank" href="%s"><b>Sieve Erweiterung</b></a>.<br />Nach dem Herunterladen der Erweiterung starten Sie Thunderbird, öffnen das Fenster für Erweiterungen und ziehen die heruntergeladene Datei in das offene Fenster.<br />Der Servername lautet <b>%s</b>, als Port konfigurieren Sie bitte <b>4190</b>. Die Anmeldedaten entsprechen dem E-Mail Login.';
+$lang['start']['service'] = 'Dienstname';
+$lang['start']['encryption'] = 'Verschlüsselungstyp';
+$lang['start']['help'] = 'Hilfe ein-/ausblenden';
+$lang['start']['hostname'] = 'Hostname';
+$lang['start']['port'] = 'Port';
+$lang['start']['footer'] = '';
+$lang['header']['mailcow_settings'] = 'Konfiguration';
+$lang['header']['administration'] = 'Administration';
+$lang['header']['mailboxes'] = 'Mailboxen';
+$lang['header']['user_settings'] = 'Benutzereinstellungen';
+$lang['header']['login'] = 'Anmeldung';
+$lang['header']['logged_in_as_logout'] = 'Eingeloggt als <b>%s</b> (abmelden)';
+$lang['header']['logged_in_as_logout_dual'] = 'Eingeloggt als <b>%s <span class="text-info">[%s]</span></b>';
+$lang['header']['locale'] = 'Sprache';
+$lang['mailbox']['domain'] = 'Domain';
+$lang['mailbox']['alias'] = 'Alias';
+$lang['mailbox']['aliases'] = 'Aliasse';
+$lang['mailbox']['multiple_bookings'] = 'Mehrfachbuchen';
+$lang['mailbox']['kind'] = 'Art';
+$lang['mailbox']['description'] = 'Beschreibung';
+$lang['mailbox']['resources'] = 'Ressourcen';
+$lang['mailbox']['resource_name'] = 'Ressourcenname';
+$lang['mailbox']['domains'] = 'Domains';
+$lang['mailbox']['mailboxes'] = 'Mailboxen';
+$lang['mailbox']['mailbox_quota'] = 'Max. Größe einer Mailbox';
+$lang['mailbox']['domain_quota'] = 'Gesamtspeicher';
+$lang['mailbox']['ratelimit'] = 'Limit ausgehend/Stunde';
+$lang['mailbox']['active'] = 'Aktiv';
+$lang['mailbox']['action'] = 'Aktion';
+$lang['mailbox']['backup_mx'] = 'Backup MX';
+$lang['mailbox']['domain_aliases'] = 'Domain-Aliasse';
+$lang['mailbox']['target_domain'] = 'Ziel-Domain';
+$lang['mailbox']['target_address'] = 'Ziel-Adresse';
+$lang['mailbox']['username'] = 'Benutzername';
+$lang['mailbox']['fname'] = 'Name';
+$lang['mailbox']['filter_table'] = 'Tabelle filtern';
+$lang['mailbox']['yes'] = '&#10004;';
+$lang['mailbox']['no'] = '&#10008;';
+$lang['mailbox']['quota'] = 'Speicherplatz';
+$lang['mailbox']['in_use'] = 'Prozentualer Gebrauch';
+$lang['mailbox']['msg_num'] = 'Anzahl Nachrichten';
+$lang['mailbox']['remove'] = 'Entfernen';
+$lang['mailbox']['edit'] = 'Bearbeiten';
+$lang['mailbox']['archive'] = 'Archiv-Zugriff';
+$lang['mailbox']['no_record'] = 'Kein Eintrag für Objekt %s';
+$lang['mailbox']['no_record_single'] = 'Kein Eintrag';
+$lang['mailbox']['add_domain'] = 'Domain hinzufügen';
+$lang['mailbox']['add_domain_alias'] = 'Domain-Alias hinzufügen';
+$lang['mailbox']['add_mailbox'] = 'Mailbox hinzufügen';
+$lang['mailbox']['add_resource'] = 'Ressource hinzufügen';
+$lang['mailbox']['add_alias'] = 'Alias hinzufügen';
+
+$lang['info']['no_action'] = 'Keine Aktion anwendbar';
+
+$lang['delete']['title'] = 'Objekt entfernen';
+$lang['delete']['remove_domain_warning'] = '<b>Warnung:</b> Sie entfernen die 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_alias_warning'] = '<b>Warnung:</b> Sie entfernen die Alias-Adresse <b>%s</b>!';
+$lang['delete']['remove_syncjob_warning'] = '<b>Warnung:</b> Sie entfernen einen Sync-Job des Benutzers <b>%s</b>!';
+$lang['delete']['remove_mailbox_warning'] = '<b>Warnung:</b> Sie entfernen die Mailbox <b>%s</b>!';
+$lang['delete']['remove_mailbox_details'] = 'Die Mailbox wird <b>vollständig und permanent</b> entfernt!';
+$lang['delete']['remove_resource_warning'] = '<b>Warnung:</b> Sie entfernen die Ressource <b>%s</b>!';
+$lang['delete']['remove_resource_details'] = 'Die Ressource wird <b>vollständig und permanent</b> entfernt!';
+$lang['delete']['remove_domain_details'] = 'Diese Aktion entfernt ebenfalls Domain-Aliasse.<br /><br /><b>Eine Domain muss leer sein, um entfernt zu werden.</b>';
+$lang['delete']['remove_syncjob_details'] = 'Objekte dieses Sync-Jobs werden nicht mehr vom entfernten Server abgeholt.';
+$lang['delete']['remove_alias_details'] = 'Benutzer werden keine Nachrichten mehr von dieser Adresse erhalten und versenden koennen!</b>';
+$lang['delete']['remove_button'] = 'Entfernen';
+$lang['delete']['previous'] = 'Vorherige Seite';
+
+$lang['edit']['syncjob'] = 'Sync-Job bearbeiten';
+$lang['edit']['save'] = 'Änderungen speichern';
+$lang['edit']['username'] = 'Benutzername';
+$lang['edit']['hostname'] = 'Servername';
+$lang['edit']['encryption'] = 'Verschlüsselungsmethode';
+$lang['edit']['maxage'] = 'Maximales Alter in Tagen einer Nachricht, die kopiert werden soll</br ><small>(0 = alle Nachrichten kopieren)</small>';
+$lang['edit']['subfolder2'] = 'Ziel-Ordner<br /><small>(leer = kein Unterordner)</small>';
+$lang['edit']['mins_interval'] = 'Intervall (min)';
+$lang['edit']['exclude'] = 'Elemente ausschließen (Regex)';
+$lang['edit']['archive'] = 'Archiv-Zugriff';
+$lang['edit']['max_mailboxes'] = 'Max. Mailboxanzahl:';
+$lang['edit']['title'] = 'Objekt bearbeiten';
+$lang['edit']['target_address'] = 'Ziel-Adresse(n) <small>(getrennt durch Komma)</small>:';
+$lang['edit']['active'] = 'Aktiv';
+$lang['edit']['target_domain'] = 'Ziel-Domain:';
+$lang['edit']['password'] = 'Passwort:';
+$lang['edit']['ratelimit'] = 'Limit ausgehender Nachrichten/Stunde:';
+$lang['danger']['ratelimt_less_one'] = 'Limit ausgehender Nachrichten/Stunde darf nicht kleiner als 1 sein';
+$lang['edit']['password_repeat'] = 'Passwort (Wiederholung):';
+$lang['edit']['domain_admin'] = 'Domain-Administrator bearbeiten';
+$lang['edit']['domain'] = 'Domain bearbeiten';
+$lang['edit']['edit_alias_domain'] = 'Alias-Domain bearbeiten';
+$lang['edit']['alias_domain'] = 'Alias-Domain';
+$lang['edit']['domains'] = 'Domains';
+$lang['edit']['destroy'] = 'Manuelle Eingabe des Wertes';
+$lang['edit']['alias'] = 'Alias bearbeiten';
+$lang['edit']['mailbox'] = 'Mailbox bearbeiten';
+$lang['edit']['description'] = 'Beschreibung:';
+$lang['edit']['max_aliases'] = 'Max. Aliasse:';
+$lang['edit']['max_quota'] = 'Max. Größe per Mailbox (MiB):';
+$lang['edit']['domain_quota'] = 'Domain Speicherplatz gesamt (MiB):';
+$lang['edit']['backup_mx_options'] = 'Backup MX Optionen:';
+$lang['edit']['relay_domain'] = 'Relay Domain';
+$lang['edit']['relay_all'] = 'Alle Empfänger-Adressen relayen';
+$lang['edit']['dkim_signature'] = 'DKIM-Signatur:';
+$lang['edit']['dkim_record_info'] = '<small>Bitte hinterlegen Sie einen TXT-Record mit obigem Wert in den DNS-Einstellungen Ihrer Domainverwaltung.</small>';
+$lang['edit']['relay_all_info'] = '<small>Wenn Sie <b>nicht</b> alle Empfänger-Adressen relayen möchten, müssen Sie eine ("blinde") Mailbox für jede Adresse, die relayt werden soll, erstellen.</small>';
+$lang['edit']['full_name'] = 'Voller Name';
+$lang['edit']['quota_mb'] = 'Speicherplatz (MiB)';
+$lang['edit']['sender_acl'] = 'Darf Nachrichten versenden als';
+$lang['edit']['sender_acl_info'] = 'Aliasse sind nicht abwählbar und vorausgewählt.';
+$lang['edit']['dkim_txt_name'] = 'TXT-Record Name:';
+$lang['edit']['dkim_txt_value'] = 'TXT-Record Wert:';
+$lang['edit']['previous'] = 'Vorherige Seite';
+$lang['edit']['unchanged_if_empty'] = 'Unverändert, wenn leer';
+$lang['edit']['dont_check_sender_acl'] = 'Absender für Domain %s nicht prüfen';
+$lang['edit']['multiple_bookings'] = 'Mehrfaches Buchen';
+$lang['edit']['kind'] = 'Art';
+$lang['edit']['resource'] = 'Ressource';
+
+$lang['add']['syncjob'] = 'Sync-Job erstellen';
+$lang['add']['syncjob_hint'] = 'Passwörter werden unverschlüsselt abgelegt!';
+$lang['add']['hostname'] = 'Servername';
+$lang['add']['username'] = 'Benutzername';
+$lang['add']['enc_method'] = 'Verschlüsselungsmethode';
+$lang['add']['maxage'] = 'Maximum age of messages that will be polled from remote (0 = ignore age)';
+$lang['add']['subfolder2'] = 'Sync into subfolder on destination';
+$lang['add']['mins_interval'] = 'Abrufintervall (Minuten)';
+$lang['add']['exclude'] = 'Elemente ausschließen (Regex)';
+$lang['add']['delete2duplicates'] = 'Lösche Duplikate im Ziel';
+
+$lang['add']['title'] = 'Objekt anlegen';
+$lang['add']['domain'] = 'Domain';
+$lang['add']['active'] = 'Aktiv';
+$lang['add']['multiple_bookings'] = 'Mehrfaches Buchen möglich';
+$lang['add']['save'] = 'Änderungen speichern';
+$lang['add']['description'] = 'Beschreibung:';
+$lang['add']['max_aliases'] = 'Max. mögliche Aliasse:';
+$lang['add']['max_mailboxes'] = 'Max. mögliche Mailboxen:';
+$lang['add']['mailbox_quota_m'] = 'Max. Speicherplatz pro Mailbox (MiB):';
+$lang['add']['domain_quota_m'] = 'Domain Speicherplatz gesamt (MiB):';
+$lang['add']['backup_mx_options'] = 'Backup MX Optionen:';
+$lang['add']['relay_all'] = 'Alle Empfänger-Adressen relayen';
+$lang['add']['relay_domain'] = 'Relay Domain';
+$lang['add']['relay_all_info'] = '<small>Wenn Sie <b>nicht</b> alle Empfänger-Adressen relayen möchten, müssen Sie eine Mailbox für jede Adresse, die relayt werden soll, erstellen.</small>';
+$lang['add']['alias'] = 'Alias(se)';
+$lang['add']['alias_spf_fail'] = '<b>Hinweis:</b> Wählen Sie ein externes Postfach als Ziel-Adresse, kann es unter Umständen zu fehlerhaften Spam-Erkennungen <b>beim Empfänger</b> kommen. Weitere Informationen zu diesem Thema finden Sie <a href="https://www.heinlein-support.de/blog/news/gmx-de-und-web-de-haben-mail-rejects-durch-spf/" target="_blank">hier.</a>';
+$lang['add']['alias_address'] = 'Alias-Adresse(n):';
+$lang['add']['alias_address_info'] = '<small>Vollständige E-Mail-Adresse(n) oder @example.com, um alle Nachrichten einer Domain weiterzuleiten. Getrennt durch Komma. <b>Nur eigene Domains</b>.</small>';
+$lang['add']['alias_domain_info'] = '<small>Nur gültige Domains. Getrennt durch Komma.</small>';
+$lang['add']['target_address'] = 'Ziel-Adresse(n):';
+$lang['add']['target_address_info'] = '<small>Vollständige E-Mail-Adresse(n). Getrennt durch Komma.</small>';
+$lang['add']['alias_domain'] = 'Alias-Domain';
+$lang['add']['select'] = 'Bitte auswählen';
+$lang['add']['target_domain'] = 'Ziel-Domain:';
+$lang['add']['mailbox'] = 'Mailbox';
+$lang['add']['resource'] = 'Ressource';
+$lang['add']['kind'] = 'Art';
+$lang['add']['mailbox_username'] = 'Benutzername (linker Teil der E-Mail-Adresse):';
+$lang['add']['resource_name'] = 'Ressourcenname:';
+$lang['add']['full_name'] = 'Vor- und Zuname:';
+$lang['add']['quota_mb'] = 'Speicherplatz (MiB):';
+$lang['add']['select_domain'] = 'Bitte zuerst eine Domain auswählen';
+$lang['add']['password'] = 'Passwort:';
+$lang['add']['password_repeat'] = 'Passwort (Wiederholung):';
+$lang['add']['previous'] = 'Vorherige Seite';
+$lang['add']['restart_sogo_hint'] = 'Der SOGo Container muss nach dem Hinzufügen einer neuen Domain neugestartet werden!';
+
+$lang['login']['title'] = 'Anmeldung';
+$lang['login']['administration'] = 'Administration';
+$lang['login']['administration_details'] = 'Bitte verwenden Sie Ihre Administrator Anmeldedaten, um administrative Aufgaben wie das Anlegen einer Mailbox zu starten.';
+$lang['login']['user_settings'] = 'Benutzereinstellungen';
+$lang['login']['user_settings_details'] = 'Als E-Mail Benutzer vewenden Sie bitte Ihre E-Mail Anmeldedaten, um Passwörter zu verändern, temporäre (Spam-)Aliasse zu erstellen, den Spamfilter einzustellen oder auch um E-Mails zu importieren.';
+$lang['login']['username'] = 'Benutzername';
+$lang['login']['password'] = 'Passwort';
+$lang['login']['reset_password'] = 'Mein Passwort zurücksetzen';
+$lang['login']['login'] = 'Anmelden';
+$lang['login']['previous'] = 'Vorherige Seite';
+$lang['login']['delayed'] = 'Login wurde zur Sicherheit um %s Sekunde/n verzögert.';
+
+$lang['tfa']['tfa'] = "Two-Factor Authentication";
+$lang['tfa']['set_tfa'] = "Konfiguriere Two-Factor Authentication Methode";
+$lang['tfa']['yubi_otp'] = "Yubico OTP Authentifizierung";
+$lang['tfa']['key_id'] = "Ein Name für diesen YubiKey";
+$lang['tfa']['api_register'] = 'mailcow verwendet die Yubico Cloud API. Ein API-Key für den Yubico Stick kann <a href="https://upgrade.yubico.com/getapikey/" target="_blank">hier</a> bezogen werden.';
+$lang['tfa']['u2f'] = "U2F Authentifizierung";
+$lang['tfa']['hotp'] = "HOTP Authentifizierung";
+$lang['tfa']['totp'] = "TOTP Authentifizierung";
+$lang['tfa']['none'] = "Deaktiviert";
+$lang['tfa']['delete_tfa'] = "Deaktiviere TFA";
+$lang['tfa']['disable_tfa'] = "Deaktiviere TFA bis zur nächsten erfolgreichen Anmeldung";
+$lang['tfa']['confirm_tfa'] = "Please confirm your one-time password in the below field";
+$lang['tfa']['confirm'] = "Bestätigen";
+$lang['tfa']['otp'] = "Einmalpasswort";
+$lang['tfa']['trash_login'] = "Login verwerfen";
+$lang['tfa']['select'] = "Bitte auswählen";
+$lang['tfa']['waiting_usb_auth'] = "<i>Warte auf USB-Gerät...</i><br /><br />Bitte jetzt den vorgesehenen Taster des U2F USB-Gerätes berühren.";
+$lang['tfa']['waiting_usb_register'] = "<i>Warte auf USB-Gerät...</i><br /><br />Bitte zuerst das obere Passwortfeld ausfüllen und erst dann den vorgesehenen Taster des U2F USB-Gerätes berühren.";
+
+$lang['admin']['search_domain_da'] = 'Domains durchsuchen';
+$lang['admin']['restrictions'] = 'Postifx Restriktionen';
+$lang['admin']['rr'] = 'Postifx Recipient Restriktionen';
+$lang['admin']['sr'] = 'Postifx Sender Restriktionen';
+$lang['admin']['reset_defaults'] = 'Standard wiederherstellen';
+$lang['admin']['r_inactive'] = 'Inaktive Restriktionen';
+$lang['admin']['r_active'] = 'Aktive Restriktionen';
+$lang['admin']['r_info'] = 'Ausgegraute/deaktivierte Elemente sind mailcow nicht bekannt und können nicht in die Liste inaktiver Elemente verschoben werden. Unbekannte Restriktionen werden trotzdem in Reihenfolge der Erscheinung gesetzt.<br />Sie können ein Element in der Datei <code>inc/vars.local.inc.php</code> als bekannt hinzufügen, um es zu bewegen.';
+$lang['admin']['public_folders'] = 'Öffentliche Ordner';
+$lang['admin']['public_folders_text'] = 'Ein Namespace "Public" wird erstellt. Der untenstehende Ordnername betrifft den Namen der automatisch erstellten Mailbox in diesem Namespace.';
+$lang['admin']['public_folder_name'] = 'Ordnername <small>(alphanumerisch)</small>';
+$lang['admin']['public_folder_enable'] = 'Öffentliche Ordner aktivieren';
+$lang['admin']['public_folder_enable_text'] = 'Das Umschalten dieser Option entfernt keine Nachrichten aus den öffentlichen Ordnern.';
+$lang['admin']['public_folder_pusf'] = 'Aktiviere "per-user seen flag"';
+$lang['admin']['public_folder_pusf_text'] = 'Ein "per-user seen flag"-aktiviertes System markiert Nachrichten nicht als gelesen, wenn nur ein Benutzer sie gelesen hat. Jeder Benutzer verwaltet seine eigenen "seen flags".';
+$lang['admin']['privacy'] = 'Datenschutz';
+$lang['admin']['privacy_text'] = 'Diese Option aktiviert eine PCRE-Prüfung, die die Werte der Kopfzeilen "User-Agent", "X-Enigmail", "X-Mailer", "X-Originating-IP" sowie "Received: from" durch "localhost" bzw. "127.0.0.1" ersetzt.';
+$lang['admin']['privacy_anon_mail'] = 'Anonymisiere ausgehende Kopfzeilen';
+$lang['admin']['msg_size'] = 'Aktuelles Limit der Nachrichtengröße';
+$lang['admin']['msg_size_limit'] = 'Aktuelles Limit der Nachrichtengröße';
+$lang['admin']['msg_size_limit_details'] = 'Diese Einstellung wird Postfix und den Webserver neuladen.';
+$lang['admin']['save'] = 'Änderungen speichern';
+$lang['admin']['maintenance'] = 'Wartung und Information';
+$lang['admin']['sys_info'] = 'Systeminformation';
+$lang['admin']['dkim_add_key'] = 'DKIM-Key hinzufügen';
+$lang['admin']['dkim_keys'] = 'DKIM-Keys';
+$lang['admin']['dkim_key_valid'] = 'Key gültig';
+$lang['admin']['dkim_key_unused'] = 'Key ohne Zuweisung';
+$lang['admin']['dkim_key_missing'] = 'Key fehlt';
+$lang['admin']['dkim_key_hint'] = 'Der Selector für DKIM-Keys lautet immer <code>dkim</code>.';
+$lang['admin']['add'] = 'Hinzufügen';
+$lang['admin']['configuration'] = 'Konfiguration';
+$lang['admin']['password'] = 'Passwort';
+$lang['admin']['password_repeat'] = 'Passwort (Wiederholung)';
+$lang['admin']['active'] = 'Aktiv';
+$lang['admin']['action'] = 'Aktion';
+$lang['admin']['add_domain_admin'] = 'Domain-Administrator hinzufügen';
+$lang['admin']['admin_domains'] = 'Domain-Zuweisungen';
+$lang['admin']['domain_admins'] = 'Domain-Administratoren';
+$lang['admin']['username'] = 'Benutzername';
+$lang['admin']['edit'] = 'Bearbeiten';
+$lang['admin']['remove'] = 'Entfernen';
+$lang['admin']['save'] = 'Änderungen speichern';
+$lang['admin']['admin'] = 'Administrator';
+$lang['admin']['admin_details'] = 'Administrator bearbeiten';
+$lang['admin']['unchanged_if_empty'] = 'Unverändert, wenn leer';
+$lang['admin']['yes'] = '&#10004;';
+$lang['admin']['no'] = '&#10008;';
+$lang['admin']['access'] = 'Zugang';
+$lang['admin']['invalid_max_msg_size'] = 'Invalid max. message size'; // NEEDS TRANSLATION
+$lang['admin']['site_not_found'] = 'Kann mailcow Site-Konfiguration nicht finden';
+$lang['admin']['public_folder_empty'] = 'Public folder name must not be empty'; // NEEDS TRANSLATION
+$lang['admin']['set_rr_failed'] = 'Kann Postfix Restriktionen nicht setzen';
+$lang['admin']['no_record'] = 'Kein Eintrag';
+?>
diff --git a/data/web/lang/lang.en.php b/data/web/lang/lang.en.php
new file mode 100644
index 00000000..7abafe13
--- /dev/null
+++ b/data/web/lang/lang.en.php
@@ -0,0 +1,458 @@
+<?php
+/*
+//
+//  English language file
+//
+*/
+$lang['footer']['loading'] = "Please wait...";
+$lang['header']['restart_sogo'] = 'Restart SOGo';
+$lang['footer']['restart_sogo'] = 'Restart SOGo';
+$lang['footer']['restart_now'] = 'Restart now';
+$lang['footer']['restart_sogo_info'] = 'Some tasks, e.g. adding a domain, require you to restart SOGo to catch changes made in the mailcow UI.<br /><br /><b>Important:</b> A graceful restart may take a while to complete, please wait for it to finish.';
+$lang['dkim']['confirm'] = "Are you sure?";
+$lang['danger']['dkim_not_found'] = "DKIM key not found";
+$lang['danger']['dkim_remove_failed'] = "Cannot remove selected DKIM key";
+$lang['danger']['dkim_add_failed'] = "Cannot add given DKIM key";
+$lang['danger']['dkim_domain_or_sel_invalid'] = "DKIM domain or selector invalid";
+$lang['danger']['dkim_key_length_invalid'] = "DKIM key length invalid";
+$lang['success']['dkim_removed'] = "DKIM key has been removed";
+$lang['success']['dkim_added'] = "DKIM key has been saved";
+$lang['danger']['access_denied'] = "Access denied or invalid form data";
+$lang['danger']['whitelist_from_invalid'] = "Whitelist entry invalid";
+$lang['danger']['domain_invalid'] = "Domain name is invalid";
+$lang['danger']['mailbox_quota_exceeds_domain_quota'] = "Max. quota exceeds domain quota limit";
+$lang['danger']['object_is_not_numeric'] = "Value %s is not numeric";
+$lang['success']['domain_added'] = "Added domain %s";
+$lang['danger']['alias_empty'] = "Alias address must not be empty";
+$lang['danger']['last_key'] = 'Last key cannot be deleted';
+$lang['danger']['goto_empty'] = "Goto address must not be empty";
+$lang['danger']['policy_list_from_exists'] = "A record with given name exists";
+$lang['danger']['policy_list_from_invalid'] = "Record has invalid format";
+$lang['danger']['whitelist_exists'] = "A whitelist record with that name exists";
+$lang['danger']['whitelist_from_invalid'] = "Whitelist record has invalid format";
+$lang['danger']['alias_invalid'] = "Alias address is invalid";
+$lang['danger']['goto_invalid'] = "Goto address is invalid";
+$lang['danger']['alias_domain_invalid'] = "Alias domain is invalid";
+$lang['danger']['target_domain_invalid'] = "Goto domain is invalid";
+$lang['danger']['object_exists'] = "Object %s already exists";
+$lang['danger']['domain_exists'] = "Domain %s already exists";
+$lang['danger']['alias_goto_identical'] = "Alias and goto address must not be identical";
+$lang['danger']['aliasd_targetd_identical'] = "Alias domain must not be equal to target domain";
+$lang['success']['alias_added'] = "Alias address/es has/have been added";
+$lang['success']['alias_modified'] = "Changes to alias have been saved";
+$lang['success']['aliasd_modified'] = "Changes to alias domain have been saved";
+$lang['success']['mailbox_modified'] = "Changes to mailbox %s have been saved";
+$lang['success']['resource_modified'] = "Changes to mailbox %s have been saved";
+$lang['success']['object_modified'] = "Changes to object %s have been saved";
+$lang['success']['msg_size_saved'] = "Message size limit has been set";
+$lang['danger']['aliasd_not_found'] = "Alias domain not found";
+$lang['danger']['targetd_not_found'] = "Target domain not found";
+$lang['danger']['aliasd_exists'] = "Alias domain already exists";
+$lang['success']['aliasd_added'] = "Added alias domain %s";
+$lang['success']['aliasd_modified'] = "Changes to alias domain %s have been saved";
+$lang['success']['domain_modified'] = "Changes to domain %s have been saved";
+$lang['success']['domain_admin_modified'] = "Changes to domain administrator %s have been saved";
+$lang['success']['domain_admin_added'] = "Domain administrator %s has been added";
+$lang['success']['changes_general'] = 'Changes have been saved';
+$lang['success']['admin_modified'] = "Changes to administrator have been saved";
+$lang['danger']['exit_code_not_null'] = "Error: Exit code was %d";
+$lang['danger']['mailbox_not_available'] = "Mailbox not available";
+$lang['danger']['username_invalid'] = "Username cannot be used";
+$lang['danger']['password_mismatch'] = "Confirmation password is not identical";
+$lang['danger']['password_complexity'] = "Password does not meet requirements (upper and lowercase letters and at least one number, min. 6 characters long)";
+$lang['danger']['password_empty'] = "Password must not be empty";
+$lang['danger']['login_failed'] = "Login failed";
+$lang['danger']['mailbox_invalid'] = "Mailbox name is invalid";
+$lang['danger']['description_invalid'] = 'Resource description is invalid';
+$lang['danger']['resource_invalid'] = "Resource name is invalid";
+$lang['danger']['mailbox_invalid_suggest'] = 'Mailbox name is invalid, did you mean to type "%s"?';
+$lang['danger']['is_alias'] = "%s is already known as an alias address";
+$lang['danger']['is_alias_or_mailbox'] = "%s is already known as an alias or a mailbox";
+$lang['danger']['is_spam_alias'] = "%s is already known as a spam alias address";
+$lang['danger']['quota_not_0_not_numeric'] = "Quota must be numeric and >= 0";
+$lang['danger']['domain_not_found'] = "Domain not found.";
+$lang['danger']['max_mailbox_exceeded'] = "Max. mailboxes exceeded (%d of %d)";
+$lang['danger']['mailbox_quota_exceeded'] = "Quota exceeds the domain limit (max. %d MiB)";
+$lang['danger']['mailbox_quota_left_exceeded'] = "Not enough space left (space left: %d MiB)";
+$lang['success']['mailbox_added'] = "Mailbox %s has been added";
+$lang['success']['resource_added'] = "Resource %s has been added";
+$lang['success']['domain_removed'] = "Domain %s has been removed";
+$lang['success']['alias_removed'] = "Alias-Adresse %s has been removed";
+$lang['success']['alias_domain_removed'] = "Alias domain %s has been removed";
+$lang['success']['domain_admin_removed'] = "Domain administrator %s has been removed";
+$lang['success']['mailbox_removed'] = "Mailbox %s has been removed";
+$lang['success']['eas_reset'] = "ActiveSync devices for user %s were reset";
+$lang['success']['resource_removed'] = "Resource %s has been removed";
+$lang['danger']['max_quota_in_use'] = "Mailbox quota must be greater or equal to %d MiB";
+$lang['danger']['domain_quota_m_in_use'] = "Domain quota must be greater or equal to %s MiB";
+$lang['danger']['mailboxes_in_use'] = "Max. mailboxes must be greater or equal to %d";
+$lang['danger']['aliases_in_use'] = "Max. aliases must be greater or equal to %d";
+$lang['danger']['sender_acl_invalid'] = "Sender ACL value is invalid";
+$lang['danger']['domain_not_empty'] = "Cannot remove non-empty domain";
+$lang['warning']['spam_alias_temp_error'] = "Temporary error: Cannot add spam alias, please try again later.";
+$lang['danger']['spam_alias_max_exceeded'] = "Max. allowed spam alias addresses exceeded";
+$lang['danger']['validity_missing'] = 'Please assign a period of validity';
+$lang['user']['on'] = "On";
+$lang['user']['off'] = "Off";
+$lang['user']['user_change_fn'] = "";
+$lang['user']['user_settings'] = 'User settings';
+$lang['user']['mailbox_settings'] = 'Mailbox settings';
+$lang['user']['mailbox_details'] = 'Mailbox details';
+$lang['user']['change_password'] = 'Change password';
+$lang['user']['new_password'] = 'New password';
+$lang['user']['save_changes'] = 'Save changes';
+$lang['user']['password_now'] = 'Current password (confirm changes)';
+$lang['user']['new_password_repeat'] = 'Confirmation password (repeat)';
+$lang['user']['new_password_description'] = 'Requirement: 6 characters long, letters and numbers.';
+$lang['user']['did_you_know'] = '<b>Did you know?</b> You can use tags in your email address ("me+<b>privat</b>@example.com") to move messages to a folder automatically (example: "privat").';
+$lang['user']['spam_aliases'] = 'Temporary email aliases';
+$lang['user']['alias'] = 'Alias';
+$lang['user']['aliases'] = 'Aliases';
+$lang['user']['domain_aliases'] = 'Domain alias addresses';
+$lang['user']['is_catch_all'] = 'Catch-all for domain/s';
+$lang['user']['aliases_also_send_as'] = 'Also allowed to send as';
+$lang['user']['aliases_send_as_all'] = 'Do not check sender access for following domain/s';
+$lang['user']['alias_create_random'] = 'Generate random alias';
+$lang['user']['alias_extend_all'] = 'Extend aliases by 1 hour';
+$lang['user']['alias_valid_until'] = 'Valid until';
+$lang['user']['alias_remove_all'] = 'Remove all aliases';
+$lang['user']['alias_time_left'] = 'Time left';
+$lang['user']['alias_full_date'] = 'd.m.Y, H:i:s T';
+$lang['user']['syncjob_full_date'] = 'd.m.Y, H:i:s T';
+$lang['user']['alias_select_validity'] = 'Period of validity';
+$lang['user']['sync_jobs'] = 'Sync jobs';
+$lang['user']['hour'] = 'Hour';
+$lang['user']['hours'] = 'Hours';
+$lang['user']['day'] = 'Day';
+$lang['user']['week'] = 'Week';
+$lang['user']['weeks'] = 'Weeks';
+$lang['user']['spamfilter'] = 'Spam filter';
+$lang['user']['spamfilter_wl'] = 'Whitelist';
+$lang['user']['spamfilter_wl_desc'] = 'Whitelisted email addresses to <b>never</b> classify as spam. Wildcards maybe used.';
+$lang['user']['spamfilter_bl'] = 'Blacklist';
+$lang['user']['spamfilter_bl_desc'] = 'Blacklisted email addresses to <b>always</b> classify as spam and reject. Wildcards maybe used.';
+$lang['user']['spamfilter_behavior'] = 'Rating';
+$lang['user']['spamfilter_table_rule'] = 'Rule';
+$lang['user']['spamfilter_table_action'] = 'Action';
+$lang['user']['spamfilter_table_empty'] = 'No data to display';
+$lang['user']['spamfilter_table_remove'] = 'remove';
+$lang['user']['spamfilter_table_add'] = 'Add item';
+$lang['user']['spamfilter_default_score'] = 'Spam score:';
+$lang['user']['spamfilter_green'] = 'Green: this message is not spam';
+$lang['user']['spamfilter_yellow'] = 'Yellow: this message may be spam, will be tagged as spam and moved to your junk folder';
+$lang['user']['spamfilter_red'] = 'Red: This message is spam and will be rejected by the server';
+$lang['user']['spamfilter_default_score'] = 'Default values:';
+$lang['user']['spamfilter_hint'] = 'The first value describes the "low spam score", the second represents the "high spam score".';
+$lang['user']['spamfilter_table_domain_policy'] = "n/a (domain policy)";
+
+$lang['user']['tls_policy_warning'] = '<strong>Warning:</strong> If you decide to enforce encrypted mail transfer, you may lose emails.<br />Messages to not satisfy the policy will be bounced with a hard fail by the mail system.';
+$lang['user']['tls_policy'] = 'Encryption policy';
+$lang['user']['tls_enforce_in'] = 'Enforce TLS incoming';
+$lang['user']['tls_enforce_out'] = 'Enforce TLS outgoing';
+$lang['user']['no_record'] = 'No record';
+
+$lang['user']['misc_settings'] = 'Other profile settings';
+$lang['user']['misc_delete_profile'] = 'Other profile settings';
+
+$lang['user']['tag_handling'] = 'Set handling for tagged mail';
+$lang['user']['tag_in_subfolder'] = 'In subfolder';
+$lang['user']['tag_in_subject'] = 'In subject';
+$lang['user']['tag_help_explain'] = 'In subfolder: a new subfolder named after the tag will be created below INBOX ("INBOX/Facebook").<br />
+In subject: the tags name will be prepended to the mails subject, example: "[Facebook] Meine Neuigkeiten".';
+$lang['user']['tag_help_example'] = 'Example for a tagged email address: ich<b>+Facebook</b>@example.org';
+$lang['user']['eas_reset'] = 'Reset ActiveSync device cache';
+$lang['user']['eas_reset_now'] = 'Reset now';
+$lang['user']['eas_reset_help'] = 'In many cases a device cache reset will help to recover a broken ActiveSync profile.<br /><b>Attention:</b> All elements will be redownloaded!';
+
+$lang['user']['encryption'] = 'Encyrption';
+$lang['user']['username'] = 'Username';
+$lang['user']['password'] = 'Password';
+$lang['user']['last_run'] = 'Last run';
+$lang['user']['excludes'] = 'Excludes';
+$lang['user']['interval'] = 'Interval';
+$lang['user']['active'] = 'Active';
+$lang['user']['action'] = 'Action';
+$lang['user']['edit'] = 'Edit';
+$lang['user']['remove'] = 'Remove';
+$lang['user']['delete_now'] = 'Remove now';
+$lang['user']['create_syncjob'] = 'Create new sync job';
+
+$lang['start']['dashboard'] = '%s - dashboard';
+$lang['start']['start_rc'] = 'Open Roundcube';
+$lang['start']['start_sogo'] = 'Open SOGo';
+$lang['start']['mailcow_apps_detail'] = 'Use a mailcow app to access your mails, calendar, contacts and more.';
+$lang['start']['mailcow_panel'] = 'Start mailcow UI';
+$lang['start']['mailcow_panel_description'] = 'The mailcow UI is available for administrators and mailbox users.';
+$lang['start']['mailcow_panel_detail'] = '<b>Domain administrators</b> create, modify or delete mailboxes and aliases, change domains and read further information about their assigned domains.<br />
+	<b>Mailbox users</b> are able to create time-limited aliases (spam aliases), change their password and spam filter settings.';
+$lang['start']['recommended_config'] = 'Recommended configuration (without ActiveSync)';
+$lang['start']['imap_smtp_server'] = 'IMAP- and SMTP server data';
+$lang['start']['imap_smtp_server_description'] = 'For the best experience we recommend to use <a href="%s" target="_blank"><b>Mozilla Thunderbird</b></a>.';
+$lang['start']['imap_smtp_server_badge'] = 'Read/Write emails';
+$lang['start']['imap_smtp_server_auth_info'] = 'Please use your full email address and the PLAIN authentication mechanism.<br />
+Your login data will be encrypted by the server-side mandatory encryption.';
+$lang['start']['managesieve'] = 'ManageSieve';
+$lang['start']['managesieve_badge'] = 'Email filter';
+$lang['start']['managesieve_description'] = 'Please use <b>Mozilla Thunderbird</b> with the <a style="text-decoration:none" target="_blank" href="%s"><b>nightly sieve extension</b></a>.<br />Start Thunderbird, open the add-on settings and drop the newly downloaded xpi file into the opened window.<br />The server name is <b>%s</b>, use port <b>4190</b> if you are asked for. The login data match your email login.';
+$lang['start']['service'] = 'Service';
+$lang['start']['encryption'] = 'Encryption method';
+$lang['start']['help'] = 'Show/Hide help panel';
+$lang['start']['hostname'] = 'Hostname';
+$lang['start']['port'] = 'Port';
+$lang['start']['footer'] = '';
+$lang['header']['mailcow_settings'] = 'Configuration';
+$lang['header']['administration'] = 'Administration';
+$lang['header']['mailboxes'] = 'Mailboxes';
+$lang['header']['user_settings'] = 'User settings';
+$lang['header']['login'] = 'Login';
+$lang['header']['logged_in_as_logout'] = 'Logged in as <b>%s</b> (logout)';
+$lang['header']['logged_in_as_logout_dual'] = 'Logged in as <b>%s <span class="text-info">[%s]</span></b>';
+$lang['header']['locale'] = 'Language';
+$lang['mailbox']['domain'] = 'Domain';
+$lang['mailbox']['multiple_bookings'] = 'Multiple bookings';
+$lang['mailbox']['kind'] = 'Kind';
+$lang['mailbox']['description'] = 'Description';
+$lang['mailbox']['alias'] = 'Alias';
+$lang['mailbox']['resource_name'] = 'Resource name';
+$lang['mailbox']['aliases'] = 'Aliases';
+$lang['mailbox']['domains'] = 'Domains';
+$lang['mailbox']['mailboxes'] = 'Mailboxes';
+$lang['mailbox']['resources'] = 'Resources';
+$lang['mailbox']['mailbox_quota'] = 'Max. size of a mailbox';
+$lang['mailbox']['domain_quota'] = 'Quota';
+$lang['mailbox']['active'] = 'Active';
+$lang['mailbox']['action'] = 'Action';
+$lang['mailbox']['ratelimit'] = 'Outgoing rate limit/h';
+$lang['mailbox']['backup_mx'] = 'Backup MX';
+$lang['mailbox']['domain_aliases'] = 'Domain aliases';
+$lang['mailbox']['target_domain'] = 'Target domain';
+$lang['mailbox']['target_address'] = 'Goto address';
+$lang['mailbox']['username'] = 'Username';
+$lang['mailbox']['fname'] = 'Full name';
+$lang['mailbox']['filter_table'] = 'Filter table';
+$lang['mailbox']['yes'] = '&#10004;';
+$lang['mailbox']['no'] = '&#10008;';
+$lang['mailbox']['quota'] = 'Quota';
+$lang['mailbox']['in_use'] = 'In use (%)';
+$lang['mailbox']['msg_num'] = 'Message #';
+$lang['mailbox']['remove'] = 'Remove';
+$lang['mailbox']['edit'] = 'Edit';
+$lang['mailbox']['archive'] = 'Archive';
+$lang['mailbox']['no_record'] = 'No record for object %s';
+$lang['mailbox']['no_record_single'] = 'No record';
+$lang['mailbox']['add_domain'] = 'Add domain';
+$lang['mailbox']['add_domain_alias'] = 'Add domain alias';
+$lang['mailbox']['add_mailbox'] = 'Add mailbox';
+$lang['mailbox']['add_resource'] = 'Add resource';
+$lang['mailbox']['add_alias'] = 'Add alias';
+
+$lang['info']['no_action'] = 'No action applicable';
+
+$lang['delete']['title'] = 'Remove object';
+$lang['delete']['remove_domain_warning'] = '<b>Warning:</b> You are about to remove the domain <b>%s</b>!';
+$lang['delete']['remove_syncjob_warning'] = '<b>Warning:</b> You are about to remove a sync job for user <b>%s</b>!';
+$lang['delete']['remove_domainalias_warning'] = '<b>Warning:</b> You are about to remove the domain alias <b>%s</b>!';
+$lang['delete']['remove_domainadmin_warning'] = '<b>Warning:</b> You are about to remove the domain administrator <b>%s</b>!';
+$lang['delete']['remove_alias_warning'] = '<b>Warning:</b> You are about to remove the alias address <b>%s</b>!';
+$lang['delete']['remove_mailbox_warning'] = '<b>Warning:</b> You are about to remove the mailbox <b>%s</b>!';
+$lang['delete']['remove_mailbox_details'] = 'The mailbox will be <b>purged permanently</b>!';
+$lang['delete']['remove_resource_warning'] = '<b>Warning:</b> You are about to remove the resource <b>%s</b>!';
+$lang['delete']['remove_resource_details'] = 'The resource will be <b>purged permanently</b>!';
+$lang['delete']['remove_domain_details'] = 'This also removes domain aliases.<br /><br /><b>A domain must be empty to be removed.</b>';
+$lang['delete']['remove_syncjob_details'] = 'Objects from this sync job will not be pulled from the remote server anymore.';
+$lang['delete']['remove_alias_details'] = 'Users will no longer be able to receive mail for or send mail from this address.</b>';
+$lang['delete']['remove_button'] = 'Remove';
+$lang['delete']['previous'] = 'Previous page';
+
+$lang['edit']['syncjob'] = 'Edit sync job';
+$lang['edit']['save'] = 'Save changes';
+$lang['edit']['username'] = 'Username';
+$lang['edit']['hostname'] = 'Hostname';
+$lang['edit']['encryption'] = 'Encryption';
+$lang['edit']['maxage'] = 'Maximum age of messages in days that will be polled from remote<br /><small>(0 = ignore age)</small>';
+$lang['edit']['subfolder2'] = 'Sync into subfolder on destination<br /><small>(empty = do not use subfolder)</small>';
+$lang['edit']['mins_interval'] = 'Interval (min)';
+$lang['edit']['exclude'] = 'Exclude objects (regex)';
+$lang['edit']['save'] = 'Save changes';
+$lang['edit']['archive'] = 'Archive access';
+$lang['edit']['max_mailboxes'] = 'Max. possible mailboxes';
+$lang['edit']['title'] = 'Edit object';
+$lang['edit']['target_address'] = 'Goto address/es <small>(comma-separated)</small>';
+$lang['edit']['active'] = 'Active';
+$lang['edit']['target_domain'] = 'Target domain';
+$lang['edit']['password'] = 'Password';
+$lang['edit']['ratelimit'] = 'Outgoing rate limit/h';
+$lang['danger']['ratelimt_less_one'] = 'Outgoing rate limit/h must not be less than 1';
+$lang['edit']['password_repeat'] = 'Confirmation password (repeat)';
+$lang['edit']['domain_admin'] = 'Edit domain administrator';
+$lang['edit']['domain'] = 'Edit domain';
+$lang['edit']['alias_domain'] = 'Alias domain';
+$lang['edit']['edit_alias_domain'] = 'Edit Alias domain';
+$lang['edit']['domains'] = 'Domains';
+$lang['edit']['destroy'] = 'Manual data input';
+$lang['edit']['alias'] = 'Edit alias';
+$lang['edit']['mailbox'] = 'Edit mailbox';
+$lang['edit']['description'] = 'Description';
+$lang['edit']['max_aliases'] = 'Max. aliases';
+$lang['edit']['max_quota'] = 'Max. quota per mailbox (MiB)';
+$lang['edit']['domain_quota'] = 'Domain quota';
+$lang['edit']['backup_mx_options'] = 'Backup MX options';
+$lang['edit']['relay_domain'] = 'Relay domain';
+$lang['edit']['relay_all'] = 'Relay all recipients';
+$lang['edit']['dkim_signature'] = 'DKIM signature';
+$lang['edit']['dkim_record_info'] = '<small>Please add a TXT record with the given value to your DNS settings.</small>';
+$lang['edit']['relay_all_info'] = '<small>If you choose <b>not</b> to relay all recipients, you will need to add a ("blind") mailbox for every single recipient that should be relayed.</small>';
+$lang['edit']['full_name'] = 'Full name';
+$lang['edit']['quota_mb'] = 'Quota (MiB)';
+$lang['edit']['sender_acl'] = 'Allow to send as';
+$lang['edit']['sender_acl_info'] = 'Aliases cannot be deselected.';
+$lang['edit']['dkim_txt_name'] = 'TXT record name:';
+$lang['edit']['dkim_txt_value'] = 'TXT record value:';
+$lang['edit']['previous'] = 'Previous page';
+$lang['edit']['unchanged_if_empty'] = 'If unchanged leave blank';
+$lang['edit']['dont_check_sender_acl'] = 'Do not check sender for domain %s';
+$lang['edit']['multiple_bookings'] = 'Multiple bookings';
+$lang['edit']['kind'] = 'Kind';
+$lang['edit']['resource'] = 'Resource';
+
+$lang['add']['syncjob'] = 'Add sync job';
+$lang['add']['syncjob_hint'] = 'Be aware that passwords need to be saved plain-text!';
+$lang['add']['hostname'] = 'Hostname';
+$lang['add']['username'] = 'Username';
+$lang['add']['enc_method'] = 'Encryption method';
+$lang['add']['mins_interval'] = 'Polling interval (minutes)';
+$lang['add']['maxage'] = 'Maximum age of messages that will be polled from remote (0 = ignore age)';
+$lang['add']['subfolder2'] = 'Sync into subfolder on destination';
+$lang['add']['exclude'] = 'Exclude objects (regex)';
+$lang['add']['delete2duplicates'] = 'Delete duplicates on destination';
+
+$lang['add']['title'] = 'Add object';
+$lang['add']['domain'] = 'Domain';
+$lang['add']['active'] = 'Active';
+$lang['add']['multiple_bookings'] = 'Multiple bookings';
+$lang['add']['save'] = 'Save changes';
+$lang['add']['description'] = 'Description:';
+$lang['add']['max_aliases'] = 'Max. possible aliases:';
+$lang['add']['resource_name'] = 'Resource name';
+$lang['add']['max_mailboxes'] = 'Max. possible mailboxes:';
+$lang['add']['mailbox_quota_m'] = 'Max. quota per mailbox (MiB):';
+$lang['add']['domain_quota_m'] = 'Total domain quota (MiB):';
+$lang['add']['backup_mx_options'] = 'Backup MX options:';
+$lang['add']['relay_all'] = 'Relay all recipients';
+$lang['add']['relay_domain'] = 'Relay this domain';
+$lang['add']['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['add']['alias'] = 'Alias(es)';
+$lang['add']['alias_spf_fail'] = '<b>Note:</b> If your chosen destination address is an external mailbox, the <b>receiving mailserver</b> may reject your message due to an SPF failure.</a>';
+$lang['add']['alias_address'] = 'Alias address/es:';
+$lang['add']['alias_address_info'] = '<small>Full email address/es or @example.com, to catch all messages for a domain (comma-separated). <b>mailcow domains only</b>.</small>';
+$lang['add']['alias_domain_info'] = '<small>Valid domain names only (comma-separated).</small>';
+$lang['add']['target_address'] = 'Goto addresses:';
+$lang['add']['target_address_info'] = '<small>Full email address/es (comma-separated).</small>';
+$lang['add']['alias_domain'] = 'Alias domain';
+$lang['add']['select'] = 'Please select...';
+$lang['add']['target_domain'] = 'Target domain:';
+$lang['add']['mailbox'] = 'Mailbox';
+$lang['add']['resource'] = 'Resource';
+$lang['add']['kind'] = 'Kind';
+$lang['add']['mailbox_username'] = 'Username (left part of an email address):';
+$lang['add']['full_name'] = 'Full name:';
+$lang['add']['quota_mb'] = 'Quota (MiB):';
+$lang['add']['select_domain'] = 'Please select a domain first';
+$lang['add']['password'] = 'Password:';
+$lang['add']['password_repeat'] = 'Confirmation password (repeat):';
+$lang['add']['previous'] = 'Previous page';
+$lang['add']['restart_sogo_hint'] = 'You will need to restart the SOGo service container after adding a new domain!';
+
+$lang['login']['title'] = 'Login';
+$lang['login']['administration'] = 'Administration';
+$lang['login']['administration_details'] = 'Please use your Administrator login to perform administrative tasks.';
+$lang['login']['user_settings'] = 'User settings';
+$lang['login']['user_settings_details'] = 'Mailbox users can use mailcow UI to change their passwords, create temporary aliases (spam aliases), adjust the spam filter behaviour or import messages from a remote IMAP server.';
+$lang['login']['username'] = 'Username';
+$lang['login']['password'] = 'Password';
+$lang['login']['reset_password'] = 'Reset my password';
+$lang['login']['login'] = 'Login';
+$lang['login']['previous'] = "Previous page";
+$lang['login']['delayed'] = 'Login was delayed by %s seconds.';
+
+$lang['tfa']['tfa'] = "Two-factor authentication";
+$lang['tfa']['set_tfa'] = "Set two-factor authentication method";
+$lang['tfa']['yubi_otp'] = "Yubico OTP authentication";
+$lang['tfa']['key_id'] = "An identifier for your YubiKey";
+$lang['tfa']['api_register'] = 'mailcow uses the Yubico Cloud API. Please get an API key for your key <a href="https://upgrade.yubico.com/getapikey/" target="_blank">here</a>';
+$lang['tfa']['u2f'] = "U2F authentication";
+$lang['tfa']['hotp'] = "HOTP authentication";
+$lang['tfa']['totp'] = "TOTP authentication";
+$lang['tfa']['none'] = "Deaktiviert";
+$lang['tfa']['delete_tfa'] = "Disable TFA";
+$lang['tfa']['disable_tfa'] = "Disable TFA until next successful login";
+$lang['tfa']['confirm_tfa'] = "Please confirm your one-time password in the below field";
+$lang['tfa']['confirm'] = "Confirm";
+$lang['tfa']['otp'] = "One-time password";
+$lang['tfa']['trash_login'] = "Trash login";
+$lang['tfa']['select'] = "Please select";
+$lang['tfa']['waiting_usb_auth'] = "<i>Waiting for USB device...</i><br /><br />Please tap the button on your U2F USB device now.";
+$lang['tfa']['waiting_usb_register'] = "<i>Waiting for USB device...</i><br /><br />Please enter your password above and confirm your U2F registration by tapping the button on your U2F USB device.";
+
+$lang['admin']['search_domain_da'] = 'Search domains';
+$lang['admin']['restrictions'] = 'Postifx Restrictions';
+$lang['admin']['rr'] = 'Postifx Recipient Restrictions';
+$lang['admin']['sr'] = 'Postifx Sender Restrictions';
+$lang['admin']['reset_defaults'] = 'Reset to defaults';
+$lang['admin']['sr'] = 'Postifx Sender Restrictions';
+$lang['admin']['r_inactive'] = 'Inactive restrictions';
+$lang['admin']['r_active'] = 'Active restrictions';
+$lang['admin']['r_info'] = 'Greyed out/disabled elements on the list of active restrictions are not known as valid restrictions to mailcow and cannot be moved. Unknown restrictions will be set in order of appearance anyway. <br />You can add new elements in <code>inc/vars.local.inc.php</code> to be able to toggle them.';
+$lang['admin']['public_folders'] = 'Public Folders';
+$lang['admin']['public_folders_text'] = 'A namespace "Public" is created. Below\'s public folder name indicates the name of the first auto-created mailbox within this namespace.';
+$lang['admin']['public_folder_name'] = 'Folder name <small>(alphanumeric)</small>';
+$lang['admin']['public_folder_enable'] = 'Enable public folder';
+$lang['admin']['public_folder_enable_text'] = 'Toggling this option does not delete mail in any public folder.';
+$lang['admin']['public_folder_pusf'] = 'Enable per-user seen flag';
+$lang['admin']['public_folder_pusf_text'] = 'A "per-user seen flag"-enabled system will not mark a mail as read for User B, when User A has seen it, but User B did not.';
+$lang['admin']['privacy'] = 'Privacy';
+$lang['admin']['privacy_text'] = 'This option enables a PCRE table to remove "User-Agent", "X-Enigmail", "X-Mailer", "X-Originating-IP" and replaces "Received: from" headers with localhost/127.0.0.1.';
+$lang['admin']['privacy_anon_mail'] = 'Anonymize outgoing mail';
+$lang['admin']['dkim_txt_name'] = 'TXT record name:';
+$lang['admin']['dkim_txt_value'] = 'TXT record value:';
+$lang['admin']['dkim_key_length'] = 'DKIM key length (bits)';
+$lang['admin']['dkim_key_valid'] = 'Key valid';
+$lang['admin']['dkim_key_unused'] = 'Key unused';
+$lang['admin']['dkim_key_missing'] = 'Key missing';
+$lang['admin']['dkim_key_hint'] = 'Selector for DKIM keys is always <code>dkim</code>.';
+$lang['admin']['previous'] = 'Previous page';
+$lang['admin']['quota_mb'] = 'Quota (MiB):';
+$lang['admin']['sender_acl'] = 'Allow to send as:';
+$lang['admin']['msg_size'] = 'Message size';
+$lang['admin']['msg_size_limit'] = 'Message size limit now';
+$lang['admin']['msg_size_limit_details'] = 'Applying a new limit will reload Postfix and the webserver.';
+$lang['admin']['save'] = 'Save changes';
+$lang['admin']['maintenance'] = 'Maintenance and Information';
+$lang['admin']['sys_info'] = 'System information';
+$lang['admin']['dkim_add_key'] = 'Add DKIM key';
+$lang['admin']['dkim_keys'] = 'DKIM keys';
+$lang['admin']['add'] = 'Add';
+$lang['admin']['configuration'] = 'Configuration';
+$lang['admin']['password'] = 'Password';
+$lang['admin']['password_repeat'] = 'Confirmation password (repeat)';
+$lang['admin']['active'] = 'Active';
+$lang['admin']['action'] = 'Action';
+$lang['admin']['add_domain_admin'] = 'Add Domain administrator';
+$lang['admin']['admin_domains'] = 'Domain assignments';
+$lang['admin']['domain_admins'] = 'Domain administrators';
+$lang['admin']['username'] = 'Username';
+$lang['admin']['edit'] = 'Edit';
+$lang['admin']['remove'] = 'Remove';
+$lang['admin']['save'] = 'Save changes';
+$lang['admin']['admin'] = 'Administrator';
+$lang['admin']['admin_details'] = 'Edit administrator details';
+$lang['admin']['unchanged_if_empty'] = 'If unchanged leave blank';
+$lang['admin']['yes'] = '&#10004;';
+$lang['admin']['no'] = '&#10008;';
+$lang['admin']['access'] = 'Access';
+$lang['admin']['invalid_max_msg_size'] = 'Invalid max. message size';
+$lang['admin']['site_not_found'] = 'Cannot locate mailcow site configuration';
+$lang['admin']['public_folder_empty'] = 'Public folder name must not be empty';
+$lang['admin']['set_rr_failed'] = 'Cannot set Postfix restrictions';
+$lang['admin']['no_record'] = 'No record';
+?>
diff --git a/data/web/lang/lang.es.php b/data/web/lang/lang.es.php
new file mode 100644
index 00000000..dbe3f3d0
--- /dev/null
+++ b/data/web/lang/lang.es.php
@@ -0,0 +1,378 @@
+<?php
+/*
+//
+//  Spanish language file
+//
+*/
+$lang['footer']['loading'] = "Espere Por Favor...";
+$lang['header']['restart_sogo'] = 'Reiniciar SOGo';
+$lang['footer']['restart_sogo'] = 'Reiniciar SOGo';
+$lang['footer']['restart_now'] = 'Reiniciar ahora';
+$lang['footer']['restart_sogo_info'] = 'Algunas tareas, por ejemplo agregar un dominio, requieren que reinicies SOGo para detectar cambios hechos en la UI de mailcow.<br /><br /><b>Importante:</b> Un reinicio sencillo puede tardar un poco en completarse, por favor espere a que termine.';
+$lang['dkim']['confirm'] = "¿Estás Seguro?";
+$lang['danger']['dkim_not_found'] = "Registro DKIM no encontrado";
+$lang['danger']['dkim_remove_failed'] = "No se puede eliminar el registro DKIM seleccionado";
+$lang['danger']['dkim_add_failed'] = "No se puede agregar el registro DKIM dado";
+$lang['danger']['dkim_domain_or_sel_invalid'] = "Dominio DKIM ó selector inválido";
+$lang['danger']['dkim_key_length_invalid'] = "Longitud de la llave DKIM inválida";
+$lang['success']['dkim_removed'] = "Registro DKIM removido";
+$lang['success']['dkim_added'] = "Registro DKIM guardado";
+$lang['danger']['access_denied'] = "Acceso denegado o datos del formulario inválidos";
+$lang['danger']['whitelist_from_invalid'] = "Entrada de la lista blanca inválida";
+$lang['danger']['domain_invalid'] = "Nombre de dominio inválido";
+$lang['danger']['mailbox_quota_exceeds_domain_quota'] = "Cuota máx. excede el limite de cuota del dominio";
+$lang['danger']['object_is_not_numeric'] = "El valor %s no es numérico";
+$lang['success']['domain_added'] = "Dominio agregado %s";
+$lang['danger']['alias_empty'] = "Dirección alias no debe estar vacía";
+$lang['danger']['goto_empty'] = "Dirección \"goto\" no debe estar vacía";
+$lang['danger']['policy_list_from_exists'] = "Un registro con ese nombre ya existe";
+$lang['danger']['policy_list_from_invalid'] = "El registro tiene formato inválido";
+$lang['danger']['whitelist_exists'] = "Ya existe un registro con ese nombre en la lista blanca";
+$lang['danger']['whitelist_from_invalid'] = "Formato inválido para el registro de lista blanca";
+$lang['danger']['alias_invalid'] = "Dirección alias inválida";
+$lang['danger']['goto_invalid'] = "Dirección \"goto\" inválida";
+$lang['danger']['alias_domain_invalid'] = "El dominio alias es inválido";
+$lang['danger']['target_domain_invalid'] = "El dominio \"goto\" es inválido";
+$lang['danger']['object_exists'] = "El objeto %s ya existe";
+$lang['danger']['domain_exists'] = "El dominio %s ya existe";
+$lang['danger']['alias_goto_identical'] = "Las direcciones alias y \"goto\" no deben ser idénticas";
+$lang['danger']['aliasd_targetd_identical'] = "El dominio alias no debe ser igual al dominio destino";
+$lang['success']['alias_added'] = "Dirección/es alias ha/han sidgo agregada";
+$lang['success']['alias_modified'] = "Cambios al alias guardados";
+$lang['success']['aliasd_modified'] = "Cambios al dominio alias guardados";
+$lang['success']['mailbox_modified'] = "Cambios al buzón %s guardados";
+$lang['success']['msg_size_saved'] = "Limite del mensaje establecido";
+$lang['danger']['aliasd_not_found'] = "Dominio alias no encontrado";
+$lang['danger']['targetd_not_found'] = "Dominio destino no encontrado";
+$lang['danger']['aliasd_exists'] = "Dominio alias ya existe";
+$lang['success']['aliasd_added'] = "Agregado dominio alias %s";
+$lang['success']['aliasd_modified'] = "Cambios al dominio alias %s guardados";
+$lang['success']['domain_modified'] = "Cambios al dominio %s guardados";
+$lang['success']['domain_admin_modified'] = "Cambios al administrador del dominio %s guardados";
+$lang['success']['domain_admin_added'] = "Administrador del dominio %s agregado";
+$lang['success']['changes_general'] = 'Cambios guardados';
+$lang['success']['admin_modified'] = "Cambios al administrador guardados";
+$lang['danger']['exit_code_not_null'] = "Error: Código de salida es %d";
+$lang['danger']['mailbox_not_available'] = "Buzón no disponible";
+$lang['danger']['username_invalid'] = "Nombre de usuario no se puede utilizar";
+$lang['danger']['password_mismatch'] = "Confirmación de contraseña no es identica";
+$lang['danger']['password_complexity'] = "La contraseña no cumple con los requisitos";
+$lang['danger']['password_empty'] = "El campo de la contraseña no debe estar vacío";
+$lang['danger']['login_failed'] = "Inicio de sesión fallido";
+$lang['danger']['mailbox_invalid'] = "Nombre de buzón inválido";
+$lang['danger']['mailbox_invalid_suggest'] = 'El nombre del buzón es inválido, ¿pretendías escribir "%s"?';
+$lang['info']['fetchmail_planned'] = "La tarea para buscar correos se ha planeado. Por favor verifica el proceso más tarde.";
+$lang['danger']['fetchmail_source_empty'] = "Por favor define una carpeta fuente";
+$lang['danger']['fetchmail_dest_empty'] = "Por favor define una carpeta destino";
+$lang['danger']['is_alias'] = "%s ya está definida como una dirección alias";
+$lang['danger']['is_alias_or_mailbox'] = "%s ya está definido como un alias ó como un buzón";
+$lang['danger']['is_spam_alias'] = "%s ya está definida como una dirección alias de correo no deseado";
+$lang['danger']['quota_not_0_not_numeric'] = "Cuota debe ser numérica y >= 0";
+$lang['danger']['domain_not_found'] = "Dominio no encontrado.";
+$lang['danger']['max_mailbox_exceeded'] = "Máx. de buzones superado (%d de %d)";
+$lang['danger']['mailbox_quota_exceeded'] = "Cuota excede el límite de dominio (máx. %d MiB)";
+$lang['danger']['mailbox_quota_left_exceeded'] = "No queda espacio suficiente (espacio libre: %d MiB)";
+$lang['success']['mailbox_added'] = "Buzón %s agregado";
+$lang['success']['domain_removed'] = "Dominio %s removido";
+$lang['success']['alias_removed'] = "Dirección alias %s removida";
+$lang['success']['alias_domain_removed'] = "Dominio alias %s removido";
+$lang['success']['domain_admin_removed'] = "Administrador del dominio %s removido";
+$lang['success']['mailbox_removed'] = "Buzón %s removido";
+$lang['danger']['max_quota_in_use'] = "Cuota del buzón debe ser mayor o igual a %d MiB";
+$lang['danger']['domain_quota_m_in_use'] = "Cuota del dominio debe ser mayor o igual a %d MiB";
+$lang['danger']['mailboxes_in_use'] = "Máx. de buzones debe ser mayor o igual a %d";
+$lang['danger']['aliases_in_use'] = "Máx. de alias debe ser mayor o igual a %d";
+$lang['danger']['sender_acl_invalid'] = "Valor del remitente ACL inválido";
+$lang['danger']['domain_not_empty'] = "No se puede eliminar un dominio que no esté vacío";
+$lang['warning']['spam_alias_temp_error'] = "Error temporal: No se puede agregar ese \"spam alias\", inténtelo más tarde.";
+$lang['danger']['spam_alias_max_exceeded'] = "Máx. direcciones \"spam alias\" permitidas excedido";
+$lang['danger']['fetchmail_active'] = "Un proceso ya se está ejecutando, por favor espera a que termine.";
+$lang['danger']['validity_missing'] = 'Por favor asigna un periodo de validez';
+$lang['user']['off'] = "Apagado";
+$lang['user']['user_change_fn'] = "";
+$lang['user']['user_settings'] = 'Configuración del usuario';
+$lang['user']['mailbox_settings'] = 'Configuración del buzón';
+$lang['user']['mailbox_details'] = 'Detalles del buzón';
+$lang['user']['change_password'] = 'Cambiar contraseña';
+$lang['user']['new_password'] = 'Nueva contraseña:';
+$lang['user']['save_changes'] = 'Guardar cambios';
+$lang['user']['password_now'] = 'Contraseña actual (confirmar cambios):';
+$lang['user']['new_password_repeat'] = 'Confirmación de contraseña (repetir):';
+$lang['user']['new_password_description'] = 'Requisitos: longitud de 6 caracteres, letras y números.';
+$lang['user']['did_you_know'] = '<b>¿Sabías qué?</b> Puedes utilizar etiquetas en tu dirección email ("me+<b>privat</b>@example.com") para mover mensajes a una carpeta automáticamente (ejemplo: "privat").';
+$lang['user']['spam_aliases'] = 'Alias de email temporales';
+$lang['user']['alias'] = 'Alias';
+$lang['user']['aliases'] = 'Alias';
+$lang['user']['is_catch_all'] = 'Atrapa-Todo para el/los dominio/s';
+$lang['user']['aliases_also_send_as'] = 'También permitido para mandarse como';
+$lang['user']['aliases_send_as_all'] = 'No verifiques el acceso del remitente para los siguientes dominios';
+$lang['user']['alias_create_random'] = 'Generar alias aleatorio';
+$lang['user']['alias_extend_all'] = 'Extender alias por 1 hora';
+$lang['user']['alias_valid_until'] = 'Válido hasta';
+$lang['user']['alias_remove_all'] = 'Eliminar todos los alias';
+$lang['user']['alias_time_left'] = 'Tiempo restante';
+$lang['user']['alias_full_date'] = 'd.m.Y, H:i:s T';
+$lang['user']['alias_select_validity'] = 'Periodo de validez';
+$lang['user']['hour'] = 'Hora';
+$lang['user']['hours'] = 'Horas';
+$lang['user']['day'] = 'Día';
+$lang['user']['week'] = 'Semana';
+$lang['user']['weeks'] = 'Semanas';
+$lang['user']['spamfilter'] = 'Filtro de spam';
+$lang['user']['spamfilter_wl'] = 'Lista blanca';
+$lang['user']['spamfilter_wl_desc'] = 'Direcciones en la lista blanca <b>nunca</b> clasificarán como spam. Probablemente se usará un comodín.';
+$lang['user']['spamfilter_bl'] = 'Lista negra';
+$lang['user']['spamfilter_bl_desc'] = 'Direcciones en la lista negra <b>siempre</b> clasificarán como spam. Probablemente se usará un comodín.';
+$lang['user']['spamfilter_behavior'] = 'Clasificación';
+$lang['user']['spamfilter_table_rule'] = 'Regla';
+$lang['user']['spamfilter_table_action'] = 'Acción';
+$lang['user']['spamfilter_table_empty'] = 'No hay datos para mostrar';
+$lang['user']['spamfilter_table_remove'] = 'eliminar';
+$lang['user']['spamfilter_table_add'] = 'Agregar elemento';
+$lang['user']['spamfilter_default_score'] = 'Calificación de spam:';
+$lang['user']['spamfilter_green'] = 'Verde: éste mensaje no es spam';
+$lang['user']['spamfilter_yellow'] = 'Amarillo: éste mensaje puede ser spam, será etiquetado como spam y trasladado a tu carpeta basura';
+$lang['user']['spamfilter_red'] = 'Rojo: Este mensaje es spam y sera rechazado por el servidor';
+$lang['user']['spamfilter_default_score'] = 'Valores por defecto:';
+$lang['user']['spamfilter_hint'] = 'El primer valor representa la "calificación baja de spam", el segundo representa la "calificación alta de spam".';
+
+$lang['user']['tls_policy_warning'] = '<strong>Advertencia:</strong> Si decides forzar la transmisión de correo encriptado, puedes perder correos.<br />Mensajes que no satisfagan la política serán rebotados con una falla grave en el sistema de correos .';
+$lang['user']['tls_policy'] = 'Política de encriptación';
+$lang['user']['tls_enforce_in'] = 'Aplicar TLS entrante';
+$lang['user']['tls_enforce_out'] = 'Aplicar TLS saliente';
+$lang['user']['no_record'] = 'Sin registro';
+
+$lang['user']['misc_settings'] = 'Otras configuraciones de usuario';
+$lang['user']['misc_delete_profile'] = 'Otras configuraciones de usuario';
+
+$lang['user']['tag_handling'] = 'Establecer manejo para el correo etiquetado';
+$lang['user']['tag_in_subfolder'] = 'En subcarpeta';
+$lang['user']['tag_in_subject'] = 'En asunto';
+$lang['user']['tag_help_explain'] = 'En subcarpeta: una nueva subcarpeta llamada como la etiqueta será creada debajo de INBOX ("INBOX/Facebook").<br />
+En asunto: los nombres de las etiquetas serán añadidos al asunto de los correos, ejemplo: "[Facebook] Mis Noticias".';
+$lang['user']['tag_help_example'] = 'Ejemplo de una dirección email etiquetada: mi<b>+Facebook</b>@ejemplo.org';
+
+$lang['start']['dashboard'] = '%s - panel';
+$lang['start']['start_rc'] = 'Abrir Roundcube';
+$lang['start']['start_sogo'] = 'Abrir SOGo';
+$lang['start']['mailcow_apps_detail'] = 'Utiliza una aplicación de mailcow para acceder a tus correos, calendario, contactos y más.';
+$lang['start']['mailcow_panel'] = 'Iniciar mailcow UI';
+$lang['start']['mailcow_panel_description'] = 'Mailcow UI está disponible para administradores y usuarios de buzón.';
+$lang['start']['mailcow_panel_detail'] = '<b>Administradores del dominio</b> crean, modifican o eliminan buzones y alias, cambia dominios y lee información más detallada sobre sus dominios asignados<br />
+	<b>Usuarios de buzón</b> son capaces de crear alias de tiempo limitado (spam alias), cambiar su contraseña y la configuración del filtro de spam.';
+$lang['start']['recommended_config'] = 'Configuración recomendada (sin ActiveSync)';
+$lang['start']['imap_smtp_server'] = 'IMAP- y SMTP datos del servidor';
+$lang['start']['imap_smtp_server_description'] = 'Para la mejor experiencia recomendamos utilizar <a href="%s" target="_blank"><b>Mozilla Thunderbird</b></a>.';
+$lang['start']['imap_smtp_server_badge'] = 'Leer/Escribir correos';
+$lang['start']['imap_smtp_server_auth_info'] = 'Por favor utiliza tu dirección de correo completa y el mecanismo de autenticación PLANO.<br />
+Tus datos para iniciar sesión serán encriptados por la encriptación obligatoria del servidor';
+$lang['start']['managesieve'] = 'ManageSieve';
+$lang['start']['managesieve_badge'] = 'Filtro de correos';
+$lang['start']['managesieve_description'] = 'Por favor utiliza <b>Mozilla Thunderbird</b> con la <a style="text-decoration:none" target="_blank" href="%s"><b>extensión nightly sieve</b></a>.<br />Inicia Thunderbird, abre la configuración de complementos y suelta el archivo xpi descargado en la ventana abierta.<br />El servidor es <b>%s</b>, utiliza el puerto <b>4190</b> si se te pregunta. Los datos para iniciar sesión coinciden con los datos de tu correo.';
+$lang['start']['service'] = 'Servicio';
+$lang['start']['encryption'] = 'Método de encriptación';
+$lang['start']['help'] = 'Mostrar/Ocultar panel de ayuda';
+$lang['start']['hostname'] = 'Hostname';
+$lang['start']['port'] = 'Port';
+$lang['start']['footer'] = '';
+$lang['header']['mailcow_settings'] = 'Configuracion';
+$lang['header']['administration'] = 'Administración';
+$lang['header']['mailboxes'] = 'Buzones';
+$lang['header']['user_settings'] = 'Configuraciones de usuario';
+$lang['header']['login'] = 'Inicio de sesión';
+$lang['header']['logged_in_as_logout'] = 'Sesión iniciada como <b>%s</b> (cerrar sesión)';
+$lang['header']['locale'] = 'Idioma';
+$lang['mailbox']['domain'] = 'Dominio';
+$lang['mailbox']['alias'] = 'Alias';
+$lang['mailbox']['aliases'] = 'Alias';
+$lang['mailbox']['domains'] = 'Dominios';
+$lang['mailbox']['mailboxes'] = 'Buzones';
+$lang['mailbox']['mailbox_quota'] = 'Tamaño máx. de cuota';
+$lang['mailbox']['domain_quota'] = 'Cuota';
+$lang['mailbox']['active'] = 'Activo';
+$lang['mailbox']['action'] = 'Acción';
+$lang['mailbox']['ratelimit'] = 'Límite de la tarifa saliente/h';
+$lang['mailbox']['backup_mx'] = 'Respaldar MX';
+$lang['mailbox']['domain_aliases'] = 'Alias de dominio';
+$lang['mailbox']['target_domain'] = 'Dominio destino';
+$lang['mailbox']['target_address'] = 'Dirección Goto';
+$lang['mailbox']['username'] = 'Nombre de usuario';
+$lang['mailbox']['fname'] = 'Nombre completo';
+$lang['mailbox']['filter_table'] = 'Filtrar tabla';
+$lang['mailbox']['yes'] = '&#10004;';
+$lang['mailbox']['no'] = '&#10008;';
+$lang['mailbox']['quota'] = 'Cuota';
+$lang['mailbox']['in_use'] = 'En uso (%)';
+$lang['mailbox']['msg_num'] = 'Mensaje #';
+$lang['mailbox']['remove'] = 'Eliminar';
+$lang['mailbox']['edit'] = 'Editar';
+$lang['mailbox']['archive'] = 'Archivar';
+$lang['mailbox']['no_record'] = 'Sin registro';
+$lang['mailbox']['add_domain'] = 'Agregar dominio';
+$lang['mailbox']['add_domain_alias'] = 'Agregar alias de dominio';
+$lang['mailbox']['add_mailbox'] = 'Agregar buzón';
+$lang['mailbox']['add_alias'] = 'Agregar alias';
+
+$lang['info']['no_action'] = 'No hay acción aplicable';
+
+$lang['delete']['title'] = 'Eliminar objeto';
+$lang['delete']['remove_domain_warning'] = '<b>Advertencia:</b> ¡Estás a punto de eliminar el dominio <b>%s</b>!';
+$lang['delete']['remove_domainalias_warning'] = '<b>Advertencia:</b> ¡Estás a punto de eliminar el alias de dominio <b>%s</b>!';
+$lang['delete']['remove_domainadmin_warning'] = '<b>Advertencia:</b> ¡Estás a punto de eliminar el administrador de dominio <b>%s</b>!';
+$lang['delete']['remove_alias_warning'] = '<b>Advertencia:</b> ¡Estás a punto de eliminar la dirección alias <b>%s</b>!';
+$lang['delete']['remove_mailbox_warning'] = '<b>Advertencia:</b> ¡Estás a punto de eliminar el buzón <b>%s</b>!';
+$lang['delete']['remove_mailbox_details'] = 'El buzón será <b>purgado permanentemente</b>!';
+$lang['delete']['remove_domain_details'] = 'Esto también eliminará alias de dominio.<br /><br /><b>Un dominio debe estar vacío para poder ser eliminado.</b>';
+$lang['delete']['remove_alias_details'] = 'Los usuarios ya no serán capaces de recibir correos o enviar correos desde esta dirección.</b>';
+$lang['delete']['remove_button'] = 'Eliminar';
+$lang['delete']['previous'] = 'Página anterior';
+
+$lang['edit']['save'] = 'Guardar cambios';
+$lang['edit']['archive'] = 'Acceso a archivos';
+$lang['edit']['max_mailboxes'] = 'Máx. buzones posibles:';
+$lang['edit']['title'] = 'Editas objeto';
+$lang['edit']['target_address'] = 'Dirección/es goto <small>(separadas por coma)</small>:';
+$lang['edit']['active'] = 'Activo';
+$lang['edit']['target_domain'] = 'Dominio destino:';
+$lang['edit']['password'] = 'Contraseña:';
+$lang['edit']['ratelimit'] = 'Límite de la tarifa saliente/h:';
+$lang['danger']['ratelimt_less_one'] = 'El límite de la tarifa saliente/h no puede ser menos que 1';
+$lang['edit']['password_repeat'] = 'Confirmación de contraseña (repetir):';
+$lang['edit']['domain_admin'] = 'Editar administrador del dominio';
+$lang['edit']['domain'] = 'Editar dominio';
+$lang['edit']['alias_domain'] = 'Alias de dominio';
+$lang['edit']['edit_alias_domain'] = 'Editar alias de dominio';
+$lang['edit']['domains'] = 'Dominios';
+$lang['edit']['destroy'] = 'Entrada manual de datos';
+$lang['edit']['alias'] = 'Editar alias';
+$lang['edit']['mailbox'] = 'Editar buzón';
+$lang['edit']['description'] = 'Descripción:';
+$lang['edit']['max_aliases'] = 'Máx. alias:';
+$lang['edit']['max_quota'] = 'Máx. cuota por buzón (MiB):';
+$lang['edit']['domain_quota'] = 'Cuota de dominio:';
+$lang['edit']['backup_mx_options'] = 'Opciones del respaldo MX:';
+$lang['edit']['relay_domain'] = 'Dominio de retransmisión';
+$lang['edit']['relay_all'] = 'Retransmitir todos los recipientes';
+$lang['edit']['dkim_signature'] = 'Firma DKIM:';
+$lang['edit']['dkim_record_info'] = '<small>Por favor agrega un registro TXT con el siguiente valor a tu configuración DNS.</small>';
+$lang['edit']['relay_all_info'] = '<small>Si eliges <b>no</b> retransmitir a todos los recipientes, necesitas agregar un buzón "blind"("ciego") por cada recipiente que debe ser retransmitido.</small>';
+$lang['edit']['full_name'] = 'Nombre completo';
+$lang['edit']['full_name'] = 'Nombre completo';
+$lang['edit']['quota_mb'] = 'Cuota (MiB)';
+$lang['edit']['sender_acl'] = 'Permitir envío como:';
+$lang['edit']['sender_acl_info'] = 'Los alias no pueden deseleccionarse.';
+$lang['edit']['dkim_txt_name'] = 'Nombre del registro TXT:';
+$lang['edit']['dkim_txt_value'] = 'Valor del registro TXT:';
+$lang['edit']['previous'] = 'Página anterior';
+$lang['edit']['unchanged_if_empty'] = 'Si no hay cambios dejalo en blanco';
+$lang['edit']['dont_check_sender_acl'] = 'No verifiques remitente para el dominio %s';
+
+$lang['add']['title'] = 'Agregar objeto';
+$lang['add']['domain'] = 'Dominio';
+$lang['add']['active'] = 'Activo';
+$lang['add']['save'] = 'Guardar cambios';
+$lang['add']['description'] = 'Descripción:';
+$lang['add']['max_aliases'] = 'Máx. alias posibles:';
+$lang['add']['max_mailboxes'] = 'Máx. buzones posibles:';
+$lang['add']['mailbox_quota_m'] = 'Máx. cuota por buzón (MiB):';
+$lang['add']['domain_quota_m'] = 'Cuota total del dominio (MiB):';
+$lang['add']['backup_mx_options'] = 'Opciones del respaldo MX:';
+$lang['add']['relay_all'] = 'Retransmitir todos los recipientes';
+$lang['add']['relay_domain'] = 'Retransmitir este dominio';
+$lang['add']['relay_all_info'] = '<small>Si eliges <b>no</b> retransmitir a todos los recipientes, necesitas agregar un buzón "blind"("ciego") por cada recipiente que debe ser retransmitido.</small>';
+$lang['add']['alias'] = 'Alias';
+$lang['add']['alias_spf_fail'] = '<b>Nota:</b> Si tu dirección destino está en un buzón externo, el <b>servidor de correo que recibe</b> puede rechazar tu mensaje por una falla SPF.</a>';
+$lang['add']['alias_address'] = 'Dirección/es alias:';
+$lang['add']['alias_address_info'] = '<small>Dirección/es de correo completa/s ó @ejemplo.com, para atrapar todos los mensajes para un dominio (separado por coma). <b>Dominios mailcow solamente</b>.</small>';
+$lang['add']['alias_domain_info'] = '<small>Nombres de dominio válidos solamente (separado por coma).</small>';
+$lang['add']['target_address'] = 'Direcciones goto:';
+$lang['add']['target_address_info'] = '<small>Dirección/es de correo completa/s (separado por coma).</small>';
+$lang['add']['alias_domain'] = 'Dominio alias';
+$lang['add']['select'] = 'Por favor selecciona...';
+$lang['add']['target_domain'] = 'Dominio destino:';
+$lang['add']['mailbox'] = 'Buzón';
+$lang['add']['mailbox_username'] = 'Nombre de usuario (parte izquierda de una dirección de correo):';
+$lang['add']['full_name'] = 'Nombre completo:';
+$lang['add']['quota_mb'] = 'Cuota (MiB):';
+$lang['add']['select_domain'] = 'Por favor elige un dominio primero';
+$lang['add']['password'] = 'Constraseña:';
+$lang['add']['password_repeat'] = 'Confirmación de contraseña (repetir):';
+$lang['add']['previous'] = 'Página anterior';
+$lang['add']['restart_sogo_hint'] = '¡Necesitas reiniciar el contenedor del servicio SOGo antes de agregar un nuevo dominio!';
+
+$lang['login']['title'] = 'Inicio de sesión';
+$lang['login']['administration'] = 'Administración';
+$lang['login']['administration_details'] = 'Por favor utiliza tu inicio de sesión de Administrador para realizar tareas administrativas.';
+$lang['login']['user_settings'] = 'Configuración de usuario';
+$lang['login']['user_settings_details'] = 'Usuarios de buzón pueden utilizar mailcow UI para cambiar sus contraseñas, crear alias temporales (alias de spam), ajustar el comportamiento del filtro de spam ó importar mensajes desde un servidor IMAP remoto.';
+$lang['login']['username'] = 'Nombre de usuario';
+$lang['login']['password'] = 'Contraseña';
+$lang['login']['reset_password'] = 'Reiniciar mi contraseña';
+$lang['login']['login'] = 'Inicio de sesión';
+$lang['login']['previous'] = "Página anterior";
+$lang['login']['delayed'] = 'El inicio de sesión ha sido retrasado %s segundos.';
+
+$lang['login']['tfa'] = "Autenticación de dos factores";
+$lang['login']['tfa_details'] = "Por favor confirma tu contraseña de un solo uso en el campo de abajo";
+$lang['login']['confirm'] = "Confirmar";
+$lang['login']['otp'] = "Contraseña de un solo uso";
+$lang['login']['trash_login'] = "Inicio de sesión basura";
+
+$lang['admin']['search_domain_da'] = 'Buscar dominios';
+$lang['admin']['restrictions'] = 'Restricciones Postfix';
+$lang['admin']['rr'] = 'Restricciones Postfix para recipientes';
+$lang['admin']['sr'] = 'Restricciones Postfix para remitentes';
+$lang['admin']['reset_defaults'] = 'Restablecer los valores predeterminados';
+$lang['admin']['sr'] = 'Restricciones Postfix para remitentes';
+$lang['admin']['r_inactive'] = 'Restricciones inactivas';
+$lang['admin']['r_active'] = 'Restricciones activas';
+$lang['admin']['r_info'] = 'Elementos en gris/deshabilitados en la lista de restricciones activas no son reconocidas como restricciones válidas para mailcow y no pueden ser movidas. Restricciones desconocidas serán establecidas en el orden de aparicion de todas maneras. <br />Puedes agregar nuevos elementos en <code>inc/vars.local.inc.php</code> para ser capaz de habilitarlas.';
+$lang['admin']['public_folders'] = 'Carpetas Públicas';
+$lang['admin']['public_folders_text'] = 'Un espacio de nombres "Public" (Público) será creado. Debajo del nombre de la carpeta pública se indica el nombre del primer buzón creado automáticamente dentro de este espacio de nombres';
+$lang['admin']['public_folder_name'] = 'Nombre de la carpeta <small>(alfanumérico)</small>';
+$lang['admin']['public_folder_enable'] = 'Habilitar carpeta pública';
+$lang['admin']['public_folder_enable_text'] = 'Activar ésta opción no elimina correos en cualquier otra carpeta pública.';
+$lang['admin']['public_folder_pusf'] = 'Habilitar el indicador visto por usuario';
+$lang['admin']['public_folder_pusf_text'] = 'Un sistema habilitado por indicador "por usuario visto" no marcará un correo como leído para el Usuario B, cuando el Usuario A lo haya visto, pero el Usuario B no.';
+$lang['admin']['privacy'] = 'Privacidad';
+$lang['admin']['privacy_text'] = 'Ésta opción activa una tabla PCRE para remover "User-Agent", "X-Enigmail", "X-Mailer", "X-Originating-IP" y remplaza las cabezeras "Received: from" con localhost/127.0.0.1.';
+$lang['admin']['privacy_anon_mail'] = 'Anonimizar correo saliente';
+$lang['admin']['dkim_txt_name'] = 'Nombre del registro TXT:';
+$lang['admin']['dkim_txt_value'] = 'Valor del registro TXT:';
+$lang['admin']['dkim_key_length'] = 'Longitud de la llave DKIM (bits)';
+$lang['admin']['previous'] = 'Página anterior';
+$lang['admin']['quota_mb'] = 'Cuota (MiB):';
+$lang['admin']['sender_acl'] = 'Permitir envío como:';
+$lang['admin']['msg_size'] = 'Tamaño del mensaje';
+$lang['admin']['msg_size_limit'] = 'Límite del tamaño del mensaje ahora';
+$lang['admin']['msg_size_limit_details'] = 'Aplicando un nuebo límite reiniciará Postfix y el servidor web.';
+$lang['admin']['save'] = 'Guardar cambios';
+$lang['admin']['maintenance'] = 'Mantenimiento e Información';
+$lang['admin']['sys_info'] = 'información del sistema';
+$lang['admin']['dkim_add_key'] = 'Agregar registro DKIM';
+$lang['admin']['dkim_keys'] = 'Registros DKIM';
+$lang['admin']['add'] = 'Agregar';
+$lang['admin']['configuration'] = 'Configuración';
+$lang['admin']['password'] = 'Contraseña';
+$lang['admin']['password_repeat'] = 'Confirmación de contraseña (repetir)';
+$lang['admin']['active'] = 'Activo';
+$lang['admin']['action'] = 'Acción';
+$lang['admin']['add_domain_admin'] = 'Agregar Administrador del dominio';
+$lang['admin']['admin_domains'] = 'Asignaciones de dominio';
+$lang['admin']['domain_admins'] = 'Administradores de dominio';
+$lang['admin']['username'] = 'Nombre de usuario';
+$lang['admin']['edit'] = 'Editar';
+$lang['admin']['remove'] = 'Eliminar';
+$lang['admin']['save'] = 'Guardar cambios';
+$lang['admin']['admin'] = 'Administrador';
+$lang['admin']['admin_details'] = 'Editar detalles del administrador';
+$lang['admin']['unchanged_if_empty'] = 'Si no hay cambios dejalo en blanco';
+$lang['admin']['yes'] = '&#10004;';
+$lang['admin']['no'] = '&#10008;';
+$lang['admin']['access'] = 'Acceso';
+$lang['admin']['invalid_max_msg_size'] = 'Tamaño máx. del mensaje no válido';
+$lang['admin']['site_not_found'] = 'No se puede localizar la configuración del sitio de mailcow';
+$lang['admin']['public_folder_empty'] = 'El nombre de la carpeta pública no debe estar vacío';
+$lang['admin']['set_rr_failed'] = 'No se pueden establecer las restricciones de Postfix';
+$lang['admin']['no_record'] = 'Sin registro';
+?>
diff --git a/data/web/lang/lang.nl.php b/data/web/lang/lang.nl.php
new file mode 100644
index 00000000..35192ddb
--- /dev/null
+++ b/data/web/lang/lang.nl.php
@@ -0,0 +1,369 @@
+<?php
+/*
+//
+//  Dutch language file
+*/
+$lang['footer']['loading'] = "Even geduld a.u.b. ...";
+$lang['header']['restart_sogo'] = 'SOGo herstarten';
+$lang['footer']['restart_sogo'] = 'SOGo herstarten';
+$lang['footer']['restart_now'] = 'Nu opnieuw starten';
+$lang['footer']['restart_sogo_info'] = 'Sommige taken, zoals het toevoegen van een domein, vereisen een herstart van SOGo om de veranderingen door te voeren.<br /><br /><b>Belangrijk:</b> Het opnieuw opstarten kan een poos duren, wacht a.u.b. totdat dit volledig voltooid is.';
+$lang['dkim']['confirm'] = "Weet u het zeker?";
+$lang['danger']['dkim_not_found'] = "DKIM record niet gevonden.";
+$lang['danger']['dkim_remove_failed'] = "Kan geselecteerde DKIM record niet verwijderen.";
+$lang['danger']['dkim_add_failed'] = "Kan  DKIM record niet toevoegen.";
+$lang['danger']['dkim_domain_or_sel_invalid'] = "DKIM domein of selector zijn ongeldig.";
+$lang['danger']['dkim_key_length_invalid'] = "Lengte DKIM sleutel ongeldig.";
+$lang['success']['dkim_removed'] = "DKIM record is verwijderd.";
+$lang['success']['dkim_added'] = "DKIM record is opgeslagen.";
+$lang['danger']['access_denied'] = "Toegang geweigerd of ongeldige gegevens.";
+$lang['danger']['whitelist_from_invalid'] = "Witte lijst invoer ongeldig.";
+$lang['danger']['domain_invalid'] = "Domeinnaam is ongeldig.";
+$lang['danger']['mailbox_quota_exceeds_domain_quota'] = "Max. quotum > Domeinquotum.";
+$lang['danger']['object_is_not_numeric'] = "%s is niet numeriek.";
+$lang['success']['domain_added'] = "Domein toegevoegd: %s.";
+$lang['danger']['alias_empty'] = "Aliasadres mag niet leeg blijven.";
+$lang['danger']['goto_empty'] = "Doeladres mag niet leeg blijven.";
+$lang['danger']['policy_list_from_exists'] = "Deze invoer bestaat al.";
+$lang['danger']['policy_list_from_invalid'] = "Deze invoer heeft een ongeldig format.";
+$lang['danger']['whitelist_exists'] = "Deze invoer staat op de witte lijst.";
+$lang['danger']['whitelist_from_invalid'] = "Witte lijst invoer heeft een ongeldig format.";
+$lang['danger']['alias_invalid'] = "Aliasadres is ongeldig.";
+$lang['danger']['goto_invalid'] = "Doeladres is ongeldig.";
+$lang['danger']['alias_domain_invalid'] = "Aliasdomein is ongeldig.";
+$lang['danger']['target_domain_invalid'] = "Doeldomein is ongeldig.";
+$lang['danger']['object_exists'] = "Object %s bestaat reeds.";
+$lang['danger']['domain_exists'] = "Domein %s bestaat reeds.";
+$lang['danger']['alias_goto_identical'] = "Het Aliasadres en het Doeladres moeten van elkaar verschillen.";
+$lang['danger']['aliasd_targetd_identical'] = "Het Aliasdomein kan niet gelijk zijn aan het doel.";
+$lang['success']['alias_added'] = "Aliasadres(sen) toegevoegd.";
+$lang['success']['alias_modified'] = "Wijzigingen aan Alias zijn opgeslagen.";
+$lang['success']['mailbox_modified'] = "Wijzigingen aan postvak %s zijn opgeslagen.";
+$lang['success']['msg_size_saved'] = "Maximale berichtgrootte opgeslagen.";
+$lang['danger']['aliasd_not_found'] = "Aliasdomein werd niet gevonden.";
+$lang['danger']['targetd_not_found'] = "Doeldomein werd niet gevonden.";
+$lang['danger']['aliasd_exists'] = "Aliasdomein bestaat al.";
+$lang['success']['aliasd_added'] = "Aliasdomein %s toegevoegd.";
+$lang['success']['aliasd_modified'] = "Wijzigingen aan aliasdomein %s zijn opgeslagen.";
+$lang['success']['domain_modified'] = "Wijzigingen aan domein %s zijn opgeslagen.";
+$lang['success']['domain_admin_modified'] = "Wijzigingen aan domeinbeheerder %s zijn opgeslagen.";
+$lang['success']['domain_admin_added'] = "Domeinbeheerder %s is toegevoegd.";
+$lang['success']['changes_general'] = 'Wijzigingen zijn opgeslagen.';
+$lang['success']['admin_modified'] = "Wijzigingen aan de beheerder zijn opgeslagen.";
+$lang['danger']['exit_code_not_null'] = "Fout: Exitcode was %d.";
+$lang['danger']['mailbox_not_available'] = "Postvak niet beschikbaar.";
+$lang['danger']['username_invalid'] = "Gebruikersnaam kan niet worden gebruikt.";
+$lang['danger']['password_mismatch'] = "Bevestigingswachtwoord verschilt.";
+$lang['danger']['password_complexity'] = "Het wachtwoord voldoet niet aan de vereisten.";
+$lang['danger']['password_empty'] = "Er moet een wachtwoord worden ingesteld.";
+$lang['danger']['login_failed'] = "Aanmelding mislukt.";
+$lang['danger']['mailbox_invalid'] = "De naam van het postvak is ongeldig.";
+$lang['danger']['mailbox_invalid_suggest'] = "De naam van het postvak is ongeldig, bedoelde u \"%s\"?";
+$lang['info']['fetchmail_planned'] = "Taak voor het ophalen van e-mails is gepland. Controleer dit proces later.";
+$lang['danger']['fetchmail_source_empty'] = "Geef een bron-map op.";
+$lang['danger']['fetchmail_dest_empty'] = "Geef een doel-map op.";
+$lang['danger']['is_alias'] = "%s is reeds een Aliasadres.";
+$lang['danger']['is_alias_or_mailbox'] = "%s is reeds een Alias of een postvak.";
+$lang['danger']['is_spam_alias'] = "%s is reeds bekend als spam-alias adres.";
+$lang['danger']['quota_not_0_not_numeric'] = "Quotum moet numeriek zijn en >= 0.";
+$lang['danger']['domain_not_found'] = "Domein werd niet gevonden.";
+$lang['danger']['max_mailbox_exceeded'] = "Max. aantal postvakken overschreden (%d van %d).";
+$lang['danger']['mailbox_quota_exceeded'] = "Quotum heeft het domeinlimiet overschreven (max. %d MiB).";
+$lang['danger']['mailbox_quota_left_exceeded'] = "Onvoldoende ruimte beschikbaar (%d MiB).";
+$lang['success']['mailbox_added'] = "Postvak %s is toegevoegd.";
+$lang['success']['domain_removed'] = "Domein %s is verwijderd.";
+$lang['success']['alias_removed'] = "Aliasadres %s is verwijderd.";
+$lang['success']['alias_domain_removed'] = "Aliasdomein %s is verwijderd.";
+$lang['success']['domain_admin_removed'] = "Domeinbeheerder %s is verwijderd.";
+$lang['success']['mailbox_removed'] = "Postvak %s  is verwijderd.";
+$lang['danger']['max_quota_in_use'] = "Postvakquotum moet >= %d MiB.";
+$lang['danger']['domain_quota_m_in_use'] = "Domeinquotum moet >= %s MiB.";
+$lang['danger']['mailboxes_in_use'] = "Maximaal aantal postvakken moet >= %d.";
+$lang['danger']['aliases_in_use'] = "Maximaal aantal aliassen moet >= %d.";
+$lang['danger']['sender_acl_invalid'] = "Verzender ACL-waarde is ongeldig.";
+$lang['danger']['domain_not_empty'] = "Kan domein in gebruik niet verwijderen.";
+$lang['warning']['spam_alias_temp_error'] = "Tijdelijke fout: Kan geen spam-alias toevoegen. Probeer het later nogmaals.";
+$lang['danger']['spam_alias_max_exceeded'] = "Maximaal aantal spam-aliassen bereikt.";
+$lang['danger']['fetchmail_active'] = "Er draait reeds een proces, wacht tot deze klaar is.";
+$lang['danger']['validity_missing'] = 'Voer een geldigheidstermijn in.';
+$lang['user']['on'] = "Aan";
+$lang['user']['off'] = "Uit";
+$lang['user']['user_change_fn'] = "";
+$lang['user']['user_settings'] = 'Gebruikersinstellingen';
+$lang['user']['mailbox_settings'] = 'Postvakinstellingen';
+$lang['user']['mailbox_details'] = 'Postvakdetails';
+$lang['user']['change_password'] = 'Verander wachtwoord';
+$lang['user']['new_password'] = 'Nieuw wachtwoord';
+$lang['user']['save_changes'] = 'Wijzigingen opslaan';
+$lang['user']['password_now'] = 'Huidig wachtwoord (bevestig wijzigingen)';
+$lang['user']['new_password_repeat'] = 'Bevestig wachtwoord (herhalen)';
+$lang['user']['new_password_description'] = 'Vereisten: 6 karakters lang, letters en nummers.';
+$lang['user']['did_you_know'] = '<b>Wist u dat?</b> U kunt tags in het e-mailadres gebruiken ("me+<b>prive</b>@voorbeeld.nl") om berichten automatisch naar een bijbehorende map te sturen (voorbeeld: "prive").';
+$lang['user']['spam_aliases'] = 'Tijdelijk e-mailadres';
+$lang['user']['alias'] = 'Alias';
+$lang['user']['alias_create_random'] = 'Creëer willekeurige alias';
+$lang['user']['alias_extend_all'] = 'Verleng alias met 1 uur';
+$lang['user']['alias_valid_until'] = 'Geldig tot';
+$lang['user']['alias_remove_all'] = 'Verwijder alle aliassen';
+$lang['user']['alias_time_left'] = 'Tijd over';
+$lang['user']['alias_full_date'] = 'd.m.Y, H:i:s T';
+$lang['user']['alias_select_validity'] = 'Geldigheid';
+$lang['user']['hour'] = 'Uur';
+$lang['user']['hours'] = 'Uren';
+$lang['user']['day'] = 'Dag';
+$lang['user']['week'] = 'Week';
+$lang['user']['weeks'] = 'Weken';
+$lang['user']['spamfilter'] = 'Spam filter';
+$lang['user']['spamfilter_wl'] = 'Witte lijst';
+$lang['user']['spamfilter_wl_desc'] = 'Zet e-mailadressen op de witte lijst om ze <b>nooit</b> als spam te classificeren. Wildcards (*) zijn toegestaan.';
+$lang['user']['spamfilter_bl'] = 'Zwarte lijst';
+$lang['user']['spamfilter_bl_desc'] = 'E-mailadressen op de zwarte lijst worden <b>altijd</b> als spam geclassificeerd en geweigerd. Wildcards (*) zijn toegestaan.';
+$lang['user']['spamfilter_behavior'] = 'Beoordeling';
+$lang['user']['spamfilter_table_rule'] = 'Regel';
+$lang['user']['spamfilter_table_action'] = 'Handeling';
+$lang['user']['spamfilter_table_empty'] = 'Geen gegevens om weer te geven.';
+$lang['user']['spamfilter_table_remove'] = 'verwijder';
+$lang['user']['spamfilter_table_add'] = 'Voeg item toe';
+$lang['user']['spamfilter_default_score'] = 'Spamscore:';
+$lang['user']['spamfilter_green'] = 'Groen: Dit bericht is geen spam.';
+$lang['user']['spamfilter_yellow'] = 'Geel: Dit bericht is mogelijk spam, zal worden gelabeled en verplaatst worden naar de Junk-map.';
+$lang['user']['spamfilter_red'] = 'Rood: Dit bericht is spam en zal worden geweigerd.';
+$lang['user']['spamfilter_default_score'] = 'Standaardwaarden:';
+$lang['user']['spamfilter_hint'] = 'De eerste waarde omschrijft een "lage spam score", de tweede waarde een "hoge spam score".';
+
+$lang['user']['tls_policy_warning'] = '<strong>Attentie:</strong> Door versleutelde e-mails te forceren, worden mogelijk niet alle e-mails afgeleverd.<br />Berichten die niet aan het ingestelde beleid voldoen worden resoluut geweigerd (bounced met hard-fail).';
+$lang['user']['tls_policy'] = 'Versleutelbeleid';
+$lang['user']['tls_enforce_in'] = 'Forceer TLS-gebruik inkomend';
+$lang['user']['tls_enforce_out'] = 'Forceer TLS-gebruik uitgaand';
+$lang['user']['no_record'] = 'Geen vermelding.';
+
+$lang['user']['misc_settings'] = 'Andere profielinstellingen';
+$lang['user']['misc_delete_profile'] = 'Andere profielinstellingen';
+$lang['user']['tag_handling'] = 'Omgaan met e-mail tags';
+$lang['user']['tag_in_subfolder'] = 'In onderliggende map';
+$lang['user']['tag_in_subject'] = 'In onderwerp';
+$lang['user']['tag_help_explain'] = 'In onderliggende map: maakt onder INBOX een nieuwe map aan met de naam van de tag (bijv.: "INBOX/Facebook").<br />
+In onderwerp: de tag wordt vóór het oorspronkelijke e-mail onderwerp geplaatst (bijv.: "[Facebook] Mijn nieuws").';
+$lang['user']['tag_help_example'] = 'Voorbeeld van een e-mailadres met tag: ik<b>+Facebook</b>@voorbeeld.org';
+$lang['start']['dashboard'] = '%s - startpagina';
+$lang['start']['start_rc'] = 'Open Roundcube';
+$lang['start']['start_sogo'] = 'Open SOGo';
+$lang['start']['mailcow_apps_detail'] = 'Gebruik een mailcow app om toegang te hebben tot uw e-mails, kalender, contactpersonen en meer.';
+$lang['start']['mailcow_panel'] = 'Start mailcow UI';
+$lang['start']['mailcow_panel_description'] = 'De mailcow UI is beschikbaar voor zowel beheerders als gebruikers.';
+$lang['start']['mailcow_panel_detail'] = '<b>Domeinbeheerders</b> kunnen postvakken en aliassen aanmaken, wijzigen of verwijderen, domeinen veranderen of informatie krijgen over hun domein.<br />
+	<b>Gebruikers</b> kunnen tijdsgelimiteerde aliassen (spam-aliasses) aanmaken, hun wachtwoord wijzigen en spamfilterinstellingen wijzigen.';
+$lang['start']['recommended_config'] = 'Aanbevoen instellingen (zonder ActiveSync)';
+$lang['start']['imap_smtp_server'] = 'IMAP- en SMTP-server gegevens';
+$lang['start']['imap_smtp_server_description'] = 'Voor de best mogelijke ervaring bevelen wij <a href="%s" target="_blank"><b>Mozilla Thunderbird</b></a> aan.';
+$lang['start']['imap_smtp_server_badge'] = 'Lees/schrijf e-mails';
+$lang['start']['imap_smtp_server_auth_info'] = 'Gebruik uw volledige e-mailadres en de onversleutelde (PLAIN) verificatiemechanisme.<br />
+De aanmeldgegevens zullen door de server worden versleuteld.';
+$lang['start']['managesieve'] = 'ManageSieve';
+$lang['start']['managesieve_badge'] = 'Emailfilter';
+$lang['start']['managesieve_description'] = 'Gebruik <b>Mozilla Thunderbird</b> met een <a style="text-decoration:none" target="_blank" href="%s"><b>nightly sieve addon</b></a>.<br />Start Thunderbird, open de add-on instellingen en sleep het gedownloadde xpi-bestand naar dit venster.<br />Servernaam <b>%s</b>, Poort <b>4190</b>. De aanmeldgegevens zijn gelijk aan de gegevens voor uw e-mail.';
+$lang['start']['service'] = 'Service';
+$lang['start']['encryption'] = 'Versleutelmethode';
+$lang['start']['help'] = 'Toon/Verberg Hulppaneel';
+$lang['start']['hostname'] = 'Hostname';
+$lang['start']['port'] = 'Poort';
+$lang['start']['footer'] = '';
+$lang['header']['mailcow_settings'] = 'Instellingen';
+$lang['header']['administration'] = 'Beheer';
+$lang['header']['mailboxes'] = 'Postvakken';
+$lang['header']['user_settings'] = 'Gebruikersinstellingen';
+$lang['header']['login'] = 'Aanmelden';
+$lang['header']['logged_in_as_logout'] = 'Aangemeld als <b>%s</b> (Afmelden)';
+$lang['header']['locale'] = 'Taal';
+$lang['mailbox']['domain'] = 'Domein';
+$lang['mailbox']['alias'] = 'Alias';
+$lang['mailbox']['aliases'] = 'Aliassen';
+$lang['mailbox']['domains'] = 'Domeinen';
+$lang['mailbox']['mailboxes'] = 'Mailboxen';
+$lang['mailbox']['mailbox_quota'] = 'Max. grootte van een postvak';
+$lang['mailbox']['domain_quota'] = 'Quotum';
+$lang['mailbox']['active'] = 'Actief';
+$lang['mailbox']['action'] = 'Handeling';
+$lang['mailbox']['ratelimit'] = 'Maximale snelheid uitgaand/uur';
+$lang['mailbox']['backup_mx'] = 'Backup MX';
+$lang['mailbox']['domain_aliases'] = 'Domein-aliassen';
+$lang['mailbox']['target_domain'] = 'Doeldomein';
+$lang['mailbox']['target_address'] = 'Doeladres';
+$lang['mailbox']['username'] = 'Gebruikersnaam';
+$lang['mailbox']['fname'] = 'Volledige naam';
+$lang['mailbox']['filter_table'] = 'Filter tabel';
+$lang['mailbox']['yes'] = '&#10004;';
+$lang['mailbox']['no'] = '&#10008;';
+$lang['mailbox']['quota'] = 'Quotum';
+$lang['mailbox']['in_use'] = 'In gebruik (%)';
+$lang['mailbox']['msg_num'] = 'Berichten #';
+$lang['mailbox']['remove'] = 'Verwijder';
+$lang['mailbox']['edit'] = 'Wijzig';
+$lang['mailbox']['archive'] = 'Archief';
+$lang['mailbox']['no_record'] = 'Geen vermelding';
+$lang['mailbox']['add_domain'] = 'Toevoegen domein';
+$lang['mailbox']['add_domain_alias'] = 'Toevoegen domein-alias';
+$lang['mailbox']['add_mailbox'] = 'Toevoegen postvak';
+$lang['mailbox']['add_alias'] = 'Toevoegen alias';
+
+$lang['info']['no_action'] = 'Geen handelingen uitvoerbaar';
+
+$lang['delete']['title'] = 'Verwijder object';
+$lang['delete']['remove_domain_warning'] = '<b>Let op:</b> U staat op het punt domein <b>%s</b> te verwijderen!';
+$lang['delete']['remove_domainalias_warning'] = '<b>Let op:</b> U staat op het punt domeinalias <b>%s</b> te verwijderen!';
+$lang['delete']['remove_domainadmin_warning'] = '<b>Let op:</b> U staat op het punt domeinbeheerder <b>%s</b> te verwijderen!';
+$lang['delete']['remove_alias_warning'] = '<b>Let op:</b> U staat op het punt alias <b>%s</b> te verwijderen!';
+$lang['delete']['remove_mailbox_warning'] = '<b>Let op::</b> U staat op het punt postvak <b>%s</b> te verwijderen!';
+$lang['delete']['remove_mailbox_details'] = 'Het postvak zal <b>permanent</b> worden verwijderd!';
+$lang['delete']['remove_domain_details'] = 'Dit verwijdert ook de domeinaliassen. <br /><br /><b>Een domein moet leeg zijn alvorens deze verwijderd kan worden.</b>';
+$lang['delete']['remove_alias_details'] = '<b>Gebruikers zullen niet meer in staat zijn e-mails te ontvangen op -of te versturen vanaf- dit adres.</b>';
+$lang['delete']['remove_button'] = 'Verwijder';
+$lang['delete']['previous'] = 'Vorige pagina';
+
+$lang['edit']['save'] = 'Wijzigingen opslaan';
+$lang['edit']['archive'] = 'Toegang tot archief';
+$lang['edit']['max_mailboxes'] = 'Max. aantal postvakken:';
+$lang['edit']['title'] = 'Wijzig object';
+$lang['edit']['target_address'] = 'Doeladres(sen) <small>(scheiden met komma)</small>:';
+$lang['edit']['active'] = 'Actief';
+$lang['edit']['target_domain'] = 'Doeldomein:';
+$lang['edit']['password'] = 'Wachtwoord:';
+$lang['edit']['ratelimit'] = 'Uitgaande e-mail beperking (aantal/uur):';
+$lang['danger']['ratelimt_less_one'] = 'De uitgaande e-mail beperking moet >= 1';
+$lang['edit']['password_repeat'] = 'Bevestig wachtwoord (herhalen):';
+$lang['edit']['domain_admin'] = 'Wijzig domeinbeheerder';
+$lang['edit']['domain'] = 'Wijzig domein';
+$lang['edit']['alias_domain'] = 'Aliasdomein';
+$lang['edit']['edit_alias_domain'] = 'Wijzig aliasdomein';
+$lang['edit']['domains'] = 'Domeinen';
+$lang['edit']['destroy'] = 'Handmatige invoer';
+$lang['edit']['alias'] = 'Wijzig alias';
+$lang['edit']['mailbox'] = 'Wijzig postvak';
+$lang['edit']['description'] = 'Beschrijving:';
+$lang['edit']['max_aliases'] = 'Max. aliassen:';
+$lang['edit']['max_quota'] = 'Max. grootte per postvak (MiB):';
+$lang['edit']['domain_quota'] = 'Domeinquotum';
+$lang['edit']['backup_mx_options'] = 'Backup MX opties:';
+$lang['edit']['relay_domain'] = 'Doorschakeldomein';
+$lang['edit']['relay_all'] = 'Schakel alle ontvangers door';
+$lang['edit']['dkim_signature'] = 'DKIM handtekening:';
+$lang['edit']['dkim_record_info'] = '<small>Voeg de volgende TXT-record toe aan de DNS-instellingen van uw domein.</small>';
+$lang['edit']['relay_all_info'] = '<small>Indien u ervoor kiest om <b>niet</b> alle ontvangers door te schakelen, dient u per ontvanger die u wenst door te schakelen een (lege) postvak aan te maken.</small>';
+$lang['edit']['full_name'] = 'Volledige naam';
+$lang['edit']['quota_mb'] = 'Quotum (MiB)';
+$lang['edit']['sender_acl'] = 'Toestaan te verzenden als:';
+$lang['edit']['sender_acl_info'] = 'Aliassen kunnen niet worden deselecteerd.';
+$lang['edit']['dkim_txt_name'] = 'Naam TXT-record:';
+$lang['edit']['dkim_txt_value'] = 'Waarde TXT-record:';
+$lang['edit']['previous'] = 'Vorige pagina';
+$lang['edit']['unchanged_if_empty'] = 'Leeg laten indien niet veranderd.';
+$lang['edit']['dont_check_sender_acl'] = 'Geen zenderverificatie uitvoeren voor domein %s.';
+
+$lang['add']['title'] = 'Object toevoegen';
+$lang['add']['domain'] = 'Domein';
+$lang['add']['active'] = 'Actief';
+$lang['add']['save'] = 'Wijzigingen opslaan';
+$lang['add']['description'] = 'Beschrijving:';
+$lang['add']['max_aliases'] = 'Max. aantal aliassen:';
+$lang['add']['max_mailboxes'] = 'Max. aantal postvakken:';
+$lang['add']['mailbox_quota_m'] = 'Max. grootte postvak (MiB):';
+$lang['add']['domain_quota_m'] = 'Totale grootte domein (MiB):';
+$lang['add']['backup_mx_options'] = 'Backup MX opties:';
+$lang['add']['relay_all'] = 'Doorschakelen van alle ontvangers';
+$lang['add']['relay_domain'] = 'Schakel dit domein door';
+$lang['add']['relay_all_info'] = '<small>Indien u ervoor kiest om <b>niet</b> alle ontvangers door te schakelen, moet u voor iedere ontvanger die u wenst door te schakelen een een (lege) mailbox aanmaken.</small>';
+$lang['add']['alias'] = 'Alias(sen)';
+$lang['add']['alias_spf_fail'] = '<b>Opmerking:</b> Als het gekozen doeladres een extern postvak is, kan de <b>ontvangende mailservver</b> mogelijk het bericht weigeren vanwege niet kloppende SPF-gegevens.</a>';
+$lang['add']['alias_address'] = 'Aliasadres(sen):';
+$lang['add']['alias_address_info'] = '<small>Volledig(e) emailadres(sen) of @voorbeeld.nl om alle berichten op te vangen van een domein (scheiden met komma). <b>Alleen mailcow-domeinen!</b></small>';
+$lang['add']['alias_domain_info'] = '<small>Alleen geldige domeinen (scheiden met komma).</small>';
+$lang['add']['target_address'] = 'Doeladressen:';
+$lang['add']['target_address_info'] = '<small>Volledig(e) e-mailadres(sen) (scheiden met komma).</small>';
+$lang['add']['alias_domain'] = 'Aliasdomein';
+$lang['add']['select'] = 'Kies uit...';
+$lang['add']['target_domain'] = 'Doeldomein:';
+$lang['add']['mailbox'] = 'Postvak';
+$lang['add']['mailbox_username'] = 'Gebruikersnaam (linker deel van het e-mailadres):';
+$lang['add']['full_name'] = 'Volledige naam:';
+$lang['add']['quota_mb'] = 'Quotum (MiB):';
+$lang['add']['select_domain'] = 'Selecteer eerst een domein';
+$lang['add']['password'] = 'Wachtwoord:';
+$lang['add']['password_repeat'] = 'Bevestig wachtwoord (herhalen):';
+$lang['add']['previous'] = 'Vorige pagina';
+$lang['add']['restart_sogo_hint'] = 'SOGo dient opnieuw te worden gestart nadat een domein is toegevoegd!';
+
+$lang['login']['title'] = 'Aanmelden';
+$lang['login']['administration'] = 'Beheer';
+$lang['login']['administration_details'] = 'Gebruik uw beheerders-login om administratieve taken uit te voeren.';
+$lang['login']['user_settings'] = 'Gebruikersinstellingen';
+$lang['login']['user_settings_details'] = 'Mailbox-gebruikers kunnen in het mailcow-gebruikersbeheer hun wachtwoorden wijzigen, tijdelijke aliassen aanmaken (tegengaan van spam), de spamfilter aanpassen of berichten van externe IMAP-servers importeren.';
+$lang['login']['username'] = 'Gebruikersnaam';
+$lang['login']['password'] = 'Wachtwoord';
+$lang['login']['reset_password'] = 'Wijzig mijn wachtwoord';
+$lang['login']['login'] = 'Aanmelden';
+$lang['login']['previous'] = "Vorige pagina";
+$lang['login']['delayed'] = 'Aanmelding met %s sec. vertraagd.';
+
+$lang['login']['tfa'] = "Two-factor authentication";
+$lang['login']['tfa_details'] = "Voer uw eenmalige wachtwoord hieronder in.";
+$lang['login']['confirm'] = "Bevestigen";
+$lang['login']['otp'] = "Eenmalig wachtwoord";
+$lang['login']['trash_login'] = "Verwijder login";
+
+$lang['admin']['search_domain_da'] = 'Doorzoek domeinen';
+$lang['admin']['restrictions'] = 'Postfix beperkingen';
+$lang['admin']['rr'] = 'Postfix ontvangersbeperkingen';
+$lang['admin']['sr'] = 'Postifx verzendersbeperkingen';
+$lang['admin']['reset_defaults'] = 'Herstel standaardwaarden';
+$lang['admin']['r_inactive'] = 'Inactieve beperkingen';
+$lang['admin']['r_active'] = 'Actieve beperkignen';
+$lang['admin']['r_info'] = 'Grijze, uitgeschakelde, elementen in de lijst met actieve beperkingen zijn voor mailcow niet bekend als valide en kunnen daarom niet verplaatst worden.<br />U kunt nieuwe elementen toevoegen in <code>inc/vars.inc.php</code> om ze te (de)activeren.';
+$lang['admin']['public_folders'] = 'Gemeenschappelijke mappen';
+$lang['admin']['public_folders_text'] = 'Een namespace "Public" wordt aangemaakt. Onder deze map worden de automatisch aangemaakte postvakken in deze namespace weergegeven.';
+$lang['admin']['public_folder_name'] = 'Mapnaam <small>(alphanumeriek)</small>';
+$lang['admin']['public_folder_enable'] = 'Inschakelen gemeenschappelijke map';
+$lang['admin']['public_folder_enable_text'] = 'Deze optie uitschakelen verwijderd géén e-mails uit de gemeenschappelijke mappen.';
+$lang['admin']['public_folder_pusf'] = 'De \'gelezen\'-markering per gebruiker';
+$lang['admin']['public_folder_pusf_text'] = 'Deze "\'gelezen\'-markering per gebruiker"-optie zorgt ervoor dat er per gebruiker afzonderlijk wordt bepaald of deze het bericht heeft gelezen.';
+$lang['admin']['privacy'] = 'Privacy';
+$lang['admin']['privacy_text'] = 'Deze optie activeert een PCRE-tabel die de "User-Agent", "X-Enigmail", "X-Mailer", "X-Originating-IP"-headers verwijderd en de "Received: from"-headers vervangt met localhost/127.0.0.1.';
+$lang['admin']['privacy_anon_mail'] = 'Anonimiseer uitgaande e-mails';
+$lang['admin']['dkim_txt_name'] = 'Naam TXT-record:';
+$lang['admin']['dkim_txt_value'] = 'Waarde TXT-record:';
+$lang['admin']['dkim_key_length'] = 'DKIM sleutel lengte (bits)';
+$lang['admin']['previous'] = 'Vorige pagina';
+$lang['admin']['quota_mb'] = 'Quotum (MiB):';
+$lang['admin']['sender_acl'] = 'Toestaan te verzenden als:';
+$lang['admin']['msg_size'] = 'Berichtgrootte';
+$lang['admin']['msg_size_limit'] = 'Huidige limiet berichtgroote';
+$lang['admin']['msg_size_limit_details'] = 'Een nieuw limiet doorvoeren zal Postfix en de webserver herladen.';
+$lang['admin']['save'] = 'Wijzigingen opslaan';
+$lang['admin']['maintenance'] = 'Onderhoud en informatie';
+$lang['admin']['sys_info'] = 'Systeeminformatie';
+$lang['admin']['dkim_add_key'] = 'DKIM-record toevoegen';
+$lang['admin']['dkim_keys'] = 'DKIM records';
+$lang['admin']['add'] = 'Toevoegen';
+$lang['admin']['configuration'] = 'Instellingen';
+$lang['admin']['password'] = 'Wachtwoord';
+$lang['admin']['password_repeat'] = 'Bevestig wachtwoord (herhalen)';
+$lang['admin']['active'] = 'Actief';
+$lang['admin']['action'] = 'Handeling';
+$lang['admin']['add_domain_admin'] = 'Voeg domeinbeheerder toe';
+$lang['admin']['admin_domains'] = 'Toegewezen domein';
+$lang['admin']['domain_admins'] = 'Domeinbeheerders';
+$lang['admin']['username'] = 'Gebruikersnaam';
+$lang['admin']['edit'] = 'Wijzig';
+$lang['admin']['remove'] = 'Verwijder';
+$lang['admin']['save'] = 'Wijzigingen opslaan';
+$lang['admin']['admin'] = 'Beheerder';
+$lang['admin']['admin_details'] = 'Wijzig details beheerder';
+$lang['admin']['unchanged_if_empty'] = 'Leeg laten indien onveranderd';
+$lang['admin']['yes'] = '&#10004;';
+$lang['admin']['no'] = '&#10008;';
+$lang['admin']['access'] = 'Toegang';
+$lang['admin']['invalid_max_msg_size'] = 'Ongeldige max. berichtgrootte';
+$lang['admin']['site_not_found'] = 'Kan mailcow instellingenbeheer niet vinden';
+$lang['admin']['public_folder_empty'] = 'Namen van gemeenschappelijke mappen mogen niet leeg blijven.';
+$lang['admin']['set_rr_failed'] = 'Kan Postfix beperkingen niet opleggen.';
+$lang['admin']['no_record'] = 'Geen vermelding';
+?>
diff --git a/data/web/lang/lang.pt.php b/data/web/lang/lang.pt.php
new file mode 100644
index 00000000..f8c0e330
--- /dev/null
+++ b/data/web/lang/lang.pt.php
@@ -0,0 +1,355 @@
+<?php
+/*
+//
+//  Portuguese (pt) language file - Português do Brasil (pt_BR) - ISO-8859-1
+//
+*/
+$lang['footer']['loading'] = "Aguarde...";
+$lang['getmail']['no_status'] = "Nenhum registro anterior encontrado";
+$lang['dkim']['confirm'] = "Tem certeza?";
+$lang['danger']['dkim_not_found'] = "Registro DKIM não encontrado";
+$lang['danger']['dkim_remove_failed'] = "Não foi possível remover o registro DKIM selecionado";
+$lang['danger']['dkim_add_failed'] = "Não foi possível adicionar o registro DKIM fornecido";
+$lang['danger']['dkim_domain_or_sel_invalid'] = " Registro DKIM inválido";
+$lang['danger']['dkim_key_length_invalid'] = "Registro DKIM com tamanho inválido";
+$lang['success']['dkim_removed'] = " Registro DKIM removido com sucesso";
+$lang['success']['dkim_added'] = "Registro DKIM salvo com sucesso";
+$lang['danger']['access_denied'] = "Acesso negado ou dados inválidos";
+$lang['danger']['domain_invalid'] = "Domínio inválido";
+$lang['danger']['mailbox_quota_exceeds_domain_quota'] = "Max. espaço excede o espaço do domínio";
+$lang['danger']['object_is_not_numeric'] = "Valor %s não é numérico";
+$lang['success']['domain_added'] = "Domínio adicionado %s";
+$lang['danger']['alias_empty'] = "Você deve preencher o campo do Apelido";
+$lang['danger']['goto_empty'] = "Você deve preencher o campo Encaminhar para";
+$lang['danger']['blacklist_exists'] = "O registro já existe na BlackList";
+$lang['danger']['blacklist_from_invalid'] = "O registro Blacklist possui formato inválido";
+$lang['danger']['whitelist_exists'] = "O registro já existe na WhiteList";
+$lang['danger']['whitelist_from_invalid'] = "O registro Whitelist possui formato inválido";
+$lang['danger']['alias_invalid'] = "O endereço digitado como Apelido é inválido";
+$lang['danger']['goto_invalid'] = "O endereço digitado como Encaminhar para é inválido";
+$lang['danger']['alias_domain_invalid'] = "O endereço do Encaminhamento de Domínio é inválido";
+$lang['danger']['target_domain_invalid'] = "O endereço de Domínio Destino é inválido";
+$lang['danger']['object_exists'] = "Objeto %s já existe";
+$lang['danger']['domain_exists'] = "Domínio %s já existe";
+$lang['danger']['alias_goto_identical'] = "o Apelido e o Encaminhar para devem ser diferentes";
+$lang['danger']['aliasd_targetd_identical'] = "o Encaminhamento de Domínio não pode ser igual ao Domínio Destino";
+$lang['success']['alias_added'] = "Apelido(s) adicionado(s) com sucesso";
+$lang['success']['alias_modified'] = "Apelido(s) alterados(s) com sucesso";
+$lang['success']['aliasd_modified'] = "Direcionamento de Domínio(s) alterados(s) com sucesso";
+$lang['success']['mailbox_modified'] = "A conta %s foi alterada com sucesso";
+$lang['success']['msg_size_saved'] = "Limite do tamanho de mensagem ajustado com sucesso";
+$lang['danger']['aliasd_not_found'] = "Encaminhamento de Domínio não encontrado";
+$lang['danger']['targetd_not_found'] = "Domínio de Destino não encontrado";
+$lang['danger']['aliasd_exists'] = "Encaminhamento de Domínio já existe";
+$lang['success']['aliasd_added'] = "Adicionado Encaminhamento de Domínio %s";
+$lang['success']['aliasd_modified'] = "Encaminhamento de Domínio %s alterado com sucesso";
+$lang['success']['domain_modified'] = "Domínio %s alterado com sucesso";
+$lang['success']['domain_admin_modified'] = "Changes to domain administrator %s have been saved";
+$lang['success']['domain_admin_added'] = "Domínio administrator %s has been added";
+$lang['success']['changes_general'] = 'Alteração efetuada com sucesso';
+$lang['success']['admin_modified'] = "Administrador alterado com sucesso";
+$lang['danger']['exit_code_not_null'] = "Falha: código de erro %d";
+$lang['danger']['mailbox_not_available'] = "Conta não disponível";
+$lang['danger']['username_invalid'] = "Nome de usuário inválido";
+$lang['danger']['password_mismatch'] = "As senhas não estão iguais";
+$lang['danger']['password_complexity'] = "A senha não atende aos parâmetros de segurança";
+$lang['danger']['password_empty'] = "A senha não pode ser vazia ou em branco";
+$lang['danger']['login_failed'] = "Login falhou";
+$lang['danger']['mailbox_invalid'] = "Conta inválida";
+$lang['danger']['mailbox_invalid_suggest'] = 'Conta inválida, sugestão: "%s"?';
+$lang['info']['fetchmail_planned'] = "Procedimento de retirada de emails foi agendado. Verifique o processo mais tarde.";
+$lang['danger']['fetchmail_source_empty'] = "Definir a pasta de origem";
+$lang['danger']['fetchmail_dest_empty'] = "Definir a pasta de destino";
+$lang['danger']['is_alias'] = "o endereço %s já é um Apelido";
+$lang['danger']['is_alias_or_mailbox'] = "o endereço %s já é uma Conta ou Apelido";
+$lang['danger']['is_spam_alias'] = "%s foi registrado como Apelido para Spam";
+$lang['danger']['quota_not_0_not_numeric'] = "Espaço deve ser um campo numérico >= 0";
+$lang['danger']['domain_not_found'] = "Domínio não encontrado.";
+$lang['danger']['max_mailbox_exceeded'] = "Número máximo de contas exedido (%d of %d)";
+$lang['danger']['mailbox_quota_exceeded'] = "Espaço excede o limite do domínio (max. %d MiB)";
+$lang['danger']['mailbox_quota_left_exceeded'] = "Não existe espaço suficiente (espaço disponível: %d MiB)";
+$lang['success']['mailbox_added'] = "Conta %s adicionada com sucesso";
+$lang['success']['domain_removed'] = "Domínio %s removido com sucesso";
+$lang['success']['alias_removed'] = "Apelido %s removido com sucesso";
+$lang['success']['alias_domain_removed'] = "Encaminhamento de Domínio %s removido com sucesso";
+$lang['success']['domain_admin_removed'] = "Administrator do domínio %s removido com sucesso";
+$lang['success']['mailbox_removed'] = "Conta %s removida com sucesso";
+$lang['danger']['max_quota_in_use'] = "Espaço da Conta deve ser maior ou igual a %d MiB";
+$lang['danger']['domain_quota_m_in_use'] = "Espaço do Domínio deve ser maior ou igual a %s MiB";
+$lang['danger']['mailboxes_in_use'] = "O máximo de Contas deve ser maior ou igual a %d";
+$lang['danger']['aliases_in_use'] = "O máximo de Apelidos deve ser maior ou igual a %d";
+$lang['danger']['sender_acl_invalid'] = "Campo Sender ACL é inválido";
+$lang['danger']['domain_not_empty'] = "Não é possível remover um domínio com Contas/Apelidos/Direcionamentos";
+$lang['warning']['spam_alias_temp_error'] = "Falha Temporária: Não foi possível adicionar Apelido para Spam.";
+$lang['danger']['spam_alias_max_exceeded'] = "O número máximo de Apelidos para Spam foi excedido";
+$lang['danger']['fetchmail_active'] = "O processo esta em andamento, aguarde o seu término.";
+$lang['danger']['validity_missing'] = 'Você deve definir um período de validade';
+$lang['user']['on'] = "On";
+$lang['user']['off'] = "Off";
+$lang['user']['user_change_fn'] = "";
+$lang['user']['user_settings'] = 'Configurações do usuário';
+$lang['user']['mailbox_settings'] = 'Configrações da conta';
+$lang['user']['mailbox_details'] = 'Detalhes da conta';
+$lang['user']['change_password'] = 'Alterar senha';
+$lang['user']['new_password'] = 'Nova senha';
+$lang['user']['save_changes'] = 'Salvar';
+$lang['user']['password_now'] = 'Senha atual (confirme a alteração)';
+$lang['user']['new_password_repeat'] = 'Confirmar senha (repetir)';
+$lang['user']['new_password_description'] = 'Requerido: mínimo de 6 characteres com letras e números.';
+$lang['user']['did_you_know'] = '<b>Você sabia?</b> Você pode usar tags no endereço de email ("conta+<b>privado</b>@example.com") para classificar as mensagens automaticamente para uma determinada pasta (exemplo: "privado").';
+$lang['user']['spam_aliases'] = 'Apelidos temporários';
+$lang['user']['alias'] = 'Apelido';
+$lang['user']['aliases'] = 'Apelidos';
+$lang['user']['aliases_send_as_all'] = 'Não verificar remetente para os domínios';
+$lang['user']['alias_create_random'] = 'Gerar um apelido automaticamente';
+$lang['user']['alias_extend_all'] = 'Extender apelido por 1 hora';
+$lang['user']['alias_valid_until'] = 'Válido até';
+$lang['user']['alias_remove_all'] = 'Remover todos os apelidos';
+$lang['user']['alias_time_left'] = 'Tempo restante';
+$lang['user']['alias_full_date'] = 'd.m.Y, H:i:s T';
+$lang['user']['alias_select_validity'] = 'Período de validade';
+$lang['user']['hour'] = 'Hora';
+$lang['user']['hours'] = 'Horas';
+$lang['user']['day'] = 'Dia';
+$lang['user']['week'] = 'Semana';
+$lang['user']['weeks'] = 'Semanas';
+$lang['user']['spamfilter'] = 'Filtro de Spam';
+$lang['user']['spamfilter_wl'] = 'WhiteList';
+$lang['user']['spamfilter_wl_desc'] = 'Endereços em  WhiteList <b>nunca</b> classificar como spam. Pode ser usado coringa *@example.com.';
+$lang['user']['spamfilter_bl'] = 'BlackList';
+$lang['user']['spamfilter_bl_desc'] = 'Endereços em BlackList <b>sempre</b> classificar como spam e rejeitar. Pode ser usado coringa *@example.com.';
+$lang['user']['spamfilter_behavior'] = 'Classificação';
+$lang['user']['spamfilter_table_rule'] = 'Regra';
+$lang['user']['spamfilter_table_action'] = 'Ação';
+$lang['user']['spamfilter_table_empty'] = 'Nenhum registro';
+$lang['user']['spamfilter_table_remove'] = 'remover';
+$lang['user']['spamfilter_table_add'] = "Adicionar registro";
+$lang['user']['spamfilter_behavior'] = 'Verificar';
+$lang['user']['spamfilter_default_score'] = 'Nivel de Spam:';
+$lang['user']['spamfilter_green'] = 'Verde: essa mensagem <b>não é</b> spam';
+$lang['user']['spamfilter_yellow'] = 'Amarelo: essa mensagem <b>pode ser</b> spam, será marcada como spam e classificada na pasta Spam';
+$lang['user']['spamfilter_red'] = 'Vermelho: essa mensagem <b>é mesmo spam</b> e será rejeitada definitivamente pelo servidor';
+$lang['user']['spamfilter_default_score'] = 'Valores padrão:';
+$lang['user']['spamfilter_hint'] = 'O primeiro espaço indica "baixo nível de spam", a segunda representa "alto nível de spam".';
+$lang['user']['tls_policy_warning'] = '<strong>Aviso:</strong> Se você selecionar para forçar o envio encryptado , alguns emails poderão ser rejeitados.<br />Mensages que não satisfizerem as politicas dos outros servidores serão rejeitadas definitivamente.';
+$lang['user']['tls_policy'] = 'Regras de Encryptação';
+$lang['user']['tls_enforce_in'] = 'Forçar TLS na entrada';
+$lang['user']['tls_enforce_out'] = 'Forçar TLS na saída';
+$lang['user']['misc_settings'] = 'Outras configurações';
+$lang['user']['misc_delete_profile'] = 'Outras configurações';
+$lang['user']['no_record'] = 'Nenhum registro';
+$lang['start']['dashboard'] = '%s - Painel';
+$lang['start']['start_rc'] = 'Webmail Roundcube';
+$lang['start']['start_sogo'] = 'Abrir SOGo';
+$lang['start']['mailcow_apps_detail'] = 'Use um mailcow app para acessar seus emails, calendário, contatos e outras informações.';
+$lang['start']['mailcow_panel'] = 'Iniciar mailcow UI';
+$lang['start']['mailcow_panel_description'] = 'O mailcow UI está disponível para Administradores e Usuários.';
+$lang['start']['mailcow_panel_detail'] = '<b>Administradores:</b> podem criar, alterar ou apagar contas e apelidos , alterar domínios e outras informações de seus domínios atribuídos.<br />
+	<b>Usuários:</b> podem criar apelidos por tempo determinado , alterar senha e configuração do nível do filtro de spam.';
+$lang['start']['recommended_config'] = 'Configuração recomendada (sem o ActiveSync)';
+$lang['start']['imap_smtp_server'] = 'IMAP e SMTP server data';
+$lang['start']['imap_smtp_server_description'] = 'Para uma melhor utilização use o <a href="%s" target="_blank"><b>Mozilla Thunderbird</b></a>.';
+$lang['start']['imap_smtp_server_badge'] = 'Ler/Criar emails';
+$lang['start']['imap_smtp_server_auth_info'] = 'Utilize o endereço de email completo com o método de autentucação PLAIN.<br />
+Os dados de login serão encryptados pelo servidor.';
+$lang['start']['managesieve'] = 'ManageSieve';
+$lang['start']['managesieve_badge'] = 'Filtro de email';
+$lang['start']['managesieve_description'] = 'Utilize o <b>Mozilla Thunderbird</b> com a <a style="text-decoration:none" target="_blank" href="%s"><b>extensão para sieve</b></a>.<br />Inicie o Thunderbird, acesse os Complementos e solte o arquivo xpi que foi baixado, na janela aberta.<br />Preencha com o servidor <b>%s</b>, porta <b>4190</b> se for solicitado. Os dados de acesso são os mesmos da sua conta de email.';
+$lang['start']['service'] = 'Serviço';
+$lang['start']['encryption'] = 'Método de criptografia';
+$lang['start']['help'] = 'Mostrar/Ocultar painel de ajuda';
+$lang['start']['hostname'] = 'Hostname';
+$lang['start']['port'] = 'Porta';
+$lang['start']['footer'] = 'Rodapé';
+$lang['header']['mailcow_settings'] = 'Configuração';
+$lang['header']['administration'] = 'Administração';
+$lang['header']['mailboxes'] = 'Contas';
+$lang['header']['user_settings'] = 'Configurações do usuário';
+$lang['header']['login'] = 'Entrar';
+$lang['header']['logged_in_as_logout'] = 'Olá <b>%s</b> (Clique para Sair)';
+$lang['header']['locale'] = 'Idioma';
+$lang['mailbox']['domain'] = 'Domínio';
+$lang['mailbox']['alias'] = 'Apelido';
+$lang['mailbox']['aliases'] = 'Apelidos';
+$lang['mailbox']['domains'] = 'Domínios';
+$lang['mailbox']['mailboxes'] = 'Contas';
+$lang['mailbox']['mailbox_quota'] = 'Espaço máximo da Conta';
+$lang['mailbox']['domain_quota'] = 'Espaço';
+$lang['mailbox']['active'] = 'Ativo';
+$lang['mailbox']['action'] = 'Ação';
+$lang['mailbox']['ratelimit'] = 'Limite de envios por hora';
+$lang['mailbox']['backup_mx'] = 'Backup MX';
+$lang['mailbox']['domain_aliases'] = 'Encaminhamento de Domínio';
+$lang['mailbox']['target_domain'] = 'Domínio Destino';
+$lang['mailbox']['target_address'] = 'Encaminhar para';
+$lang['mailbox']['username'] = 'Usuário';
+$lang['mailbox']['fname'] = 'Nome';
+$lang['mailbox']['filter_table'] = 'Procurar';
+$lang['mailbox']['yes'] = '&#10004;';
+$lang['mailbox']['no'] = '&#10008;';
+$lang['mailbox']['quota'] = 'Espaço';
+$lang['mailbox']['in_use'] = 'Em uso (%)';
+$lang['mailbox']['msg_num'] = 'Mensagens';
+$lang['mailbox']['remove'] = 'Remover';
+$lang['mailbox']['edit'] = 'Alterar';
+$lang['mailbox']['archive'] = 'Arquivo';
+$lang['mailbox']['no_record'] = 'Nenhum registro';
+$lang['mailbox']['add_domain'] = 'Adicionar Domínio';
+$lang['mailbox']['add_domain_alias'] = 'Adicionar Apelido de Domínio';
+$lang['mailbox']['add_mailbox'] = 'Adicionar Conta de Email';
+$lang['mailbox']['add_alias'] = 'Adicionar Apelido';
+$lang['info']['no_action'] = 'Nenhuma ação foi definida';
+$lang['delete']['title'] = 'Remover objeto';
+$lang['delete']['remove_domain_warning'] = '<b>Aviso:</b> Você está prestes a remover o Domínio <b>%s</b>!';
+$lang['delete']['remove_domainalias_warning'] = '<b>Aviso:</b> Você está prestes a remover o Encaminhamento de Domínio <b>%s</b>!';
+$lang['delete']['remove_domainadmin_warning'] = '<b>Aviso:</b> Você está prestes a remover o Administrador <b>%s</b>!';
+$lang['delete']['remove_alias_warning'] = '<b>Aviso:</b> Você está prestes a remover o Apelido <b>%s</b>!';
+$lang['delete']['remove_mailbox_warning'] = '<b>Aviso:</b> Você está prestes a remover a Conta <b>%s</b>!';
+$lang['delete']['remove_mailbox_details'] = 'A Conta será <b>excluída permanentemente</b>!';
+$lang['delete']['remove_domain_details'] = 'Esse procedimento removerá o Encaminhamento de Domínio.<br /><br /><b>O Domínio deve estar sem nenhuma configuração para ser removido.</b>';
+$lang['delete']['remove_alias_details'] = 'Os usuários não poderão mais enviar ou receber emails através deste endereço.</b>';
+$lang['delete']['remove_button'] = 'Remover';
+$lang['delete']['previous'] = 'Voltar';
+$lang['edit']['save'] = 'Salvar';
+$lang['edit']['archive'] = 'Acesso ao arquivo';
+$lang['edit']['max_mailboxes'] = 'Máximo de contas:';
+$lang['edit']['title'] = 'Editar dos Objetos';
+$lang['edit']['target_address'] = 'Enviar para os emails <small>(separar por vírgula)</small>:';
+$lang['edit']['active'] = 'Ativo';
+$lang['edit']['target_domain'] = 'Domínio de Destino:';
+$lang['edit']['password'] = 'Senha:';
+$lang['edit']['ratelimit'] = 'Volume de envios por hora:';
+$lang['danger']['ratelimt_less_one'] = 'Limite da taxa de saída por hora não pode ser inferior a 1';
+$lang['edit']['password_repeat'] = 'Confirmar senha (repetir):';
+$lang['edit']['domain_admin'] = 'Editar administrador de domínio';
+$lang['edit']['domain'] = 'Editar domínio';
+$lang['edit']['alias_domain'] = 'Encaminhar domínio';
+$lang['edit']['edit_alias_domain'] = 'Editar encaminhamento de domínio';
+$lang['edit']['domains'] = 'Domínios';
+$lang['edit']['destroy'] = 'Inserir manualmente';
+$lang['edit']['alias'] = 'Editar apelido';
+$lang['edit']['mailbox'] = 'Editar conta';
+$lang['edit']['description'] = 'Descrição:';
+$lang['edit']['max_aliases'] = 'Máximo apelidos:';
+$lang['edit']['max_quota'] = 'Máximo espaço por conta (MiB):';
+$lang['edit']['domain_quota'] = 'Espaço do domínio:';
+$lang['edit']['backup_mx_options'] = 'Opções de Backup MX:';
+$lang['edit']['relay_domain'] = 'Relay de domínio';
+$lang['edit']['relay_all'] = 'Relay para todas as contas';
+$lang['edit']['dkim_signature'] = 'Assinatura DKIM:';
+$lang['edit']['dkim_record_info'] = '<small>Adicione um registro TXT com o mesmo a mesma configuração dos registros DNS.</small>';
+$lang['edit']['relay_all_info'] = '<small>Se você escolher <b>não</b> direcionar todas as contas de email, você deve adicionar um ("blind") para cada uma das contas.</small>';
+$lang['edit']['full_name'] = 'Nome completo';
+$lang['edit']['quota_mb'] = 'Espaço (MiB)';
+$lang['edit']['sender_acl'] = 'Permitir Enviar como';
+$lang['edit']['sender_acl_info'] = 'Apelidos não podem ser removidos.';
+$lang['edit']['dkim_txt_name'] = 'Nome do registro TXT:';
+$lang['edit']['dkim_txt_value'] = 'Valor do registro TXT:';
+$lang['edit']['previous'] = 'Voltar';
+$lang['edit']['unchanged_if_empty'] = 'Deixar em branco para não modificar';
+$lang['edit']['dont_check_sender_acl'] = 'Não verificar o remetente para o domínio %s';
+$lang['add']['title'] = 'Adicionar objeto';
+$lang['add']['domain'] = 'Domínio';
+$lang['add']['active'] = 'Ativo';
+$lang['add']['save'] = 'Salvar';
+$lang['add']['description'] = 'Descrição:';
+$lang['add']['max_aliases'] = 'Máximo de apelidos:';
+$lang['add']['max_mailboxes'] = 'Máximo de contas:';
+$lang['add']['mailbox_quota_m'] = 'Máximo espaço por conta (MiB):';
+$lang['add']['domain_quota_m'] = 'Total de espaço por domínio(MiB):';
+$lang['add']['backup_mx_options'] = 'Opções Backup MX:';
+$lang['add']['relay_all'] = 'Relay para todas as contas';
+$lang['add']['relay_domain'] = 'Relay para todo domínio';
+$lang['add']['relay_all_info'] = '<small>Se <b>não</b> selecionar para retransmitir todas as contas, você deve adicionar uma ("blind") para cada conta que será direcionada.</small>';
+$lang['add']['alias'] = 'Apelido(s)';
+$lang['add']['alias_spf_fail'] = '<b>Aviso:</b> Se você escolher uma conta externa, o <b>servidor externo</b> poderá rejeitar algumas mensagens por erro de SPF.</a>';
+$lang['add']['alias_address'] = 'Apelidos:';
+$lang['add']['alias_address_info'] = '<small>Endereço de email completo ou @example.com, para uma conta coringa -catch all. (separado por vírgula). <b>apenas domínios cadastrados</b>.</small>';
+$lang['add']['alias_domain_info'] = '<small>Domínios válidos apenas (separado por vírgulas).</small>';
+$lang['add']['target_address'] = 'Encaminhar para:';
+$lang['add']['target_address_info'] = '<small>Endereço de email completo (separado por vírgulas).</small>';
+$lang['add']['alias_domain'] = 'Encaminhamento de Domínio';
+$lang['add']['select'] = 'Selecione...';
+$lang['add']['target_domain'] = 'Domínio de Destino:';
+$lang['add']['mailbox'] = 'Conta';
+$lang['add']['mailbox_username'] = 'Usuário (primeira parte do endereço de email):';
+$lang['add']['full_name'] = 'Nome:';
+$lang['add']['quota_mb'] = 'Espaço (MiB):';
+$lang['add']['select_domain'] = 'Selecione um domínio antes';
+$lang['add']['password'] = 'Senha:';
+$lang['add']['password_repeat'] = 'Confirmar a senha (repetir):';
+$lang['add']['previous'] = 'Voltar';
+$lang['login']['title'] = 'Entrar';
+$lang['login']['administration'] = 'Administração';
+$lang['login']['administration_details'] = 'Utilize o login de Administrador para efetuar tarefas de administração.';
+$lang['login']['user_settings'] = 'Configuração do usuário';
+$lang['login']['user_settings_details'] = 'Usuários podem utilizar o mailcow UI para alterar suas senhas, criar apelidos temporários (Apelido Anti-Spam), adjustar a sensibilidade do filtro the spam ou importar mensagens de um servidor IMAP.';
+$lang['login']['username'] = 'Usuário';
+$lang['login']['password'] = 'Senha';
+$lang['login']['reset_password'] = 'Esqueci minha senha';
+$lang['login']['login'] = 'Entrar';
+$lang['login']['previous'] = "Voltar";
+$lang['login']['delayed'] = 'Sua entrada será atrasada por %s segundos.';
+$lang['login']['tfa'] = "Autenticação em duas etapas";
+$lang['login']['tfa_details'] = "Confirme sua senha no campo abaixo";
+$lang['login']['confirm'] = "Confirmar";
+$lang['login']['otp'] = "Senha única";
+$lang['login']['trash_login'] = "Tentativas de entrada";
+$lang['admin']['search_domain_da'] = 'Selecione o(s) domínio(s)';
+$lang['admin']['restrictions'] = 'Postfix Restrictions';
+$lang['admin']['rr'] = 'Postfix Recipient Restrictions';
+$lang['admin']['sr'] = 'Postfix Sender Restrictions';
+$lang['admin']['reset_defaults'] = 'Voltar configuração padrão';
+$lang['admin']['sr'] = 'Postfix Sender Restrictions';
+$lang['admin']['r_inactive'] = 'Restrictions Inativos';
+$lang['admin']['r_active'] = 'Restrictions Ativos';
+$lang['admin']['r_info'] = 'Greyed out/disabled elements on the list of active restrictions are not known as valid restrictions to mailcow and cannot be moved. Unknown restrictions will be set in order of appearance anyway. <br />You can add new elements in <code>inc/vars.local.inc.php</code> to be able to toggle them.';
+$lang['admin']['public_folders'] = 'Pastas públicas';
+$lang['admin']['public_folders_text'] = 'A pasta "Public" esta criada. Abaixo a pasta pública indica o nome da primeira pasta criada automaticamente na conta, com este nome.';
+$lang['admin']['public_folder_name'] = 'Nome da Pasta <small>(alfa numérico)</small>';
+$lang['admin']['public_folder_enable'] = 'Habilitar Pasta Pública';
+$lang['admin']['public_folder_enable_text'] = 'Ao alterar esta configuração os emails das pastas públicas não serão apagados.';
+$lang['admin']['public_folder_pusf'] = 'Habilitar visualização por usuário';
+$lang['admin']['public_folder_pusf_text'] = 'A "per-user seen flag"-enabled system will not mark a mail as read for User B, when User A has seen it, but User B did not.';
+$lang['admin']['privacy'] = 'Privacidade';
+$lang['admin']['privacy_text'] = 'Esta opção habilita a tabela PCRE para remover "User-Agent", "X-Enigmail", "X-Mailer", "X-Originating-IP" e substitui pelo Header "Received: from" localhost/127.0.0.1.';
+$lang['admin']['privacy_anon_mail'] = 'Limpar o Cabeçalho dos emails de saída';
+$lang['admin']['dkim_txt_name'] = 'Registro TXT:';
+$lang['admin']['dkim_txt_value'] = 'Valor do TXT:';
+$lang['admin']['dkim_key_length'] = 'Tamanho do registro DKIM (bits)';
+$lang['admin']['previous'] = 'Voltar';
+$lang['admin']['quota_mb'] = 'Espaço (MiB):';
+$lang['admin']['sender_acl'] = 'Permitir Enviar como:';
+$lang['admin']['msg_size'] = 'Tamanho da mensagem';
+$lang['admin']['msg_size_limit'] = 'Tamanho limite de mensagem atual';
+$lang['admin']['msg_size_limit_details'] = 'Ao aplicar um novo limite os Serviços de Email e Web serão reiniciados.';
+$lang['admin']['save'] = 'Salvar';
+$lang['admin']['maintenance'] = 'Manutenção e Informação';
+$lang['admin']['sys_info'] = 'Informações de Sistema';
+$lang['admin']['dkim_add_key'] = 'Adicionar registro DKIM';
+$lang['admin']['dkim_keys'] = 'Registro DKIM';
+$lang['admin']['add'] = 'Salvar';
+$lang['admin']['configuration'] = 'Configuração';
+$lang['admin']['password'] = 'Senha';
+$lang['admin']['password_repeat'] = 'Confirmar senha (repetir)';
+$lang['admin']['active'] = 'Ativo';
+$lang['admin']['action'] = 'Ação';
+$lang['admin']['add_domain_admin'] = 'Adicionar administrador de domínio(s)';
+$lang['admin']['admin_domains'] = 'Acesso aos Domínios';
+$lang['admin']['domain_admins'] = 'Administradores de domínio';
+$lang['admin']['username'] = 'Administrador';
+$lang['admin']['edit'] = 'Editar';
+$lang['admin']['remove'] = 'Remover';
+$lang['admin']['save'] = 'Salvar';
+$lang['admin']['admin'] = 'Administrador';
+$lang['admin']['admin_details'] = 'Editar informações do administrator';
+$lang['admin']['unchanged_if_empty'] = 'Deixar em branco para não alterar';
+$lang['admin']['yes'] = '&#10004;';
+$lang['admin']['no'] = '&#10008;';
+$lang['admin']['access'] = 'Acessos';
+$lang['admin']['invalid_max_msg_size'] = 'Tamanho máximo da mensagem inválido';
+$lang['admin']['site_not_found'] = 'Não foi possível localizar as configuração do painel mailcow';
+$lang['admin']['public_folder_empty'] = 'O nome da Pasta Pública deve ser preenchido';
+$lang['admin']['set_rr_failed'] = 'Não foi possível alterar Postfix Restrictions';
+$lang['admin']['no_record'] = 'Nenhum registro';
+?>
diff --git a/data/web/mailbox.php b/data/web/mailbox.php
new file mode 100644
index 00000000..08798ffe
--- /dev/null
+++ b/data/web/mailbox.php
@@ -0,0 +1,426 @@
+<?php
+require_once "inc/prerequisites.inc.php";
+
+if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "admin" || $_SESSION['mailcow_cc_role'] == "domainadmin")) {
+require_once "inc/header.inc.php";
+$_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
+?>
+<div class="container">
+	<div class="row">
+		<div class="col-md-12">
+			<div class="panel panel-default">
+				<div class="panel-heading">
+				<h3 class="panel-title"><?=$lang['mailbox']['domains'];?> <span class="badge" id="numRowsDomain"></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>
+				<?php
+				if ($_SESSION['mailcow_cc_role'] == "admin"):
+				?>
+					<a href="/add.php?domain"><span class="glyphicon glyphicon-plus"></span></a>
+				<?php
+				endif;
+				?>
+				</div>
+				</div>
+				<div class="panel-body">
+					<input type="text" class="form-control" id="domaintable-filter" data-action="filter" data-filters="#domaintable" placeholder="Filter" />
+				</div>
+				<div class="table-responsive">
+				<table class="table table-striped sortable-theme-bootstrap" data-sortable id="domaintable">
+					<thead>
+						<tr>
+							<th class="sort-table" style="min-width: 86px;"><?=$lang['mailbox']['domain'];?></th>
+							<th class="sort-table" style="min-width: 81px;"><?=$lang['mailbox']['aliases'];?></th>
+							<th class="sort-table" style="min-width: 99px;"><?=$lang['mailbox']['mailboxes'];?></th>
+							<th class="sort-table" style="min-width: 172px;"><?=$lang['mailbox']['mailbox_quota'];?></th>
+							<th class="sort-table" style="min-width: 117px;"><?=$lang['mailbox']['domain_quota'];?></th>
+							<?php
+							if ($_SESSION['mailcow_cc_role'] == "admin"):
+							?>
+								<th class="sort-table" style="min-width: 105px;"><?=$lang['mailbox']['backup_mx'];?></th>
+							<?php
+							endif;
+							?>
+							<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
+          $domains = mailbox_get_domains();
+	        if (!empty($domains)):
+					foreach ($domains as $domain):
+            $domaindata = mailbox_get_domain_details($domain);
+					?>
+						<tr id="data">
+							<td><?=htmlspecialchars($domaindata['domain_name']);?></td>
+							<td><?=$domaindata['aliases_in_domain'];?> / <?=$domaindata['max_num_aliases_for_domain'];?></td>
+							<td><?=$domaindata['mboxes_in_domain'];?> / <?=$domaindata['max_num_mboxes_for_domain'];?></td>
+							<td><?=formatBytes($domaindata['max_quota_for_mbox']);?></td>
+							<td><?=formatBytes($domaindata['quota_used_in_domain'], 2);?> / <?=formatBytes($domaindata['max_quota_for_domain'], 2);?></td>
+							<?php
+							if ($_SESSION['mailcow_cc_role'] == "admin"):
+							?>
+								<td><?=$domaindata['backupmx'];?></td>
+							<?php
+							endif;
+							?>
+							<td><?=$domaindata['active'];?></td>
+							<?php
+							if ($_SESSION['mailcow_cc_role'] == "admin"):
+							?>
+								<td style="text-align: right;">
+									<div class="btn-group">
+										<a href="/edit.php?domain=<?=urlencode($domaindata['domain_name']);?>" class="btn btn-xs btn-default"><span class="glyphicon glyphicon-pencil"></span> <?=$lang['mailbox']['edit'];?></a>
+										<a href="/delete.php?domain=<?=urlencode($domaindata['domain_name']);?>" class="btn btn-xs btn-danger"><span class="glyphicon glyphicon-trash"></span> <?=$lang['mailbox']['remove'];?></a>
+									</div>
+								</td>
+							<?php
+							else:
+							?>
+								<td style="text-align: right;">
+									<div class="btn-group">
+										<a href="/edit.php?domain=<?=urlencode($domaindata['domain_name']);?>" class="btn btn-xs btn-default"><span class="glyphicon glyphicon-pencil"></span> <?=$lang['mailbox']['edit'];?></a>
+									</div>
+								</td>
+						</tr>
+              <?php
+              endif;
+            endforeach;
+            else:
+							?>
+              <tr id="no-data"><td colspan="8" style="text-align: center; font-style: italic;"><?=$lang['mailbox']['no_record_single'];?></td></tr>
+            <?php
+            endif;
+            ?>
+					</tbody>
+						<?php
+						if ($_SESSION['mailcow_cc_role'] == "admin"):
+						?>
+					<tfoot>
+						<tr id="no-data">
+							<td colspan="8" style="text-align: center; font-style: normal; border-top: 1px solid #e7e7e7;">
+								<a href="/add.php?domain"><?=$lang['mailbox']['add_domain'];?></a>
+							</td>
+						</tr>
+					</tfoot>
+						<?php
+						endif;
+						?>
+				</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']['mailboxes'];?> <span class="badge" id="numRowsMailbox"></span></h3>
+					<div class="pull-right">
+						<span class="clickable filter" data-toggle="tooltip" title="<?=$lang['mailbox']['filter_table'];?>" data-container="body">
+							<i class="glyphicon glyphicon-filter"></i>
+						</span>
+						<a href="/add.php?mailbox"><span class="glyphicon glyphicon-plus"></span></a>
+					</div>
+				</div>
+				<div class="panel-body">
+					<input type="text" class="form-control" id="mailboxtable-filter" data-action="filter" data-filters="#mailboxtable" placeholder="Filter" />
+				</div>
+				<div class="table-responsive">
+				<table class="table table-striped sortable-theme-bootstrap" data-sortable id="mailboxtable">
+					<thead>
+						<tr>
+							<th class="sort-table" style="min-width: 100px;"><?=$lang['mailbox']['username'];?></th>
+							<th class="sort-table" style="min-width: 98px;"><?=$lang['mailbox']['fname'];?></th>
+							<th class="sort-table" style="min-width: 86px;"><?=$lang['mailbox']['domain'];?></th>
+							<th class="sort-table" style="min-width: 75px;"><?=$lang['mailbox']['quota'];?></th>
+							<th class="sort-table" style="min-width: 99px;"><?=$lang['mailbox']['in_use'];?></th>
+							<th class="sort-table" style="min-width: 100px;"><?=$lang['mailbox']['msg_num'];?></th>
+							<th class="sort-table" style="min-width: 76px;"><?=$lang['mailbox']['active'];?></th>
+							<th style="text-align: right; min-width: 200px;" data-sortable="false"><?=$lang['mailbox']['action'];?></th>
+						</tr>
+					</thead>
+					<tbody>
+						<?php
+            foreach (mailbox_get_domains() as $domain) {
+              $mailboxes = mailbox_get_mailboxes($domain);
+              if (!empty($mailboxes)) {
+                foreach ($mailboxes as $mailbox) {
+                  $mailboxdata = mailbox_get_mailbox_details($mailbox);
+						?>
+						<tr id="data">
+							<td><?=($mailboxdata['is_relayed'] == "0") ? htmlspecialchars($mailboxdata['username']) : '<span data-toggle="tooltip" title="Relayed"><i class="glyphicon glyphicon-forward"></i>' . htmlspecialchars($mailboxdata['username']) . '</span>';?></td>
+							<td><?=htmlspecialchars($mailboxdata['name'], ENT_QUOTES, 'UTF-8');?></td>
+							<td><?=htmlspecialchars($mailboxdata['domain']);?></td>
+							<td><?=formatBytes($mailboxdata['quota_used'], 2);?> / <?=formatBytes($mailboxdata['quota'], 2);?></td>
+							<td style="min-width:120px;">
+								<div class="progress">
+									<div class="progress-bar progress-bar-<?=$mailboxdata['percent_class'];?>" role="progressbar" aria-valuenow="<?=$mailboxdata['percent_in_use'];?>" aria-valuemin="0" aria-valuemax="100" style="min-width:2em;width: <?=$mailboxdata['percent_in_use'];?>%;">
+										<?=$mailboxdata['percent_in_use'];?>%
+									</div>
+								</div>
+							</td>
+							<td><?=$mailboxdata['messages'];?></td>
+							<td><?=$mailboxdata['active'];?></td>
+							<td style="text-align: right;">
+								<div class="btn-group">
+									<a href="/edit.php?mailbox=<?=urlencode($mailboxdata['username']);?>" class="btn btn-xs btn-default"><span class="glyphicon glyphicon-pencil"></span> <?=$lang['mailbox']['edit'];?></a>
+									<a href="/delete.php?mailbox=<?=urlencode($mailboxdata['username']);?>" class="btn btn-xs btn-danger"><span class="glyphicon glyphicon-trash"></span> <?=$lang['mailbox']['remove'];?></a>
+									<?php if ($_SESSION['mailcow_cc_role'] == "admin"): ?>
+                  <a href="/index.php?duallogin=<?=urlencode($mailboxdata['username']);?>" class="btn btn-xs btn-success"><span class="glyphicon glyphicon-user"></span> Login</a>
+                  <?php endif; ?>
+								</div>
+							</td>
+						</tr>
+						<?php
+                }
+              }
+              else {
+                  ?>
+                  <tr id="no-data"><td colspan="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?mailbox"><?=$lang['mailbox']['add_mailbox'];?></a>
+							</td>
+						</tr>
+					</tfoot>
+				</table>
+				</div>
+			</div>
+		</div>
+	</div>
+	<div class="row">
+		<div class="col-md-12">
+			<div class="panel panel-default">
+				<div class="panel-heading">
+					<h3 class="panel-title"><?=$lang['mailbox']['resources'];?> <span class="badge" id="numRowsResource"></span></h3>
+					<div class="pull-right">
+						<span class="clickable filter" data-toggle="tooltip" title="<?=$lang['mailbox']['filter_table'];?>" data-container="body">
+							<i class="glyphicon glyphicon-filter"></i>
+						</span>
+						<a href="/add.php?resource"><span class="glyphicon glyphicon-plus"></span></a>
+					</div>
+				</div>
+				<div class="panel-body">
+					<input type="text" class="form-control" id="resourcetable-filter" data-action="filter" data-filters="#resourcetable" placeholder="Filter" />
+				</div>
+				<div class="table-responsive">
+				<table class="table table-striped sortable-theme-bootstrap" data-sortable id="resourcetable">
+					<thead>
+						<tr>
+							<th class="sort-table" style="min-width: 98px;"><?=$lang['mailbox']['description'];?></th>
+							<th class="sort-table" style="min-width: 98px;"><?=$lang['mailbox']['kind'];?></th>
+							<th class="sort-table" style="min-width: 86px;"><?=$lang['mailbox']['domain'];?></th>
+							<th class="sort-table" style="min-width: 98px;"><?=$lang['mailbox']['multiple_bookings'];?></th>
+							<th class="sort-table" style="min-width: 76px;"><?=$lang['mailbox']['active'];?></th>
+							<th style="text-align: right; min-width: 200px;" data-sortable="false"><?=$lang['mailbox']['action'];?></th>
+						</tr>
+					</thead>
+					<tbody>
+						<?php
+            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="col-md-12">
+			<div class="panel panel-default">
+				<div class="panel-heading">
+					<h3 class="panel-title"><?=$lang['mailbox']['aliases'];?> <span class="badge" id="numRowsAlias"></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?alias"><span class="glyphicon glyphicon-plus"></span></a>
+					</div>
+				</div>
+				<div class="panel-body">
+					<input type="text" class="form-control" id="aliastable-filter" data-action="filter" data-filters="#aliastable" placeholder="Filter" />
+				</div>
+				<div class="table-responsive">
+				<table class="table table-striped sortable-theme-bootstrap" data-sortable id="aliastable">
+					<thead>
+						<tr>
+							<th class="sort-table" style="min-width: 67px;"><?=$lang['mailbox']['alias'];?></th>
+							<th class="sort-table" style="min-width: 119px;"><?=$lang['mailbox']['target_address'];?></th>
+							<th class="sort-table" style="min-width: 86px;"><?=$lang['mailbox']['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 (array_merge(mailbox_get_domains(), mailbox_get_alias_domains()) as $domain) {
+            $aliases = mailbox_get_aliases($domain);
+            if (!empty($aliases)) {
+              foreach ($aliases as $alias) {
+                $aliasdata = mailbox_get_alias_details($alias);
+					?>
+						<tr id="data">
+							<td>
+							<?= ($aliasdata['is_catch_all'] == "1") ? '<span class="glyphicon glyphicon-pushpin" aria-hidden="true"></span> Catch-all ' . htmlspecialchars($aliasdata['address']) : htmlspecialchars($aliasdata['address']); ?>
+							</td>
+							<td>
+							<?php
+							foreach(explode(",", $aliasdata['goto']) as $goto) {
+								echo nl2br(htmlspecialchars($goto.PHP_EOL));
+							}
+							?>
+							</td>
+							<td><?=htmlspecialchars($aliasdata['domain']);?></td>
+							<td><?=$aliasdata['active'];?></td>
+							<td style="text-align: right;">
+								<div class="btn-group">
+									<a href="/edit.php?alias=<?=urlencode($aliasdata['address']);?>" class="btn btn-xs btn-default"><span class="glyphicon glyphicon-pencil"></span> <?=$lang['mailbox']['edit'];?></a>
+									<a href="/delete.php?alias=<?=urlencode($aliasdata['address']);?>" class="btn btn-xs btn-danger"><span class="glyphicon glyphicon-trash"></span> <?=$lang['mailbox']['remove'];?></a>
+								</div>
+							</td>
+						</tr>
+						<?php
+                }
+              }
+              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?alias"><?=$lang['mailbox']['add_alias'];?></a>
+							</td>
+						</tr>
+					</tfoot>
+				</table>
+				</div>
+			</div>
+		</div>
+	</div>
+</div> <!-- /container -->
+<script src="js/sorttable.js"></script>
+<script src="js/mailbox.js"></script>
+<?php
+require_once("inc/footer.inc.php");
+} else {
+	header('Location: /');
+	exit();
+}
+?>
diff --git a/data/web/robots.txt b/data/web/robots.txt
new file mode 100644
index 00000000..1f53798b
--- /dev/null
+++ b/data/web/robots.txt
@@ -0,0 +1,2 @@
+User-agent: *
+Disallow: /
diff --git a/data/web/u2f_api.php b/data/web/u2f_api.php
new file mode 100644
index 00000000..634757ce
--- /dev/null
+++ b/data/web/u2f_api.php
@@ -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>
\ No newline at end of file
diff --git a/data/web/user.php b/data/web/user.php
new file mode 100644
index 00000000..253715b6
--- /dev/null
+++ b/data/web/user.php
@@ -0,0 +1,529 @@
+<?php
+require_once("inc/prerequisites.inc.php");
+if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == 'domainadmin') {
+
+  /*
+  / DOMAIN ADMIN
+  */
+
+	require_once("inc/header.inc.php");
+	$_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
+  $tfa_data = get_tfa();
+	$username = $_SESSION['mailcow_cc_username'];
+?>
+<div class="container">
+  <h3><?=$lang['user']['user_settings'];?></h3>
+  <div class="panel panel-default">
+  <div class="panel-heading"><?=$lang['user']['user_settings'];?></div>
+  <div class="panel-body">
+    <div class="row">
+      <div class="col-sm-offset-3 col-sm-9">
+        <p><a href="#pwChangeModal" data-toggle="modal">[<?=$lang['user']['change_password'];?>]</a></p>
+      </div>
+    </div>
+    <hr>
+    <div class="row">
+      <div class="col-md-3 col-xs-5 text-right"><?=$lang['tfa']['tfa'];?></div>
+        <div class="col-sm-9 col-xs-7">
+          <p id="tfa_pretty"><?=$tfa_data['pretty'];?></p>
+            <div id="tfa_additional">
+              <?php if($tfa_data['additional']):
+              foreach ($tfa_data['additional'] as $key_info): ?>
+                <form style="display:inline;" method="post">
+                <input type="hidden" name="unset_tfa_key" value="<?=$key_info['id'];?>" />
+                <div class="label label-default">🔑 <?=$key_info['key_id'];?> <a href="#" style="font-weight:bold;color:white" onClick="$(this).closest('form').submit()">[<?=strtolower($lang['admin']['remove']);?>]</a></div>
+              </form>
+              <?php endforeach;
+              endif;?>
+            </div>
+            <br />
+        </div>
+    </div>
+    <div class="row">
+      <div class="col-md-3 col-xs-5 text-right"><?=$lang['tfa']['set_tfa'];?></div>
+      <div class="col-md-9 col-xs-7">
+        <select id="selectTFA" class="selectpicker" title="<?=$lang['tfa']['select'];?>">
+          <option value="yubi_otp"><?=$lang['tfa']['yubi_otp'];?></option>
+          <option value="u2f"><?=$lang['tfa']['u2f'];?></option>
+          <option value="none"><?=$lang['tfa']['none'];?></option>
+        </select>
+      </div>
+    </div>
+  </div>
+</div>
+<?php
+}
+elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == 'user') {
+
+  /*
+  / USER
+  */
+
+	require_once("inc/header.inc.php");
+	$_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
+	$username = $_SESSION['mailcow_cc_username'];
+	$get_tls_policy = get_tls_policy($_SESSION['mailcow_cc_username']);
+?>
+<div class="container">
+<h3><?=$lang['user']['user_settings'];?></h3>
+
+<div class="panel panel-default">
+<div class="panel-heading"><?=$lang['user']['mailbox_details'];?></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>
+  <?php // Get user information about aliases
+  $user_get_alias_details = user_get_alias_details($username);?>
+  <div class="row">
+    <div class="col-md-3 col-xs-5 text-right"><?=$lang['user']['aliases'];?>:</div>
+    <div class="col-md-9 col-xs-7">
+    <p><?=$user_get_alias_details['aliases'];?></p>
+    </div>
+  </div>
+  <div class="row">
+    <div class="col-md-3 col-xs-5 text-right"><?=$lang['user']['domain_aliases'];?>:</div>
+    <div class="col-md-9 col-xs-7">
+    <p><?=$user_get_alias_details['ad_alias'];?></p>
+    </div>
+  </div>
+  <div class="row">
+    <div class="col-md-3 col-xs-5 text-right"><?=$lang['user']['aliases_also_send_as'];?>:</div>
+    <div class="col-md-9 col-xs-7">
+    <p><?=$user_get_alias_details['aliases_also_send_as'];?></p>
+    </div>
+  </div>
+  <div class="row">
+    <div class="col-md-3 col-xs-5 text-right"><?=$lang['user']['aliases_send_as_all'];?>:</div>
+    <div class="col-md-9 col-xs-7">
+    <p><?=$user_get_alias_details['aliases_send_as_all'];?></p>
+    </div>
+  </div>
+  <div class="row">
+    <div class="col-md-3 col-xs-5 text-right"><?=$lang['user']['is_catch_all'];?>:</div>
+    <div class="col-md-9 col-xs-7">
+    <p><?=$user_get_alias_details['is_catch_all'];?></p>
+    </div>
+  </div>
+  <hr>
+  <?php // Show tagging options ?>
+  <form class="form-horizontal" role="form" method="post">
+  <?php $get_tagging_options = get_delimiter_action()['wants_tagged_subject'];?>
+  <div class="row">
+    <div class="col-md-3 col-xs-5 text-right"><?=$lang['user']['tag_handling'];?>:</div>
+    <div class="col-md-9 col-xs-7">
+    <input type="hidden" name="edit_delimiter_action" value="1">
+    <select name="tagged_mail_handler" class="selectpicker" onchange="this.form.submit()">
+      <option value="subfolder" <?=($get_tagging_options == "0") ? 'selected' : null; ?>><?=$lang['user']['tag_in_subfolder'];?></option>
+      <option value="subject" <?=($get_tagging_options == "1") ? 'selected' : null; ?>><?=$lang['user']['tag_in_subject'];?></option>
+    </select>
+    <p class="help-block"><?=$lang['user']['tag_help_explain'];?></p>
+    <p class="help-block"><?=$lang['user']['tag_help_example'];?></p>
+    </div>
+  </div>
+  </form>
+  <?php // Rest EAS devices ?>
+  <form class="form-horizontal" role="form" method="post">
+  <div class="row">
+    <div class="col-md-3 col-xs-5 text-right"><?=$lang['user']['eas_reset'];?>:</div>
+    <div class="col-md-9 col-xs-7">
+    <button type="submit" name="mailbox_reset_eas" id="mailbox_reset_eas" value="1" class="btn btn-xs btn-default"><?=$lang['user']['eas_reset_now'];?></button>
+    <p class="help-block"><?=$lang['user']['eas_reset_help'];?></p>
+    </div>
+  </div>
+  </form>
+</div>
+</div>
+
+<!-- Nav tabs -->
+<ul class="nav nav-pills nav-justified" role="tablist">
+	<li role="presentation" class="active"><a href="#SpamAliases" aria-controls="SpamAliases" role="tab" data-toggle="tab"><?=$lang['user']['spam_aliases'];?></a></li>
+	<li role="presentation"><a href="#Spamfilter" aria-controls="Spamfilter" role="tab" data-toggle="tab"><?=$lang['user']['spamfilter'];?></a></li>
+	<li role="presentation"><a href="#TLSPolicy" aria-controls="TLSPolicy" role="tab" data-toggle="tab"><?=$lang['user']['tls_policy'];?></a></li>
+	<li role="presentation"><a href="#Syncjobs" aria-controls="Syncjobs" role="tab" data-toggle="tab"><?=$lang['user']['sync_jobs'];?></a></li>
+</ul>
+<hr>
+
+<div class="tab-content">
+	<div role="tabpanel" class="tab-pane active" id="SpamAliases">
+		<div class="row">
+			<div class="col-xs-6">
+				<p><b><?=$lang['user']['alias'];?></b></p>
+			</div>
+			<div class="col-xs-2">
+				<p><b><?=$lang['user']['alias_valid_until'];?></b></p>
+			</div>
+			<div class="col-xs-2">
+        <p><b><?=$lang['user']['action'];?></b></p>
+			</div>
+    </div>
+			<?php
+      $get_time_limited_aliases = get_time_limited_aliases($username);
+      if (!empty($get_time_limited_aliases)):
+        foreach ($get_time_limited_aliases as $row):
+        ?>
+		<div class="row">
+      <div class="col-xs-6">
+        <p><?=htmlspecialchars($row['address']);?></p>
+      </div>
+      <div class="col-xs-2">
+        <p><?=htmlspecialchars(date($lang['user']['alias_full_date'], $row['validity']));?></p>
+      </div>
+      <div class="col-xs-1">
+        <form class="form-inline" role="form" method="post">
+          <a class="text-danger" href="#" onclick="$(this).closest('form').submit()"><span class="glyphicon glyphicon-remove"></span></a>
+          <input type="hidden" name="set_time_limited_aliases" value="delete">
+          <input type="hidden" name="item" value="<?=htmlspecialchars($row['address']);?>">
+        </form>
+      </div>
+      <div class="col-xs-1">
+        <form class="form-inline" role="form" method="post">
+          <a href="#" onclick="$(this).closest('form').submit()"><span class="glyphicon glyphicon-time"></span> + 1h</a>
+          <input type="hidden" name="set_time_limited_aliases" value="extend">
+          <input type="hidden" name="item" value="<?=htmlspecialchars($row['address']);?>">
+        </form>
+      </div>
+    </div>
+        <?php
+        endforeach;
+			else:
+			?>
+      <div class="col-xs-12">
+        <center><i><?=$lang['user']['no_record'];?></i></center>
+      </div>
+			<?php
+			endif;	
+			?>
+    <form class="form-horizontal" role="form" method="post">
+		<div class="form-group">
+			<div class="col-sm-9">
+				<select id="validity" name="validity" title="<?=$lang['user']['alias_select_validity'];?>">
+					<option value="1">1 <?=$lang['user']['hour'];?></option>
+					<option value="6">6 <?=$lang['user']['hours'];?></option>
+					<option value="24">1 <?=$lang['user']['day'];?></option>
+					<option value="168">1 <?=$lang['user']['week'];?></option>
+					<option value="672">4 <?=$lang['user']['weeks'];?></option>
+				</select>
+				<button type="submit" name="set_time_limited_aliases" id="generate_tla" value="generate" class="btn btn-success"><?=$lang['user']['alias_create_random'];?></button>
+			</div>
+		</div>
+		<div class="form-group">
+			<div class="col-sm-12">
+				<button type="submit" name="set_time_limited_aliases" value="deleteall" class="btn-danger btn btn-sm">
+					<span class="glyphicon glyphicon-remove" aria-hidden="true"></span> <?=$lang['user']['alias_remove_all'];?>
+				</button>
+				<button type="submit" name="set_time_limited_aliases" value="extendall" class="btn-default btn btn-sm">
+					<span class="glyphicon glyphicon-time" aria-hidden="true"></span> <?=$lang['user']['alias_extend_all'];?>
+				</button>
+			</div>
+		</div>
+		</form>
+	</div>
+	<div role="tabpanel" class="tab-pane" id="Spamfilter">
+		<h4><?=$lang['user']['spamfilter_behavior'];?></h4>
+		<form class="form-horizontal" role="form" method="post">
+			<div class="form-group">
+				<div class="col-sm-offset-2 col-sm-10">
+					<input name="score" id="score" type="text" 
+						data-provide="slider"
+						data-slider-min="1"
+						data-slider-max="30"
+						data-slider-step="0.5"
+						data-slider-range="true"
+						data-slider-tooltip='always'
+						data-slider-id="slider1"
+						data-slider-value="[<?=get_spam_score($username);?>]"
+						data-slider-step="1" />
+					<br /><br />
+					<ul>
+						<li><?=$lang['user']['spamfilter_green'];?></li>
+						<li><?=$lang['user']['spamfilter_yellow'];?></li>
+						<li><?=$lang['user']['spamfilter_red'];?></li>
+					</ul>
+					<p><i><?=$lang['user']['spamfilter_default_score'];?> 5:15</i></p>
+					<p><?=$lang['user']['spamfilter_hint'];?></p>
+				</div>
+			</div>
+			<div class="form-group">
+				<div class="col-sm-offset-2 col-sm-10">
+					<button type="submit" id="edit_spam_score" name="edit_spam_score" class="btn btn-success"><?=$lang['user']['save_changes'];?></button>
+				</div>
+			</div>
+		</form>
+		<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($username);
+				if (empty($get_policy_list['whitelist'])):
+				?>
+					<div class="row">
+						<div class="col-sm-12"><i><?=$lang['user']['spamfilter_table_empty'];?></i></div>
+					</div>
+				<?php
+				else:
+          foreach($get_policy_list['whitelist'] as $wl):
+          ?>
+          <div class="row striped">
+            <form class="form-inline" method="post">
+            <div class="col-xs-6"><code><?=$wl['value'];?></code></div>
+            <div class="col-xs-6">
+              <input type="hidden" name="delete_prefid" value="<?=$wl['prefid'];?>">
+              <?php
+              if (filter_var($wl['object'], FILTER_VALIDATE_EMAIL)):
+              ?>
+                <input type="hidden" name="delete_policy_list_item">
+                <a href="#" onclick="$(this).closest('form').submit()" data-toggle="tooltip" data-placement="left" title="<?=$lang['user']['delete_now'];?>"><span class="glyphicon glyphicon-remove"></span></a>
+              <?php
+              else:
+              ?>
+                <span style="cursor:not-allowed"><?=$lang['user']['spamfilter_table_domain_policy'];?></span>
+              <?php
+              endif;
+              ?>
+            </div>
+            </form>
+          </div>
+          <?php
+          endforeach;
+        endif;
+				?>
+				<hr style="margin:5px 0px 7px 0px">
+				<div class="row">
+					<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">
+					</div>
+					<div class="col-xs-6">
+						<button type="submit" id="add_policy_list_item" name="add_policy_list_item" class="btn btn-xs btn-default"><?=$lang['user']['spamfilter_table_add'];?></button>
+					</div>
+					</form>
+				</div>
+			</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">
+              <?php
+              if (filter_var($bl['object'], FILTER_VALIDATE_EMAIL)):
+              ?>
+                <input type="hidden" name="delete_prefid" value="<?=$bl['prefid'];?>">
+                <input type="hidden" name="delete_policy_list_item">
+                <a href="#" onclick="$(this).closest('form').submit()" data-toggle="tooltip" data-placement="left" title="<?=$lang['user']['delete_now'];?>"><span class="glyphicon glyphicon-remove"></span></a>
+              <?php
+              else:
+              ?>
+                <span style="cursor:not-allowed"><?=$lang['user']['spamfilter_table_domain_policy'];?></span>
+              <?php
+              endif;
+              ?>
+            </div>
+            </form>
+          </div>
+          <?php
+          endforeach;
+        endif;
+				?>
+				<hr style="margin:5px 0px 7px 0px">
+				<div class="row">
+					<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">
+					</div>
+					<div class="col-xs-6">
+						<button type="submit" id="add_policy_list_item" name="add_policy_list_item" class="btn btn-xs btn-default"><?=$lang['user']['spamfilter_table_add'];?></button>
+					</div>
+					</form>
+				</div>
+			</div>
+		</div>
+	</div>
+	<div role="tabpanel" class="tab-pane" id="TLSPolicy">
+		<form class="form-horizontal" role="form" method="post">
+			<p class="help-block"><?=$lang['user']['tls_policy_warning'];?></p>
+			<div class="form-group">
+				<div class="col-sm-6">
+					<div class="checkbox">
+						<h4><span class="glyphicon glyphicon-download" aria-hidden="true"></span> <?=$lang['user']['tls_enforce_in'];?></h4>
+						<input type="checkbox" id="tls_in" name="tls_in" <?=($get_tls_policy['tls_enforce_in'] == "1") ? "checked" : null;?> data-on-text="<?=$lang['user']['on'];?>" data-off-text="<?=$lang['user']['off'];?>">
+					</div>
+				</div>
+				<div class="col-sm-6">
+					<div class="checkbox">
+						<h4><span class="glyphicon glyphicon-upload" aria-hidden="true"></span> <?=$lang['user']['tls_enforce_out'];?></h4>
+						<input type="checkbox" id="tls_out" name="tls_out" <?=($get_tls_policy['tls_enforce_out'] == "1") ? "checked" : null;?> data-on-text="<?=$lang['user']['on'];?>" data-off-text="<?=$lang['user']['off'];?>">
+					</div>
+				</div>
+			</div>
+			<div class="form-group">
+				<div class="col-sm-12">
+					<button type="submit" id="edit_tls_policy" name="edit_tls_policy" class="btn btn-default"><?=$lang['user']['save_changes'];?></button>
+				</div>
+			</div>
+		</form>
+	</div>
+	<div role="tabpanel" class="tab-pane" id="Syncjobs">
+		<div class="table-responsive">
+		<table class="table table-striped sortable-theme-bootstrap" data-sortable id="timelimitedaliases">
+			<thead>
+			<tr>
+				<th class="sort-table" style="min-width: 96px;">Server:Port</th>
+				<th class="sort-table" style="min-width: 96px;"><?=$lang['user']['encryption'];?></th>
+				<th class="sort-table" style="min-width: 96px;"><?=$lang['user']['username'];?></th>
+				<th class="sort-table" style="min-width: 96px;"><?=$lang['user']['excludes'];?></th>
+				<th class="sort-table" style="min-width: 35px;"><?=$lang['user']['interval'];?></th>
+				<th class="sort-table" style="min-width: 35px;"><?=$lang['user']['last_run'];?></th>
+				<th class="sort-table" style="min-width: 35px;">Log</th>
+				<th class="sort-table" style="max-width: 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'] == '') ? '&#10008;' : '<code>' . $row['exclude'] . '</code>';?></td>
+				<td><?=htmlspecialchars($row['mins_interval']);?> min</td>
+				<td><?=(empty($row['last_run'])) ? '&#10008;' : htmlspecialchars(date($lang['user']['syncjob_full_date'], strtotime($row['last_run'] . ' UTC')));?></td>
+				<td>
+        <?php
+        if (empty($row['returned_text'])) {
+          echo '&#10008;';
+        }
+        else {
+        ?>
+          <a href="#logModal" data-toggle="modal" data-log-text="<?=htmlspecialchars($row['returned_text']);?>">Open logs</a>
+        <?php
+        }
+        ?>
+        </td>
+				<td><?=($row['active'] == '1') ? '&#10004;' : '&#10008;';?></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> <!-- /container -->
+<script src="js/sorttable.js"></script>
+<script src="js/user.js"></script>
+<?php
+require_once("inc/footer.inc.php");
+}
+else {
+	header('Location: /');
+	exit();
+}
+?>
diff --git a/docker-compose.yml b/docker-compose.yml
new file mode 100644
index 00000000..34b917f6
--- /dev/null
+++ b/docker-compose.yml
@@ -0,0 +1,241 @@
+version: '2.1'
+
+services:
+    pdns-mailcow:
+      image: andryyy/mailcow-dockerized:pdns
+      depends_on:
+        mysql-mailcow:
+          condition: service_healthy
+      volumes:
+        - ./data/conf/pdns/:/etc/powerdns/
+      restart: always
+      networks:
+        mailcow-network:
+          ipv4_address: 172.22.1.254
+          aliases:
+            - pdns
+
+    mysql-mailcow:
+      image: mariadb:10.1
+      healthcheck:
+        test: ["CMD", "mysqladmin", "ping", "--host", "localhost", "--silent"]
+        interval: 10s
+        timeout: 30s
+        retries: 5
+      volumes:
+        - mysql-vol-1:/var/lib/mysql/
+        - ./data/conf/mysql/:/etc/mysql/conf.d/:ro
+      dns:
+        - 172.22.1.254
+      dns_search: mailcow-network
+      environment:
+        - MYSQL_ROOT_PASSWORD=${DBROOT}
+        - MYSQL_DATABASE=${DBNAME}
+        - MYSQL_USER=${DBUSER}
+        - MYSQL_PASSWORD=${DBPASS}
+      restart: always
+      networks:
+        mailcow-network:
+          aliases:
+            - mysql
+
+    redis-mailcow:
+      image: redis
+      depends_on:
+        - pdns-mailcow
+      volumes:
+        - redis-vol-1:/data/
+      restart: always
+      dns:
+        - 172.22.1.254
+      dns_search: mailcow-network
+      networks:
+        mailcow-network:
+          aliases:
+            - redis
+
+    rspamd-mailcow:
+      image: andryyy/mailcow-dockerized:rspamd
+      depends_on:
+        - nginx-mailcow
+      volumes:
+        - ./data/conf/rspamd/override.d/:/etc/rspamd/override.d:ro
+        - ./data/conf/rspamd/local.d/:/etc/rspamd/local.d:ro
+        - ./data/conf/rspamd/lua/:/etc/rspamd/lua/:ro
+        - dkim-vol-1:/data/dkim
+        - rspamd-vol-1:/var/lib/rspamd
+      restart: always
+      dns:
+        - 172.22.1.254
+      dns_search: mailcow-network
+      networks:
+        mailcow-network:
+          ipv4_address: 172.22.1.253
+          aliases:
+            - rspamd
+
+    php-fpm-mailcow:
+      image: andryyy/mailcow-dockerized:phpfpm
+      command: "php-fpm -d date.timezone=${TZ}"
+      depends_on:
+        - pdns-mailcow
+      volumes:
+        - ./data/web:/web:ro
+        - ./data/conf/rspamd/dynmaps:/dynmaps:ro
+        - dkim-vol-1:/data/dkim
+      dns:
+        - 172.22.1.254
+      dns_search: mailcow-network
+      environment:
+        - DBNAME=${DBNAME}
+        - DBUSER=${DBUSER}
+        - DBPASS=${DBPASS}
+        - MAILCOW_HOSTNAME=${MAILCOW_HOSTNAME}
+      restart: always
+      networks:
+        mailcow-network:
+          aliases:
+            - phpfpm
+
+    sogo-mailcow:
+      image: andryyy/mailcow-dockerized:sogo
+      depends_on:
+        - pdns-mailcow
+      environment:
+        - DBNAME=${DBNAME}
+        - DBUSER=${DBUSER}
+        - DBPASS=${DBPASS}
+        - TZ=${TZ}
+      dns:
+        - 172.22.1.254
+      dns_search: mailcow-network
+      volumes:
+        - ./data/conf/sogo/:/etc/sogo/
+        - /usr/lib/GNUstep/SOGo/WebServerResources/
+      restart: always
+      networks:
+        mailcow-network:
+          ipv4_address: 172.22.1.252
+          aliases:
+            - sogo
+
+    rmilter-mailcow:
+      image: andryyy/mailcow-dockerized:rmilter
+      depends_on:
+        - pdns-mailcow
+      volumes:
+        - ./data/conf/rmilter/:/etc/rmilter.conf.d/:ro
+      restart: always
+      dns:
+        - 172.22.1.254
+      dns_search: mailcow-network
+      networks:
+        mailcow-network:
+          aliases:
+            - rmilter
+
+    dovecot-mailcow:
+      image: andryyy/mailcow-dockerized:dovecot
+      depends_on:
+        - pdns-mailcow
+      volumes:
+        - ./data/conf/dovecot:/etc/dovecot
+        - ./data/assets/ssl:/etc/ssl/mail/:ro
+        - ./data/conf/sogo/:/etc/sogo/
+        - vmail-vol-1:/var/vmail
+      environment:
+        - DBNAME=${DBNAME}
+        - DBUSER=${DBUSER}
+        - DBPASS=${DBPASS}
+      ports:
+        - "${IMAP_PORT:-143}:143"
+        - "${IMAPS_PORT:-993}:993"
+        - "${POP_PORT-110}:110"
+        - "${POPS_PORT:-995}:995"
+        - "${SIEVE_PORT:-4190}:4190"
+      dns:
+        - 172.22.1.254
+      dns_search: mailcow-network
+      restart: always
+      hostname: ${MAILCOW_HOSTNAME}
+      networks:
+        mailcow-network:
+          aliases:
+            - dovecot
+
+    postfix-mailcow:
+      image: andryyy/mailcow-dockerized:postfix
+      depends_on:
+        - pdns-mailcow
+      volumes:
+        - ./data/conf/postfix:/opt/postfix/conf
+        - ./data/assets/ssl:/etc/ssl/mail/:ro
+      environment:
+        - DBNAME=${DBNAME}
+        - DBUSER=${DBUSER}
+        - DBPASS=${DBPASS}
+      ports:
+        - "${SMTP_PORT:-25}:25"
+        - "${SMTPS_PORT:-465}:465"
+        - "${SUBMISSION_PORT:-587}:587"
+      restart: always
+      hostname: ${MAILCOW_HOSTNAME}
+      dns:
+        - 172.22.1.254
+      dns_search: mailcow-network
+      networks:
+        mailcow-network:
+          aliases:
+            - postfix
+
+    memcached-mailcow:
+      image: memcached
+      depends_on:
+        - pdns-mailcow
+      restart: always
+      dns:
+        - 172.22.1.254
+      dns_search: mailcow-network
+      networks:
+        mailcow-network:
+          aliases:
+            - memcached
+
+    nginx-mailcow:
+      depends_on:
+        - sogo-mailcow
+        - php-fpm-mailcow
+      image: nginx:mainline
+      command: /bin/bash -c "envsubst < /etc/nginx/conf.d/listen.template > /etc/nginx/conf.d/listen.active && nginx -g 'daemon off;'"
+      environment:
+        - HTTPS_PORT=${HTTPS_PORT:-443}
+      volumes:
+        - ./data/web:/web:ro
+        - ./data/conf/rspamd/dynmaps:/dynmaps:ro
+        - ./data/assets/ssl/:/etc/ssl/mail/:ro
+        - ./data/conf/nginx/:/etc/nginx/conf.d/:rw
+      dns:
+        - 172.22.1.254
+      dns_search: mailcow-network
+      ports:
+        - "${HTTPS_PORT:-443}:${HTTPS_PORT:-443}"
+      restart: always
+      networks:
+        mailcow-network:
+          aliases:
+            - nginx
+
+networks:
+  mailcow-network:
+    driver: bridge
+    ipam:
+      driver: default
+      config:
+        - subnet: 172.22.1.0/24
+
+volumes:
+  vmail-vol-1:
+  mysql-vol-1:
+  dkim-vol-1:
+  redis-vol-1:
+  rspamd-vol-1:
diff --git a/docs/first_steps.md b/docs/first_steps.md
new file mode 100644
index 00000000..85a1d09f
--- /dev/null
+++ b/docs/first_steps.md
@@ -0,0 +1,76 @@
+# Change default language
+
+Change `data/conf/sogo/sogo.conf` and replace English by your language.
+
+Create a file `data/web/inc/vars.local.inc.php` and add "DEFAULT_LANG" with either "en", "pt", "de" or "nl":
+```
+<?php
+$DEFAULT_LANG = "de";
+```
+
+# SSL (and: How to use Let's Encrypt)
+
+mailcow dockerized comes with a snakeoil CA "mailcow" and a server certificate in `data/assets/ssl`. Please use your own trusted certificates.
+
+mailcow uses 3 domain names that should be covered by your new certificate:
+
+- ${MAILCOW_HOSTNAME}
+- autodiscover.*example.org*
+- autoconfig.*example.org*
+
+## Obtain multi-SAN certificate by Let's Encrypt
+
+This is just an example of how to obtain certificates with certbot. There are several methods!
+
+1. Get the certbot client:
+```
+wget https://dl.eff.org/certbot-auto -O /usr/local/sbin/certbot && chmod +x /usr/local/sbin/certbot
+```
+2. Make sure you set `HTTP_BIND=0.0.0.0` in `mailcow.conf` or setup a reverse proxy to enable connections to port 80. If you changed HTTP_BIND, then restart Nginx: `docker-compose restart nginx-mailcow`.
+
+3. Request the certificate with the webroot method:
+
+```
+cd /path/to/git/clone/mailcow-dockerized
+source mailcow.conf
+certbot certonly \
+        --webroot \
+        -w ${PWD}/data/web \
+        -d ${MAILCOW_HOSTNAME} \
+        -d autodiscover.example.org \
+        -d autoconfig.example.org \
+        --email [email protected] \
+        --agree-tos
+```
+
+3. Create hard links to the full path of the new certificates. Assuming you are still in the mailcow root folder:
+```
+mv data/assets/ssl/cert.{pem,pem.backup}
+mv data/assets/ssl/key.{pem,pem.backup}
+ln $(readlink -f /etc/letsencrypt/live/${MAILCOW_HOSTNAME}/fullchain.pem) data/assets/ssl/cert.pem
+ln $(readlink -f /etc/letsencrypt/live/${MAILCOW_HOSTNAME}/privkey.pem) data/assets/ssl/key.pem
+```
+4. Restart containers which use the certificate:
+```
+docker-compose restart postfix-mailcow dovecot-mailcow nginx-mailcow
+```
+When renewing certificates, run the last two steps (link + restart) as post-hook in a script.
+
+# Rspamd UI access
+At first you may want to setup Rspamds web interface which provides some useful features and information.
+
+1. Generate a Rspamd controller password hash:
+```
+docker-compose exec rspamd-mailcow rspamadm pw
+```
+2. Replace the default hash in `data/conf/rspamd/override.d/worker-controller.inc` by your newly generated:
+```
+enable_password = "myhash";
+```
+3. Restart rspamd:
+
+```
+docker-compose restart rspamd-mailcow
+```
+
+Open https://${MAILCOW_HOSTNAME}/rspamd in a browser and login!
diff --git a/docs/index.md b/docs/index.md
new file mode 100644
index 00000000..f44f3308
--- /dev/null
+++ b/docs/index.md
@@ -0,0 +1,42 @@
+# mailcow: dockerized - 🐮 + 🐋 = 💕
+
+[![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=JWBSYHF4SMC68)
+
+mailcow dockerized comes with **11 containers** linked in **one bridged network**.
+
+- Dovecot
+- Memcached
+- Redis
+- MySQL
+- Bind9 (Resolver) (formerly PDNS Recursor)
+- PHP-FPM
+- Postfix
+- Nginx
+- Rmilter
+- Rspamd
+- SOGo
+
+**6 volumes** to keep dynamic data - take care of them!
+
+- vmail-vol-1
+- dkim-vol-1
+- redis-vol-1
+- mysql-vol-1
+- rspamd-vol-1
+- postfix-vol-1
+
+The integrated **mailcow UI** allows administrative work on your mail server instance as well as separated domain administrator and mailbox user access:
+
+- DKIM key management
+- Black- and whitelists per domain and per user
+- Spam score managment per-user (reject spam, mark spam, greylist)
+- Allow mailbox users to create temporary spam aliases
+- Prepend mail tags to subject or move mail to subfolder (per-user)
+- Allow mailbox users to toggle incoming and outgoing TLS enforcement
+- Allow users to reset SOGo ActiveSync device caches
+- imapsync to migrate or pull remote mailboxes regularly
+- TFA: Yubi OTP and U2F USB (Google Chrome and derivates only)
+- Add domains, mailboxes, aliases, domain aliases and SOGo resources
+
+
+*[Looking for a farm to host your cow?](https://www.servercow.de)* 
diff --git a/docs/install.md b/docs/install.md
new file mode 100644
index 00000000..352bfa64
--- /dev/null
+++ b/docs/install.md
@@ -0,0 +1,61 @@
+# Install mailcow
+
+1. You need Docker.
+
+    - Most systems can install Docker by running `wget -qO- https://get.docker.com/ | sh`
+
+2. You need Docker Compose.
+
+    - Learn [how to install Docker Compose](https://docs.docker.com/compose/install/) - or:
+	- `curl -L "https://github.com/docker/compose/releases/download/composer_version/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose && chmod +x /usr/local/bin/docker-compose`
+
+3. Clone the master branch of the repository and run `./generate_config.sh` to generate a file "mailcow.conf". You will be asked for a hostname and a timezone:
+
+    - `git clone https://github.com/andryyy/mailcow-dockerized && cd mailcow-dockerized`
+	- `./generate_config.sh`
+	- Open and check "mailcow.conf" if you need or want to make changes to ports (for example changing the default HTTPS port)
+
+4. Run the composer file.
+    - `docker-compose up -d`
+
+Done.
+
+You can now access **https://${MAILCOW_HOSTNAME}** with the default credentials `admin` + password `moohoo`.
+
+It may take a while for MySQL to warm up, so please wait half a minute.
+
+The database will be initialized right after a connection to MySQL can be established.
+
+# Update mailcow
+
+There is no update routine.
+
+You need to refresh your pulled repository clone by running `git pull` - this will likely fail due to changes to your local configuration. But that's why we use git! :-)
+
+Whatever file has local changes, add and commit it to your repository clone. For example:
+
+```
+git add data/conf/postfix/main.cf data/conf/dovecot/dovecot.conf
+git commit -m "My changes to main.cf and dovecot.conf
+```
+
+Try running `git pull` again and resolve conflicts, if any.
+
+Now update all images, apply changes to containers and restart all services:
+
+```
+docker-compose pull
+docker-compose up -d --remove-orphans
+docker-compose restart
+```
+
+## Use dev branch (not recommended)
+
+When you checkout the dev branch, you will most likely end up using the "master" images with code base of "dev".
+If there were critical changes to the images in dev, mailcow will not work.
+
+But you can still build the images by yourself:
+
+```
+docker-compose up -d --build
+```
diff --git a/docs/u_and_e.md b/docs/u_and_e.md
new file mode 100644
index 00000000..0f88118e
--- /dev/null
+++ b/docs/u_and_e.md
@@ -0,0 +1,390 @@
+# Anonymize headers for smtp relayed
+
+Save as `data/conf/postfix/mailcow_anonymize_headers.pcre`:
+
+```
+/^\s*Received:[^\)]+\)\s+\(Authenticated sender:(.+)/
+	REPLACE Received: from localhost (localhost [127.0.0.1]) (Authenticated sender:$1
+/^\s*User-Agent/        IGNORE
+/^\s*X-Enigmail/        IGNORE
+/^\s*X-Mailer/          IGNORE
+/^\s*X-Originating-IP/  IGNORE
+/^\s*X-Forward/         IGNORE
+/^\s*Mime-Version:/     IGNORE
+```
+
+Add this to `data/conf/postfix/main.cf`:
+
+```
+smtp_header_checks = pcre:/opt/postfix/conf/mailcow_anonymize_headers.pcre
+```
+
+# Backup and restore maildir (simple tar file)
+
+### **Backup**
+
+This line backups the vmail directory to a file backup_vmail.tar.gz in the mailcow root directory:
+```
+cd /path/to/mailcow-dockerized
+source mailcow.conf
+DATE=$(date +"%Y%m%d_%H%M%S")
+docker run --rm -it -v $(docker inspect --format '{{ range .Mounts }}{{ if eq .Destination "/var/vmail" }}{{ .Name }}{{ end }}{{ end }}' $(docker-compose ps -q dovecot-mailcow)):/vmail -v ${PWD}:/backup debian:jessie tar cvfz /backup/backup_vmail.tar.gz /vmail
+```
+
+You can change the path by adjusting ${PWD} (which equals to the current directory) to any path you have write-access to.
+Set the filename `backup_vmail.tar.gz` to any custom name, but leave the path as it is. Example: `[...] tar cvfz /backup/my_own_filename_.tar.gz`
+
+### **Restore**
+
+```
+cd /path/to/mailcow-dockerized
+source mailcow.conf
+DATE=$(date +"%Y%m%d_%H%M%S")
+docker run --rm -it -v $(docker inspect --format '{{ range .Mounts }}{{ if eq .Destination "/var/vmail" }}{{ .Name }}{{ end }}{{ end }}' $(docker-compose ps -q dovecot-mailcow)):/vmail -v ${PWD}:/backup debian:jessie tar xvfz /backup/backup_vmail.tar.gz
+```
+For the tab-tab... :-)
+
+```
+curl -L https://raw.githubusercontent.com/docker/compose/$(docker-compose version --short)/contrib/completion/bash/docker-compose -o /etc/bash_completion.d/docker-compose
+```
+# Black- and Whitelist
+
+Edit a domain as (domain) administrator to add an item to the filter table.
+
+Beware that a mailbox user can login to mailcow and override a domain policy filter item. 
+
+# Change default theme
+
+mailcow uses [Bootstrap](http://getbootstrap.com/), a HTML, CSS, and JS framework.
+
+Open or create the file `data/web/inc/vars.local.inc.php` and change `DEFAULT_THEME` to either cerulean, cosmo, custom, cyborg, darkly, flatly, journal, paper, readable, sandstone, simplex, slate, spacelab, superhero, united or yeti (see https://bootswatch.com/):
+```
+<?php
+$DEFAULT_THEME = "paper";
+```
+# Customize Dockerfiles
+
+Make your changes in `data/Dockerfiles/$service` and build the image locally:
+
+```
+docker build data/Dockerfiles/service -t andryyy/mailcow-dockerized:$service
+```
+
+Now auto-recreate modified containers:
+
+```
+docker-compose up -d
+```
+
+# Do not check sender addresses for any domain
+
+This option is not best-practice and should only be implemented when there is no other option available to archive whatever you are trying to do.
+
+Simply create a file `data/conf/postfix/check_sender_access` and enter the following content:
+```
[email protected] OK
+```
+
+Open `data/conf/postfix/main.cf` and find `smtpd_sender_restrictions`. Prepend `check_sasl_access hash:/opt/postfix/conf/check_sender_access` like this:
+```
+smtpd_sender_restrictions = check_sasl_access hash:/opt/postfix/conf/check_sender_access reject_authenticated_sender [...]
+```
+
+Run postmap on check_sasl_access:
+
+```
+docker-compose exec postfix-mailcow postmap /opt/postfix/conf/check_sasl_access
+``` 
+
+Restart the Postfix container.
+# Install Roundcube
+
+Download Roundcube 1.3.x (beta at the time of Feb 2017) to the web htdocs directory and extract it (here `rc/`):
+
+```
+cd data/web/rc
+wget -O - https://github.com/roundcube/roundcubemail/releases/download/1.3-beta/roundcubemail-1.3-beta-complete.tar.gz | tar xfvz -
+# Change folder name
+mv roundcubemail-1.3* rc
+# Change permissions
+chown -R root: rc/
+```
+
+Create a file `data/web/rc/config/config.inc.php` with the following content.
+
+**Change the `des_key` parameter to a random value.** It is used to temporarily store your IMAP password.
+
+```
+<?php
+error_reporting(0);
+if (!file_exists('/tmp/mime.types')) {
+file_put_contents("/tmp/mime.types", fopen("http://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types", 'r'));
+}
+$config = array();
+$config['db_dsnw'] = 'mysql://' . getenv('DBUSER') . ':' . getenv('DBPASS') . '@mysql/' . getenv('DBNAME');
+$config['default_host'] = 'tls://dovecot';
+$config['default_port'] = '143';
+$config['smtp_server'] = 'tls://postfix';
+$config['smtp_port'] = 587;
+$config['smtp_user'] = '%u';
+$config['smtp_pass'] = '%p';
+$config['support_url'] = '';
+$config['product_name'] = 'Roundcube Webmail';
+$config['des_key'] = 'rcmail-!24ByteDESkey*Str';
+$config['log_dir'] = '/dev/null';
+$config['temp_dir'] = '/tmp';
+$config['plugins'] = array(
+    'archive',
+);
+$config['skin'] = 'larry';
+$config['mime_types'] = '/tmp/mime.types';
+$config['imap_conn_options'] = array(
+'ssl' => array('verify_peer' => false, 'verify_peer_name' => false, 'allow_self_signed' => true)
+);
+$config['enable_installer'] = false;
+$config['smtp_conn_options'] = array(
+'ssl' => array('verify_peer' => false, 'verify_peer_name' => false, 'allow_self_signed' => true)
+);
+```
+
+Point your browser to `https://myserver/rc/installer` and follow the instructions.
+Initialize the database and leave the installer.
+**Delete the directory `data/web/rc/installer` after a successful installation!**
+
+## Enable password changing
+
+Open `data/web/rc/config.inc.php` and enable the password plugin:
+
+```
+...
+$config['plugins'] = array(
+    'archive',
+    'password',
+);
+...
+```
+
+Open `data/web/rc/plugins/password/password.php`, search for `case 'ssha':` and add above:
+
+```
+        case 'ssha256':
+            $salt = rcube_utils::random_bytes(8);
+            $crypted = base64_encode( hash('sha256', $password . $salt, TRUE ) . $salt );
+            $prefix  = '{SSHA256}';
+            break;
+```
+
+Open `data/web/rc/plugins/password/config.inc.php` and change the following parameters (or add them at the bottom of that file):
+
+```
+$config['password_driver'] = 'sql';
+$config['password_algorithm'] = 'ssha256';
+$config['password_algorithm_prefix'] = '{SSHA256}';
+$config['password_query'] = "UPDATE mailbox SET password = %P WHERE username = %u";
+```
+
+# Learn spam and ham
+
+Rspamd learns mail as spam or ham when you move a message in or out of the junk folder to any mailbox besides trash.
+This is archived by using the Dovecot plugin "antispam" and a simple parser script.
+
+Rspamd also auto-learns mail when a high or low score is detected (see https://rspamd.com/doc/configuration/statistic.html#autolearning)
+
+The bayes statistics are written to Redis as keys `BAYES_HAM` and `BAYES_SPAM`.
+
+You can also use Rspamds web ui to learn ham and/or spam.
+
+# MySQL
+
+### Connect to the MySQL database:
+```
+source mailcow.conf
+docker-compose exec mysql-mailcow mysql -u${DBUSER} -p${DBPASS} ${DBNAME}
+```
+
+### Backup the database:
+```
+cd /path/to/mailcow-dockerized
+source mailcow.conf
+DATE=$(date +"%Y%m%d_%H%M%S")
+docker-compose exec mysql-mailcow mysqldump --default-character-set=utf8mb4 -u${DBUSER} -p${DBPASS} ${DBNAME} > backup_${DBNAME}_${DATE}.sql
+```
+
+### Restore the database:
+```
+cd /path/to/mailcow-dockerized
+source mailcow.conf
+docker-compose exec mysql-mailcow mysql -u${DBUSER} -p${DBPASS} ${DBNAME} < backup_file.sql
+```
+
+# Read logs
+You can use `docker-compose logs $service-name` for all containers.
+
+Run `docker-compose logs` for all logs at once.
+
+Follow the log output by running docker-compose with `logs -f`.
+
+# Redirect port 80 to 443
+
+Since February the 28th 2017 mailcow does come with port 80 and 443 enabled.
+
+Open `mailcow.conf` and set `HTTP_BIND=0.0.0.0`.
+
+Open `data/conf/nginx/site.conf` and add a new "catch-all" site at the top of that file:
+
+```
+server {
+	listen 80 default_server;
+	server_name _;
+	return 301 https://$host$request_uri;
+}
+```
+
+Restart the stack, changed containers will be updated:
+
+`docker-compose up -d`
+
+# Redis
+
+## Connect to redis key store:
+
+```
+docker-compose exec redis-mailcow redis-cli
+```
+
+# Remove persistent data
+
+- Remove volume `mysql-vol-1` to remove all MySQL data.
+- Remove volume `redis-vol-1` to remove all Redis data.
+- Remove volume `vmail-vol-1` to remove all contents of `/var/vmail` mounted to `dovecot-mailcow`.
+- Remove volume `dkim-vol-1` to remove all DKIM keys.
+- Remove volume `rspamd-vol-1` to remove all Rspamd data.
+
+Running `docker-compose down -v` will **destroy all mailcow: dockerized volumes** and delete any related containers.Reset mailcow admin to `admin:moohoo`:
+
+1. Drop admin table
+
+```
+source mailcow.conf
+docker-compose exec mysql-mailcow mysql -u${DBUSER} -p${DBPASS} ${DBNAME} -e "DROP TABLE admin;"
+```
+
+2. Open mailcow UI to auto-init the db
+
+# Rspamd
+
+## Rspamd CLI tools
+
+```
+docker-compose exec rspamd-mailcow rspamc --help
+docker-compose exec rspamd-mailcow rspamadm --help
+```
+
+See [Rspamd documentation](https://rspamd.com/doc/index.html)
+
+# Adjust service configurations
+The most important configuration files are mounted from the host into the related containers:
+```
+data/conf
+├── bind9
+│   └── named.conf
+├── dovecot
+│   ├── dovecot.conf
+│   ├── dovecot-master.passwd
+│   ├── sieve_after
+│   └── sql
+│       ├── dovecot-dict-sql.conf
+│       └── dovecot-mysql.conf
+├── mysql
+│   └── my.cnf
+├── nginx
+│   ├── dynmaps.conf
+│   ├── site.conf
+│   └── templates
+│       ├── listen_plain.template
+│       ├── listen_ssl.template
+│       └── server_name.template
+├── pdns
+│   ├── pdns_custom.lua
+│   └── recursor.conf
+├── postfix
+│   ├── main.cf
+│   ├── master.cf
+│   ├── postscreen_access.cidr
+│   ├── smtp_dsn_filter
+│   └── sql
+│       ├── mysql_relay_recipient_maps.cf
+│       ├── mysql_tls_enforce_in_policy.cf
+│       ├── mysql_tls_enforce_out_policy.cf
+│       ├── mysql_virtual_alias_domain_catchall_maps.cf
+│       ├── mysql_virtual_alias_domain_maps.cf
+│       ├── mysql_virtual_alias_maps.cf
+│       ├── mysql_virtual_domains_maps.cf
+│       ├── mysql_virtual_mailbox_maps.cf
+│       ├── mysql_virtual_relay_domain_maps.cf
+│       ├── mysql_virtual_sender_acl.cf
+│       └── mysql_virtual_spamalias_maps.cf
+├── rmilter
+│   └── rmilter.conf
+├── rspamd
+│   ├── dynmaps
+│   │   ├── authoritative.php
+│   │   ├── settings.php
+│   │   ├── tags.php
+│   │   └── vars.inc.php -> ../../../web/inc/vars.inc.php
+│   ├── local.d
+│   │   ├── dkim.conf
+│   │   ├── metrics.conf
+│   │   ├── options.inc
+│   │   ├── redis.conf
+│   │   ├── rspamd.conf.local
+│   │   └── statistic.conf
+│   ├── lua
+│   │   └── rspamd.local.lua
+│   └── override.d
+│       ├── logging.inc
+│       ├── worker-controller.inc
+│       └── worker-normal.inc
+└── sogo
+    ├── sieve.creds
+    └── sogo.conf
+
+```
+Just change the according configuration file on the host and restart the related service: `docker-compose restart service-mailcow`
+# Tagging
+
+Mailbox users can tag their mail address like in `[email protected]` and choose between to setups to handle this tag:
+
+1. Move this message to a subfolder "facebook" (will be created lower case if not existing)
+2. Prepend the tag to the subject: "[facebook] Subject"
+# Two-factor authentication
+
+So far two methods for TFA are impelemented. Both work with the fantastic [Yubikey](https://www.yubico.com). 
+
+While Yubi OTP needs an active internet connection and an API ID/key, U2F will work with any FIDO U2F USB key out of the box.
+
+Both methods support mulitple YubiKeys.
+
+As administrator you are able to temporary disable a domain adminsitrators TFA login until they successfully logged in.
+
+The key used to login will be displayed in green, while other keys remain grey.
+
+## Yubi OTP
+
+The Yubi API ID and Key will be checked against the Yubico Cloud API. When setting up TFA you will be asked for your personal API account for this key.
+The API ID, API key and the first 12 characters (your YubiKeys ID in modhex) are stored in the MySQL table as secret.
+
+## U2F
+
+Only Google Chrome (+derivates) and Opera support U2F authentication to this day natively.
+For Firefox you will need to install the "U2F Support Add-on" as provided on [mozilla.org](https://addons.mozilla.org/en-US/firefox/addon/u2f-support-add-on/).
+U2F works without an internet connection.# Why does mailcow come with a DNS resolver?
+
+For DNS blacklist lookups and DNSSEC.
+
+Most systems use either a public or a local caching DNS resolver.
+That's a very bad idea when it comes to filter spam using DNS-based blackhole lists (DNSBL) or similar technics.
+Most if not all providers apply a rate limit based on the DNS resolver that is used to query their service.
+Using a public resolver like Googles 4x8, OpenDNS or any other shared DNS resolver like your ISPs will hit that limit very soon.
+
+
diff --git a/first_steps/index.html b/first_steps/index.html
deleted file mode 100644
index 5df22073..00000000
--- a/first_steps/index.html
+++ /dev/null
@@ -1,255 +0,0 @@
-<!DOCTYPE html>
-<!--[if IE 8]><html class="no-js lt-ie9" lang="en" > <![endif]-->
-<!--[if gt IE 8]><!--> <html class="no-js" lang="en" > <!--<![endif]-->
-<head>
-  <meta charset="utf-8">
-  <meta http-equiv="X-UA-Compatible" content="IE=edge">
-  <meta name="viewport" content="width=device-width, initial-scale=1.0">
-  
-  
-  <link rel="shortcut icon" href="../img/favicon.ico">
-  <title>First Steps - mailcow: dockerized</title>
-  <link href='https://fonts.googleapis.com/css?family=Lato:400,700|Roboto+Slab:400,700|Inconsolata:400,700' rel='stylesheet' type='text/css'>
-
-  <link rel="stylesheet" href="../css/theme.css" type="text/css" />
-  <link rel="stylesheet" href="../css/theme_extra.css" type="text/css" />
-  <link rel="stylesheet" href="../css/highlight.css">
-  
-  <script>
-    // Current page data
-    var mkdocs_page_name = "First Steps";
-    var mkdocs_page_input_path = "first_steps.md";
-    var mkdocs_page_url = "/first_steps/";
-  </script>
-  
-  <script src="../js/jquery-2.1.1.min.js"></script>
-  <script src="../js/modernizr-2.8.3.min.js"></script>
-  <script type="text/javascript" src="../js/highlight.pack.js"></script> 
-  
-</head>
-
-<body class="wy-body-for-nav" role="document">
-
-  <div class="wy-grid-for-nav">
-
-    
-    <nav data-toggle="wy-nav-shift" class="wy-nav-side stickynav">
-      <div class="wy-side-nav-search">
-        <a href=".." class="icon icon-home"> mailcow: dockerized</a>
-        <div role="search">
-  <form id ="rtd-search-form" class="wy-form" action="../search.html" method="get">
-    <input type="text" name="q" placeholder="Search docs" />
-  </form>
-</div>
-      </div>
-
-      <div class="wy-menu wy-menu-vertical" data-spy="affix" role="navigation" aria-label="main navigation">
-	<ul class="current">
-          
-            <li>
-    <li class="toctree-l1 ">
-        <a class="" href="..">Overview</a>
-        
-    </li>
-<li>
-          
-            <li>
-    <li class="toctree-l1 ">
-        <a class="" href="../install/">Installation</a>
-        
-    </li>
-<li>
-          
-            <li>
-    <li class="toctree-l1 current">
-        <a class="current" href="./">First Steps</a>
-        
-            <ul>
-            
-                <li class="toctree-l3"><a href="#change-default-language">Change default language</a></li>
-                
-            
-                <li class="toctree-l3"><a href="#ssl-and-how-to-use-lets-encrypt">SSL (and: How to use Let's Encrypt)</a></li>
-                
-                    <li><a class="toctree-l4" href="#obtain-multi-san-certificate-by-lets-encrypt">Obtain multi-SAN certificate by Let's Encrypt</a></li>
-                
-            
-                <li class="toctree-l3"><a href="#rspamd-ui-access">Rspamd UI access</a></li>
-                
-            
-            </ul>
-        
-    </li>
-<li>
-          
-            <li>
-    <li class="toctree-l1 ">
-        <a class="" href="../u_and_e/">Usage & Examples</a>
-        
-    </li>
-<li>
-          
-        </ul>
-      </div>
-      &nbsp;
-    </nav>
-
-    <section data-toggle="wy-nav-shift" class="wy-nav-content-wrap">
-
-      
-      <nav class="wy-nav-top" role="navigation" aria-label="top navigation">
-        <i data-toggle="wy-nav-top" class="fa fa-bars"></i>
-        <a href="..">mailcow: dockerized</a>
-      </nav>
-
-      
-      <div class="wy-nav-content">
-        <div class="rst-content">
-          <div role="navigation" aria-label="breadcrumbs navigation">
-  <ul class="wy-breadcrumbs">
-    <li><a href="..">Docs</a> &raquo;</li>
-    
-      
-    
-    <li>First Steps</li>
-    <li class="wy-breadcrumbs-aside">
-      
-        <a href="https://github.com/andryyy/mailcow-dockerized/edit/master/docs/first_steps.md"
-          class="icon icon-github"> Edit on GitHub</a>
-      
-    </li>
-  </ul>
-  <hr/>
-</div>
-          <div role="main">
-            <div class="section">
-              
-                <h1 id="change-default-language">Change default language</h1>
-<p>Change <code>data/conf/sogo/sogo.conf</code> and replace English by your language.</p>
-<p>Create a file <code>data/web/inc/vars.local.inc.php</code> and add "DEFAULT_LANG" with either "en", "pt", "de" or "nl":</p>
-<pre><code>&lt;?php
-$DEFAULT_LANG = &quot;de&quot;;
-</code></pre>
-
-<h1 id="ssl-and-how-to-use-lets-encrypt">SSL (and: How to use Let's Encrypt)</h1>
-<p>mailcow dockerized comes with a snakeoil CA "mailcow" and a server certificate in <code>data/assets/ssl</code>. Please use your own trusted certificates.</p>
-<p>mailcow uses 3 domain names that should be covered by your new certificate:</p>
-<ul>
-<li>${MAILCOW_HOSTNAME}</li>
-<li>autodiscover.<em>example.org</em></li>
-<li>autoconfig.<em>example.org</em></li>
-</ul>
-<h2 id="obtain-multi-san-certificate-by-lets-encrypt">Obtain multi-SAN certificate by Let's Encrypt</h2>
-<p>This is just an example of how to obtain certificates with certbot. There are several methods!</p>
-<ol>
-<li>Get the certbot client:</li>
-</ol>
-<pre><code>wget https://dl.eff.org/certbot-auto -O /usr/local/sbin/certbot &amp;&amp; chmod +x /usr/local/sbin/certbot
-</code></pre>
-
-<ol>
-<li>
-<p>Make sure you set <code>HTTP_BIND=0.0.0.0</code> in <code>mailcow.conf</code> or setup a reverse proxy to enable connections to port 80. If you changed HTTP_BIND, then restart Nginx: <code>docker-compose restart nginx-mailcow</code>.</p>
-</li>
-<li>
-<p>Request the certificate with the webroot method:</p>
-</li>
-</ol>
-<pre><code>cd /path/to/git/clone/mailcow-dockerized
-source mailcow.conf
-certbot certonly \
-        --webroot \
-        -w ${PWD}/data/web \
-        -d ${MAILCOW_HOSTNAME} \
-        -d autodiscover.example.org \
-        -d autoconfig.example.org \
-        --email [email protected] \
-        --agree-tos
-</code></pre>
-
-<ol>
-<li>Create hard links to the full path of the new certificates. Assuming you are still in the mailcow root folder:</li>
-</ol>
-<pre><code>mv data/assets/ssl/cert.{pem,pem.backup}
-mv data/assets/ssl/key.{pem,pem.backup}
-ln $(readlink -f /etc/letsencrypt/live/${MAILCOW_HOSTNAME}/fullchain.pem) data/assets/ssl/cert.pem
-ln $(readlink -f /etc/letsencrypt/live/${MAILCOW_HOSTNAME}/privkey.pem) data/assets/ssl/key.pem
-</code></pre>
-
-<ol>
-<li>Restart containers which use the certificate:</li>
-</ol>
-<pre><code>docker-compose restart postfix-mailcow dovecot-mailcow nginx-mailcow
-</code></pre>
-
-<p>When renewing certificates, run the last two steps (link + restart) as post-hook in a script.</p>
-<h1 id="rspamd-ui-access">Rspamd UI access</h1>
-<p>At first you may want to setup Rspamds web interface which provides some useful features and information.</p>
-<ol>
-<li>Generate a Rspamd controller password hash:</li>
-</ol>
-<pre><code>docker-compose exec rspamd-mailcow rspamadm pw
-</code></pre>
-
-<ol>
-<li>Replace the default hash in <code>data/conf/rspamd/override.d/worker-controller.inc</code> by your newly generated:</li>
-</ol>
-<pre><code>enable_password = &quot;myhash&quot;;
-</code></pre>
-
-<ol>
-<li>Restart rspamd:</li>
-</ol>
-<pre><code>docker-compose restart rspamd-mailcow
-</code></pre>
-
-<p>Open https://${MAILCOW_HOSTNAME}/rspamd in a browser and login!</p>
-              
-            </div>
-          </div>
-          <footer>
-  
-    <div class="rst-footer-buttons" role="navigation" aria-label="footer navigation">
-      
-        <a href="../u_and_e/" class="btn btn-neutral float-right" title="Usage & Examples">Next <span class="icon icon-circle-arrow-right"></span></a>
-      
-      
-        <a href="../install/" class="btn btn-neutral" title="Installation"><span class="icon icon-circle-arrow-left"></span> Previous</a>
-      
-    </div>
-  
-
-  <hr/>
-
-  <div role="contentinfo">
-    <!-- Copyright etc -->
-    
-  </div>
-
-  Built with <a href="http://www.mkdocs.org">MkDocs</a> using a <a href="https://github.com/snide/sphinx_rtd_theme">theme</a> provided by <a href="https://readthedocs.org">Read the Docs</a>.
-</footer>
-	  
-        </div>
-      </div>
-
-    </section>
-    
-  </div>
-
-  <div class="rst-versions" role="note" style="cursor: pointer">
-    <span class="rst-current-version" data-toggle="rst-current-version">
-      
-          <a href="https://github.com/andryyy/mailcow-dockerized/" class="icon icon-github" style="float: left; color: #fcfcfc"> GitHub</a>
-      
-      
-        <span><a href="../install/" style="color: #fcfcfc;">&laquo; Previous</a></span>
-      
-      
-        <span style="margin-left: 15px"><a href="../u_and_e/" style="color: #fcfcfc">Next &raquo;</a></span>
-      
-    </span>
-</div>
-    <script src="../js/theme.js"></script>
-
-</body>
-</html>
diff --git a/fonts/fontawesome-webfont.eot b/fonts/fontawesome-webfont.eot
deleted file mode 100755
index 0662cb96..00000000
Binary files a/fonts/fontawesome-webfont.eot and /dev/null differ
diff --git a/fonts/fontawesome-webfont.svg b/fonts/fontawesome-webfont.svg
deleted file mode 100755
index 2edb4ec3..00000000
--- a/fonts/fontawesome-webfont.svg
+++ /dev/null
@@ -1,399 +0,0 @@
-<?xml version="1.0" standalone="no"?>
-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
-<svg xmlns="http://www.w3.org/2000/svg">
-<metadata></metadata>
-<defs>
-<font id="fontawesomeregular" horiz-adv-x="1536" >
-<font-face units-per-em="1792" ascent="1536" descent="-256" />
-<missing-glyph horiz-adv-x="448" />
-<glyph unicode=" "  horiz-adv-x="448" />
-<glyph unicode="&#x09;" horiz-adv-x="448" />
-<glyph unicode="&#xa0;" horiz-adv-x="448" />
-<glyph unicode="&#xa8;" horiz-adv-x="1792" />
-<glyph unicode="&#xa9;" horiz-adv-x="1792" />
-<glyph unicode="&#xae;" horiz-adv-x="1792" />
-<glyph unicode="&#xb4;" horiz-adv-x="1792" />
-<glyph unicode="&#xc6;" horiz-adv-x="1792" />
-<glyph unicode="&#x2000;" horiz-adv-x="768" />
-<glyph unicode="&#x2001;" />
-<glyph unicode="&#x2002;" horiz-adv-x="768" />
-<glyph unicode="&#x2003;" />
-<glyph unicode="&#x2004;" horiz-adv-x="512" />
-<glyph unicode="&#x2005;" horiz-adv-x="384" />
-<glyph unicode="&#x2006;" horiz-adv-x="256" />
-<glyph unicode="&#x2007;" horiz-adv-x="256" />
-<glyph unicode="&#x2008;" horiz-adv-x="192" />
-<glyph unicode="&#x2009;" horiz-adv-x="307" />
-<glyph unicode="&#x200a;" horiz-adv-x="85" />
-<glyph unicode="&#x202f;" horiz-adv-x="307" />
-<glyph unicode="&#x205f;" horiz-adv-x="384" />
-<glyph unicode="&#x2122;" horiz-adv-x="1792" />
-<glyph unicode="&#x221e;" horiz-adv-x="1792" />
-<glyph unicode="&#x2260;" horiz-adv-x="1792" />
-<glyph unicode="&#xe000;" horiz-adv-x="500" d="M0 0z" />
-<glyph unicode="&#xf000;" horiz-adv-x="1792" d="M1699 1350q0 -35 -43 -78l-632 -632v-768h320q26 0 45 -19t19 -45t-19 -45t-45 -19h-896q-26 0 -45 19t-19 45t19 45t45 19h320v768l-632 632q-43 43 -43 78q0 23 18 36.5t38 17.5t43 4h1408q23 0 43 -4t38 -17.5t18 -36.5z" />
-<glyph unicode="&#xf001;" d="M1536 1312v-1120q0 -50 -34 -89t-86 -60.5t-103.5 -32t-96.5 -10.5t-96.5 10.5t-103.5 32t-86 60.5t-34 89t34 89t86 60.5t103.5 32t96.5 10.5q105 0 192 -39v537l-768 -237v-709q0 -50 -34 -89t-86 -60.5t-103.5 -32t-96.5 -10.5t-96.5 10.5t-103.5 32t-86 60.5t-34 89 t34 89t86 60.5t103.5 32t96.5 10.5q105 0 192 -39v967q0 31 19 56.5t49 35.5l832 256q12 4 28 4q40 0 68 -28t28 -68z" />
-<glyph unicode="&#xf002;" horiz-adv-x="1664" d="M1152 704q0 185 -131.5 316.5t-316.5 131.5t-316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5t316.5 131.5t131.5 316.5zM1664 -128q0 -52 -38 -90t-90 -38q-54 0 -90 38l-343 342q-179 -124 -399 -124q-143 0 -273.5 55.5t-225 150t-150 225t-55.5 273.5 t55.5 273.5t150 225t225 150t273.5 55.5t273.5 -55.5t225 -150t150 -225t55.5 -273.5q0 -220 -124 -399l343 -343q37 -37 37 -90z" />
-<glyph unicode="&#xf003;" horiz-adv-x="1792" d="M1664 32v768q-32 -36 -69 -66q-268 -206 -426 -338q-51 -43 -83 -67t-86.5 -48.5t-102.5 -24.5h-1h-1q-48 0 -102.5 24.5t-86.5 48.5t-83 67q-158 132 -426 338q-37 30 -69 66v-768q0 -13 9.5 -22.5t22.5 -9.5h1472q13 0 22.5 9.5t9.5 22.5zM1664 1083v11v13.5t-0.5 13 t-3 12.5t-5.5 9t-9 7.5t-14 2.5h-1472q-13 0 -22.5 -9.5t-9.5 -22.5q0 -168 147 -284q193 -152 401 -317q6 -5 35 -29.5t46 -37.5t44.5 -31.5t50.5 -27.5t43 -9h1h1q20 0 43 9t50.5 27.5t44.5 31.5t46 37.5t35 29.5q208 165 401 317q54 43 100.5 115.5t46.5 131.5z M1792 1120v-1088q0 -66 -47 -113t-113 -47h-1472q-66 0 -113 47t-47 113v1088q0 66 47 113t113 47h1472q66 0 113 -47t47 -113z" />
-<glyph unicode="&#xf004;" horiz-adv-x="1792" d="M896 -128q-26 0 -44 18l-624 602q-10 8 -27.5 26t-55.5 65.5t-68 97.5t-53.5 121t-23.5 138q0 220 127 344t351 124q62 0 126.5 -21.5t120 -58t95.5 -68.5t76 -68q36 36 76 68t95.5 68.5t120 58t126.5 21.5q224 0 351 -124t127 -344q0 -221 -229 -450l-623 -600 q-18 -18 -44 -18z" />
-<glyph unicode="&#xf005;" horiz-adv-x="1664" d="M1664 889q0 -22 -26 -48l-363 -354l86 -500q1 -7 1 -20q0 -21 -10.5 -35.5t-30.5 -14.5q-19 0 -40 12l-449 236l-449 -236q-22 -12 -40 -12q-21 0 -31.5 14.5t-10.5 35.5q0 6 2 20l86 500l-364 354q-25 27 -25 48q0 37 56 46l502 73l225 455q19 41 49 41t49 -41l225 -455 l502 -73q56 -9 56 -46z" />
-<glyph unicode="&#xf006;" horiz-adv-x="1664" d="M1137 532l306 297l-422 62l-189 382l-189 -382l-422 -62l306 -297l-73 -421l378 199l377 -199zM1664 889q0 -22 -26 -48l-363 -354l86 -500q1 -7 1 -20q0 -50 -41 -50q-19 0 -40 12l-449 236l-449 -236q-22 -12 -40 -12q-21 0 -31.5 14.5t-10.5 35.5q0 6 2 20l86 500 l-364 354q-25 27 -25 48q0 37 56 46l502 73l225 455q19 41 49 41t49 -41l225 -455l502 -73q56 -9 56 -46z" />
-<glyph unicode="&#xf007;" horiz-adv-x="1408" d="M1408 131q0 -120 -73 -189.5t-194 -69.5h-874q-121 0 -194 69.5t-73 189.5q0 53 3.5 103.5t14 109t26.5 108.5t43 97.5t62 81t85.5 53.5t111.5 20q9 0 42 -21.5t74.5 -48t108 -48t133.5 -21.5t133.5 21.5t108 48t74.5 48t42 21.5q61 0 111.5 -20t85.5 -53.5t62 -81 t43 -97.5t26.5 -108.5t14 -109t3.5 -103.5zM1088 1024q0 -159 -112.5 -271.5t-271.5 -112.5t-271.5 112.5t-112.5 271.5t112.5 271.5t271.5 112.5t271.5 -112.5t112.5 -271.5z" />
-<glyph unicode="&#xf008;" horiz-adv-x="1920" d="M384 -64v128q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h128q26 0 45 19t19 45zM384 320v128q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h128q26 0 45 19t19 45zM384 704v128q0 26 -19 45t-45 19h-128 q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h128q26 0 45 19t19 45zM1408 -64v512q0 26 -19 45t-45 19h-768q-26 0 -45 -19t-19 -45v-512q0 -26 19 -45t45 -19h768q26 0 45 19t19 45zM384 1088v128q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45 t45 -19h128q26 0 45 19t19 45zM1792 -64v128q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h128q26 0 45 19t19 45zM1408 704v512q0 26 -19 45t-45 19h-768q-26 0 -45 -19t-19 -45v-512q0 -26 19 -45t45 -19h768q26 0 45 19t19 45zM1792 320v128 q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h128q26 0 45 19t19 45zM1792 704v128q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h128q26 0 45 19t19 45zM1792 1088v128q0 26 -19 45t-45 19h-128q-26 0 -45 -19 t-19 -45v-128q0 -26 19 -45t45 -19h128q26 0 45 19t19 45zM1920 1248v-1344q0 -66 -47 -113t-113 -47h-1600q-66 0 -113 47t-47 113v1344q0 66 47 113t113 47h1600q66 0 113 -47t47 -113z" />
-<glyph unicode="&#xf009;" horiz-adv-x="1664" d="M768 512v-384q0 -52 -38 -90t-90 -38h-512q-52 0 -90 38t-38 90v384q0 52 38 90t90 38h512q52 0 90 -38t38 -90zM768 1280v-384q0 -52 -38 -90t-90 -38h-512q-52 0 -90 38t-38 90v384q0 52 38 90t90 38h512q52 0 90 -38t38 -90zM1664 512v-384q0 -52 -38 -90t-90 -38 h-512q-52 0 -90 38t-38 90v384q0 52 38 90t90 38h512q52 0 90 -38t38 -90zM1664 1280v-384q0 -52 -38 -90t-90 -38h-512q-52 0 -90 38t-38 90v384q0 52 38 90t90 38h512q52 0 90 -38t38 -90z" />
-<glyph unicode="&#xf00a;" horiz-adv-x="1792" d="M512 288v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM512 800v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM1152 288v-192q0 -40 -28 -68t-68 -28h-320 q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM512 1312v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM1152 800v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28 h320q40 0 68 -28t28 -68zM1792 288v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM1152 1312v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM1792 800v-192 q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM1792 1312v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68z" />
-<glyph unicode="&#xf00b;" horiz-adv-x="1792" d="M512 288v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM512 800v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM1792 288v-192q0 -40 -28 -68t-68 -28h-960 q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h960q40 0 68 -28t28 -68zM512 1312v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM1792 800v-192q0 -40 -28 -68t-68 -28h-960q-40 0 -68 28t-28 68v192q0 40 28 68t68 28 h960q40 0 68 -28t28 -68zM1792 1312v-192q0 -40 -28 -68t-68 -28h-960q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h960q40 0 68 -28t28 -68z" />
-<glyph unicode="&#xf00c;" horiz-adv-x="1792" d="M1671 970q0 -40 -28 -68l-724 -724l-136 -136q-28 -28 -68 -28t-68 28l-136 136l-362 362q-28 28 -28 68t28 68l136 136q28 28 68 28t68 -28l294 -295l656 657q28 28 68 28t68 -28l136 -136q28 -28 28 -68z" />
-<glyph unicode="&#xf00d;" horiz-adv-x="1408" d="M1298 214q0 -40 -28 -68l-136 -136q-28 -28 -68 -28t-68 28l-294 294l-294 -294q-28 -28 -68 -28t-68 28l-136 136q-28 28 -28 68t28 68l294 294l-294 294q-28 28 -28 68t28 68l136 136q28 28 68 28t68 -28l294 -294l294 294q28 28 68 28t68 -28l136 -136q28 -28 28 -68 t-28 -68l-294 -294l294 -294q28 -28 28 -68z" />
-<glyph unicode="&#xf00e;" horiz-adv-x="1664" d="M1024 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-224v-224q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v224h-224q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h224v224q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5v-224h224 q13 0 22.5 -9.5t9.5 -22.5zM1152 704q0 185 -131.5 316.5t-316.5 131.5t-316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5t316.5 131.5t131.5 316.5zM1664 -128q0 -53 -37.5 -90.5t-90.5 -37.5q-54 0 -90 38l-343 342q-179 -124 -399 -124q-143 0 -273.5 55.5 t-225 150t-150 225t-55.5 273.5t55.5 273.5t150 225t225 150t273.5 55.5t273.5 -55.5t225 -150t150 -225t55.5 -273.5q0 -220 -124 -399l343 -343q37 -37 37 -90z" />
-<glyph unicode="&#xf010;" horiz-adv-x="1664" d="M1024 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-576q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h576q13 0 22.5 -9.5t9.5 -22.5zM1152 704q0 185 -131.5 316.5t-316.5 131.5t-316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5t316.5 131.5t131.5 316.5z M1664 -128q0 -53 -37.5 -90.5t-90.5 -37.5q-54 0 -90 38l-343 342q-179 -124 -399 -124q-143 0 -273.5 55.5t-225 150t-150 225t-55.5 273.5t55.5 273.5t150 225t225 150t273.5 55.5t273.5 -55.5t225 -150t150 -225t55.5 -273.5q0 -220 -124 -399l343 -343q37 -37 37 -90z " />
-<glyph unicode="&#xf011;" d="M1536 640q0 -156 -61 -298t-164 -245t-245 -164t-298 -61t-298 61t-245 164t-164 245t-61 298q0 182 80.5 343t226.5 270q43 32 95.5 25t83.5 -50q32 -42 24.5 -94.5t-49.5 -84.5q-98 -74 -151.5 -181t-53.5 -228q0 -104 40.5 -198.5t109.5 -163.5t163.5 -109.5 t198.5 -40.5t198.5 40.5t163.5 109.5t109.5 163.5t40.5 198.5q0 121 -53.5 228t-151.5 181q-42 32 -49.5 84.5t24.5 94.5q31 43 84 50t95 -25q146 -109 226.5 -270t80.5 -343zM896 1408v-640q0 -52 -38 -90t-90 -38t-90 38t-38 90v640q0 52 38 90t90 38t90 -38t38 -90z" />
-<glyph unicode="&#xf012;" horiz-adv-x="1792" d="M256 96v-192q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h192q14 0 23 -9t9 -23zM640 224v-320q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v320q0 14 9 23t23 9h192q14 0 23 -9t9 -23zM1024 480v-576q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23 v576q0 14 9 23t23 9h192q14 0 23 -9t9 -23zM1408 864v-960q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v960q0 14 9 23t23 9h192q14 0 23 -9t9 -23zM1792 1376v-1472q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v1472q0 14 9 23t23 9h192q14 0 23 -9t9 -23z" />
-<glyph unicode="&#xf013;" d="M1024 640q0 106 -75 181t-181 75t-181 -75t-75 -181t75 -181t181 -75t181 75t75 181zM1536 749v-222q0 -12 -8 -23t-20 -13l-185 -28q-19 -54 -39 -91q35 -50 107 -138q10 -12 10 -25t-9 -23q-27 -37 -99 -108t-94 -71q-12 0 -26 9l-138 108q-44 -23 -91 -38 q-16 -136 -29 -186q-7 -28 -36 -28h-222q-14 0 -24.5 8.5t-11.5 21.5l-28 184q-49 16 -90 37l-141 -107q-10 -9 -25 -9q-14 0 -25 11q-126 114 -165 168q-7 10 -7 23q0 12 8 23q15 21 51 66.5t54 70.5q-27 50 -41 99l-183 27q-13 2 -21 12.5t-8 23.5v222q0 12 8 23t19 13 l186 28q14 46 39 92q-40 57 -107 138q-10 12 -10 24q0 10 9 23q26 36 98.5 107.5t94.5 71.5q13 0 26 -10l138 -107q44 23 91 38q16 136 29 186q7 28 36 28h222q14 0 24.5 -8.5t11.5 -21.5l28 -184q49 -16 90 -37l142 107q9 9 24 9q13 0 25 -10q129 -119 165 -170q7 -8 7 -22 q0 -12 -8 -23q-15 -21 -51 -66.5t-54 -70.5q26 -50 41 -98l183 -28q13 -2 21 -12.5t8 -23.5z" />
-<glyph unicode="&#xf014;" horiz-adv-x="1408" d="M512 800v-576q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v576q0 14 9 23t23 9h64q14 0 23 -9t9 -23zM768 800v-576q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v576q0 14 9 23t23 9h64q14 0 23 -9t9 -23zM1024 800v-576q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v576 q0 14 9 23t23 9h64q14 0 23 -9t9 -23zM1152 76v948h-896v-948q0 -22 7 -40.5t14.5 -27t10.5 -8.5h832q3 0 10.5 8.5t14.5 27t7 40.5zM480 1152h448l-48 117q-7 9 -17 11h-317q-10 -2 -17 -11zM1408 1120v-64q0 -14 -9 -23t-23 -9h-96v-948q0 -83 -47 -143.5t-113 -60.5h-832 q-66 0 -113 58.5t-47 141.5v952h-96q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h309l70 167q15 37 54 63t79 26h320q40 0 79 -26t54 -63l70 -167h309q14 0 23 -9t9 -23z" />
-<glyph unicode="&#xf015;" horiz-adv-x="1664" d="M1408 544v-480q0 -26 -19 -45t-45 -19h-384v384h-256v-384h-384q-26 0 -45 19t-19 45v480q0 1 0.5 3t0.5 3l575 474l575 -474q1 -2 1 -6zM1631 613l-62 -74q-8 -9 -21 -11h-3q-13 0 -21 7l-692 577l-692 -577q-12 -8 -24 -7q-13 2 -21 11l-62 74q-8 10 -7 23.5t11 21.5 l719 599q32 26 76 26t76 -26l244 -204v195q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-408l219 -182q10 -8 11 -21.5t-7 -23.5z" />
-<glyph unicode="&#xf016;" horiz-adv-x="1280" d="M128 0h1024v768h-416q-40 0 -68 28t-28 68v416h-512v-1280zM768 896h376q-10 29 -22 41l-313 313q-12 12 -41 22v-376zM1280 864v-896q0 -40 -28 -68t-68 -28h-1088q-40 0 -68 28t-28 68v1344q0 40 28 68t68 28h640q40 0 88 -20t76 -48l312 -312q28 -28 48 -76t20 -88z " />
-<glyph unicode="&#xf017;" d="M896 992v-448q0 -14 -9 -23t-23 -9h-320q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h224v352q0 14 9 23t23 9h64q14 0 23 -9t9 -23zM1312 640q0 148 -73 273t-198 198t-273 73t-273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73t273 73t198 198t73 273zM1536 640 q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
-<glyph unicode="&#xf018;" horiz-adv-x="1920" d="M1111 540v4l-24 320q-1 13 -11 22.5t-23 9.5h-186q-13 0 -23 -9.5t-11 -22.5l-24 -320v-4q-1 -12 8 -20t21 -8h244q12 0 21 8t8 20zM1870 73q0 -73 -46 -73h-704q13 0 22 9.5t8 22.5l-20 256q-1 13 -11 22.5t-23 9.5h-272q-13 0 -23 -9.5t-11 -22.5l-20 -256 q-1 -13 8 -22.5t22 -9.5h-704q-46 0 -46 73q0 54 26 116l417 1044q8 19 26 33t38 14h339q-13 0 -23 -9.5t-11 -22.5l-15 -192q-1 -14 8 -23t22 -9h166q13 0 22 9t8 23l-15 192q-1 13 -11 22.5t-23 9.5h339q20 0 38 -14t26 -33l417 -1044q26 -62 26 -116z" />
-<glyph unicode="&#xf019;" horiz-adv-x="1664" d="M1280 192q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1536 192q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1664 416v-320q0 -40 -28 -68t-68 -28h-1472q-40 0 -68 28t-28 68v320q0 40 28 68t68 28h465l135 -136 q58 -56 136 -56t136 56l136 136h464q40 0 68 -28t28 -68zM1339 985q17 -41 -14 -70l-448 -448q-18 -19 -45 -19t-45 19l-448 448q-31 29 -14 70q17 39 59 39h256v448q0 26 19 45t45 19h256q26 0 45 -19t19 -45v-448h256q42 0 59 -39z" />
-<glyph unicode="&#xf01a;" d="M1120 608q0 -12 -10 -24l-319 -319q-11 -9 -23 -9t-23 9l-320 320q-15 16 -7 35q8 20 30 20h192v352q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-352h192q14 0 23 -9t9 -23zM768 1184q-148 0 -273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73t273 73t198 198t73 273 t-73 273t-198 198t-273 73zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
-<glyph unicode="&#xf01b;" d="M1118 660q-8 -20 -30 -20h-192v-352q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v352h-192q-14 0 -23 9t-9 23q0 12 10 24l319 319q11 9 23 9t23 -9l320 -320q15 -16 7 -35zM768 1184q-148 0 -273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73t273 73t198 198 t73 273t-73 273t-198 198t-273 73zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
-<glyph unicode="&#xf01c;" d="M1023 576h316q-1 3 -2.5 8t-2.5 8l-212 496h-708l-212 -496q-1 -2 -2.5 -8t-2.5 -8h316l95 -192h320zM1536 546v-482q0 -26 -19 -45t-45 -19h-1408q-26 0 -45 19t-19 45v482q0 62 25 123l238 552q10 25 36.5 42t52.5 17h832q26 0 52.5 -17t36.5 -42l238 -552 q25 -61 25 -123z" />
-<glyph unicode="&#xf01d;" d="M1184 640q0 -37 -32 -55l-544 -320q-15 -9 -32 -9q-16 0 -32 8q-32 19 -32 56v640q0 37 32 56q33 18 64 -1l544 -320q32 -18 32 -55zM1312 640q0 148 -73 273t-198 198t-273 73t-273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73t273 73t198 198t73 273zM1536 640 q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
-<glyph unicode="&#xf01e;" d="M1536 1280v-448q0 -26 -19 -45t-45 -19h-448q-42 0 -59 40q-17 39 14 69l138 138q-148 137 -349 137q-104 0 -198.5 -40.5t-163.5 -109.5t-109.5 -163.5t-40.5 -198.5t40.5 -198.5t109.5 -163.5t163.5 -109.5t198.5 -40.5q119 0 225 52t179 147q7 10 23 12q14 0 25 -9 l137 -138q9 -8 9.5 -20.5t-7.5 -22.5q-109 -132 -264 -204.5t-327 -72.5q-156 0 -298 61t-245 164t-164 245t-61 298t61 298t164 245t245 164t298 61q147 0 284.5 -55.5t244.5 -156.5l130 129q29 31 70 14q39 -17 39 -59z" />
-<glyph unicode="&#xf021;" d="M1511 480q0 -5 -1 -7q-64 -268 -268 -434.5t-478 -166.5q-146 0 -282.5 55t-243.5 157l-129 -129q-19 -19 -45 -19t-45 19t-19 45v448q0 26 19 45t45 19h448q26 0 45 -19t19 -45t-19 -45l-137 -137q71 -66 161 -102t187 -36q134 0 250 65t186 179q11 17 53 117 q8 23 30 23h192q13 0 22.5 -9.5t9.5 -22.5zM1536 1280v-448q0 -26 -19 -45t-45 -19h-448q-26 0 -45 19t-19 45t19 45l138 138q-148 137 -349 137q-134 0 -250 -65t-186 -179q-11 -17 -53 -117q-8 -23 -30 -23h-199q-13 0 -22.5 9.5t-9.5 22.5v7q65 268 270 434.5t480 166.5 q146 0 284 -55.5t245 -156.5l130 129q19 19 45 19t45 -19t19 -45z" />
-<glyph unicode="&#xf022;" horiz-adv-x="1792" d="M384 352v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM384 608v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M384 864v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM1536 352v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-960q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h960q13 0 22.5 -9.5t9.5 -22.5z M1536 608v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-960q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h960q13 0 22.5 -9.5t9.5 -22.5zM1536 864v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-960q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h960q13 0 22.5 -9.5 t9.5 -22.5zM1664 160v832q0 13 -9.5 22.5t-22.5 9.5h-1472q-13 0 -22.5 -9.5t-9.5 -22.5v-832q0 -13 9.5 -22.5t22.5 -9.5h1472q13 0 22.5 9.5t9.5 22.5zM1792 1248v-1088q0 -66 -47 -113t-113 -47h-1472q-66 0 -113 47t-47 113v1088q0 66 47 113t113 47h1472q66 0 113 -47 t47 -113z" />
-<glyph unicode="&#xf023;" horiz-adv-x="1152" d="M320 768h512v192q0 106 -75 181t-181 75t-181 -75t-75 -181v-192zM1152 672v-576q0 -40 -28 -68t-68 -28h-960q-40 0 -68 28t-28 68v576q0 40 28 68t68 28h32v192q0 184 132 316t316 132t316 -132t132 -316v-192h32q40 0 68 -28t28 -68z" />
-<glyph unicode="&#xf024;" horiz-adv-x="1792" d="M320 1280q0 -72 -64 -110v-1266q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v1266q-64 38 -64 110q0 53 37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1792 1216v-763q0 -25 -12.5 -38.5t-39.5 -27.5q-215 -116 -369 -116q-61 0 -123.5 22t-108.5 48 t-115.5 48t-142.5 22q-192 0 -464 -146q-17 -9 -33 -9q-26 0 -45 19t-19 45v742q0 32 31 55q21 14 79 43q236 120 421 120q107 0 200 -29t219 -88q38 -19 88 -19q54 0 117.5 21t110 47t88 47t54.5 21q26 0 45 -19t19 -45z" />
-<glyph unicode="&#xf025;" horiz-adv-x="1664" d="M1664 650q0 -166 -60 -314l-20 -49l-185 -33q-22 -83 -90.5 -136.5t-156.5 -53.5v-32q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v576q0 14 9 23t23 9h64q14 0 23 -9t9 -23v-32q71 0 130 -35.5t93 -95.5l68 12q29 95 29 193q0 148 -88 279t-236.5 209t-315.5 78 t-315.5 -78t-236.5 -209t-88 -279q0 -98 29 -193l68 -12q34 60 93 95.5t130 35.5v32q0 14 9 23t23 9h64q14 0 23 -9t9 -23v-576q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v32q-88 0 -156.5 53.5t-90.5 136.5l-185 33l-20 49q-60 148 -60 314q0 151 67 291t179 242.5 t266 163.5t320 61t320 -61t266 -163.5t179 -242.5t67 -291z" />
-<glyph unicode="&#xf026;" horiz-adv-x="768" d="M768 1184v-1088q0 -26 -19 -45t-45 -19t-45 19l-333 333h-262q-26 0 -45 19t-19 45v384q0 26 19 45t45 19h262l333 333q19 19 45 19t45 -19t19 -45z" />
-<glyph unicode="&#xf027;" horiz-adv-x="1152" d="M768 1184v-1088q0 -26 -19 -45t-45 -19t-45 19l-333 333h-262q-26 0 -45 19t-19 45v384q0 26 19 45t45 19h262l333 333q19 19 45 19t45 -19t19 -45zM1152 640q0 -76 -42.5 -141.5t-112.5 -93.5q-10 -5 -25 -5q-26 0 -45 18.5t-19 45.5q0 21 12 35.5t29 25t34 23t29 35.5 t12 57t-12 57t-29 35.5t-34 23t-29 25t-12 35.5q0 27 19 45.5t45 18.5q15 0 25 -5q70 -27 112.5 -93t42.5 -142z" />
-<glyph unicode="&#xf028;" horiz-adv-x="1664" d="M768 1184v-1088q0 -26 -19 -45t-45 -19t-45 19l-333 333h-262q-26 0 -45 19t-19 45v384q0 26 19 45t45 19h262l333 333q19 19 45 19t45 -19t19 -45zM1152 640q0 -76 -42.5 -141.5t-112.5 -93.5q-10 -5 -25 -5q-26 0 -45 18.5t-19 45.5q0 21 12 35.5t29 25t34 23t29 35.5 t12 57t-12 57t-29 35.5t-34 23t-29 25t-12 35.5q0 27 19 45.5t45 18.5q15 0 25 -5q70 -27 112.5 -93t42.5 -142zM1408 640q0 -153 -85 -282.5t-225 -188.5q-13 -5 -25 -5q-27 0 -46 19t-19 45q0 39 39 59q56 29 76 44q74 54 115.5 135.5t41.5 173.5t-41.5 173.5 t-115.5 135.5q-20 15 -76 44q-39 20 -39 59q0 26 19 45t45 19q13 0 26 -5q140 -59 225 -188.5t85 -282.5zM1664 640q0 -230 -127 -422.5t-338 -283.5q-13 -5 -26 -5q-26 0 -45 19t-19 45q0 36 39 59q7 4 22.5 10.5t22.5 10.5q46 25 82 51q123 91 192 227t69 289t-69 289 t-192 227q-36 26 -82 51q-7 4 -22.5 10.5t-22.5 10.5q-39 23 -39 59q0 26 19 45t45 19q13 0 26 -5q211 -91 338 -283.5t127 -422.5z" />
-<glyph unicode="&#xf029;" horiz-adv-x="1408" d="M384 384v-128h-128v128h128zM384 1152v-128h-128v128h128zM1152 1152v-128h-128v128h128zM128 129h384v383h-384v-383zM128 896h384v384h-384v-384zM896 896h384v384h-384v-384zM640 640v-640h-640v640h640zM1152 128v-128h-128v128h128zM1408 128v-128h-128v128h128z M1408 640v-384h-384v128h-128v-384h-128v640h384v-128h128v128h128zM640 1408v-640h-640v640h640zM1408 1408v-640h-640v640h640z" />
-<glyph unicode="&#xf02a;" horiz-adv-x="1792" d="M63 0h-63v1408h63v-1408zM126 1h-32v1407h32v-1407zM220 1h-31v1407h31v-1407zM377 1h-31v1407h31v-1407zM534 1h-62v1407h62v-1407zM660 1h-31v1407h31v-1407zM723 1h-31v1407h31v-1407zM786 1h-31v1407h31v-1407zM943 1h-63v1407h63v-1407zM1100 1h-63v1407h63v-1407z M1226 1h-63v1407h63v-1407zM1352 1h-63v1407h63v-1407zM1446 1h-63v1407h63v-1407zM1635 1h-94v1407h94v-1407zM1698 1h-32v1407h32v-1407zM1792 0h-63v1408h63v-1408z" />
-<glyph unicode="&#xf02b;" d="M448 1088q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1515 512q0 -53 -37 -90l-491 -492q-39 -37 -91 -37q-53 0 -90 37l-715 716q-38 37 -64.5 101t-26.5 117v416q0 52 38 90t90 38h416q53 0 117 -26.5t102 -64.5 l715 -714q37 -39 37 -91z" />
-<glyph unicode="&#xf02c;" horiz-adv-x="1920" d="M448 1088q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1515 512q0 -53 -37 -90l-491 -492q-39 -37 -91 -37q-53 0 -90 37l-715 716q-38 37 -64.5 101t-26.5 117v416q0 52 38 90t90 38h416q53 0 117 -26.5t102 -64.5 l715 -714q37 -39 37 -91zM1899 512q0 -53 -37 -90l-491 -492q-39 -37 -91 -37q-36 0 -59 14t-53 45l470 470q37 37 37 90q0 52 -37 91l-715 714q-38 38 -102 64.5t-117 26.5h224q53 0 117 -26.5t102 -64.5l715 -714q37 -39 37 -91z" />
-<glyph unicode="&#xf02d;" horiz-adv-x="1664" d="M1639 1058q40 -57 18 -129l-275 -906q-19 -64 -76.5 -107.5t-122.5 -43.5h-923q-77 0 -148.5 53.5t-99.5 131.5q-24 67 -2 127q0 4 3 27t4 37q1 8 -3 21.5t-3 19.5q2 11 8 21t16.5 23.5t16.5 23.5q23 38 45 91.5t30 91.5q3 10 0.5 30t-0.5 28q3 11 17 28t17 23 q21 36 42 92t25 90q1 9 -2.5 32t0.5 28q4 13 22 30.5t22 22.5q19 26 42.5 84.5t27.5 96.5q1 8 -3 25.5t-2 26.5q2 8 9 18t18 23t17 21q8 12 16.5 30.5t15 35t16 36t19.5 32t26.5 23.5t36 11.5t47.5 -5.5l-1 -3q38 9 51 9h761q74 0 114 -56t18 -130l-274 -906 q-36 -119 -71.5 -153.5t-128.5 -34.5h-869q-27 0 -38 -15q-11 -16 -1 -43q24 -70 144 -70h923q29 0 56 15.5t35 41.5l300 987q7 22 5 57q38 -15 59 -43zM575 1056q-4 -13 2 -22.5t20 -9.5h608q13 0 25.5 9.5t16.5 22.5l21 64q4 13 -2 22.5t-20 9.5h-608q-13 0 -25.5 -9.5 t-16.5 -22.5zM492 800q-4 -13 2 -22.5t20 -9.5h608q13 0 25.5 9.5t16.5 22.5l21 64q4 13 -2 22.5t-20 9.5h-608q-13 0 -25.5 -9.5t-16.5 -22.5z" />
-<glyph unicode="&#xf02e;" horiz-adv-x="1280" d="M1164 1408q23 0 44 -9q33 -13 52.5 -41t19.5 -62v-1289q0 -34 -19.5 -62t-52.5 -41q-19 -8 -44 -8q-48 0 -83 32l-441 424l-441 -424q-36 -33 -83 -33q-23 0 -44 9q-33 13 -52.5 41t-19.5 62v1289q0 34 19.5 62t52.5 41q21 9 44 9h1048z" />
-<glyph unicode="&#xf02f;" horiz-adv-x="1664" d="M384 0h896v256h-896v-256zM384 640h896v384h-160q-40 0 -68 28t-28 68v160h-640v-640zM1536 576q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1664 576v-416q0 -13 -9.5 -22.5t-22.5 -9.5h-224v-160q0 -40 -28 -68t-68 -28h-960q-40 0 -68 28t-28 68 v160h-224q-13 0 -22.5 9.5t-9.5 22.5v416q0 79 56.5 135.5t135.5 56.5h64v544q0 40 28 68t68 28h672q40 0 88 -20t76 -48l152 -152q28 -28 48 -76t20 -88v-256h64q79 0 135.5 -56.5t56.5 -135.5z" />
-<glyph unicode="&#xf030;" horiz-adv-x="1920" d="M960 864q119 0 203.5 -84.5t84.5 -203.5t-84.5 -203.5t-203.5 -84.5t-203.5 84.5t-84.5 203.5t84.5 203.5t203.5 84.5zM1664 1280q106 0 181 -75t75 -181v-896q0 -106 -75 -181t-181 -75h-1408q-106 0 -181 75t-75 181v896q0 106 75 181t181 75h224l51 136 q19 49 69.5 84.5t103.5 35.5h512q53 0 103.5 -35.5t69.5 -84.5l51 -136h224zM960 128q185 0 316.5 131.5t131.5 316.5t-131.5 316.5t-316.5 131.5t-316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5z" />
-<glyph unicode="&#xf031;" horiz-adv-x="1664" d="M725 977l-170 -450q73 -1 153.5 -2t119 -1.5t52.5 -0.5l29 2q-32 95 -92 241q-53 132 -92 211zM21 -128h-21l2 79q22 7 80 18q89 16 110 31q20 16 48 68l237 616l280 724h75h53l11 -21l205 -480q103 -242 124 -297q39 -102 96 -235q26 -58 65 -164q24 -67 65 -149 q22 -49 35 -57q22 -19 69 -23q47 -6 103 -27q6 -39 6 -57q0 -14 -1 -26q-80 0 -192 8q-93 8 -189 8q-79 0 -135 -2l-200 -11l-58 -2q0 45 4 78l131 28q56 13 68 23q12 12 12 27t-6 32l-47 114l-92 228l-450 2q-29 -65 -104 -274q-23 -64 -23 -84q0 -31 17 -43 q26 -21 103 -32q3 0 13.5 -2t30 -5t40.5 -6q1 -28 1 -58q0 -17 -2 -27q-66 0 -349 20l-48 -8q-81 -14 -167 -14z" />
-<glyph unicode="&#xf032;" horiz-adv-x="1408" d="M555 15q76 -32 140 -32q131 0 216 41t122 113q38 70 38 181q0 114 -41 180q-58 94 -141 126q-80 32 -247 32q-74 0 -101 -10v-144l-1 -173l3 -270q0 -15 12 -44zM541 761q43 -7 109 -7q175 0 264 65t89 224q0 112 -85 187q-84 75 -255 75q-52 0 -130 -13q0 -44 2 -77 q7 -122 6 -279l-1 -98q0 -43 1 -77zM0 -128l2 94q45 9 68 12q77 12 123 31q17 27 21 51q9 66 9 194l-2 497q-5 256 -9 404q-1 87 -11 109q-1 4 -12 12q-18 12 -69 15q-30 2 -114 13l-4 83l260 6l380 13l45 1q5 0 14 0.5t14 0.5q1 0 21.5 -0.5t40.5 -0.5h74q88 0 191 -27 q43 -13 96 -39q57 -29 102 -76q44 -47 65 -104t21 -122q0 -70 -32 -128t-95 -105q-26 -20 -150 -77q177 -41 267 -146q92 -106 92 -236q0 -76 -29 -161q-21 -62 -71 -117q-66 -72 -140 -108q-73 -36 -203 -60q-82 -15 -198 -11l-197 4q-84 2 -298 -11q-33 -3 -272 -11z" />
-<glyph unicode="&#xf033;" horiz-adv-x="1024" d="M0 -126l17 85q4 1 77 20q76 19 116 39q29 37 41 101l27 139l56 268l12 64q8 44 17 84.5t16 67t12.5 46.5t9 30.5t3.5 11.5l29 157l16 63l22 135l8 50v38q-41 22 -144 28q-28 2 -38 4l19 103l317 -14q39 -2 73 -2q66 0 214 9q33 2 68 4.5t36 2.5q-2 -19 -6 -38 q-7 -29 -13 -51q-55 -19 -109 -31q-64 -16 -101 -31q-12 -31 -24 -88q-9 -44 -13 -82q-44 -199 -66 -306l-61 -311l-38 -158l-43 -235l-12 -45q-2 -7 1 -27q64 -15 119 -21q36 -5 66 -10q-1 -29 -7 -58q-7 -31 -9 -41q-18 0 -23 -1q-24 -2 -42 -2q-9 0 -28 3q-19 4 -145 17 l-198 2q-41 1 -174 -11q-74 -7 -98 -9z" />
-<glyph unicode="&#xf034;" horiz-adv-x="1792" d="M81 1407l54 -27q20 -5 211 -5h130l19 3l115 1l215 -1h293l34 -2q14 -1 28 7t21 16l7 8l42 1q15 0 28 -1v-104.5t1 -131.5l1 -100l-1 -58q0 -32 -4 -51q-39 -15 -68 -18q-25 43 -54 128q-8 24 -15.5 62.5t-11.5 65.5t-6 29q-13 15 -27 19q-7 2 -42.5 2t-103.5 -1t-111 -1 q-34 0 -67 -5q-10 -97 -8 -136l1 -152v-332l3 -359l-1 -147q-1 -46 11 -85q49 -25 89 -32q2 0 18 -5t44 -13t43 -12q30 -8 50 -18q5 -45 5 -50q0 -10 -3 -29q-14 -1 -34 -1q-110 0 -187 10q-72 8 -238 8q-88 0 -233 -14q-48 -4 -70 -4q-2 22 -2 26l-1 26v9q21 33 79 49 q139 38 159 50q9 21 12 56q8 192 6 433l-5 428q-1 62 -0.5 118.5t0.5 102.5t-2 57t-6 15q-6 5 -14 6q-38 6 -148 6q-43 0 -100 -13.5t-73 -24.5q-13 -9 -22 -33t-22 -75t-24 -84q-6 -19 -19.5 -32t-20.5 -13q-44 27 -56 44v297v86zM1744 128q33 0 42 -18.5t-11 -44.5 l-126 -162q-20 -26 -49 -26t-49 26l-126 162q-20 26 -11 44.5t42 18.5h80v1024h-80q-33 0 -42 18.5t11 44.5l126 162q20 26 49 26t49 -26l126 -162q20 -26 11 -44.5t-42 -18.5h-80v-1024h80z" />
-<glyph unicode="&#xf035;" d="M81 1407l54 -27q20 -5 211 -5h130l19 3l115 1l446 -1h318l34 -2q14 -1 28 7t21 16l7 8l42 1q15 0 28 -1v-104.5t1 -131.5l1 -100l-1 -58q0 -32 -4 -51q-39 -15 -68 -18q-25 43 -54 128q-8 24 -15.5 62.5t-11.5 65.5t-6 29q-13 15 -27 19q-7 2 -58.5 2t-138.5 -1t-128 -1 q-94 0 -127 -5q-10 -97 -8 -136l1 -152v52l3 -359l-1 -147q-1 -46 11 -85q49 -25 89 -32q2 0 18 -5t44 -13t43 -12q30 -8 50 -18q5 -45 5 -50q0 -10 -3 -29q-14 -1 -34 -1q-110 0 -187 10q-72 8 -238 8q-82 0 -233 -13q-45 -5 -70 -5q-2 22 -2 26l-1 26v9q21 33 79 49 q139 38 159 50q9 21 12 56q6 137 6 433l-5 44q0 265 -2 278q-2 11 -6 15q-6 5 -14 6q-38 6 -148 6q-50 0 -168.5 -14t-132.5 -24q-13 -9 -22 -33t-22 -75t-24 -84q-6 -19 -19.5 -32t-20.5 -13q-44 27 -56 44v297v86zM1505 113q26 -20 26 -49t-26 -49l-162 -126 q-26 -20 -44.5 -11t-18.5 42v80h-1024v-80q0 -33 -18.5 -42t-44.5 11l-162 126q-26 20 -26 49t26 49l162 126q26 20 44.5 11t18.5 -42v-80h1024v80q0 33 18.5 42t44.5 -11z" />
-<glyph unicode="&#xf036;" horiz-adv-x="1792" d="M1792 192v-128q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1664q26 0 45 -19t19 -45zM1408 576v-128q0 -26 -19 -45t-45 -19h-1280q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1280q26 0 45 -19t19 -45zM1664 960v-128q0 -26 -19 -45 t-45 -19h-1536q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1536q26 0 45 -19t19 -45zM1280 1344v-128q0 -26 -19 -45t-45 -19h-1152q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1152q26 0 45 -19t19 -45z" />
-<glyph unicode="&#xf037;" horiz-adv-x="1792" d="M1792 192v-128q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1664q26 0 45 -19t19 -45zM1408 576v-128q0 -26 -19 -45t-45 -19h-896q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h896q26 0 45 -19t19 -45zM1664 960v-128q0 -26 -19 -45t-45 -19 h-1408q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1408q26 0 45 -19t19 -45zM1280 1344v-128q0 -26 -19 -45t-45 -19h-640q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h640q26 0 45 -19t19 -45z" />
-<glyph unicode="&#xf038;" horiz-adv-x="1792" d="M1792 192v-128q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1664q26 0 45 -19t19 -45zM1792 576v-128q0 -26 -19 -45t-45 -19h-1280q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1280q26 0 45 -19t19 -45zM1792 960v-128q0 -26 -19 -45 t-45 -19h-1536q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1536q26 0 45 -19t19 -45zM1792 1344v-128q0 -26 -19 -45t-45 -19h-1152q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1152q26 0 45 -19t19 -45z" />
-<glyph unicode="&#xf039;" horiz-adv-x="1792" d="M1792 192v-128q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1664q26 0 45 -19t19 -45zM1792 576v-128q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1664q26 0 45 -19t19 -45zM1792 960v-128q0 -26 -19 -45 t-45 -19h-1664q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1664q26 0 45 -19t19 -45zM1792 1344v-128q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1664q26 0 45 -19t19 -45z" />
-<glyph unicode="&#xf03a;" horiz-adv-x="1792" d="M256 224v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-192q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h192q13 0 22.5 -9.5t9.5 -22.5zM256 608v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-192q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h192q13 0 22.5 -9.5 t9.5 -22.5zM256 992v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-192q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h192q13 0 22.5 -9.5t9.5 -22.5zM1792 224v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1344q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1344 q13 0 22.5 -9.5t9.5 -22.5zM256 1376v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-192q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h192q13 0 22.5 -9.5t9.5 -22.5zM1792 608v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1344q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5 t22.5 9.5h1344q13 0 22.5 -9.5t9.5 -22.5zM1792 992v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1344q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1344q13 0 22.5 -9.5t9.5 -22.5zM1792 1376v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1344q-13 0 -22.5 9.5t-9.5 22.5v192 q0 13 9.5 22.5t22.5 9.5h1344q13 0 22.5 -9.5t9.5 -22.5z" />
-<glyph unicode="&#xf03b;" horiz-adv-x="1792" d="M384 992v-576q0 -13 -9.5 -22.5t-22.5 -9.5q-14 0 -23 9l-288 288q-9 9 -9 23t9 23l288 288q9 9 23 9q13 0 22.5 -9.5t9.5 -22.5zM1792 224v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1728q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1728q13 0 22.5 -9.5 t9.5 -22.5zM1792 608v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1088q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1088q13 0 22.5 -9.5t9.5 -22.5zM1792 992v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1088q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1088 q13 0 22.5 -9.5t9.5 -22.5zM1792 1376v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1728q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1728q13 0 22.5 -9.5t9.5 -22.5z" />
-<glyph unicode="&#xf03c;" horiz-adv-x="1792" d="M352 704q0 -14 -9 -23l-288 -288q-9 -9 -23 -9q-13 0 -22.5 9.5t-9.5 22.5v576q0 13 9.5 22.5t22.5 9.5q14 0 23 -9l288 -288q9 -9 9 -23zM1792 224v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1728q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1728q13 0 22.5 -9.5 t9.5 -22.5zM1792 608v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1088q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1088q13 0 22.5 -9.5t9.5 -22.5zM1792 992v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1088q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1088 q13 0 22.5 -9.5t9.5 -22.5zM1792 1376v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1728q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1728q13 0 22.5 -9.5t9.5 -22.5z" />
-<glyph unicode="&#xf03d;" horiz-adv-x="1792" d="M1792 1184v-1088q0 -42 -39 -59q-13 -5 -25 -5q-27 0 -45 19l-403 403v-166q0 -119 -84.5 -203.5t-203.5 -84.5h-704q-119 0 -203.5 84.5t-84.5 203.5v704q0 119 84.5 203.5t203.5 84.5h704q119 0 203.5 -84.5t84.5 -203.5v-165l403 402q18 19 45 19q12 0 25 -5 q39 -17 39 -59z" />
-<glyph unicode="&#xf03e;" horiz-adv-x="1920" d="M640 960q0 -80 -56 -136t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM1664 576v-448h-1408v192l320 320l160 -160l512 512zM1760 1280h-1600q-13 0 -22.5 -9.5t-9.5 -22.5v-1216q0 -13 9.5 -22.5t22.5 -9.5h1600q13 0 22.5 9.5t9.5 22.5v1216 q0 13 -9.5 22.5t-22.5 9.5zM1920 1248v-1216q0 -66 -47 -113t-113 -47h-1600q-66 0 -113 47t-47 113v1216q0 66 47 113t113 47h1600q66 0 113 -47t47 -113z" />
-<glyph unicode="&#xf040;" d="M363 0l91 91l-235 235l-91 -91v-107h128v-128h107zM886 928q0 22 -22 22q-10 0 -17 -7l-542 -542q-7 -7 -7 -17q0 -22 22 -22q10 0 17 7l542 542q7 7 7 17zM832 1120l416 -416l-832 -832h-416v416zM1515 1024q0 -53 -37 -90l-166 -166l-416 416l166 165q36 38 90 38 q53 0 91 -38l235 -234q37 -39 37 -91z" />
-<glyph unicode="&#xf041;" horiz-adv-x="1024" d="M768 896q0 106 -75 181t-181 75t-181 -75t-75 -181t75 -181t181 -75t181 75t75 181zM1024 896q0 -109 -33 -179l-364 -774q-16 -33 -47.5 -52t-67.5 -19t-67.5 19t-46.5 52l-365 774q-33 70 -33 179q0 212 150 362t362 150t362 -150t150 -362z" />
-<glyph unicode="&#xf042;" d="M768 96v1088q-148 0 -273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
-<glyph unicode="&#xf043;" horiz-adv-x="1024" d="M512 384q0 36 -20 69q-1 1 -15.5 22.5t-25.5 38t-25 44t-21 50.5q-4 16 -21 16t-21 -16q-7 -23 -21 -50.5t-25 -44t-25.5 -38t-15.5 -22.5q-20 -33 -20 -69q0 -53 37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1024 512q0 -212 -150 -362t-362 -150t-362 150t-150 362 q0 145 81 275q6 9 62.5 90.5t101 151t99.5 178t83 201.5q9 30 34 47t51 17t51.5 -17t33.5 -47q28 -93 83 -201.5t99.5 -178t101 -151t62.5 -90.5q81 -127 81 -275z" />
-<glyph unicode="&#xf044;" horiz-adv-x="1792" d="M888 352l116 116l-152 152l-116 -116v-56h96v-96h56zM1328 1072q-16 16 -33 -1l-350 -350q-17 -17 -1 -33t33 1l350 350q17 17 1 33zM1408 478v-190q0 -119 -84.5 -203.5t-203.5 -84.5h-832q-119 0 -203.5 84.5t-84.5 203.5v832q0 119 84.5 203.5t203.5 84.5h832 q63 0 117 -25q15 -7 18 -23q3 -17 -9 -29l-49 -49q-14 -14 -32 -8q-23 6 -45 6h-832q-66 0 -113 -47t-47 -113v-832q0 -66 47 -113t113 -47h832q66 0 113 47t47 113v126q0 13 9 22l64 64q15 15 35 7t20 -29zM1312 1216l288 -288l-672 -672h-288v288zM1756 1084l-92 -92 l-288 288l92 92q28 28 68 28t68 -28l152 -152q28 -28 28 -68t-28 -68z" />
-<glyph unicode="&#xf045;" horiz-adv-x="1664" d="M1408 547v-259q0 -119 -84.5 -203.5t-203.5 -84.5h-832q-119 0 -203.5 84.5t-84.5 203.5v832q0 119 84.5 203.5t203.5 84.5h255v0q13 0 22.5 -9.5t9.5 -22.5q0 -27 -26 -32q-77 -26 -133 -60q-10 -4 -16 -4h-112q-66 0 -113 -47t-47 -113v-832q0 -66 47 -113t113 -47h832 q66 0 113 47t47 113v214q0 19 18 29q28 13 54 37q16 16 35 8q21 -9 21 -29zM1645 1043l-384 -384q-18 -19 -45 -19q-12 0 -25 5q-39 17 -39 59v192h-160q-323 0 -438 -131q-119 -137 -74 -473q3 -23 -20 -34q-8 -2 -12 -2q-16 0 -26 13q-10 14 -21 31t-39.5 68.5t-49.5 99.5 t-38.5 114t-17.5 122q0 49 3.5 91t14 90t28 88t47 81.5t68.5 74t94.5 61.5t124.5 48.5t159.5 30.5t196.5 11h160v192q0 42 39 59q13 5 25 5q26 0 45 -19l384 -384q19 -19 19 -45t-19 -45z" />
-<glyph unicode="&#xf046;" horiz-adv-x="1664" d="M1408 606v-318q0 -119 -84.5 -203.5t-203.5 -84.5h-832q-119 0 -203.5 84.5t-84.5 203.5v832q0 119 84.5 203.5t203.5 84.5h832q63 0 117 -25q15 -7 18 -23q3 -17 -9 -29l-49 -49q-10 -10 -23 -10q-3 0 -9 2q-23 6 -45 6h-832q-66 0 -113 -47t-47 -113v-832 q0 -66 47 -113t113 -47h832q66 0 113 47t47 113v254q0 13 9 22l64 64q10 10 23 10q6 0 12 -3q20 -8 20 -29zM1639 1095l-814 -814q-24 -24 -57 -24t-57 24l-430 430q-24 24 -24 57t24 57l110 110q24 24 57 24t57 -24l263 -263l647 647q24 24 57 24t57 -24l110 -110 q24 -24 24 -57t-24 -57z" />
-<glyph unicode="&#xf047;" horiz-adv-x="1792" d="M1792 640q0 -26 -19 -45l-256 -256q-19 -19 -45 -19t-45 19t-19 45v128h-384v-384h128q26 0 45 -19t19 -45t-19 -45l-256 -256q-19 -19 -45 -19t-45 19l-256 256q-19 19 -19 45t19 45t45 19h128v384h-384v-128q0 -26 -19 -45t-45 -19t-45 19l-256 256q-19 19 -19 45 t19 45l256 256q19 19 45 19t45 -19t19 -45v-128h384v384h-128q-26 0 -45 19t-19 45t19 45l256 256q19 19 45 19t45 -19l256 -256q19 -19 19 -45t-19 -45t-45 -19h-128v-384h384v128q0 26 19 45t45 19t45 -19l256 -256q19 -19 19 -45z" />
-<glyph unicode="&#xf048;" horiz-adv-x="1024" d="M979 1395q19 19 32 13t13 -32v-1472q0 -26 -13 -32t-32 13l-710 710q-9 9 -13 19v-678q0 -26 -19 -45t-45 -19h-128q-26 0 -45 19t-19 45v1408q0 26 19 45t45 19h128q26 0 45 -19t19 -45v-678q4 11 13 19z" />
-<glyph unicode="&#xf049;" horiz-adv-x="1792" d="M1747 1395q19 19 32 13t13 -32v-1472q0 -26 -13 -32t-32 13l-710 710q-9 9 -13 19v-710q0 -26 -13 -32t-32 13l-710 710q-9 9 -13 19v-678q0 -26 -19 -45t-45 -19h-128q-26 0 -45 19t-19 45v1408q0 26 19 45t45 19h128q26 0 45 -19t19 -45v-678q4 11 13 19l710 710 q19 19 32 13t13 -32v-710q4 11 13 19z" />
-<glyph unicode="&#xf04a;" horiz-adv-x="1664" d="M1619 1395q19 19 32 13t13 -32v-1472q0 -26 -13 -32t-32 13l-710 710q-8 9 -13 19v-710q0 -26 -13 -32t-32 13l-710 710q-19 19 -19 45t19 45l710 710q19 19 32 13t13 -32v-710q5 11 13 19z" />
-<glyph unicode="&#xf04b;" horiz-adv-x="1408" d="M1384 609l-1328 -738q-23 -13 -39.5 -3t-16.5 36v1472q0 26 16.5 36t39.5 -3l1328 -738q23 -13 23 -31t-23 -31z" />
-<glyph unicode="&#xf04c;" d="M1536 1344v-1408q0 -26 -19 -45t-45 -19h-512q-26 0 -45 19t-19 45v1408q0 26 19 45t45 19h512q26 0 45 -19t19 -45zM640 1344v-1408q0 -26 -19 -45t-45 -19h-512q-26 0 -45 19t-19 45v1408q0 26 19 45t45 19h512q26 0 45 -19t19 -45z" />
-<glyph unicode="&#xf04d;" d="M1536 1344v-1408q0 -26 -19 -45t-45 -19h-1408q-26 0 -45 19t-19 45v1408q0 26 19 45t45 19h1408q26 0 45 -19t19 -45z" />
-<glyph unicode="&#xf04e;" horiz-adv-x="1664" d="M45 -115q-19 -19 -32 -13t-13 32v1472q0 26 13 32t32 -13l710 -710q8 -8 13 -19v710q0 26 13 32t32 -13l710 -710q19 -19 19 -45t-19 -45l-710 -710q-19 -19 -32 -13t-13 32v710q-5 -10 -13 -19z" />
-<glyph unicode="&#xf050;" horiz-adv-x="1792" d="M45 -115q-19 -19 -32 -13t-13 32v1472q0 26 13 32t32 -13l710 -710q8 -8 13 -19v710q0 26 13 32t32 -13l710 -710q8 -8 13 -19v678q0 26 19 45t45 19h128q26 0 45 -19t19 -45v-1408q0 -26 -19 -45t-45 -19h-128q-26 0 -45 19t-19 45v678q-5 -10 -13 -19l-710 -710 q-19 -19 -32 -13t-13 32v710q-5 -10 -13 -19z" />
-<glyph unicode="&#xf051;" horiz-adv-x="1024" d="M45 -115q-19 -19 -32 -13t-13 32v1472q0 26 13 32t32 -13l710 -710q8 -8 13 -19v678q0 26 19 45t45 19h128q26 0 45 -19t19 -45v-1408q0 -26 -19 -45t-45 -19h-128q-26 0 -45 19t-19 45v678q-5 -10 -13 -19z" />
-<glyph unicode="&#xf052;" horiz-adv-x="1538" d="M14 557l710 710q19 19 45 19t45 -19l710 -710q19 -19 13 -32t-32 -13h-1472q-26 0 -32 13t13 32zM1473 0h-1408q-26 0 -45 19t-19 45v256q0 26 19 45t45 19h1408q26 0 45 -19t19 -45v-256q0 -26 -19 -45t-45 -19z" />
-<glyph unicode="&#xf053;" horiz-adv-x="1152" d="M742 -37l-652 651q-37 37 -37 90.5t37 90.5l652 651q37 37 90.5 37t90.5 -37l75 -75q37 -37 37 -90.5t-37 -90.5l-486 -486l486 -485q37 -38 37 -91t-37 -90l-75 -75q-37 -37 -90.5 -37t-90.5 37z" />
-<glyph unicode="&#xf054;" horiz-adv-x="1152" d="M1099 704q0 -52 -37 -91l-652 -651q-37 -37 -90 -37t-90 37l-76 75q-37 39 -37 91q0 53 37 90l486 486l-486 485q-37 39 -37 91q0 53 37 90l76 75q36 38 90 38t90 -38l652 -651q37 -37 37 -90z" />
-<glyph unicode="&#xf055;" d="M1216 576v128q0 26 -19 45t-45 19h-256v256q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-256h-256q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h256v-256q0 -26 19 -45t45 -19h128q26 0 45 19t19 45v256h256q26 0 45 19t19 45zM1536 640q0 -209 -103 -385.5 t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
-<glyph unicode="&#xf056;" d="M1216 576v128q0 26 -19 45t-45 19h-768q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h768q26 0 45 19t19 45zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5 t103 -385.5z" />
-<glyph unicode="&#xf057;" d="M1149 414q0 26 -19 45l-181 181l181 181q19 19 19 45q0 27 -19 46l-90 90q-19 19 -46 19q-26 0 -45 -19l-181 -181l-181 181q-19 19 -45 19q-27 0 -46 -19l-90 -90q-19 -19 -19 -46q0 -26 19 -45l181 -181l-181 -181q-19 -19 -19 -45q0 -27 19 -46l90 -90q19 -19 46 -19 q26 0 45 19l181 181l181 -181q19 -19 45 -19q27 0 46 19l90 90q19 19 19 46zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
-<glyph unicode="&#xf058;" d="M1284 802q0 28 -18 46l-91 90q-19 19 -45 19t-45 -19l-408 -407l-226 226q-19 19 -45 19t-45 -19l-91 -90q-18 -18 -18 -46q0 -27 18 -45l362 -362q19 -19 45 -19q27 0 46 19l543 543q18 18 18 45zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103 t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
-<glyph unicode="&#xf059;" d="M896 160v192q0 14 -9 23t-23 9h-192q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h192q14 0 23 9t9 23zM1152 832q0 88 -55.5 163t-138.5 116t-170 41q-243 0 -371 -213q-15 -24 8 -42l132 -100q7 -6 19 -6q16 0 25 12q53 68 86 92q34 24 86 24q48 0 85.5 -26t37.5 -59 q0 -38 -20 -61t-68 -45q-63 -28 -115.5 -86.5t-52.5 -125.5v-36q0 -14 9 -23t23 -9h192q14 0 23 9t9 23q0 19 21.5 49.5t54.5 49.5q32 18 49 28.5t46 35t44.5 48t28 60.5t12.5 81zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5 t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
-<glyph unicode="&#xf05a;" d="M1024 160v160q0 14 -9 23t-23 9h-96v512q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23v-160q0 -14 9 -23t23 -9h96v-320h-96q-14 0 -23 -9t-9 -23v-160q0 -14 9 -23t23 -9h448q14 0 23 9t9 23zM896 1056v160q0 14 -9 23t-23 9h-192q-14 0 -23 -9t-9 -23v-160q0 -14 9 -23 t23 -9h192q14 0 23 9t9 23zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
-<glyph unicode="&#xf05b;" d="M1197 512h-109q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h109q-32 108 -112.5 188.5t-188.5 112.5v-109q0 -26 -19 -45t-45 -19h-128q-26 0 -45 19t-19 45v109q-108 -32 -188.5 -112.5t-112.5 -188.5h109q26 0 45 -19t19 -45v-128q0 -26 -19 -45t-45 -19h-109 q32 -108 112.5 -188.5t188.5 -112.5v109q0 26 19 45t45 19h128q26 0 45 -19t19 -45v-109q108 32 188.5 112.5t112.5 188.5zM1536 704v-128q0 -26 -19 -45t-45 -19h-143q-37 -161 -154.5 -278.5t-278.5 -154.5v-143q0 -26 -19 -45t-45 -19h-128q-26 0 -45 19t-19 45v143 q-161 37 -278.5 154.5t-154.5 278.5h-143q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h143q37 161 154.5 278.5t278.5 154.5v143q0 26 19 45t45 19h128q26 0 45 -19t19 -45v-143q161 -37 278.5 -154.5t154.5 -278.5h143q26 0 45 -19t19 -45z" />
-<glyph unicode="&#xf05c;" d="M1097 457l-146 -146q-10 -10 -23 -10t-23 10l-137 137l-137 -137q-10 -10 -23 -10t-23 10l-146 146q-10 10 -10 23t10 23l137 137l-137 137q-10 10 -10 23t10 23l146 146q10 10 23 10t23 -10l137 -137l137 137q10 10 23 10t23 -10l146 -146q10 -10 10 -23t-10 -23 l-137 -137l137 -137q10 -10 10 -23t-10 -23zM1312 640q0 148 -73 273t-198 198t-273 73t-273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73t273 73t198 198t73 273zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5 t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
-<glyph unicode="&#xf05d;" d="M1171 723l-422 -422q-19 -19 -45 -19t-45 19l-294 294q-19 19 -19 45t19 45l102 102q19 19 45 19t45 -19l147 -147l275 275q19 19 45 19t45 -19l102 -102q19 -19 19 -45t-19 -45zM1312 640q0 148 -73 273t-198 198t-273 73t-273 -73t-198 -198t-73 -273t73 -273t198 -198 t273 -73t273 73t198 198t73 273zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
-<glyph unicode="&#xf05e;" d="M1312 643q0 161 -87 295l-754 -753q137 -89 297 -89q111 0 211.5 43.5t173.5 116.5t116 174.5t43 212.5zM313 344l755 754q-135 91 -300 91q-148 0 -273 -73t-198 -199t-73 -274q0 -162 89 -299zM1536 643q0 -157 -61 -300t-163.5 -246t-245 -164t-298.5 -61t-298.5 61 t-245 164t-163.5 246t-61 300t61 299.5t163.5 245.5t245 164t298.5 61t298.5 -61t245 -164t163.5 -245.5t61 -299.5z" />
-<glyph unicode="&#xf060;" d="M1536 640v-128q0 -53 -32.5 -90.5t-84.5 -37.5h-704l293 -294q38 -36 38 -90t-38 -90l-75 -76q-37 -37 -90 -37q-52 0 -91 37l-651 652q-37 37 -37 90q0 52 37 91l651 650q38 38 91 38q52 0 90 -38l75 -74q38 -38 38 -91t-38 -91l-293 -293h704q52 0 84.5 -37.5 t32.5 -90.5z" />
-<glyph unicode="&#xf061;" d="M1472 576q0 -54 -37 -91l-651 -651q-39 -37 -91 -37q-51 0 -90 37l-75 75q-38 38 -38 91t38 91l293 293h-704q-52 0 -84.5 37.5t-32.5 90.5v128q0 53 32.5 90.5t84.5 37.5h704l-293 294q-38 36 -38 90t38 90l75 75q38 38 90 38q53 0 91 -38l651 -651q37 -35 37 -90z" />
-<glyph unicode="&#xf062;" horiz-adv-x="1664" d="M1611 565q0 -51 -37 -90l-75 -75q-38 -38 -91 -38q-54 0 -90 38l-294 293v-704q0 -52 -37.5 -84.5t-90.5 -32.5h-128q-53 0 -90.5 32.5t-37.5 84.5v704l-294 -293q-36 -38 -90 -38t-90 38l-75 75q-38 38 -38 90q0 53 38 91l651 651q35 37 90 37q54 0 91 -37l651 -651 q37 -39 37 -91z" />
-<glyph unicode="&#xf063;" horiz-adv-x="1664" d="M1611 704q0 -53 -37 -90l-651 -652q-39 -37 -91 -37q-53 0 -90 37l-651 652q-38 36 -38 90q0 53 38 91l74 75q39 37 91 37q53 0 90 -37l294 -294v704q0 52 38 90t90 38h128q52 0 90 -38t38 -90v-704l294 294q37 37 90 37q52 0 91 -37l75 -75q37 -39 37 -91z" />
-<glyph unicode="&#xf064;" horiz-adv-x="1792" d="M1792 896q0 -26 -19 -45l-512 -512q-19 -19 -45 -19t-45 19t-19 45v256h-224q-98 0 -175.5 -6t-154 -21.5t-133 -42.5t-105.5 -69.5t-80 -101t-48.5 -138.5t-17.5 -181q0 -55 5 -123q0 -6 2.5 -23.5t2.5 -26.5q0 -15 -8.5 -25t-23.5 -10q-16 0 -28 17q-7 9 -13 22 t-13.5 30t-10.5 24q-127 285 -127 451q0 199 53 333q162 403 875 403h224v256q0 26 19 45t45 19t45 -19l512 -512q19 -19 19 -45z" />
-<glyph unicode="&#xf065;" d="M755 480q0 -13 -10 -23l-332 -332l144 -144q19 -19 19 -45t-19 -45t-45 -19h-448q-26 0 -45 19t-19 45v448q0 26 19 45t45 19t45 -19l144 -144l332 332q10 10 23 10t23 -10l114 -114q10 -10 10 -23zM1536 1344v-448q0 -26 -19 -45t-45 -19t-45 19l-144 144l-332 -332 q-10 -10 -23 -10t-23 10l-114 114q-10 10 -10 23t10 23l332 332l-144 144q-19 19 -19 45t19 45t45 19h448q26 0 45 -19t19 -45z" />
-<glyph unicode="&#xf066;" d="M768 576v-448q0 -26 -19 -45t-45 -19t-45 19l-144 144l-332 -332q-10 -10 -23 -10t-23 10l-114 114q-10 10 -10 23t10 23l332 332l-144 144q-19 19 -19 45t19 45t45 19h448q26 0 45 -19t19 -45zM1523 1248q0 -13 -10 -23l-332 -332l144 -144q19 -19 19 -45t-19 -45 t-45 -19h-448q-26 0 -45 19t-19 45v448q0 26 19 45t45 19t45 -19l144 -144l332 332q10 10 23 10t23 -10l114 -114q10 -10 10 -23z" />
-<glyph unicode="&#xf067;" horiz-adv-x="1408" d="M1408 800v-192q0 -40 -28 -68t-68 -28h-416v-416q0 -40 -28 -68t-68 -28h-192q-40 0 -68 28t-28 68v416h-416q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h416v416q0 40 28 68t68 28h192q40 0 68 -28t28 -68v-416h416q40 0 68 -28t28 -68z" />
-<glyph unicode="&#xf068;" horiz-adv-x="1408" d="M1408 800v-192q0 -40 -28 -68t-68 -28h-1216q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h1216q40 0 68 -28t28 -68z" />
-<glyph unicode="&#xf069;" horiz-adv-x="1664" d="M1482 486q46 -26 59.5 -77.5t-12.5 -97.5l-64 -110q-26 -46 -77.5 -59.5t-97.5 12.5l-266 153v-307q0 -52 -38 -90t-90 -38h-128q-52 0 -90 38t-38 90v307l-266 -153q-46 -26 -97.5 -12.5t-77.5 59.5l-64 110q-26 46 -12.5 97.5t59.5 77.5l266 154l-266 154 q-46 26 -59.5 77.5t12.5 97.5l64 110q26 46 77.5 59.5t97.5 -12.5l266 -153v307q0 52 38 90t90 38h128q52 0 90 -38t38 -90v-307l266 153q46 26 97.5 12.5t77.5 -59.5l64 -110q26 -46 12.5 -97.5t-59.5 -77.5l-266 -154z" />
-<glyph unicode="&#xf06a;" d="M768 1408q209 0 385.5 -103t279.5 -279.5t103 -385.5t-103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103zM896 161v190q0 14 -9 23.5t-22 9.5h-192q-13 0 -23 -10t-10 -23v-190q0 -13 10 -23t23 -10h192 q13 0 22 9.5t9 23.5zM894 505l18 621q0 12 -10 18q-10 8 -24 8h-220q-14 0 -24 -8q-10 -6 -10 -18l17 -621q0 -10 10 -17.5t24 -7.5h185q14 0 23.5 7.5t10.5 17.5z" />
-<glyph unicode="&#xf06b;" d="M928 180v56v468v192h-320v-192v-468v-56q0 -25 18 -38.5t46 -13.5h192q28 0 46 13.5t18 38.5zM472 1024h195l-126 161q-26 31 -69 31q-40 0 -68 -28t-28 -68t28 -68t68 -28zM1160 1120q0 40 -28 68t-68 28q-43 0 -69 -31l-125 -161h194q40 0 68 28t28 68zM1536 864v-320 q0 -14 -9 -23t-23 -9h-96v-416q0 -40 -28 -68t-68 -28h-1088q-40 0 -68 28t-28 68v416h-96q-14 0 -23 9t-9 23v320q0 14 9 23t23 9h440q-93 0 -158.5 65.5t-65.5 158.5t65.5 158.5t158.5 65.5q107 0 168 -77l128 -165l128 165q61 77 168 77q93 0 158.5 -65.5t65.5 -158.5 t-65.5 -158.5t-158.5 -65.5h440q14 0 23 -9t9 -23z" />
-<glyph unicode="&#xf06c;" horiz-adv-x="1792" d="M1280 832q0 26 -19 45t-45 19q-172 0 -318 -49.5t-259.5 -134t-235.5 -219.5q-19 -21 -19 -45q0 -26 19 -45t45 -19q24 0 45 19q27 24 74 71t67 66q137 124 268.5 176t313.5 52q26 0 45 19t19 45zM1792 1030q0 -95 -20 -193q-46 -224 -184.5 -383t-357.5 -268 q-214 -108 -438 -108q-148 0 -286 47q-15 5 -88 42t-96 37q-16 0 -39.5 -32t-45 -70t-52.5 -70t-60 -32q-30 0 -51 11t-31 24t-27 42q-2 4 -6 11t-5.5 10t-3 9.5t-1.5 13.5q0 35 31 73.5t68 65.5t68 56t31 48q0 4 -14 38t-16 44q-9 51 -9 104q0 115 43.5 220t119 184.5 t170.5 139t204 95.5q55 18 145 25.5t179.5 9t178.5 6t163.5 24t113.5 56.5l29.5 29.5t29.5 28t27 20t36.5 16t43.5 4.5q39 0 70.5 -46t47.5 -112t24 -124t8 -96z" />
-<glyph unicode="&#xf06d;" horiz-adv-x="1408" d="M1408 -160v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-1344q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h1344q13 0 22.5 -9.5t9.5 -22.5zM1152 896q0 -78 -24.5 -144t-64 -112.5t-87.5 -88t-96 -77.5t-87.5 -72t-64 -81.5t-24.5 -96.5q0 -96 67 -224l-4 1l1 -1 q-90 41 -160 83t-138.5 100t-113.5 122.5t-72.5 150.5t-27.5 184q0 78 24.5 144t64 112.5t87.5 88t96 77.5t87.5 72t64 81.5t24.5 96.5q0 94 -66 224l3 -1l-1 1q90 -41 160 -83t138.5 -100t113.5 -122.5t72.5 -150.5t27.5 -184z" />
-<glyph unicode="&#xf06e;" horiz-adv-x="1792" d="M1664 576q-152 236 -381 353q61 -104 61 -225q0 -185 -131.5 -316.5t-316.5 -131.5t-316.5 131.5t-131.5 316.5q0 121 61 225q-229 -117 -381 -353q133 -205 333.5 -326.5t434.5 -121.5t434.5 121.5t333.5 326.5zM944 960q0 20 -14 34t-34 14q-125 0 -214.5 -89.5 t-89.5 -214.5q0 -20 14 -34t34 -14t34 14t14 34q0 86 61 147t147 61q20 0 34 14t14 34zM1792 576q0 -34 -20 -69q-140 -230 -376.5 -368.5t-499.5 -138.5t-499.5 139t-376.5 368q-20 35 -20 69t20 69q140 229 376.5 368t499.5 139t499.5 -139t376.5 -368q20 -35 20 -69z" />
-<glyph unicode="&#xf070;" horiz-adv-x="1792" d="M555 201l78 141q-87 63 -136 159t-49 203q0 121 61 225q-229 -117 -381 -353q167 -258 427 -375zM944 960q0 20 -14 34t-34 14q-125 0 -214.5 -89.5t-89.5 -214.5q0 -20 14 -34t34 -14t34 14t14 34q0 86 61 147t147 61q20 0 34 14t14 34zM1307 1151q0 -7 -1 -9 q-105 -188 -315 -566t-316 -567l-49 -89q-10 -16 -28 -16q-12 0 -134 70q-16 10 -16 28q0 12 44 87q-143 65 -263.5 173t-208.5 245q-20 31 -20 69t20 69q153 235 380 371t496 136q89 0 180 -17l54 97q10 16 28 16q5 0 18 -6t31 -15.5t33 -18.5t31.5 -18.5t19.5 -11.5 q16 -10 16 -27zM1344 704q0 -139 -79 -253.5t-209 -164.5l280 502q8 -45 8 -84zM1792 576q0 -35 -20 -69q-39 -64 -109 -145q-150 -172 -347.5 -267t-419.5 -95l74 132q212 18 392.5 137t301.5 307q-115 179 -282 294l63 112q95 -64 182.5 -153t144.5 -184q20 -34 20 -69z " />
-<glyph unicode="&#xf071;" horiz-adv-x="1792" d="M1024 161v190q0 14 -9.5 23.5t-22.5 9.5h-192q-13 0 -22.5 -9.5t-9.5 -23.5v-190q0 -14 9.5 -23.5t22.5 -9.5h192q13 0 22.5 9.5t9.5 23.5zM1022 535l18 459q0 12 -10 19q-13 11 -24 11h-220q-11 0 -24 -11q-10 -7 -10 -21l17 -457q0 -10 10 -16.5t24 -6.5h185 q14 0 23.5 6.5t10.5 16.5zM1008 1469l768 -1408q35 -63 -2 -126q-17 -29 -46.5 -46t-63.5 -17h-1536q-34 0 -63.5 17t-46.5 46q-37 63 -2 126l768 1408q17 31 47 49t65 18t65 -18t47 -49z" />
-<glyph unicode="&#xf072;" horiz-adv-x="1408" d="M1376 1376q44 -52 12 -148t-108 -172l-161 -161l160 -696q5 -19 -12 -33l-128 -96q-7 -6 -19 -6q-4 0 -7 1q-15 3 -21 16l-279 508l-259 -259l53 -194q5 -17 -8 -31l-96 -96q-9 -9 -23 -9h-2q-15 2 -24 13l-189 252l-252 189q-11 7 -13 23q-1 13 9 25l96 97q9 9 23 9 q6 0 8 -1l194 -53l259 259l-508 279q-14 8 -17 24q-2 16 9 27l128 128q14 13 30 8l665 -159l160 160q76 76 172 108t148 -12z" />
-<glyph unicode="&#xf073;" horiz-adv-x="1664" d="M128 -128h288v288h-288v-288zM480 -128h320v288h-320v-288zM128 224h288v320h-288v-320zM480 224h320v320h-320v-320zM128 608h288v288h-288v-288zM864 -128h320v288h-320v-288zM480 608h320v288h-320v-288zM1248 -128h288v288h-288v-288zM864 224h320v320h-320v-320z M512 1088v288q0 13 -9.5 22.5t-22.5 9.5h-64q-13 0 -22.5 -9.5t-9.5 -22.5v-288q0 -13 9.5 -22.5t22.5 -9.5h64q13 0 22.5 9.5t9.5 22.5zM1248 224h288v320h-288v-320zM864 608h320v288h-320v-288zM1248 608h288v288h-288v-288zM1280 1088v288q0 13 -9.5 22.5t-22.5 9.5h-64 q-13 0 -22.5 -9.5t-9.5 -22.5v-288q0 -13 9.5 -22.5t22.5 -9.5h64q13 0 22.5 9.5t9.5 22.5zM1664 1152v-1280q0 -52 -38 -90t-90 -38h-1408q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h128v96q0 66 47 113t113 47h64q66 0 113 -47t47 -113v-96h384v96q0 66 47 113t113 47 h64q66 0 113 -47t47 -113v-96h128q52 0 90 -38t38 -90z" />
-<glyph unicode="&#xf074;" horiz-adv-x="1792" d="M666 1055q-60 -92 -137 -273q-22 45 -37 72.5t-40.5 63.5t-51 56.5t-63 35t-81.5 14.5h-224q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h224q250 0 410 -225zM1792 256q0 -14 -9 -23l-320 -320q-9 -9 -23 -9q-13 0 -22.5 9.5t-9.5 22.5v192q-32 0 -85 -0.5t-81 -1t-73 1 t-71 5t-64 10.5t-63 18.5t-58 28.5t-59 40t-55 53.5t-56 69.5q59 93 136 273q22 -45 37 -72.5t40.5 -63.5t51 -56.5t63 -35t81.5 -14.5h256v192q0 14 9 23t23 9q12 0 24 -10l319 -319q9 -9 9 -23zM1792 1152q0 -14 -9 -23l-320 -320q-9 -9 -23 -9q-13 0 -22.5 9.5t-9.5 22.5 v192h-256q-48 0 -87 -15t-69 -45t-51 -61.5t-45 -77.5q-32 -62 -78 -171q-29 -66 -49.5 -111t-54 -105t-64 -100t-74 -83t-90 -68.5t-106.5 -42t-128 -16.5h-224q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h224q48 0 87 15t69 45t51 61.5t45 77.5q32 62 78 171q29 66 49.5 111 t54 105t64 100t74 83t90 68.5t106.5 42t128 16.5h256v192q0 14 9 23t23 9q12 0 24 -10l319 -319q9 -9 9 -23z" />
-<glyph unicode="&#xf075;" horiz-adv-x="1792" d="M1792 640q0 -174 -120 -321.5t-326 -233t-450 -85.5q-70 0 -145 8q-198 -175 -460 -242q-49 -14 -114 -22q-17 -2 -30.5 9t-17.5 29v1q-3 4 -0.5 12t2 10t4.5 9.5l6 9t7 8.5t8 9q7 8 31 34.5t34.5 38t31 39.5t32.5 51t27 59t26 76q-157 89 -247.5 220t-90.5 281 q0 130 71 248.5t191 204.5t286 136.5t348 50.5q244 0 450 -85.5t326 -233t120 -321.5z" />
-<glyph unicode="&#xf076;" d="M1536 704v-128q0 -201 -98.5 -362t-274 -251.5t-395.5 -90.5t-395.5 90.5t-274 251.5t-98.5 362v128q0 26 19 45t45 19h384q26 0 45 -19t19 -45v-128q0 -52 23.5 -90t53.5 -57t71 -30t64 -13t44 -2t44 2t64 13t71 30t53.5 57t23.5 90v128q0 26 19 45t45 19h384 q26 0 45 -19t19 -45zM512 1344v-384q0 -26 -19 -45t-45 -19h-384q-26 0 -45 19t-19 45v384q0 26 19 45t45 19h384q26 0 45 -19t19 -45zM1536 1344v-384q0 -26 -19 -45t-45 -19h-384q-26 0 -45 19t-19 45v384q0 26 19 45t45 19h384q26 0 45 -19t19 -45z" />
-<glyph unicode="&#xf077;" horiz-adv-x="1664" d="M1611 320q0 -53 -37 -90l-75 -75q-38 -38 -91 -38q-54 0 -90 38l-486 485l-486 -485q-36 -38 -90 -38t-90 38l-75 75q-38 36 -38 90q0 53 38 91l651 651q37 37 90 37q52 0 91 -37l650 -651q38 -38 38 -91z" />
-<glyph unicode="&#xf078;" horiz-adv-x="1664" d="M1611 832q0 -53 -37 -90l-651 -651q-38 -38 -91 -38q-54 0 -90 38l-651 651q-38 36 -38 90q0 53 38 91l74 75q39 37 91 37q53 0 90 -37l486 -486l486 486q37 37 90 37q52 0 91 -37l75 -75q37 -39 37 -91z" />
-<glyph unicode="&#xf079;" horiz-adv-x="1920" d="M1280 32q0 -13 -9.5 -22.5t-22.5 -9.5h-960q-8 0 -13.5 2t-9 7t-5.5 8t-3 11.5t-1 11.5v13v11v160v416h-192q-26 0 -45 19t-19 45q0 24 15 41l320 384q19 22 49 22t49 -22l320 -384q15 -17 15 -41q0 -26 -19 -45t-45 -19h-192v-384h576q16 0 25 -11l160 -192q7 -11 7 -21 zM1920 448q0 -24 -15 -41l-320 -384q-20 -23 -49 -23t-49 23l-320 384q-15 17 -15 41q0 26 19 45t45 19h192v384h-576q-16 0 -25 12l-160 192q-7 9 -7 20q0 13 9.5 22.5t22.5 9.5h960q8 0 13.5 -2t9 -7t5.5 -8t3 -11.5t1 -11.5v-13v-11v-160v-416h192q26 0 45 -19t19 -45z " />
-<glyph unicode="&#xf07a;" horiz-adv-x="1664" d="M640 0q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1536 0q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1664 1088v-512q0 -24 -16 -42.5t-41 -21.5 l-1044 -122q1 -7 4.5 -21.5t6 -26.5t2.5 -22q0 -16 -24 -64h920q26 0 45 -19t19 -45t-19 -45t-45 -19h-1024q-26 0 -45 19t-19 45q0 14 11 39.5t29.5 59.5t20.5 38l-177 823h-204q-26 0 -45 19t-19 45t19 45t45 19h256q16 0 28.5 -6.5t20 -15.5t13 -24.5t7.5 -26.5 t5.5 -29.5t4.5 -25.5h1201q26 0 45 -19t19 -45z" />
-<glyph unicode="&#xf07b;" horiz-adv-x="1664" d="M1664 928v-704q0 -92 -66 -158t-158 -66h-1216q-92 0 -158 66t-66 158v960q0 92 66 158t158 66h320q92 0 158 -66t66 -158v-32h672q92 0 158 -66t66 -158z" />
-<glyph unicode="&#xf07c;" horiz-adv-x="1920" d="M1879 584q0 -31 -31 -66l-336 -396q-43 -51 -120.5 -86.5t-143.5 -35.5h-1088q-34 0 -60.5 13t-26.5 43q0 31 31 66l336 396q43 51 120.5 86.5t143.5 35.5h1088q34 0 60.5 -13t26.5 -43zM1536 928v-160h-832q-94 0 -197 -47.5t-164 -119.5l-337 -396l-5 -6q0 4 -0.5 12.5 t-0.5 12.5v960q0 92 66 158t158 66h320q92 0 158 -66t66 -158v-32h544q92 0 158 -66t66 -158z" />
-<glyph unicode="&#xf07d;" horiz-adv-x="768" d="M704 1216q0 -26 -19 -45t-45 -19h-128v-1024h128q26 0 45 -19t19 -45t-19 -45l-256 -256q-19 -19 -45 -19t-45 19l-256 256q-19 19 -19 45t19 45t45 19h128v1024h-128q-26 0 -45 19t-19 45t19 45l256 256q19 19 45 19t45 -19l256 -256q19 -19 19 -45z" />
-<glyph unicode="&#xf07e;" horiz-adv-x="1792" d="M1792 640q0 -26 -19 -45l-256 -256q-19 -19 -45 -19t-45 19t-19 45v128h-1024v-128q0 -26 -19 -45t-45 -19t-45 19l-256 256q-19 19 -19 45t19 45l256 256q19 19 45 19t45 -19t19 -45v-128h1024v128q0 26 19 45t45 19t45 -19l256 -256q19 -19 19 -45z" />
-<glyph unicode="&#xf080;" horiz-adv-x="1920" d="M512 512v-384h-256v384h256zM896 1024v-896h-256v896h256zM1280 768v-640h-256v640h256zM1664 1152v-1024h-256v1024h256zM1792 32v1216q0 13 -9.5 22.5t-22.5 9.5h-1600q-13 0 -22.5 -9.5t-9.5 -22.5v-1216q0 -13 9.5 -22.5t22.5 -9.5h1600q13 0 22.5 9.5t9.5 22.5z M1920 1248v-1216q0 -66 -47 -113t-113 -47h-1600q-66 0 -113 47t-47 113v1216q0 66 47 113t113 47h1600q66 0 113 -47t47 -113z" />
-<glyph unicode="&#xf081;" d="M1280 926q-56 -25 -121 -34q68 40 93 117q-65 -38 -134 -51q-61 66 -153 66q-87 0 -148.5 -61.5t-61.5 -148.5q0 -29 5 -48q-129 7 -242 65t-192 155q-29 -50 -29 -106q0 -114 91 -175q-47 1 -100 26v-2q0 -75 50 -133.5t123 -72.5q-29 -8 -51 -8q-13 0 -39 4 q21 -63 74.5 -104t121.5 -42q-116 -90 -261 -90q-26 0 -50 3q148 -94 322 -94q112 0 210 35.5t168 95t120.5 137t75 162t24.5 168.5q0 18 -1 27q63 45 105 109zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5 t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
-<glyph unicode="&#xf082;" d="M1307 618l23 219h-198v109q0 49 15.5 68.5t71.5 19.5h110v219h-175q-152 0 -218 -72t-66 -213v-131h-131v-219h131v-635h262v635h175zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960 q119 0 203.5 -84.5t84.5 -203.5z" />
-<glyph unicode="&#xf083;" horiz-adv-x="1792" d="M928 704q0 14 -9 23t-23 9q-66 0 -113 -47t-47 -113q0 -14 9 -23t23 -9t23 9t9 23q0 40 28 68t68 28q14 0 23 9t9 23zM1152 574q0 -106 -75 -181t-181 -75t-181 75t-75 181t75 181t181 75t181 -75t75 -181zM128 0h1536v128h-1536v-128zM1280 574q0 159 -112.5 271.5 t-271.5 112.5t-271.5 -112.5t-112.5 -271.5t112.5 -271.5t271.5 -112.5t271.5 112.5t112.5 271.5zM256 1216h384v128h-384v-128zM128 1024h1536v118v138h-828l-64 -128h-644v-128zM1792 1280v-1280q0 -53 -37.5 -90.5t-90.5 -37.5h-1536q-53 0 -90.5 37.5t-37.5 90.5v1280 q0 53 37.5 90.5t90.5 37.5h1536q53 0 90.5 -37.5t37.5 -90.5z" />
-<glyph unicode="&#xf084;" horiz-adv-x="1792" d="M832 1024q0 80 -56 136t-136 56t-136 -56t-56 -136q0 -42 19 -83q-41 19 -83 19q-80 0 -136 -56t-56 -136t56 -136t136 -56t136 56t56 136q0 42 -19 83q41 -19 83 -19q80 0 136 56t56 136zM1683 320q0 -17 -49 -66t-66 -49q-9 0 -28.5 16t-36.5 33t-38.5 40t-24.5 26 l-96 -96l220 -220q28 -28 28 -68q0 -42 -39 -81t-81 -39q-40 0 -68 28l-671 671q-176 -131 -365 -131q-163 0 -265.5 102.5t-102.5 265.5q0 160 95 313t248 248t313 95q163 0 265.5 -102.5t102.5 -265.5q0 -189 -131 -365l355 -355l96 96q-3 3 -26 24.5t-40 38.5t-33 36.5 t-16 28.5q0 17 49 66t66 49q13 0 23 -10q6 -6 46 -44.5t82 -79.5t86.5 -86t73 -78t28.5 -41z" />
-<glyph unicode="&#xf085;" horiz-adv-x="1920" d="M896 640q0 106 -75 181t-181 75t-181 -75t-75 -181t75 -181t181 -75t181 75t75 181zM1664 128q0 52 -38 90t-90 38t-90 -38t-38 -90q0 -53 37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1664 1152q0 52 -38 90t-90 38t-90 -38t-38 -90q0 -53 37.5 -90.5t90.5 -37.5 t90.5 37.5t37.5 90.5zM1280 731v-185q0 -10 -7 -19.5t-16 -10.5l-155 -24q-11 -35 -32 -76q34 -48 90 -115q7 -10 7 -20q0 -12 -7 -19q-23 -30 -82.5 -89.5t-78.5 -59.5q-11 0 -21 7l-115 90q-37 -19 -77 -31q-11 -108 -23 -155q-7 -24 -30 -24h-186q-11 0 -20 7.5t-10 17.5 l-23 153q-34 10 -75 31l-118 -89q-7 -7 -20 -7q-11 0 -21 8q-144 133 -144 160q0 9 7 19q10 14 41 53t47 61q-23 44 -35 82l-152 24q-10 1 -17 9.5t-7 19.5v185q0 10 7 19.5t16 10.5l155 24q11 35 32 76q-34 48 -90 115q-7 11 -7 20q0 12 7 20q22 30 82 89t79 59q11 0 21 -7 l115 -90q34 18 77 32q11 108 23 154q7 24 30 24h186q11 0 20 -7.5t10 -17.5l23 -153q34 -10 75 -31l118 89q8 7 20 7q11 0 21 -8q144 -133 144 -160q0 -9 -7 -19q-12 -16 -42 -54t-45 -60q23 -48 34 -82l152 -23q10 -2 17 -10.5t7 -19.5zM1920 198v-140q0 -16 -149 -31 q-12 -27 -30 -52q51 -113 51 -138q0 -4 -4 -7q-122 -71 -124 -71q-8 0 -46 47t-52 68q-20 -2 -30 -2t-30 2q-14 -21 -52 -68t-46 -47q-2 0 -124 71q-4 3 -4 7q0 25 51 138q-18 25 -30 52q-149 15 -149 31v140q0 16 149 31q13 29 30 52q-51 113 -51 138q0 4 4 7q4 2 35 20 t59 34t30 16q8 0 46 -46.5t52 -67.5q20 2 30 2t30 -2q51 71 92 112l6 2q4 0 124 -70q4 -3 4 -7q0 -25 -51 -138q17 -23 30 -52q149 -15 149 -31zM1920 1222v-140q0 -16 -149 -31q-12 -27 -30 -52q51 -113 51 -138q0 -4 -4 -7q-122 -71 -124 -71q-8 0 -46 47t-52 68 q-20 -2 -30 -2t-30 2q-14 -21 -52 -68t-46 -47q-2 0 -124 71q-4 3 -4 7q0 25 51 138q-18 25 -30 52q-149 15 -149 31v140q0 16 149 31q13 29 30 52q-51 113 -51 138q0 4 4 7q4 2 35 20t59 34t30 16q8 0 46 -46.5t52 -67.5q20 2 30 2t30 -2q51 71 92 112l6 2q4 0 124 -70 q4 -3 4 -7q0 -25 -51 -138q17 -23 30 -52q149 -15 149 -31z" />
-<glyph unicode="&#xf086;" horiz-adv-x="1792" d="M1408 768q0 -139 -94 -257t-256.5 -186.5t-353.5 -68.5q-86 0 -176 16q-124 -88 -278 -128q-36 -9 -86 -16h-3q-11 0 -20.5 8t-11.5 21q-1 3 -1 6.5t0.5 6.5t2 6l2.5 5t3.5 5.5t4 5t4.5 5t4 4.5q5 6 23 25t26 29.5t22.5 29t25 38.5t20.5 44q-124 72 -195 177t-71 224 q0 139 94 257t256.5 186.5t353.5 68.5t353.5 -68.5t256.5 -186.5t94 -257zM1792 512q0 -120 -71 -224.5t-195 -176.5q10 -24 20.5 -44t25 -38.5t22.5 -29t26 -29.5t23 -25q1 -1 4 -4.5t4.5 -5t4 -5t3.5 -5.5l2.5 -5t2 -6t0.5 -6.5t-1 -6.5q-3 -14 -13 -22t-22 -7 q-50 7 -86 16q-154 40 -278 128q-90 -16 -176 -16q-271 0 -472 132q58 -4 88 -4q161 0 309 45t264 129q125 92 192 212t67 254q0 77 -23 152q129 -71 204 -178t75 -230z" />
-<glyph unicode="&#xf087;" d="M256 192q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1408 768q0 51 -39 89.5t-89 38.5h-352q0 58 48 159.5t48 160.5q0 98 -32 145t-128 47q-26 -26 -38 -85t-30.5 -125.5t-59.5 -109.5q-22 -23 -77 -91q-4 -5 -23 -30t-31.5 -41t-34.5 -42.5 t-40 -44t-38.5 -35.5t-40 -27t-35.5 -9h-32v-640h32q13 0 31.5 -3t33 -6.5t38 -11t35 -11.5t35.5 -12.5t29 -10.5q211 -73 342 -73h121q192 0 192 167q0 26 -5 56q30 16 47.5 52.5t17.5 73.5t-18 69q53 50 53 119q0 25 -10 55.5t-25 47.5q32 1 53.5 47t21.5 81zM1536 769 q0 -89 -49 -163q9 -33 9 -69q0 -77 -38 -144q3 -21 3 -43q0 -101 -60 -178q1 -139 -85 -219.5t-227 -80.5h-36h-93q-96 0 -189.5 22.5t-216.5 65.5q-116 40 -138 40h-288q-53 0 -90.5 37.5t-37.5 90.5v640q0 53 37.5 90.5t90.5 37.5h274q36 24 137 155q58 75 107 128 q24 25 35.5 85.5t30.5 126.5t62 108q39 37 90 37q84 0 151 -32.5t102 -101.5t35 -186q0 -93 -48 -192h176q104 0 180 -76t76 -179z" />
-<glyph unicode="&#xf088;" d="M256 1088q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1408 512q0 35 -21.5 81t-53.5 47q15 17 25 47.5t10 55.5q0 69 -53 119q18 32 18 69t-17.5 73.5t-47.5 52.5q5 30 5 56q0 85 -49 126t-136 41h-128q-131 0 -342 -73q-5 -2 -29 -10.5 t-35.5 -12.5t-35 -11.5t-38 -11t-33 -6.5t-31.5 -3h-32v-640h32q16 0 35.5 -9t40 -27t38.5 -35.5t40 -44t34.5 -42.5t31.5 -41t23 -30q55 -68 77 -91q41 -43 59.5 -109.5t30.5 -125.5t38 -85q96 0 128 47t32 145q0 59 -48 160.5t-48 159.5h352q50 0 89 38.5t39 89.5z M1536 511q0 -103 -76 -179t-180 -76h-176q48 -99 48 -192q0 -118 -35 -186q-35 -69 -102 -101.5t-151 -32.5q-51 0 -90 37q-34 33 -54 82t-25.5 90.5t-17.5 84.5t-31 64q-48 50 -107 127q-101 131 -137 155h-274q-53 0 -90.5 37.5t-37.5 90.5v640q0 53 37.5 90.5t90.5 37.5 h288q22 0 138 40q128 44 223 66t200 22h112q140 0 226.5 -79t85.5 -216v-5q60 -77 60 -178q0 -22 -3 -43q38 -67 38 -144q0 -36 -9 -69q49 -74 49 -163z" />
-<glyph unicode="&#xf089;" horiz-adv-x="896" d="M832 1504v-1339l-449 -236q-22 -12 -40 -12q-21 0 -31.5 14.5t-10.5 35.5q0 6 2 20l86 500l-364 354q-25 27 -25 48q0 37 56 46l502 73l225 455q19 41 49 41z" />
-<glyph unicode="&#xf08a;" horiz-adv-x="1792" d="M1664 940q0 81 -21.5 143t-55 98.5t-81.5 59.5t-94 31t-98 8t-112 -25.5t-110.5 -64t-86.5 -72t-60 -61.5q-18 -22 -49 -22t-49 22q-24 28 -60 61.5t-86.5 72t-110.5 64t-112 25.5t-98 -8t-94 -31t-81.5 -59.5t-55 -98.5t-21.5 -143q0 -168 187 -355l581 -560l580 559 q188 188 188 356zM1792 940q0 -221 -229 -450l-623 -600q-18 -18 -44 -18t-44 18l-624 602q-10 8 -27.5 26t-55.5 65.5t-68 97.5t-53.5 121t-23.5 138q0 220 127 344t351 124q62 0 126.5 -21.5t120 -58t95.5 -68.5t76 -68q36 36 76 68t95.5 68.5t120 58t126.5 21.5 q224 0 351 -124t127 -344z" />
-<glyph unicode="&#xf08b;" horiz-adv-x="1664" d="M640 96q0 -4 1 -20t0.5 -26.5t-3 -23.5t-10 -19.5t-20.5 -6.5h-320q-119 0 -203.5 84.5t-84.5 203.5v704q0 119 84.5 203.5t203.5 84.5h320q13 0 22.5 -9.5t9.5 -22.5q0 -4 1 -20t0.5 -26.5t-3 -23.5t-10 -19.5t-20.5 -6.5h-320q-66 0 -113 -47t-47 -113v-704 q0 -66 47 -113t113 -47h288h11h13t11.5 -1t11.5 -3t8 -5.5t7 -9t2 -13.5zM1568 640q0 -26 -19 -45l-544 -544q-19 -19 -45 -19t-45 19t-19 45v288h-448q-26 0 -45 19t-19 45v384q0 26 19 45t45 19h448v288q0 26 19 45t45 19t45 -19l544 -544q19 -19 19 -45z" />
-<glyph unicode="&#xf08c;" d="M237 122h231v694h-231v-694zM483 1030q-1 52 -36 86t-93 34t-94.5 -34t-36.5 -86q0 -51 35.5 -85.5t92.5 -34.5h1q59 0 95 34.5t36 85.5zM1068 122h231v398q0 154 -73 233t-193 79q-136 0 -209 -117h2v101h-231q3 -66 0 -694h231v388q0 38 7 56q15 35 45 59.5t74 24.5 q116 0 116 -157v-371zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
-<glyph unicode="&#xf08d;" horiz-adv-x="1152" d="M480 672v448q0 14 -9 23t-23 9t-23 -9t-9 -23v-448q0 -14 9 -23t23 -9t23 9t9 23zM1152 320q0 -26 -19 -45t-45 -19h-429l-51 -483q-2 -12 -10.5 -20.5t-20.5 -8.5h-1q-27 0 -32 27l-76 485h-404q-26 0 -45 19t-19 45q0 123 78.5 221.5t177.5 98.5v512q-52 0 -90 38 t-38 90t38 90t90 38h640q52 0 90 -38t38 -90t-38 -90t-90 -38v-512q99 0 177.5 -98.5t78.5 -221.5z" />
-<glyph unicode="&#xf08e;" horiz-adv-x="1792" d="M1408 608v-320q0 -119 -84.5 -203.5t-203.5 -84.5h-832q-119 0 -203.5 84.5t-84.5 203.5v832q0 119 84.5 203.5t203.5 84.5h704q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-704q-66 0 -113 -47t-47 -113v-832q0 -66 47 -113t113 -47h832q66 0 113 47t47 113v320 q0 14 9 23t23 9h64q14 0 23 -9t9 -23zM1792 1472v-512q0 -26 -19 -45t-45 -19t-45 19l-176 176l-652 -652q-10 -10 -23 -10t-23 10l-114 114q-10 10 -10 23t10 23l652 652l-176 176q-19 19 -19 45t19 45t45 19h512q26 0 45 -19t19 -45z" />
-<glyph unicode="&#xf090;" d="M1184 640q0 -26 -19 -45l-544 -544q-19 -19 -45 -19t-45 19t-19 45v288h-448q-26 0 -45 19t-19 45v384q0 26 19 45t45 19h448v288q0 26 19 45t45 19t45 -19l544 -544q19 -19 19 -45zM1536 992v-704q0 -119 -84.5 -203.5t-203.5 -84.5h-320q-13 0 -22.5 9.5t-9.5 22.5 q0 4 -1 20t-0.5 26.5t3 23.5t10 19.5t20.5 6.5h320q66 0 113 47t47 113v704q0 66 -47 113t-113 47h-288h-11h-13t-11.5 1t-11.5 3t-8 5.5t-7 9t-2 13.5q0 4 -1 20t-0.5 26.5t3 23.5t10 19.5t20.5 6.5h320q119 0 203.5 -84.5t84.5 -203.5z" />
-<glyph unicode="&#xf091;" horiz-adv-x="1664" d="M458 653q-74 162 -74 371h-256v-96q0 -78 94.5 -162t235.5 -113zM1536 928v96h-256q0 -209 -74 -371q141 29 235.5 113t94.5 162zM1664 1056v-128q0 -71 -41.5 -143t-112 -130t-173 -97.5t-215.5 -44.5q-42 -54 -95 -95q-38 -34 -52.5 -72.5t-14.5 -89.5q0 -54 30.5 -91 t97.5 -37q75 0 133.5 -45.5t58.5 -114.5v-64q0 -14 -9 -23t-23 -9h-832q-14 0 -23 9t-9 23v64q0 69 58.5 114.5t133.5 45.5q67 0 97.5 37t30.5 91q0 51 -14.5 89.5t-52.5 72.5q-53 41 -95 95q-113 5 -215.5 44.5t-173 97.5t-112 130t-41.5 143v128q0 40 28 68t68 28h288v96 q0 66 47 113t113 47h576q66 0 113 -47t47 -113v-96h288q40 0 68 -28t28 -68z" />
-<glyph unicode="&#xf092;" d="M394 184q-8 -9 -20 3q-13 11 -4 19q8 9 20 -3q12 -11 4 -19zM352 245q9 -12 0 -19q-8 -6 -17 7t0 18q9 7 17 -6zM291 305q-5 -7 -13 -2q-10 5 -7 12q3 5 13 2q10 -5 7 -12zM322 271q-6 -7 -16 3q-9 11 -2 16q6 6 16 -3q9 -11 2 -16zM451 159q-4 -12 -19 -6q-17 4 -13 15 t19 7q16 -5 13 -16zM514 154q0 -11 -16 -11q-17 -2 -17 11q0 11 16 11q17 2 17 -11zM572 164q2 -10 -14 -14t-18 8t14 15q16 2 18 -9zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-224q-16 0 -24.5 1t-19.5 5t-16 14.5t-5 27.5v239q0 97 -52 142q57 6 102.5 18t94 39 t81 66.5t53 105t20.5 150.5q0 121 -79 206q37 91 -8 204q-28 9 -81 -11t-92 -44l-38 -24q-93 26 -192 26t-192 -26q-16 11 -42.5 27t-83.5 38.5t-86 13.5q-44 -113 -7 -204q-79 -85 -79 -206q0 -85 20.5 -150t52.5 -105t80.5 -67t94 -39t102.5 -18q-40 -36 -49 -103 q-21 -10 -45 -15t-57 -5t-65.5 21.5t-55.5 62.5q-19 32 -48.5 52t-49.5 24l-20 3q-21 0 -29 -4.5t-5 -11.5t9 -14t13 -12l7 -5q22 -10 43.5 -38t31.5 -51l10 -23q13 -38 44 -61.5t67 -30t69.5 -7t55.5 3.5l23 4q0 -38 0.5 -103t0.5 -68q0 -22 -11 -33.5t-22 -13t-33 -1.5 h-224q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
-<glyph unicode="&#xf093;" horiz-adv-x="1664" d="M1280 64q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1536 64q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1664 288v-320q0 -40 -28 -68t-68 -28h-1472q-40 0 -68 28t-28 68v320q0 40 28 68t68 28h427q21 -56 70.5 -92 t110.5 -36h256q61 0 110.5 36t70.5 92h427q40 0 68 -28t28 -68zM1339 936q-17 -40 -59 -40h-256v-448q0 -26 -19 -45t-45 -19h-256q-26 0 -45 19t-19 45v448h-256q-42 0 -59 40q-17 39 14 69l448 448q18 19 45 19t45 -19l448 -448q31 -30 14 -69z" />
-<glyph unicode="&#xf094;" d="M1407 710q0 44 -7 113.5t-18 96.5q-12 30 -17 44t-9 36.5t-4 48.5q0 23 5 68.5t5 67.5q0 37 -10 55q-4 1 -13 1q-19 0 -58 -4.5t-59 -4.5q-60 0 -176 24t-175 24q-43 0 -94.5 -11.5t-85 -23.5t-89.5 -34q-137 -54 -202 -103q-96 -73 -159.5 -189.5t-88 -236t-24.5 -248.5 q0 -40 12.5 -120t12.5 -121q0 -23 -11 -66.5t-11 -65.5t12 -36.5t34 -14.5q24 0 72.5 11t73.5 11q57 0 169.5 -15.5t169.5 -15.5q181 0 284 36q129 45 235.5 152.5t166 245.5t59.5 275zM1535 712q0 -165 -70 -327.5t-196 -288t-281 -180.5q-124 -44 -326 -44 q-57 0 -170 14.5t-169 14.5q-24 0 -72.5 -14.5t-73.5 -14.5q-73 0 -123.5 55.5t-50.5 128.5q0 24 11 68t11 67q0 40 -12.5 120.5t-12.5 121.5q0 111 18 217.5t54.5 209.5t100.5 194t150 156q78 59 232 120q194 78 316 78q60 0 175.5 -24t173.5 -24q19 0 57 5t58 5 q81 0 118 -50.5t37 -134.5q0 -23 -5 -68t-5 -68q0 -10 1 -18.5t3 -17t4 -13.5t6.5 -16t6.5 -17q16 -40 25 -118.5t9 -136.5z" />
-<glyph unicode="&#xf095;" horiz-adv-x="1408" d="M1408 296q0 -27 -10 -70.5t-21 -68.5q-21 -50 -122 -106q-94 -51 -186 -51q-27 0 -52.5 3.5t-57.5 12.5t-47.5 14.5t-55.5 20.5t-49 18q-98 35 -175 83q-128 79 -264.5 215.5t-215.5 264.5q-48 77 -83 175q-3 9 -18 49t-20.5 55.5t-14.5 47.5t-12.5 57.5t-3.5 52.5 q0 92 51 186q56 101 106 122q25 11 68.5 21t70.5 10q14 0 21 -3q18 -6 53 -76q11 -19 30 -54t35 -63.5t31 -53.5q3 -4 17.5 -25t21.5 -35.5t7 -28.5q0 -20 -28.5 -50t-62 -55t-62 -53t-28.5 -46q0 -9 5 -22.5t8.5 -20.5t14 -24t11.5 -19q76 -137 174 -235t235 -174 q2 -1 19 -11.5t24 -14t20.5 -8.5t22.5 -5q18 0 46 28.5t53 62t55 62t50 28.5q14 0 28.5 -7t35.5 -21.5t25 -17.5q25 -15 53.5 -31t63.5 -35t54 -30q70 -35 76 -53q3 -7 3 -21z" />
-<glyph unicode="&#xf096;" horiz-adv-x="1408" d="M1120 1280h-832q-66 0 -113 -47t-47 -113v-832q0 -66 47 -113t113 -47h832q66 0 113 47t47 113v832q0 66 -47 113t-113 47zM1408 1120v-832q0 -119 -84.5 -203.5t-203.5 -84.5h-832q-119 0 -203.5 84.5t-84.5 203.5v832q0 119 84.5 203.5t203.5 84.5h832 q119 0 203.5 -84.5t84.5 -203.5z" />
-<glyph unicode="&#xf097;" horiz-adv-x="1280" d="M1152 1280h-1024v-1242l423 406l89 85l89 -85l423 -406v1242zM1164 1408q23 0 44 -9q33 -13 52.5 -41t19.5 -62v-1289q0 -34 -19.5 -62t-52.5 -41q-19 -8 -44 -8q-48 0 -83 32l-441 424l-441 -424q-36 -33 -83 -33q-23 0 -44 9q-33 13 -52.5 41t-19.5 62v1289 q0 34 19.5 62t52.5 41q21 9 44 9h1048z" />
-<glyph unicode="&#xf098;" d="M1280 343q0 11 -2 16q-3 8 -38.5 29.5t-88.5 49.5l-53 29q-5 3 -19 13t-25 15t-21 5q-18 0 -47 -32.5t-57 -65.5t-44 -33q-7 0 -16.5 3.5t-15.5 6.5t-17 9.5t-14 8.5q-99 55 -170.5 126.5t-126.5 170.5q-2 3 -8.5 14t-9.5 17t-6.5 15.5t-3.5 16.5q0 13 20.5 33.5t45 38.5 t45 39.5t20.5 36.5q0 10 -5 21t-15 25t-13 19q-3 6 -15 28.5t-25 45.5t-26.5 47.5t-25 40.5t-16.5 18t-16 2q-48 0 -101 -22q-46 -21 -80 -94.5t-34 -130.5q0 -16 2.5 -34t5 -30.5t9 -33t10 -29.5t12.5 -33t11 -30q60 -164 216.5 -320.5t320.5 -216.5q6 -2 30 -11t33 -12.5 t29.5 -10t33 -9t30.5 -5t34 -2.5q57 0 130.5 34t94.5 80q22 53 22 101zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
-<glyph unicode="&#xf099;" horiz-adv-x="1664" d="M1620 1128q-67 -98 -162 -167q1 -14 1 -42q0 -130 -38 -259.5t-115.5 -248.5t-184.5 -210.5t-258 -146t-323 -54.5q-271 0 -496 145q35 -4 78 -4q225 0 401 138q-105 2 -188 64.5t-114 159.5q33 -5 61 -5q43 0 85 11q-112 23 -185.5 111.5t-73.5 205.5v4q68 -38 146 -41 q-66 44 -105 115t-39 154q0 88 44 163q121 -149 294.5 -238.5t371.5 -99.5q-8 38 -8 74q0 134 94.5 228.5t228.5 94.5q140 0 236 -102q109 21 205 78q-37 -115 -142 -178q93 10 186 50z" />
-<glyph unicode="&#xf09a;" horiz-adv-x="768" d="M511 980h257l-30 -284h-227v-824h-341v824h-170v284h170v171q0 182 86 275.5t283 93.5h227v-284h-142q-39 0 -62.5 -6.5t-34 -23.5t-13.5 -34.5t-3 -49.5v-142z" />
-<glyph unicode="&#xf09b;" d="M1536 640q0 -251 -146.5 -451.5t-378.5 -277.5q-27 -5 -39.5 7t-12.5 30v211q0 97 -52 142q57 6 102.5 18t94 39t81 66.5t53 105t20.5 150.5q0 121 -79 206q37 91 -8 204q-28 9 -81 -11t-92 -44l-38 -24q-93 26 -192 26t-192 -26q-16 11 -42.5 27t-83.5 38.5t-86 13.5 q-44 -113 -7 -204q-79 -85 -79 -206q0 -85 20.5 -150t52.5 -105t80.5 -67t94 -39t102.5 -18q-40 -36 -49 -103q-21 -10 -45 -15t-57 -5t-65.5 21.5t-55.5 62.5q-19 32 -48.5 52t-49.5 24l-20 3q-21 0 -29 -4.5t-5 -11.5t9 -14t13 -12l7 -5q22 -10 43.5 -38t31.5 -51l10 -23 q13 -38 44 -61.5t67 -30t69.5 -7t55.5 3.5l23 4q0 -38 0.5 -89t0.5 -54q0 -18 -13 -30t-40 -7q-232 77 -378.5 277.5t-146.5 451.5q0 209 103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
-<glyph unicode="&#xf09c;" horiz-adv-x="1664" d="M1664 960v-256q0 -26 -19 -45t-45 -19h-64q-26 0 -45 19t-19 45v256q0 106 -75 181t-181 75t-181 -75t-75 -181v-192h96q40 0 68 -28t28 -68v-576q0 -40 -28 -68t-68 -28h-960q-40 0 -68 28t-28 68v576q0 40 28 68t68 28h672v192q0 185 131.5 316.5t316.5 131.5 t316.5 -131.5t131.5 -316.5z" />
-<glyph unicode="&#xf09d;" horiz-adv-x="1920" d="M1760 1408q66 0 113 -47t47 -113v-1216q0 -66 -47 -113t-113 -47h-1600q-66 0 -113 47t-47 113v1216q0 66 47 113t113 47h1600zM160 1280q-13 0 -22.5 -9.5t-9.5 -22.5v-224h1664v224q0 13 -9.5 22.5t-22.5 9.5h-1600zM1760 0q13 0 22.5 9.5t9.5 22.5v608h-1664v-608 q0 -13 9.5 -22.5t22.5 -9.5h1600zM256 128v128h256v-128h-256zM640 128v128h384v-128h-384z" />
-<glyph unicode="&#xf09e;" horiz-adv-x="1408" d="M384 192q0 -80 -56 -136t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM896 69q2 -28 -17 -48q-18 -21 -47 -21h-135q-25 0 -43 16.5t-20 41.5q-22 229 -184.5 391.5t-391.5 184.5q-25 2 -41.5 20t-16.5 43v135q0 29 21 47q17 17 43 17h5q160 -13 306 -80.5 t259 -181.5q114 -113 181.5 -259t80.5 -306zM1408 67q2 -27 -18 -47q-18 -20 -46 -20h-143q-26 0 -44.5 17.5t-19.5 42.5q-12 215 -101 408.5t-231.5 336t-336 231.5t-408.5 102q-25 1 -42.5 19.5t-17.5 43.5v143q0 28 20 46q18 18 44 18h3q262 -13 501.5 -120t425.5 -294 q187 -186 294 -425.5t120 -501.5z" />
-<glyph unicode="&#xf0a0;" d="M1040 320q0 -33 -23.5 -56.5t-56.5 -23.5t-56.5 23.5t-23.5 56.5t23.5 56.5t56.5 23.5t56.5 -23.5t23.5 -56.5zM1296 320q0 -33 -23.5 -56.5t-56.5 -23.5t-56.5 23.5t-23.5 56.5t23.5 56.5t56.5 23.5t56.5 -23.5t23.5 -56.5zM1408 160v320q0 13 -9.5 22.5t-22.5 9.5 h-1216q-13 0 -22.5 -9.5t-9.5 -22.5v-320q0 -13 9.5 -22.5t22.5 -9.5h1216q13 0 22.5 9.5t9.5 22.5zM178 640h1180l-157 482q-4 13 -16 21.5t-26 8.5h-782q-14 0 -26 -8.5t-16 -21.5zM1536 480v-320q0 -66 -47 -113t-113 -47h-1216q-66 0 -113 47t-47 113v320q0 25 16 75 l197 606q17 53 63 86t101 33h782q55 0 101 -33t63 -86l197 -606q16 -50 16 -75z" />
-<glyph unicode="&#xf0a1;" horiz-adv-x="1792" d="M1664 896q53 0 90.5 -37.5t37.5 -90.5t-37.5 -90.5t-90.5 -37.5v-384q0 -52 -38 -90t-90 -38q-417 347 -812 380q-58 -19 -91 -66t-31 -100.5t40 -92.5q-20 -33 -23 -65.5t6 -58t33.5 -55t48 -50t61.5 -50.5q-29 -58 -111.5 -83t-168.5 -11.5t-132 55.5q-7 23 -29.5 87.5 t-32 94.5t-23 89t-15 101t3.5 98.5t22 110.5h-122q-66 0 -113 47t-47 113v192q0 66 47 113t113 47h480q435 0 896 384q52 0 90 -38t38 -90v-384zM1536 292v954q-394 -302 -768 -343v-270q377 -42 768 -341z" />
-<glyph unicode="&#xf0a2;" horiz-adv-x="1664" d="M848 -160q0 16 -16 16q-59 0 -101.5 42.5t-42.5 101.5q0 16 -16 16t-16 -16q0 -73 51.5 -124.5t124.5 -51.5q16 0 16 16zM183 128h1298q-164 181 -246.5 411.5t-82.5 484.5q0 256 -320 256t-320 -256q0 -254 -82.5 -484.5t-246.5 -411.5zM1664 128q0 -52 -38 -90t-90 -38 h-448q0 -106 -75 -181t-181 -75t-181 75t-75 181h-448q-52 0 -90 38t-38 90q190 161 287 397.5t97 498.5q0 165 96 262t264 117q-8 18 -8 37q0 40 28 68t68 28t68 -28t28 -68q0 -19 -8 -37q168 -20 264 -117t96 -262q0 -262 97 -498.5t287 -397.5z" />
-<glyph unicode="&#xf0a3;" d="M1376 640l138 -135q30 -28 20 -70q-12 -41 -52 -51l-188 -48l53 -186q12 -41 -19 -70q-29 -31 -70 -19l-186 53l-48 -188q-10 -40 -51 -52q-12 -2 -19 -2q-31 0 -51 22l-135 138l-135 -138q-28 -30 -70 -20q-41 11 -51 52l-48 188l-186 -53q-41 -12 -70 19q-31 29 -19 70 l53 186l-188 48q-40 10 -52 51q-10 42 20 70l138 135l-138 135q-30 28 -20 70q12 41 52 51l188 48l-53 186q-12 41 19 70q29 31 70 19l186 -53l48 188q10 41 51 51q41 12 70 -19l135 -139l135 139q29 30 70 19q41 -10 51 -51l48 -188l186 53q41 12 70 -19q31 -29 19 -70 l-53 -186l188 -48q40 -10 52 -51q10 -42 -20 -70z" />
-<glyph unicode="&#xf0a4;" horiz-adv-x="1792" d="M256 192q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1664 768q0 51 -39 89.5t-89 38.5h-576q0 20 15 48.5t33 55t33 68t15 84.5q0 67 -44.5 97.5t-115.5 30.5q-24 0 -90 -139q-24 -44 -37 -65q-40 -64 -112 -145q-71 -81 -101 -106 q-69 -57 -140 -57h-32v-640h32q72 0 167 -32t193.5 -64t179.5 -32q189 0 189 167q0 26 -5 56q30 16 47.5 52.5t17.5 73.5t-18 69q53 50 53 119q0 25 -10 55.5t-25 47.5h331q52 0 90 38t38 90zM1792 769q0 -105 -75.5 -181t-180.5 -76h-169q-4 -62 -37 -119q3 -21 3 -43 q0 -101 -60 -178q1 -139 -85 -219.5t-227 -80.5q-133 0 -322 69q-164 59 -223 59h-288q-53 0 -90.5 37.5t-37.5 90.5v640q0 53 37.5 90.5t90.5 37.5h288q10 0 21.5 4.5t23.5 14t22.5 18t24 22.5t20.5 21.5t19 21.5t14 17q65 74 100 129q13 21 33 62t37 72t40.5 63t55 49.5 t69.5 17.5q125 0 206.5 -67t81.5 -189q0 -68 -22 -128h374q104 0 180 -76t76 -179z" />
-<glyph unicode="&#xf0a5;" horiz-adv-x="1792" d="M1376 128h32v640h-32q-35 0 -67.5 12t-62.5 37t-50 46t-49 54q-2 3 -3.5 4.5t-4 4.5t-4.5 5q-72 81 -112 145q-14 22 -38 68q-1 3 -10.5 22.5t-18.5 36t-20 35.5t-21.5 30.5t-18.5 11.5q-71 0 -115.5 -30.5t-44.5 -97.5q0 -43 15 -84.5t33 -68t33 -55t15 -48.5h-576 q-50 0 -89 -38.5t-39 -89.5q0 -52 38 -90t90 -38h331q-15 -17 -25 -47.5t-10 -55.5q0 -69 53 -119q-18 -32 -18 -69t17.5 -73.5t47.5 -52.5q-4 -24 -4 -56q0 -85 48.5 -126t135.5 -41q84 0 183 32t194 64t167 32zM1664 192q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45 t45 -19t45 19t19 45zM1792 768v-640q0 -53 -37.5 -90.5t-90.5 -37.5h-288q-59 0 -223 -59q-190 -69 -317 -69q-142 0 -230 77.5t-87 217.5l1 5q-61 76 -61 178q0 22 3 43q-33 57 -37 119h-169q-105 0 -180.5 76t-75.5 181q0 103 76 179t180 76h374q-22 60 -22 128 q0 122 81.5 189t206.5 67q38 0 69.5 -17.5t55 -49.5t40.5 -63t37 -72t33 -62q35 -55 100 -129q2 -3 14 -17t19 -21.5t20.5 -21.5t24 -22.5t22.5 -18t23.5 -14t21.5 -4.5h288q53 0 90.5 -37.5t37.5 -90.5z" />
-<glyph unicode="&#xf0a6;" d="M1280 -64q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1408 700q0 189 -167 189q-26 0 -56 -5q-16 30 -52.5 47.5t-73.5 17.5t-69 -18q-50 53 -119 53q-25 0 -55.5 -10t-47.5 -25v331q0 52 -38 90t-90 38q-51 0 -89.5 -39t-38.5 -89v-576 q-20 0 -48.5 15t-55 33t-68 33t-84.5 15q-67 0 -97.5 -44.5t-30.5 -115.5q0 -24 139 -90q44 -24 65 -37q64 -40 145 -112q81 -71 106 -101q57 -69 57 -140v-32h640v32q0 72 32 167t64 193.5t32 179.5zM1536 705q0 -133 -69 -322q-59 -164 -59 -223v-288q0 -53 -37.5 -90.5 t-90.5 -37.5h-640q-53 0 -90.5 37.5t-37.5 90.5v288q0 10 -4.5 21.5t-14 23.5t-18 22.5t-22.5 24t-21.5 20.5t-21.5 19t-17 14q-74 65 -129 100q-21 13 -62 33t-72 37t-63 40.5t-49.5 55t-17.5 69.5q0 125 67 206.5t189 81.5q68 0 128 -22v374q0 104 76 180t179 76 q105 0 181 -75.5t76 -180.5v-169q62 -4 119 -37q21 3 43 3q101 0 178 -60q139 1 219.5 -85t80.5 -227z" />
-<glyph unicode="&#xf0a7;" d="M1408 576q0 84 -32 183t-64 194t-32 167v32h-640v-32q0 -35 -12 -67.5t-37 -62.5t-46 -50t-54 -49q-9 -8 -14 -12q-81 -72 -145 -112q-22 -14 -68 -38q-3 -1 -22.5 -10.5t-36 -18.5t-35.5 -20t-30.5 -21.5t-11.5 -18.5q0 -71 30.5 -115.5t97.5 -44.5q43 0 84.5 15t68 33 t55 33t48.5 15v-576q0 -50 38.5 -89t89.5 -39q52 0 90 38t38 90v331q46 -35 103 -35q69 0 119 53q32 -18 69 -18t73.5 17.5t52.5 47.5q24 -4 56 -4q85 0 126 48.5t41 135.5zM1280 1344q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1536 580 q0 -142 -77.5 -230t-217.5 -87l-5 1q-76 -61 -178 -61q-22 0 -43 3q-54 -30 -119 -37v-169q0 -105 -76 -180.5t-181 -75.5q-103 0 -179 76t-76 180v374q-54 -22 -128 -22q-121 0 -188.5 81.5t-67.5 206.5q0 38 17.5 69.5t49.5 55t63 40.5t72 37t62 33q55 35 129 100 q3 2 17 14t21.5 19t21.5 20.5t22.5 24t18 22.5t14 23.5t4.5 21.5v288q0 53 37.5 90.5t90.5 37.5h640q53 0 90.5 -37.5t37.5 -90.5v-288q0 -59 59 -223q69 -190 69 -317z" />
-<glyph unicode="&#xf0a8;" d="M1280 576v128q0 26 -19 45t-45 19h-502l189 189q19 19 19 45t-19 45l-91 91q-18 18 -45 18t-45 -18l-362 -362l-91 -91q-18 -18 -18 -45t18 -45l91 -91l362 -362q18 -18 45 -18t45 18l91 91q18 18 18 45t-18 45l-189 189h502q26 0 45 19t19 45zM1536 640 q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
-<glyph unicode="&#xf0a9;" d="M1285 640q0 27 -18 45l-91 91l-362 362q-18 18 -45 18t-45 -18l-91 -91q-18 -18 -18 -45t18 -45l189 -189h-502q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h502l-189 -189q-19 -19 -19 -45t19 -45l91 -91q18 -18 45 -18t45 18l362 362l91 91q18 18 18 45zM1536 640 q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
-<glyph unicode="&#xf0aa;" d="M1284 641q0 27 -18 45l-362 362l-91 91q-18 18 -45 18t-45 -18l-91 -91l-362 -362q-18 -18 -18 -45t18 -45l91 -91q18 -18 45 -18t45 18l189 189v-502q0 -26 19 -45t45 -19h128q26 0 45 19t19 45v502l189 -189q19 -19 45 -19t45 19l91 91q18 18 18 45zM1536 640 q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
-<glyph unicode="&#xf0ab;" d="M1284 639q0 27 -18 45l-91 91q-18 18 -45 18t-45 -18l-189 -189v502q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-502l-189 189q-19 19 -45 19t-45 -19l-91 -91q-18 -18 -18 -45t18 -45l362 -362l91 -91q18 -18 45 -18t45 18l91 91l362 362q18 18 18 45zM1536 640 q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
-<glyph unicode="&#xf0ac;" d="M768 1408q209 0 385.5 -103t279.5 -279.5t103 -385.5t-103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103zM1042 887q-2 -1 -9.5 -9.5t-13.5 -9.5q2 0 4.5 5t5 11t3.5 7q6 7 22 15q14 6 52 12q34 8 51 -11 q-2 2 9.5 13t14.5 12q3 2 15 4.5t15 7.5l2 22q-12 -1 -17.5 7t-6.5 21q0 -2 -6 -8q0 7 -4.5 8t-11.5 -1t-9 -1q-10 3 -15 7.5t-8 16.5t-4 15q-2 5 -9.5 10.5t-9.5 10.5q-1 2 -2.5 5.5t-3 6.5t-4 5.5t-5.5 2.5t-7 -5t-7.5 -10t-4.5 -5q-3 2 -6 1.5t-4.5 -1t-4.5 -3t-5 -3.5 q-3 -2 -8.5 -3t-8.5 -2q15 5 -1 11q-10 4 -16 3q9 4 7.5 12t-8.5 14h5q-1 4 -8.5 8.5t-17.5 8.5t-13 6q-8 5 -34 9.5t-33 0.5q-5 -6 -4.5 -10.5t4 -14t3.5 -12.5q1 -6 -5.5 -13t-6.5 -12q0 -7 14 -15.5t10 -21.5q-3 -8 -16 -16t-16 -12q-5 -8 -1.5 -18.5t10.5 -16.5 q2 -2 1.5 -4t-3.5 -4.5t-5.5 -4t-6.5 -3.5l-3 -2q-11 -5 -20.5 6t-13.5 26q-7 25 -16 30q-23 8 -29 -1q-5 13 -41 26q-25 9 -58 4q6 1 0 15q-7 15 -19 12q3 6 4 17.5t1 13.5q3 13 12 23q1 1 7 8.5t9.5 13.5t0.5 6q35 -4 50 11q5 5 11.5 17t10.5 17q9 6 14 5.5t14.5 -5.5 t14.5 -5q14 -1 15.5 11t-7.5 20q12 -1 3 17q-5 7 -8 9q-12 4 -27 -5q-8 -4 2 -8q-1 1 -9.5 -10.5t-16.5 -17.5t-16 5q-1 1 -5.5 13.5t-9.5 13.5q-8 0 -16 -15q3 8 -11 15t-24 8q19 12 -8 27q-7 4 -20.5 5t-19.5 -4q-5 -7 -5.5 -11.5t5 -8t10.5 -5.5t11.5 -4t8.5 -3 q14 -10 8 -14q-2 -1 -8.5 -3.5t-11.5 -4.5t-6 -4q-3 -4 0 -14t-2 -14q-5 5 -9 17.5t-7 16.5q7 -9 -25 -6l-10 1q-4 0 -16 -2t-20.5 -1t-13.5 8q-4 8 0 20q1 4 4 2q-4 3 -11 9.5t-10 8.5q-46 -15 -94 -41q6 -1 12 1q5 2 13 6.5t10 5.5q34 14 42 7l5 5q14 -16 20 -25 q-7 4 -30 1q-20 -6 -22 -12q7 -12 5 -18q-4 3 -11.5 10t-14.5 11t-15 5q-16 0 -22 -1q-146 -80 -235 -222q7 -7 12 -8q4 -1 5 -9t2.5 -11t11.5 3q9 -8 3 -19q1 1 44 -27q19 -17 21 -21q3 -11 -10 -18q-1 2 -9 9t-9 4q-3 -5 0.5 -18.5t10.5 -12.5q-7 0 -9.5 -16t-2.5 -35.5 t-1 -23.5l2 -1q-3 -12 5.5 -34.5t21.5 -19.5q-13 -3 20 -43q6 -8 8 -9q3 -2 12 -7.5t15 -10t10 -10.5q4 -5 10 -22.5t14 -23.5q-2 -6 9.5 -20t10.5 -23q-1 0 -2.5 -1t-2.5 -1q3 -7 15.5 -14t15.5 -13q1 -3 2 -10t3 -11t8 -2q2 20 -24 62q-15 25 -17 29q-3 5 -5.5 15.5 t-4.5 14.5q2 0 6 -1.5t8.5 -3.5t7.5 -4t2 -3q-3 -7 2 -17.5t12 -18.5t17 -19t12 -13q6 -6 14 -19.5t0 -13.5q9 0 20 -10t17 -20q5 -8 8 -26t5 -24q2 -7 8.5 -13.5t12.5 -9.5l16 -8t13 -7q5 -2 18.5 -10.5t21.5 -11.5q10 -4 16 -4t14.5 2.5t13.5 3.5q15 2 29 -15t21 -21 q36 -19 55 -11q-2 -1 0.5 -7.5t8 -15.5t9 -14.5t5.5 -8.5q5 -6 18 -15t18 -15q6 4 7 9q-3 -8 7 -20t18 -10q14 3 14 32q-31 -15 -49 18q0 1 -2.5 5.5t-4 8.5t-2.5 8.5t0 7.5t5 3q9 0 10 3.5t-2 12.5t-4 13q-1 8 -11 20t-12 15q-5 -9 -16 -8t-16 9q0 -1 -1.5 -5.5t-1.5 -6.5 q-13 0 -15 1q1 3 2.5 17.5t3.5 22.5q1 4 5.5 12t7.5 14.5t4 12.5t-4.5 9.5t-17.5 2.5q-19 -1 -26 -20q-1 -3 -3 -10.5t-5 -11.5t-9 -7q-7 -3 -24 -2t-24 5q-13 8 -22.5 29t-9.5 37q0 10 2.5 26.5t3 25t-5.5 24.5q3 2 9 9.5t10 10.5q2 1 4.5 1.5t4.5 0t4 1.5t3 6q-1 1 -4 3 q-3 3 -4 3q7 -3 28.5 1.5t27.5 -1.5q15 -11 22 2q0 1 -2.5 9.5t-0.5 13.5q5 -27 29 -9q3 -3 15.5 -5t17.5 -5q3 -2 7 -5.5t5.5 -4.5t5 0.5t8.5 6.5q10 -14 12 -24q11 -40 19 -44q7 -3 11 -2t4.5 9.5t0 14t-1.5 12.5l-1 8v18l-1 8q-15 3 -18.5 12t1.5 18.5t15 18.5q1 1 8 3.5 t15.5 6.5t12.5 8q21 19 15 35q7 0 11 9q-1 0 -5 3t-7.5 5t-4.5 2q9 5 2 16q5 3 7.5 11t7.5 10q9 -12 21 -2q7 8 1 16q5 7 20.5 10.5t18.5 9.5q7 -2 8 2t1 12t3 12q4 5 15 9t13 5l17 11q3 4 0 4q18 -2 31 11q10 11 -6 20q3 6 -3 9.5t-15 5.5q3 1 11.5 0.5t10.5 1.5 q15 10 -7 16q-17 5 -43 -12zM879 10q206 36 351 189q-3 3 -12.5 4.5t-12.5 3.5q-18 7 -24 8q1 7 -2.5 13t-8 9t-12.5 8t-11 7q-2 2 -7 6t-7 5.5t-7.5 4.5t-8.5 2t-10 -1l-3 -1q-3 -1 -5.5 -2.5t-5.5 -3t-4 -3t0 -2.5q-21 17 -36 22q-5 1 -11 5.5t-10.5 7t-10 1.5t-11.5 -7 q-5 -5 -6 -15t-2 -13q-7 5 0 17.5t2 18.5q-3 6 -10.5 4.5t-12 -4.5t-11.5 -8.5t-9 -6.5t-8.5 -5.5t-8.5 -7.5q-3 -4 -6 -12t-5 -11q-2 4 -11.5 6.5t-9.5 5.5q2 -10 4 -35t5 -38q7 -31 -12 -48q-27 -25 -29 -40q-4 -22 12 -26q0 -7 -8 -20.5t-7 -21.5q0 -6 2 -16z" />
-<glyph unicode="&#xf0ad;" horiz-adv-x="1664" d="M384 64q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1028 484l-682 -682q-37 -37 -90 -37q-52 0 -91 37l-106 108q-38 36 -38 90q0 53 38 91l681 681q39 -98 114.5 -173.5t173.5 -114.5zM1662 919q0 -39 -23 -106q-47 -134 -164.5 -217.5 t-258.5 -83.5q-185 0 -316.5 131.5t-131.5 316.5t131.5 316.5t316.5 131.5q58 0 121.5 -16.5t107.5 -46.5q16 -11 16 -28t-16 -28l-293 -169v-224l193 -107q5 3 79 48.5t135.5 81t70.5 35.5q15 0 23.5 -10t8.5 -25z" />
-<glyph unicode="&#xf0ae;" horiz-adv-x="1792" d="M1024 128h640v128h-640v-128zM640 640h1024v128h-1024v-128zM1280 1152h384v128h-384v-128zM1792 320v-256q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19t-19 45v256q0 26 19 45t45 19h1664q26 0 45 -19t19 -45zM1792 832v-256q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19 t-19 45v256q0 26 19 45t45 19h1664q26 0 45 -19t19 -45zM1792 1344v-256q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19t-19 45v256q0 26 19 45t45 19h1664q26 0 45 -19t19 -45z" />
-<glyph unicode="&#xf0b0;" horiz-adv-x="1408" d="M1403 1241q17 -41 -14 -70l-493 -493v-742q0 -42 -39 -59q-13 -5 -25 -5q-27 0 -45 19l-256 256q-19 19 -19 45v486l-493 493q-31 29 -14 70q17 39 59 39h1280q42 0 59 -39z" />
-<glyph unicode="&#xf0b1;" horiz-adv-x="1792" d="M640 1280h512v128h-512v-128zM1792 640v-480q0 -66 -47 -113t-113 -47h-1472q-66 0 -113 47t-47 113v480h672v-160q0 -26 19 -45t45 -19h320q26 0 45 19t19 45v160h672zM1024 640v-128h-256v128h256zM1792 1120v-384h-1792v384q0 66 47 113t113 47h352v160q0 40 28 68 t68 28h576q40 0 68 -28t28 -68v-160h352q66 0 113 -47t47 -113z" />
-<glyph unicode="&#xf0b2;" d="M1283 995l-355 -355l355 -355l144 144q29 31 70 14q39 -17 39 -59v-448q0 -26 -19 -45t-45 -19h-448q-42 0 -59 40q-17 39 14 69l144 144l-355 355l-355 -355l144 -144q31 -30 14 -69q-17 -40 -59 -40h-448q-26 0 -45 19t-19 45v448q0 42 40 59q39 17 69 -14l144 -144 l355 355l-355 355l-144 -144q-19 -19 -45 -19q-12 0 -24 5q-40 17 -40 59v448q0 26 19 45t45 19h448q42 0 59 -40q17 -39 -14 -69l-144 -144l355 -355l355 355l-144 144q-31 30 -14 69q17 40 59 40h448q26 0 45 -19t19 -45v-448q0 -42 -39 -59q-13 -5 -25 -5q-26 0 -45 19z " />
-<glyph unicode="&#xf0c0;" horiz-adv-x="1920" d="M593 640q-162 -5 -265 -128h-134q-82 0 -138 40.5t-56 118.5q0 353 124 353q6 0 43.5 -21t97.5 -42.5t119 -21.5q67 0 133 23q-5 -37 -5 -66q0 -139 81 -256zM1664 3q0 -120 -73 -189.5t-194 -69.5h-874q-121 0 -194 69.5t-73 189.5q0 53 3.5 103.5t14 109t26.5 108.5 t43 97.5t62 81t85.5 53.5t111.5 20q10 0 43 -21.5t73 -48t107 -48t135 -21.5t135 21.5t107 48t73 48t43 21.5q61 0 111.5 -20t85.5 -53.5t62 -81t43 -97.5t26.5 -108.5t14 -109t3.5 -103.5zM640 1280q0 -106 -75 -181t-181 -75t-181 75t-75 181t75 181t181 75t181 -75 t75 -181zM1344 896q0 -159 -112.5 -271.5t-271.5 -112.5t-271.5 112.5t-112.5 271.5t112.5 271.5t271.5 112.5t271.5 -112.5t112.5 -271.5zM1920 671q0 -78 -56 -118.5t-138 -40.5h-134q-103 123 -265 128q81 117 81 256q0 29 -5 66q66 -23 133 -23q59 0 119 21.5t97.5 42.5 t43.5 21q124 0 124 -353zM1792 1280q0 -106 -75 -181t-181 -75t-181 75t-75 181t75 181t181 75t181 -75t75 -181z" />
-<glyph unicode="&#xf0c1;" horiz-adv-x="1664" d="M1456 320q0 40 -28 68l-208 208q-28 28 -68 28q-42 0 -72 -32q3 -3 19 -18.5t21.5 -21.5t15 -19t13 -25.5t3.5 -27.5q0 -40 -28 -68t-68 -28q-15 0 -27.5 3.5t-25.5 13t-19 15t-21.5 21.5t-18.5 19q-33 -31 -33 -73q0 -40 28 -68l206 -207q27 -27 68 -27q40 0 68 26 l147 146q28 28 28 67zM753 1025q0 40 -28 68l-206 207q-28 28 -68 28q-39 0 -68 -27l-147 -146q-28 -28 -28 -67q0 -40 28 -68l208 -208q27 -27 68 -27q42 0 72 31q-3 3 -19 18.5t-21.5 21.5t-15 19t-13 25.5t-3.5 27.5q0 40 28 68t68 28q15 0 27.5 -3.5t25.5 -13t19 -15 t21.5 -21.5t18.5 -19q33 31 33 73zM1648 320q0 -120 -85 -203l-147 -146q-83 -83 -203 -83q-121 0 -204 85l-206 207q-83 83 -83 203q0 123 88 209l-88 88q-86 -88 -208 -88q-120 0 -204 84l-208 208q-84 84 -84 204t85 203l147 146q83 83 203 83q121 0 204 -85l206 -207 q83 -83 83 -203q0 -123 -88 -209l88 -88q86 88 208 88q120 0 204 -84l208 -208q84 -84 84 -204z" />
-<glyph unicode="&#xf0c2;" horiz-adv-x="1920" d="M1920 384q0 -159 -112.5 -271.5t-271.5 -112.5h-1088q-185 0 -316.5 131.5t-131.5 316.5q0 132 71 241.5t187 163.5q-2 28 -2 43q0 212 150 362t362 150q158 0 286.5 -88t187.5 -230q70 62 166 62q106 0 181 -75t75 -181q0 -75 -41 -138q129 -30 213 -134.5t84 -239.5z " />
-<glyph unicode="&#xf0c3;" horiz-adv-x="1664" d="M1527 88q56 -89 21.5 -152.5t-140.5 -63.5h-1152q-106 0 -140.5 63.5t21.5 152.5l503 793v399h-64q-26 0 -45 19t-19 45t19 45t45 19h512q26 0 45 -19t19 -45t-19 -45t-45 -19h-64v-399zM748 813l-272 -429h712l-272 429l-20 31v37v399h-128v-399v-37z" />
-<glyph unicode="&#xf0c4;" horiz-adv-x="1792" d="M960 640q26 0 45 -19t19 -45t-19 -45t-45 -19t-45 19t-19 45t19 45t45 19zM1260 576l507 -398q28 -20 25 -56q-5 -35 -35 -51l-128 -64q-13 -7 -29 -7q-17 0 -31 8l-690 387l-110 -66q-8 -4 -12 -5q14 -49 10 -97q-7 -77 -56 -147.5t-132 -123.5q-132 -84 -277 -84 q-136 0 -222 78q-90 84 -79 207q7 76 56 147t131 124q132 84 278 84q83 0 151 -31q9 13 22 22l122 73l-122 73q-13 9 -22 22q-68 -31 -151 -31q-146 0 -278 84q-82 53 -131 124t-56 147q-5 59 15.5 113t63.5 93q85 79 222 79q145 0 277 -84q83 -52 132 -123t56 -148 q4 -48 -10 -97q4 -1 12 -5l110 -66l690 387q14 8 31 8q16 0 29 -7l128 -64q30 -16 35 -51q3 -36 -25 -56zM579 836q46 42 21 108t-106 117q-92 59 -192 59q-74 0 -113 -36q-46 -42 -21 -108t106 -117q92 -59 192 -59q74 0 113 36zM494 91q81 51 106 117t-21 108 q-39 36 -113 36q-100 0 -192 -59q-81 -51 -106 -117t21 -108q39 -36 113 -36q100 0 192 59zM672 704l96 -58v11q0 36 33 56l14 8l-79 47l-26 -26q-3 -3 -10 -11t-12 -12q-2 -2 -4 -3.5t-3 -2.5zM896 480l96 -32l736 576l-128 64l-768 -431v-113l-160 -96l9 -8q2 -2 7 -6 q4 -4 11 -12t11 -12l26 -26zM1600 64l128 64l-520 408l-177 -138q-2 -3 -13 -7z" />
-<glyph unicode="&#xf0c5;" horiz-adv-x="1792" d="M1696 1152q40 0 68 -28t28 -68v-1216q0 -40 -28 -68t-68 -28h-960q-40 0 -68 28t-28 68v288h-544q-40 0 -68 28t-28 68v672q0 40 20 88t48 76l408 408q28 28 76 48t88 20h416q40 0 68 -28t28 -68v-328q68 40 128 40h416zM1152 939l-299 -299h299v299zM512 1323l-299 -299 h299v299zM708 676l316 316v416h-384v-416q0 -40 -28 -68t-68 -28h-416v-640h512v256q0 40 20 88t48 76zM1664 -128v1152h-384v-416q0 -40 -28 -68t-68 -28h-416v-640h896z" />
-<glyph unicode="&#xf0c6;" horiz-adv-x="1408" d="M1404 151q0 -117 -79 -196t-196 -79q-135 0 -235 100l-777 776q-113 115 -113 271q0 159 110 270t269 111q158 0 273 -113l605 -606q10 -10 10 -22q0 -16 -30.5 -46.5t-46.5 -30.5q-13 0 -23 10l-606 607q-79 77 -181 77q-106 0 -179 -75t-73 -181q0 -105 76 -181 l776 -777q63 -63 145 -63q64 0 106 42t42 106q0 82 -63 145l-581 581q-26 24 -60 24q-29 0 -48 -19t-19 -48q0 -32 25 -59l410 -410q10 -10 10 -22q0 -16 -31 -47t-47 -31q-12 0 -22 10l-410 410q-63 61 -63 149q0 82 57 139t139 57q88 0 149 -63l581 -581q100 -98 100 -235 z" />
-<glyph unicode="&#xf0c7;" d="M384 0h768v384h-768v-384zM1280 0h128v896q0 14 -10 38.5t-20 34.5l-281 281q-10 10 -34 20t-39 10v-416q0 -40 -28 -68t-68 -28h-576q-40 0 -68 28t-28 68v416h-128v-1280h128v416q0 40 28 68t68 28h832q40 0 68 -28t28 -68v-416zM896 928v320q0 13 -9.5 22.5t-22.5 9.5 h-192q-13 0 -22.5 -9.5t-9.5 -22.5v-320q0 -13 9.5 -22.5t22.5 -9.5h192q13 0 22.5 9.5t9.5 22.5zM1536 896v-928q0 -40 -28 -68t-68 -28h-1344q-40 0 -68 28t-28 68v1344q0 40 28 68t68 28h928q40 0 88 -20t76 -48l280 -280q28 -28 48 -76t20 -88z" />
-<glyph unicode="&#xf0c8;" d="M1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
-<glyph unicode="&#xf0c9;" d="M1536 192v-128q0 -26 -19 -45t-45 -19h-1408q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1408q26 0 45 -19t19 -45zM1536 704v-128q0 -26 -19 -45t-45 -19h-1408q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1408q26 0 45 -19t19 -45zM1536 1216v-128q0 -26 -19 -45 t-45 -19h-1408q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1408q26 0 45 -19t19 -45z" />
-<glyph unicode="&#xf0ca;" horiz-adv-x="1792" d="M384 128q0 -80 -56 -136t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM384 640q0 -80 -56 -136t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM1792 224v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1216q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5 t22.5 9.5h1216q13 0 22.5 -9.5t9.5 -22.5zM384 1152q0 -80 -56 -136t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM1792 736v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1216q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1216q13 0 22.5 -9.5t9.5 -22.5z M1792 1248v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1216q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1216q13 0 22.5 -9.5t9.5 -22.5z" />
-<glyph unicode="&#xf0cb;" horiz-adv-x="1792" d="M381 -84q0 -80 -54.5 -126t-135.5 -46q-106 0 -172 66l57 88q49 -45 106 -45q29 0 50.5 14.5t21.5 42.5q0 64 -105 56l-26 56q8 10 32.5 43.5t42.5 54t37 38.5v1q-16 0 -48.5 -1t-48.5 -1v-53h-106v152h333v-88l-95 -115q51 -12 81 -49t30 -88zM383 543v-159h-362 q-6 36 -6 54q0 51 23.5 93t56.5 68t66 47.5t56.5 43.5t23.5 45q0 25 -14.5 38.5t-39.5 13.5q-46 0 -81 -58l-85 59q24 51 71.5 79.5t105.5 28.5q73 0 123 -41.5t50 -112.5q0 -50 -34 -91.5t-75 -64.5t-75.5 -50.5t-35.5 -52.5h127v60h105zM1792 224v-192q0 -13 -9.5 -22.5 t-22.5 -9.5h-1216q-13 0 -22.5 9.5t-9.5 22.5v192q0 14 9 23t23 9h1216q13 0 22.5 -9.5t9.5 -22.5zM384 1123v-99h-335v99h107q0 41 0.5 122t0.5 121v12h-2q-8 -17 -50 -54l-71 76l136 127h106v-404h108zM1792 736v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1216q-13 0 -22.5 9.5 t-9.5 22.5v192q0 14 9 23t23 9h1216q13 0 22.5 -9.5t9.5 -22.5zM1792 1248v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1216q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1216q13 0 22.5 -9.5t9.5 -22.5z" />
-<glyph unicode="&#xf0cc;" horiz-adv-x="1792" d="M1760 640q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-1728q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h1728zM483 704q-28 35 -51 80q-48 97 -48 188q0 181 134 309q133 127 393 127q50 0 167 -19q66 -12 177 -48q10 -38 21 -118q14 -123 14 -183q0 -18 -5 -45l-12 -3l-84 6 l-14 2q-50 149 -103 205q-88 91 -210 91q-114 0 -182 -59q-67 -58 -67 -146q0 -73 66 -140t279 -129q69 -20 173 -66q58 -28 95 -52h-743zM990 448h411q7 -39 7 -92q0 -111 -41 -212q-23 -55 -71 -104q-37 -35 -109 -81q-80 -48 -153 -66q-80 -21 -203 -21q-114 0 -195 23 l-140 40q-57 16 -72 28q-8 8 -8 22v13q0 108 -2 156q-1 30 0 68l2 37v44l102 2q15 -34 30 -71t22.5 -56t12.5 -27q35 -57 80 -94q43 -36 105 -57q59 -22 132 -22q64 0 139 27q77 26 122 86q47 61 47 129q0 84 -81 157q-34 29 -137 71z" />
-<glyph unicode="&#xf0cd;" d="M48 1313q-37 2 -45 4l-3 88q13 1 40 1q60 0 112 -4q132 -7 166 -7q86 0 168 3q116 4 146 5q56 0 86 2l-1 -14l2 -64v-9q-60 -9 -124 -9q-60 0 -79 -25q-13 -14 -13 -132q0 -13 0.5 -32.5t0.5 -25.5l1 -229l14 -280q6 -124 51 -202q35 -59 96 -92q88 -47 177 -47 q104 0 191 28q56 18 99 51q48 36 65 64q36 56 53 114q21 73 21 229q0 79 -3.5 128t-11 122.5t-13.5 159.5l-4 59q-5 67 -24 88q-34 35 -77 34l-100 -2l-14 3l2 86h84l205 -10q76 -3 196 10l18 -2q6 -38 6 -51q0 -7 -4 -31q-45 -12 -84 -13q-73 -11 -79 -17q-15 -15 -15 -41 q0 -7 1.5 -27t1.5 -31q8 -19 22 -396q6 -195 -15 -304q-15 -76 -41 -122q-38 -65 -112 -123q-75 -57 -182 -89q-109 -33 -255 -33q-167 0 -284 46q-119 47 -179 122q-61 76 -83 195q-16 80 -16 237v333q0 188 -17 213q-25 36 -147 39zM1536 -96v64q0 14 -9 23t-23 9h-1472 q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h1472q14 0 23 9t9 23z" />
-<glyph unicode="&#xf0ce;" horiz-adv-x="1664" d="M512 160v192q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM512 544v192q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM1024 160v192q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23 v-192q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM512 928v192q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM1024 544v192q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM1536 160v192 q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM1024 928v192q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM1536 544v192q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23v-192 q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM1536 928v192q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM1664 1248v-1088q0 -66 -47 -113t-113 -47h-1344q-66 0 -113 47t-47 113v1088q0 66 47 113t113 47h1344q66 0 113 -47t47 -113 z" />
-<glyph unicode="&#xf0d0;" horiz-adv-x="1664" d="M1190 955l293 293l-107 107l-293 -293zM1637 1248q0 -27 -18 -45l-1286 -1286q-18 -18 -45 -18t-45 18l-198 198q-18 18 -18 45t18 45l1286 1286q18 18 45 18t45 -18l198 -198q18 -18 18 -45zM286 1438l98 -30l-98 -30l-30 -98l-30 98l-98 30l98 30l30 98zM636 1276 l196 -60l-196 -60l-60 -196l-60 196l-196 60l196 60l60 196zM1566 798l98 -30l-98 -30l-30 -98l-30 98l-98 30l98 30l30 98zM926 1438l98 -30l-98 -30l-30 -98l-30 98l-98 30l98 30l30 98z" />
-<glyph unicode="&#xf0d1;" horiz-adv-x="1792" d="M640 128q0 52 -38 90t-90 38t-90 -38t-38 -90t38 -90t90 -38t90 38t38 90zM256 640h384v256h-158q-13 0 -22 -9l-195 -195q-9 -9 -9 -22v-30zM1536 128q0 52 -38 90t-90 38t-90 -38t-38 -90t38 -90t90 -38t90 38t38 90zM1792 1216v-1024q0 -15 -4 -26.5t-13.5 -18.5 t-16.5 -11.5t-23.5 -6t-22.5 -2t-25.5 0t-22.5 0.5q0 -106 -75 -181t-181 -75t-181 75t-75 181h-384q0 -106 -75 -181t-181 -75t-181 75t-75 181h-64q-3 0 -22.5 -0.5t-25.5 0t-22.5 2t-23.5 6t-16.5 11.5t-13.5 18.5t-4 26.5q0 26 19 45t45 19v320q0 8 -0.5 35t0 38 t2.5 34.5t6.5 37t14 30.5t22.5 30l198 198q19 19 50.5 32t58.5 13h160v192q0 26 19 45t45 19h1024q26 0 45 -19t19 -45z" />
-<glyph unicode="&#xf0d2;" d="M1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103q-111 0 -218 32q59 93 78 164q9 34 54 211q20 -39 73 -67.5t114 -28.5q121 0 216 68.5t147 188.5t52 270q0 114 -59.5 214t-172.5 163t-255 63q-105 0 -196 -29t-154.5 -77t-109 -110.5t-67 -129.5t-21.5 -134 q0 -104 40 -183t117 -111q30 -12 38 20q2 7 8 31t8 30q6 23 -11 43q-51 61 -51 151q0 151 104.5 259.5t273.5 108.5q151 0 235.5 -82t84.5 -213q0 -170 -68.5 -289t-175.5 -119q-61 0 -98 43.5t-23 104.5q8 35 26.5 93.5t30 103t11.5 75.5q0 50 -27 83t-77 33 q-62 0 -105 -57t-43 -142q0 -73 25 -122l-99 -418q-17 -70 -13 -177q-206 91 -333 281t-127 423q0 209 103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
-<glyph unicode="&#xf0d3;" d="M1248 1408q119 0 203.5 -84.5t84.5 -203.5v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-725q85 122 108 210q9 34 53 209q21 -39 73.5 -67t112.5 -28q181 0 295.5 147.5t114.5 373.5q0 84 -35 162.5t-96.5 139t-152.5 97t-197 36.5q-104 0 -194.5 -28.5t-153 -76.5 t-107.5 -109.5t-66.5 -128t-21.5 -132.5q0 -102 39.5 -180t116.5 -110q13 -5 23.5 0t14.5 19q10 44 15 61q6 23 -11 42q-50 62 -50 150q0 150 103.5 256.5t270.5 106.5q149 0 232.5 -81t83.5 -210q0 -168 -67.5 -286t-173.5 -118q-60 0 -97 43.5t-23 103.5q8 34 26.5 92.5 t29.5 102t11 74.5q0 49 -26.5 81.5t-75.5 32.5q-61 0 -103.5 -56.5t-42.5 -139.5q0 -72 24 -121l-98 -414q-24 -100 -7 -254h-183q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960z" />
-<glyph unicode="&#xf0d4;" d="M678 -57q0 -38 -10 -71h-380q-95 0 -171.5 56.5t-103.5 147.5q24 45 69 77.5t100 49.5t107 24t107 7q32 0 49 -2q6 -4 30.5 -21t33 -23t31 -23t32 -25.5t27.5 -25.5t26.5 -29.5t21 -30.5t17.5 -34.5t9.5 -36t4.5 -40.5zM385 294q-234 -7 -385 -85v433q103 -118 273 -118 q32 0 70 5q-21 -61 -21 -86q0 -67 63 -149zM558 805q0 -100 -43.5 -160.5t-140.5 -60.5q-51 0 -97 26t-78 67.5t-56 93.5t-35.5 104t-11.5 99q0 96 51.5 165t144.5 69q66 0 119 -41t84 -104t47 -130t16 -128zM1536 896v-736q0 -119 -84.5 -203.5t-203.5 -84.5h-468 q39 73 39 157q0 66 -22 122.5t-55.5 93t-72 71t-72 59.5t-55.5 54.5t-22 59.5q0 36 23 68t56 61.5t65.5 64.5t55.5 93t23 131t-26.5 145.5t-75.5 118.5q-6 6 -14 11t-12.5 7.5t-10 9.5t-10.5 17h135l135 64h-437q-138 0 -244.5 -38.5t-182.5 -133.5q0 126 81 213t207 87h960 q119 0 203.5 -84.5t84.5 -203.5v-96h-256v256h-128v-256h-256v-128h256v-256h128v256h256z" />
-<glyph unicode="&#xf0d5;" horiz-adv-x="1664" d="M876 71q0 21 -4.5 40.5t-9.5 36t-17.5 34.5t-21 30.5t-26.5 29.5t-27.5 25.5t-32 25.5t-31 23t-33 23t-30.5 21q-17 2 -50 2q-54 0 -106 -7t-108 -25t-98 -46t-69 -75t-27 -107q0 -68 35.5 -121.5t93 -84t120.5 -45.5t127 -15q59 0 112.5 12.5t100.5 39t74.5 73.5 t27.5 110zM756 933q0 60 -16.5 127.5t-47 130.5t-84 104t-119.5 41q-93 0 -144 -69t-51 -165q0 -47 11.5 -99t35.5 -104t56 -93.5t78 -67.5t97 -26q97 0 140.5 60.5t43.5 160.5zM625 1408h437l-135 -79h-135q71 -45 110 -126t39 -169q0 -74 -23 -131.5t-56 -92.5t-66 -64.5 t-56 -61t-23 -67.5q0 -26 16.5 -51t43 -48t58.5 -48t64 -55.5t58.5 -66t43 -85t16.5 -106.5q0 -160 -140 -282q-152 -131 -420 -131q-59 0 -119.5 10t-122 33.5t-108.5 58t-77 89t-30 121.5q0 61 37 135q32 64 96 110.5t145 71t155 36t150 13.5q-64 83 -64 149q0 12 2 23.5 t5 19.5t8 21.5t7 21.5q-40 -5 -70 -5q-149 0 -255.5 98t-106.5 246q0 140 95 250.5t234 141.5q94 20 187 20zM1664 1152v-128h-256v-256h-128v256h-256v128h256v256h128v-256h256z" />
-<glyph unicode="&#xf0d6;" horiz-adv-x="1920" d="M768 384h384v96h-128v448h-114l-148 -137l77 -80q42 37 55 57h2v-288h-128v-96zM1280 640q0 -70 -21 -142t-59.5 -134t-101.5 -101t-138 -39t-138 39t-101.5 101t-59.5 134t-21 142t21 142t59.5 134t101.5 101t138 39t138 -39t101.5 -101t59.5 -134t21 -142zM1792 384 v512q-106 0 -181 75t-75 181h-1152q0 -106 -75 -181t-181 -75v-512q106 0 181 -75t75 -181h1152q0 106 75 181t181 75zM1920 1216v-1152q0 -26 -19 -45t-45 -19h-1792q-26 0 -45 19t-19 45v1152q0 26 19 45t45 19h1792q26 0 45 -19t19 -45z" />
-<glyph unicode="&#xf0d7;" horiz-adv-x="1024" d="M1024 832q0 -26 -19 -45l-448 -448q-19 -19 -45 -19t-45 19l-448 448q-19 19 -19 45t19 45t45 19h896q26 0 45 -19t19 -45z" />
-<glyph unicode="&#xf0d8;" horiz-adv-x="1024" d="M1024 320q0 -26 -19 -45t-45 -19h-896q-26 0 -45 19t-19 45t19 45l448 448q19 19 45 19t45 -19l448 -448q19 -19 19 -45z" />
-<glyph unicode="&#xf0d9;" horiz-adv-x="640" d="M640 1088v-896q0 -26 -19 -45t-45 -19t-45 19l-448 448q-19 19 -19 45t19 45l448 448q19 19 45 19t45 -19t19 -45z" />
-<glyph unicode="&#xf0da;" horiz-adv-x="640" d="M576 640q0 -26 -19 -45l-448 -448q-19 -19 -45 -19t-45 19t-19 45v896q0 26 19 45t45 19t45 -19l448 -448q19 -19 19 -45z" />
-<glyph unicode="&#xf0db;" horiz-adv-x="1664" d="M160 0h608v1152h-640v-1120q0 -13 9.5 -22.5t22.5 -9.5zM1536 32v1120h-640v-1152h608q13 0 22.5 9.5t9.5 22.5zM1664 1248v-1216q0 -66 -47 -113t-113 -47h-1344q-66 0 -113 47t-47 113v1216q0 66 47 113t113 47h1344q66 0 113 -47t47 -113z" />
-<glyph unicode="&#xf0dc;" horiz-adv-x="1024" d="M1024 448q0 -26 -19 -45l-448 -448q-19 -19 -45 -19t-45 19l-448 448q-19 19 -19 45t19 45t45 19h896q26 0 45 -19t19 -45zM1024 832q0 -26 -19 -45t-45 -19h-896q-26 0 -45 19t-19 45t19 45l448 448q19 19 45 19t45 -19l448 -448q19 -19 19 -45z" />
-<glyph unicode="&#xf0dd;" horiz-adv-x="1024" d="M1024 448q0 -26 -19 -45l-448 -448q-19 -19 -45 -19t-45 19l-448 448q-19 19 -19 45t19 45t45 19h896q26 0 45 -19t19 -45z" />
-<glyph unicode="&#xf0de;" horiz-adv-x="1024" d="M1024 832q0 -26 -19 -45t-45 -19h-896q-26 0 -45 19t-19 45t19 45l448 448q19 19 45 19t45 -19l448 -448q19 -19 19 -45z" />
-<glyph unicode="&#xf0e0;" horiz-adv-x="1792" d="M1792 826v-794q0 -66 -47 -113t-113 -47h-1472q-66 0 -113 47t-47 113v794q44 -49 101 -87q362 -246 497 -345q57 -42 92.5 -65.5t94.5 -48t110 -24.5h1h1q51 0 110 24.5t94.5 48t92.5 65.5q170 123 498 345q57 39 100 87zM1792 1120q0 -79 -49 -151t-122 -123 q-376 -261 -468 -325q-10 -7 -42.5 -30.5t-54 -38t-52 -32.5t-57.5 -27t-50 -9h-1h-1q-23 0 -50 9t-57.5 27t-52 32.5t-54 38t-42.5 30.5q-91 64 -262 182.5t-205 142.5q-62 42 -117 115.5t-55 136.5q0 78 41.5 130t118.5 52h1472q65 0 112.5 -47t47.5 -113z" />
-<glyph unicode="&#xf0e1;" d="M349 911v-991h-330v991h330zM370 1217q1 -73 -50.5 -122t-135.5 -49h-2q-82 0 -132 49t-50 122q0 74 51.5 122.5t134.5 48.5t133 -48.5t51 -122.5zM1536 488v-568h-329v530q0 105 -40.5 164.5t-126.5 59.5q-63 0 -105.5 -34.5t-63.5 -85.5q-11 -30 -11 -81v-553h-329 q2 399 2 647t-1 296l-1 48h329v-144h-2q20 32 41 56t56.5 52t87 43.5t114.5 15.5q171 0 275 -113.5t104 -332.5z" />
-<glyph unicode="&#xf0e2;" d="M1536 640q0 -156 -61 -298t-164 -245t-245 -164t-298 -61q-172 0 -327 72.5t-264 204.5q-7 10 -6.5 22.5t8.5 20.5l137 138q10 9 25 9q16 -2 23 -12q73 -95 179 -147t225 -52q104 0 198.5 40.5t163.5 109.5t109.5 163.5t40.5 198.5t-40.5 198.5t-109.5 163.5 t-163.5 109.5t-198.5 40.5q-98 0 -188 -35.5t-160 -101.5l137 -138q31 -30 14 -69q-17 -40 -59 -40h-448q-26 0 -45 19t-19 45v448q0 42 40 59q39 17 69 -14l130 -129q107 101 244.5 156.5t284.5 55.5q156 0 298 -61t245 -164t164 -245t61 -298z" />
-<glyph unicode="&#xf0e3;" horiz-adv-x="1792" d="M1771 0q0 -53 -37 -90l-107 -108q-39 -37 -91 -37q-53 0 -90 37l-363 364q-38 36 -38 90q0 53 43 96l-256 256l-126 -126q-14 -14 -34 -14t-34 14q2 -2 12.5 -12t12.5 -13t10 -11.5t10 -13.5t6 -13.5t5.5 -16.5t1.5 -18q0 -38 -28 -68q-3 -3 -16.5 -18t-19 -20.5 t-18.5 -16.5t-22 -15.5t-22 -9t-26 -4.5q-40 0 -68 28l-408 408q-28 28 -28 68q0 13 4.5 26t9 22t15.5 22t16.5 18.5t20.5 19t18 16.5q30 28 68 28q10 0 18 -1.5t16.5 -5.5t13.5 -6t13.5 -10t11.5 -10t13 -12.5t12 -12.5q-14 14 -14 34t14 34l348 348q14 14 34 14t34 -14 q-2 2 -12.5 12t-12.5 13t-10 11.5t-10 13.5t-6 13.5t-5.5 16.5t-1.5 18q0 38 28 68q3 3 16.5 18t19 20.5t18.5 16.5t22 15.5t22 9t26 4.5q40 0 68 -28l408 -408q28 -28 28 -68q0 -13 -4.5 -26t-9 -22t-15.5 -22t-16.5 -18.5t-20.5 -19t-18 -16.5q-30 -28 -68 -28 q-10 0 -18 1.5t-16.5 5.5t-13.5 6t-13.5 10t-11.5 10t-13 12.5t-12 12.5q14 -14 14 -34t-14 -34l-126 -126l256 -256q43 43 96 43q52 0 91 -37l363 -363q37 -39 37 -91z" />
-<glyph unicode="&#xf0e4;" horiz-adv-x="1792" d="M384 384q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM576 832q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1004 351l101 382q6 26 -7.5 48.5t-38.5 29.5 t-48 -6.5t-30 -39.5l-101 -382q-60 -5 -107 -43.5t-63 -98.5q-20 -77 20 -146t117 -89t146 20t89 117q16 60 -6 117t-72 91zM1664 384q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1024 1024q0 53 -37.5 90.5 t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1472 832q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1792 384q0 -261 -141 -483q-19 -29 -54 -29h-1402q-35 0 -54 29 q-141 221 -141 483q0 182 71 348t191 286t286 191t348 71t348 -71t286 -191t191 -286t71 -348z" />
-<glyph unicode="&#xf0e5;" horiz-adv-x="1792" d="M896 1152q-204 0 -381.5 -69.5t-282 -187.5t-104.5 -255q0 -112 71.5 -213.5t201.5 -175.5l87 -50l-27 -96q-24 -91 -70 -172q152 63 275 171l43 38l57 -6q69 -8 130 -8q204 0 381.5 69.5t282 187.5t104.5 255t-104.5 255t-282 187.5t-381.5 69.5zM1792 640 q0 -174 -120 -321.5t-326 -233t-450 -85.5q-70 0 -145 8q-198 -175 -460 -242q-49 -14 -114 -22h-5q-15 0 -27 10.5t-16 27.5v1q-3 4 -0.5 12t2 10t4.5 9.5l6 9t7 8.5t8 9q7 8 31 34.5t34.5 38t31 39.5t32.5 51t27 59t26 76q-157 89 -247.5 220t-90.5 281q0 174 120 321.5 t326 233t450 85.5t450 -85.5t326 -233t120 -321.5z" />
-<glyph unicode="&#xf0e6;" horiz-adv-x="1792" d="M704 1152q-153 0 -286 -52t-211.5 -141t-78.5 -191q0 -82 53 -158t149 -132l97 -56l-35 -84q34 20 62 39l44 31l53 -10q78 -14 153 -14q153 0 286 52t211.5 141t78.5 191t-78.5 191t-211.5 141t-286 52zM704 1280q191 0 353.5 -68.5t256.5 -186.5t94 -257t-94 -257 t-256.5 -186.5t-353.5 -68.5q-86 0 -176 16q-124 -88 -278 -128q-36 -9 -86 -16h-3q-11 0 -20.5 8t-11.5 21q-1 3 -1 6.5t0.5 6.5t2 6l2.5 5t3.5 5.5t4 5t4.5 5t4 4.5q5 6 23 25t26 29.5t22.5 29t25 38.5t20.5 44q-124 72 -195 177t-71 224q0 139 94 257t256.5 186.5 t353.5 68.5zM1526 111q10 -24 20.5 -44t25 -38.5t22.5 -29t26 -29.5t23 -25q1 -1 4 -4.5t4.5 -5t4 -5t3.5 -5.5l2.5 -5t2 -6t0.5 -6.5t-1 -6.5q-3 -14 -13 -22t-22 -7q-50 7 -86 16q-154 40 -278 128q-90 -16 -176 -16q-271 0 -472 132q58 -4 88 -4q161 0 309 45t264 129 q125 92 192 212t67 254q0 77 -23 152q129 -71 204 -178t75 -230q0 -120 -71 -224.5t-195 -176.5z" />
-<glyph unicode="&#xf0e7;" horiz-adv-x="896" d="M885 970q18 -20 7 -44l-540 -1157q-13 -25 -42 -25q-4 0 -14 2q-17 5 -25.5 19t-4.5 30l197 808l-406 -101q-4 -1 -12 -1q-18 0 -31 11q-18 15 -13 39l201 825q4 14 16 23t28 9h328q19 0 32 -12.5t13 -29.5q0 -8 -5 -18l-171 -463l396 98q8 2 12 2q19 0 34 -15z" />
-<glyph unicode="&#xf0e8;" horiz-adv-x="1792" d="M1792 288v-320q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v320q0 40 28 68t68 28h96v192h-512v-192h96q40 0 68 -28t28 -68v-320q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v320q0 40 28 68t68 28h96v192h-512v-192h96q40 0 68 -28t28 -68v-320 q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v320q0 40 28 68t68 28h96v192q0 52 38 90t90 38h512v192h-96q-40 0 -68 28t-28 68v320q0 40 28 68t68 28h320q40 0 68 -28t28 -68v-320q0 -40 -28 -68t-68 -28h-96v-192h512q52 0 90 -38t38 -90v-192h96q40 0 68 -28t28 -68 z" />
-<glyph unicode="&#xf0e9;" horiz-adv-x="1664" d="M896 708v-580q0 -104 -76 -180t-180 -76t-180 76t-76 180q0 26 19 45t45 19t45 -19t19 -45q0 -50 39 -89t89 -39t89 39t39 89v580q33 11 64 11t64 -11zM1664 681q0 -13 -9.5 -22.5t-22.5 -9.5q-11 0 -23 10q-49 46 -93 69t-102 23q-68 0 -128 -37t-103 -97 q-7 -10 -17.5 -28t-14.5 -24q-11 -17 -28 -17q-18 0 -29 17q-4 6 -14.5 24t-17.5 28q-43 60 -102.5 97t-127.5 37t-127.5 -37t-102.5 -97q-7 -10 -17.5 -28t-14.5 -24q-11 -17 -29 -17q-17 0 -28 17q-4 6 -14.5 24t-17.5 28q-43 60 -103 97t-128 37q-58 0 -102 -23t-93 -69 q-12 -10 -23 -10q-13 0 -22.5 9.5t-9.5 22.5q0 5 1 7q45 183 172.5 319.5t298 204.5t360.5 68q140 0 274.5 -40t246.5 -113.5t194.5 -187t115.5 -251.5q1 -2 1 -7zM896 1408v-98q-42 2 -64 2t-64 -2v98q0 26 19 45t45 19t45 -19t19 -45z" />
-<glyph unicode="&#xf0ea;" horiz-adv-x="1792" d="M768 -128h896v640h-416q-40 0 -68 28t-28 68v416h-384v-1152zM1024 1312v64q0 13 -9.5 22.5t-22.5 9.5h-704q-13 0 -22.5 -9.5t-9.5 -22.5v-64q0 -13 9.5 -22.5t22.5 -9.5h704q13 0 22.5 9.5t9.5 22.5zM1280 640h299l-299 299v-299zM1792 512v-672q0 -40 -28 -68t-68 -28 h-960q-40 0 -68 28t-28 68v160h-544q-40 0 -68 28t-28 68v1344q0 40 28 68t68 28h1088q40 0 68 -28t28 -68v-328q21 -13 36 -28l408 -408q28 -28 48 -76t20 -88z" />
-<glyph unicode="&#xf0eb;" horiz-adv-x="1024" d="M736 960q0 -13 -9.5 -22.5t-22.5 -9.5t-22.5 9.5t-9.5 22.5q0 46 -54 71t-106 25q-13 0 -22.5 9.5t-9.5 22.5t9.5 22.5t22.5 9.5q50 0 99.5 -16t87 -54t37.5 -90zM896 960q0 72 -34.5 134t-90 101.5t-123 62t-136.5 22.5t-136.5 -22.5t-123 -62t-90 -101.5t-34.5 -134 q0 -101 68 -180q10 -11 30.5 -33t30.5 -33q128 -153 141 -298h228q13 145 141 298q10 11 30.5 33t30.5 33q68 79 68 180zM1024 960q0 -155 -103 -268q-45 -49 -74.5 -87t-59.5 -95.5t-34 -107.5q47 -28 47 -82q0 -37 -25 -64q25 -27 25 -64q0 -52 -45 -81q13 -23 13 -47 q0 -46 -31.5 -71t-77.5 -25q-20 -44 -60 -70t-87 -26t-87 26t-60 70q-46 0 -77.5 25t-31.5 71q0 24 13 47q-45 29 -45 81q0 37 25 64q-25 27 -25 64q0 54 47 82q-4 50 -34 107.5t-59.5 95.5t-74.5 87q-103 113 -103 268q0 99 44.5 184.5t117 142t164 89t186.5 32.5 t186.5 -32.5t164 -89t117 -142t44.5 -184.5z" />
-<glyph unicode="&#xf0ec;" horiz-adv-x="1792" d="M1792 352v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1376v-192q0 -13 -9.5 -22.5t-22.5 -9.5q-12 0 -24 10l-319 320q-9 9 -9 22q0 14 9 23l320 320q9 9 23 9q13 0 22.5 -9.5t9.5 -22.5v-192h1376q13 0 22.5 -9.5t9.5 -22.5zM1792 896q0 -14 -9 -23l-320 -320q-9 -9 -23 -9 q-13 0 -22.5 9.5t-9.5 22.5v192h-1376q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1376v192q0 14 9 23t23 9q12 0 24 -10l319 -319q9 -9 9 -23z" />
-<glyph unicode="&#xf0ed;" horiz-adv-x="1920" d="M1280 608q0 14 -9 23t-23 9h-224v352q0 13 -9.5 22.5t-22.5 9.5h-192q-13 0 -22.5 -9.5t-9.5 -22.5v-352h-224q-13 0 -22.5 -9.5t-9.5 -22.5q0 -14 9 -23l352 -352q9 -9 23 -9t23 9l351 351q10 12 10 24zM1920 384q0 -159 -112.5 -271.5t-271.5 -112.5h-1088 q-185 0 -316.5 131.5t-131.5 316.5q0 130 70 240t188 165q-2 30 -2 43q0 212 150 362t362 150q156 0 285.5 -87t188.5 -231q71 62 166 62q106 0 181 -75t75 -181q0 -76 -41 -138q130 -31 213.5 -135.5t83.5 -238.5z" />
-<glyph unicode="&#xf0ee;" horiz-adv-x="1920" d="M1280 672q0 14 -9 23l-352 352q-9 9 -23 9t-23 -9l-351 -351q-10 -12 -10 -24q0 -14 9 -23t23 -9h224v-352q0 -13 9.5 -22.5t22.5 -9.5h192q13 0 22.5 9.5t9.5 22.5v352h224q13 0 22.5 9.5t9.5 22.5zM1920 384q0 -159 -112.5 -271.5t-271.5 -112.5h-1088 q-185 0 -316.5 131.5t-131.5 316.5q0 130 70 240t188 165q-2 30 -2 43q0 212 150 362t362 150q156 0 285.5 -87t188.5 -231q71 62 166 62q106 0 181 -75t75 -181q0 -76 -41 -138q130 -31 213.5 -135.5t83.5 -238.5z" />
-<glyph unicode="&#xf0f0;" horiz-adv-x="1408" d="M384 192q0 -26 -19 -45t-45 -19t-45 19t-19 45t19 45t45 19t45 -19t19 -45zM1408 131q0 -121 -73 -190t-194 -69h-874q-121 0 -194 69t-73 190q0 68 5.5 131t24 138t47.5 132.5t81 103t120 60.5q-22 -52 -22 -120v-203q-58 -20 -93 -70t-35 -111q0 -80 56 -136t136 -56 t136 56t56 136q0 61 -35.5 111t-92.5 70v203q0 62 25 93q132 -104 295 -104t295 104q25 -31 25 -93v-64q-106 0 -181 -75t-75 -181v-89q-32 -29 -32 -71q0 -40 28 -68t68 -28t68 28t28 68q0 42 -32 71v89q0 52 38 90t90 38t90 -38t38 -90v-89q-32 -29 -32 -71q0 -40 28 -68 t68 -28t68 28t28 68q0 42 -32 71v89q0 68 -34.5 127.5t-93.5 93.5q0 10 0.5 42.5t0 48t-2.5 41.5t-7 47t-13 40q68 -15 120 -60.5t81 -103t47.5 -132.5t24 -138t5.5 -131zM1088 1024q0 -159 -112.5 -271.5t-271.5 -112.5t-271.5 112.5t-112.5 271.5t112.5 271.5t271.5 112.5 t271.5 -112.5t112.5 -271.5z" />
-<glyph unicode="&#xf0f1;" horiz-adv-x="1408" d="M1280 832q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1408 832q0 -62 -35.5 -111t-92.5 -70v-395q0 -159 -131.5 -271.5t-316.5 -112.5t-316.5 112.5t-131.5 271.5v132q-164 20 -274 128t-110 252v512q0 26 19 45t45 19q6 0 16 -2q17 30 47 48 t65 18q53 0 90.5 -37.5t37.5 -90.5t-37.5 -90.5t-90.5 -37.5q-33 0 -64 18v-402q0 -106 94 -181t226 -75t226 75t94 181v402q-31 -18 -64 -18q-53 0 -90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5q35 0 65 -18t47 -48q10 2 16 2q26 0 45 -19t19 -45v-512q0 -144 -110 -252 t-274 -128v-132q0 -106 94 -181t226 -75t226 75t94 181v395q-57 21 -92.5 70t-35.5 111q0 80 56 136t136 56t136 -56t56 -136z" />
-<glyph unicode="&#xf0f2;" horiz-adv-x="1792" d="M640 1152h512v128h-512v-128zM288 1152v-1280h-64q-92 0 -158 66t-66 158v832q0 92 66 158t158 66h64zM1408 1152v-1280h-1024v1280h128v160q0 40 28 68t68 28h576q40 0 68 -28t28 -68v-160h128zM1792 928v-832q0 -92 -66 -158t-158 -66h-64v1280h64q92 0 158 -66 t66 -158z" />
-<glyph unicode="&#xf0f3;" horiz-adv-x="1664" d="M848 -160q0 16 -16 16q-59 0 -101.5 42.5t-42.5 101.5q0 16 -16 16t-16 -16q0 -73 51.5 -124.5t124.5 -51.5q16 0 16 16zM1664 128q0 -52 -38 -90t-90 -38h-448q0 -106 -75 -181t-181 -75t-181 75t-75 181h-448q-52 0 -90 38t-38 90q190 161 287 397.5t97 498.5 q0 165 96 262t264 117q-8 18 -8 37q0 40 28 68t68 28t68 -28t28 -68q0 -19 -8 -37q168 -20 264 -117t96 -262q0 -262 97 -498.5t287 -397.5z" />
-<glyph unicode="&#xf0f4;" horiz-adv-x="1920" d="M1664 896q0 80 -56 136t-136 56h-64v-384h64q80 0 136 56t56 136zM0 128h1792q0 -106 -75 -181t-181 -75h-1280q-106 0 -181 75t-75 181zM1856 896q0 -159 -112.5 -271.5t-271.5 -112.5h-64v-32q0 -92 -66 -158t-158 -66h-704q-92 0 -158 66t-66 158v736q0 26 19 45 t45 19h1152q159 0 271.5 -112.5t112.5 -271.5z" />
-<glyph unicode="&#xf0f5;" horiz-adv-x="1408" d="M640 1472v-640q0 -61 -35.5 -111t-92.5 -70v-779q0 -52 -38 -90t-90 -38h-128q-52 0 -90 38t-38 90v779q-57 20 -92.5 70t-35.5 111v640q0 26 19 45t45 19t45 -19t19 -45v-416q0 -26 19 -45t45 -19t45 19t19 45v416q0 26 19 45t45 19t45 -19t19 -45v-416q0 -26 19 -45 t45 -19t45 19t19 45v416q0 26 19 45t45 19t45 -19t19 -45zM1408 1472v-1600q0 -52 -38 -90t-90 -38h-128q-52 0 -90 38t-38 90v512h-224q-13 0 -22.5 9.5t-9.5 22.5v800q0 132 94 226t226 94h256q26 0 45 -19t19 -45z" />
-<glyph unicode="&#xf0f6;" horiz-adv-x="1280" d="M1024 352v-64q0 -14 -9 -23t-23 -9h-704q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h704q14 0 23 -9t9 -23zM1024 608v-64q0 -14 -9 -23t-23 -9h-704q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h704q14 0 23 -9t9 -23zM128 0h1024v768h-416q-40 0 -68 28t-28 68v416h-512v-1280z M768 896h376q-10 29 -22 41l-313 313q-12 12 -41 22v-376zM1280 864v-896q0 -40 -28 -68t-68 -28h-1088q-40 0 -68 28t-28 68v1344q0 40 28 68t68 28h640q40 0 88 -20t76 -48l312 -312q28 -28 48 -76t20 -88z" />
-<glyph unicode="&#xf0f7;" horiz-adv-x="1408" d="M384 224v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM384 480v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M640 480v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM384 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M1152 224v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM896 480v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M640 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM384 992v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M1152 480v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM896 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M640 992v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM384 1248v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M1152 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM896 992v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M640 1248v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM1152 992v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M896 1248v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM1152 1248v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M896 -128h384v1536h-1152v-1536h384v224q0 13 9.5 22.5t22.5 9.5h320q13 0 22.5 -9.5t9.5 -22.5v-224zM1408 1472v-1664q0 -26 -19 -45t-45 -19h-1280q-26 0 -45 19t-19 45v1664q0 26 19 45t45 19h1280q26 0 45 -19t19 -45z" />
-<glyph unicode="&#xf0f8;" horiz-adv-x="1408" d="M384 224v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM384 480v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M640 480v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM384 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M1152 224v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM896 480v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M640 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM1152 480v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M896 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM1152 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M896 -128h384v1152h-256v-32q0 -40 -28 -68t-68 -28h-448q-40 0 -68 28t-28 68v32h-256v-1152h384v224q0 13 9.5 22.5t22.5 9.5h320q13 0 22.5 -9.5t9.5 -22.5v-224zM896 1056v320q0 13 -9.5 22.5t-22.5 9.5h-64q-13 0 -22.5 -9.5t-9.5 -22.5v-96h-128v96q0 13 -9.5 22.5 t-22.5 9.5h-64q-13 0 -22.5 -9.5t-9.5 -22.5v-320q0 -13 9.5 -22.5t22.5 -9.5h64q13 0 22.5 9.5t9.5 22.5v96h128v-96q0 -13 9.5 -22.5t22.5 -9.5h64q13 0 22.5 9.5t9.5 22.5zM1408 1088v-1280q0 -26 -19 -45t-45 -19h-1280q-26 0 -45 19t-19 45v1280q0 26 19 45t45 19h320 v288q0 40 28 68t68 28h448q40 0 68 -28t28 -68v-288h320q26 0 45 -19t19 -45z" />
-<glyph unicode="&#xf0f9;" horiz-adv-x="1920" d="M640 128q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM256 640h384v256h-158q-14 -2 -22 -9l-195 -195q-7 -12 -9 -22v-30zM1536 128q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5 t90.5 37.5t37.5 90.5zM1664 800v192q0 14 -9 23t-23 9h-224v224q0 14 -9 23t-23 9h-192q-14 0 -23 -9t-9 -23v-224h-224q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h224v-224q0 -14 9 -23t23 -9h192q14 0 23 9t9 23v224h224q14 0 23 9t9 23zM1920 1344v-1152 q0 -26 -19 -45t-45 -19h-192q0 -106 -75 -181t-181 -75t-181 75t-75 181h-384q0 -106 -75 -181t-181 -75t-181 75t-75 181h-128q-26 0 -45 19t-19 45t19 45t45 19v416q0 26 13 58t32 51l198 198q19 19 51 32t58 13h160v320q0 26 19 45t45 19h1152q26 0 45 -19t19 -45z" />
-<glyph unicode="&#xf0fa;" horiz-adv-x="1792" d="M1280 416v192q0 14 -9 23t-23 9h-224v224q0 14 -9 23t-23 9h-192q-14 0 -23 -9t-9 -23v-224h-224q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h224v-224q0 -14 9 -23t23 -9h192q14 0 23 9t9 23v224h224q14 0 23 9t9 23zM640 1152h512v128h-512v-128zM256 1152v-1280h-32 q-92 0 -158 66t-66 158v832q0 92 66 158t158 66h32zM1440 1152v-1280h-1088v1280h160v160q0 40 28 68t68 28h576q40 0 68 -28t28 -68v-160h160zM1792 928v-832q0 -92 -66 -158t-158 -66h-32v1280h32q92 0 158 -66t66 -158z" />
-<glyph unicode="&#xf0fb;" horiz-adv-x="1920" d="M1920 576q-1 -32 -288 -96l-352 -32l-224 -64h-64l-293 -352h69q26 0 45 -4.5t19 -11.5t-19 -11.5t-45 -4.5h-96h-160h-64v32h64v416h-160l-192 -224h-96l-32 32v192h32v32h128v8l-192 24v128l192 24v8h-128v32h-32v192l32 32h96l192 -224h160v416h-64v32h64h160h96 q26 0 45 -4.5t19 -11.5t-19 -11.5t-45 -4.5h-69l293 -352h64l224 -64l352 -32q261 -58 287 -93z" />
-<glyph unicode="&#xf0fc;" horiz-adv-x="1664" d="M640 640v384h-256v-256q0 -53 37.5 -90.5t90.5 -37.5h128zM1664 192v-192h-1152v192l128 192h-128q-159 0 -271.5 112.5t-112.5 271.5v320l-64 64l32 128h480l32 128h960l32 -192l-64 -32v-800z" />
-<glyph unicode="&#xf0fd;" d="M1280 192v896q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-320h-512v320q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-896q0 -26 19 -45t45 -19h128q26 0 45 19t19 45v320h512v-320q0 -26 19 -45t45 -19h128q26 0 45 19t19 45zM1536 1120v-960 q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
-<glyph unicode="&#xf0fe;" d="M1280 576v128q0 26 -19 45t-45 19h-320v320q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-320h-320q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h320v-320q0 -26 19 -45t45 -19h128q26 0 45 19t19 45v320h320q26 0 45 19t19 45zM1536 1120v-960 q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
-<glyph unicode="&#xf100;" horiz-adv-x="1024" d="M627 160q0 -13 -10 -23l-50 -50q-10 -10 -23 -10t-23 10l-466 466q-10 10 -10 23t10 23l466 466q10 10 23 10t23 -10l50 -50q10 -10 10 -23t-10 -23l-393 -393l393 -393q10 -10 10 -23zM1011 160q0 -13 -10 -23l-50 -50q-10 -10 -23 -10t-23 10l-466 466q-10 10 -10 23 t10 23l466 466q10 10 23 10t23 -10l50 -50q10 -10 10 -23t-10 -23l-393 -393l393 -393q10 -10 10 -23z" />
-<glyph unicode="&#xf101;" horiz-adv-x="1024" d="M595 576q0 -13 -10 -23l-466 -466q-10 -10 -23 -10t-23 10l-50 50q-10 10 -10 23t10 23l393 393l-393 393q-10 10 -10 23t10 23l50 50q10 10 23 10t23 -10l466 -466q10 -10 10 -23zM979 576q0 -13 -10 -23l-466 -466q-10 -10 -23 -10t-23 10l-50 50q-10 10 -10 23t10 23 l393 393l-393 393q-10 10 -10 23t10 23l50 50q10 10 23 10t23 -10l466 -466q10 -10 10 -23z" />
-<glyph unicode="&#xf102;" horiz-adv-x="1152" d="M1075 224q0 -13 -10 -23l-50 -50q-10 -10 -23 -10t-23 10l-393 393l-393 -393q-10 -10 -23 -10t-23 10l-50 50q-10 10 -10 23t10 23l466 466q10 10 23 10t23 -10l466 -466q10 -10 10 -23zM1075 608q0 -13 -10 -23l-50 -50q-10 -10 -23 -10t-23 10l-393 393l-393 -393 q-10 -10 -23 -10t-23 10l-50 50q-10 10 -10 23t10 23l466 466q10 10 23 10t23 -10l466 -466q10 -10 10 -23z" />
-<glyph unicode="&#xf103;" horiz-adv-x="1152" d="M1075 672q0 -13 -10 -23l-466 -466q-10 -10 -23 -10t-23 10l-466 466q-10 10 -10 23t10 23l50 50q10 10 23 10t23 -10l393 -393l393 393q10 10 23 10t23 -10l50 -50q10 -10 10 -23zM1075 1056q0 -13 -10 -23l-466 -466q-10 -10 -23 -10t-23 10l-466 466q-10 10 -10 23 t10 23l50 50q10 10 23 10t23 -10l393 -393l393 393q10 10 23 10t23 -10l50 -50q10 -10 10 -23z" />
-<glyph unicode="&#xf104;" horiz-adv-x="640" d="M627 992q0 -13 -10 -23l-393 -393l393 -393q10 -10 10 -23t-10 -23l-50 -50q-10 -10 -23 -10t-23 10l-466 466q-10 10 -10 23t10 23l466 466q10 10 23 10t23 -10l50 -50q10 -10 10 -23z" />
-<glyph unicode="&#xf105;" horiz-adv-x="640" d="M595 576q0 -13 -10 -23l-466 -466q-10 -10 -23 -10t-23 10l-50 50q-10 10 -10 23t10 23l393 393l-393 393q-10 10 -10 23t10 23l50 50q10 10 23 10t23 -10l466 -466q10 -10 10 -23z" />
-<glyph unicode="&#xf106;" horiz-adv-x="1152" d="M1075 352q0 -13 -10 -23l-50 -50q-10 -10 -23 -10t-23 10l-393 393l-393 -393q-10 -10 -23 -10t-23 10l-50 50q-10 10 -10 23t10 23l466 466q10 10 23 10t23 -10l466 -466q10 -10 10 -23z" />
-<glyph unicode="&#xf107;" horiz-adv-x="1152" d="M1075 800q0 -13 -10 -23l-466 -466q-10 -10 -23 -10t-23 10l-466 466q-10 10 -10 23t10 23l50 50q10 10 23 10t23 -10l393 -393l393 393q10 10 23 10t23 -10l50 -50q10 -10 10 -23z" />
-<glyph unicode="&#xf108;" horiz-adv-x="1920" d="M1792 544v832q0 13 -9.5 22.5t-22.5 9.5h-1600q-13 0 -22.5 -9.5t-9.5 -22.5v-832q0 -13 9.5 -22.5t22.5 -9.5h1600q13 0 22.5 9.5t9.5 22.5zM1920 1376v-1088q0 -66 -47 -113t-113 -47h-544q0 -37 16 -77.5t32 -71t16 -43.5q0 -26 -19 -45t-45 -19h-512q-26 0 -45 19 t-19 45q0 14 16 44t32 70t16 78h-544q-66 0 -113 47t-47 113v1088q0 66 47 113t113 47h1600q66 0 113 -47t47 -113z" />
-<glyph unicode="&#xf109;" horiz-adv-x="1920" d="M416 256q-66 0 -113 47t-47 113v704q0 66 47 113t113 47h1088q66 0 113 -47t47 -113v-704q0 -66 -47 -113t-113 -47h-1088zM384 1120v-704q0 -13 9.5 -22.5t22.5 -9.5h1088q13 0 22.5 9.5t9.5 22.5v704q0 13 -9.5 22.5t-22.5 9.5h-1088q-13 0 -22.5 -9.5t-9.5 -22.5z M1760 192h160v-96q0 -40 -47 -68t-113 -28h-1600q-66 0 -113 28t-47 68v96h160h1600zM1040 96q16 0 16 16t-16 16h-160q-16 0 -16 -16t16 -16h160z" />
-<glyph unicode="&#xf10a;" horiz-adv-x="1152" d="M640 128q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1024 288v960q0 13 -9.5 22.5t-22.5 9.5h-832q-13 0 -22.5 -9.5t-9.5 -22.5v-960q0 -13 9.5 -22.5t22.5 -9.5h832q13 0 22.5 9.5t9.5 22.5zM1152 1248v-1088q0 -66 -47 -113t-113 -47h-832 q-66 0 -113 47t-47 113v1088q0 66 47 113t113 47h832q66 0 113 -47t47 -113z" />
-<glyph unicode="&#xf10b;" horiz-adv-x="768" d="M464 128q0 33 -23.5 56.5t-56.5 23.5t-56.5 -23.5t-23.5 -56.5t23.5 -56.5t56.5 -23.5t56.5 23.5t23.5 56.5zM672 288v704q0 13 -9.5 22.5t-22.5 9.5h-512q-13 0 -22.5 -9.5t-9.5 -22.5v-704q0 -13 9.5 -22.5t22.5 -9.5h512q13 0 22.5 9.5t9.5 22.5zM480 1136 q0 16 -16 16h-160q-16 0 -16 -16t16 -16h160q16 0 16 16zM768 1152v-1024q0 -52 -38 -90t-90 -38h-512q-52 0 -90 38t-38 90v1024q0 52 38 90t90 38h512q52 0 90 -38t38 -90z" />
-<glyph unicode="&#xf10c;" d="M768 1184q-148 0 -273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73t273 73t198 198t73 273t-73 273t-198 198t-273 73zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103 t279.5 -279.5t103 -385.5z" />
-<glyph unicode="&#xf10d;" horiz-adv-x="1664" d="M768 576v-384q0 -80 -56 -136t-136 -56h-384q-80 0 -136 56t-56 136v704q0 104 40.5 198.5t109.5 163.5t163.5 109.5t198.5 40.5h64q26 0 45 -19t19 -45v-128q0 -26 -19 -45t-45 -19h-64q-106 0 -181 -75t-75 -181v-32q0 -40 28 -68t68 -28h224q80 0 136 -56t56 -136z M1664 576v-384q0 -80 -56 -136t-136 -56h-384q-80 0 -136 56t-56 136v704q0 104 40.5 198.5t109.5 163.5t163.5 109.5t198.5 40.5h64q26 0 45 -19t19 -45v-128q0 -26 -19 -45t-45 -19h-64q-106 0 -181 -75t-75 -181v-32q0 -40 28 -68t68 -28h224q80 0 136 -56t56 -136z" />
-<glyph unicode="&#xf10e;" horiz-adv-x="1664" d="M768 1216v-704q0 -104 -40.5 -198.5t-109.5 -163.5t-163.5 -109.5t-198.5 -40.5h-64q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h64q106 0 181 75t75 181v32q0 40 -28 68t-68 28h-224q-80 0 -136 56t-56 136v384q0 80 56 136t136 56h384q80 0 136 -56t56 -136zM1664 1216 v-704q0 -104 -40.5 -198.5t-109.5 -163.5t-163.5 -109.5t-198.5 -40.5h-64q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h64q106 0 181 75t75 181v32q0 40 -28 68t-68 28h-224q-80 0 -136 56t-56 136v384q0 80 56 136t136 56h384q80 0 136 -56t56 -136z" />
-<glyph unicode="&#xf110;" horiz-adv-x="1568" d="M496 192q0 -60 -42.5 -102t-101.5 -42q-60 0 -102 42t-42 102t42 102t102 42q59 0 101.5 -42t42.5 -102zM928 0q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM320 640q0 -66 -47 -113t-113 -47t-113 47t-47 113 t47 113t113 47t113 -47t47 -113zM1360 192q0 -46 -33 -79t-79 -33t-79 33t-33 79t33 79t79 33t79 -33t33 -79zM528 1088q0 -73 -51.5 -124.5t-124.5 -51.5t-124.5 51.5t-51.5 124.5t51.5 124.5t124.5 51.5t124.5 -51.5t51.5 -124.5zM992 1280q0 -80 -56 -136t-136 -56 t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM1536 640q0 -40 -28 -68t-68 -28t-68 28t-28 68t28 68t68 28t68 -28t28 -68zM1328 1088q0 -33 -23.5 -56.5t-56.5 -23.5t-56.5 23.5t-23.5 56.5t23.5 56.5t56.5 23.5t56.5 -23.5t23.5 -56.5z" />
-<glyph unicode="&#xf111;" d="M1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
-<glyph unicode="&#xf112;" horiz-adv-x="1792" d="M1792 416q0 -166 -127 -451q-3 -7 -10.5 -24t-13.5 -30t-13 -22q-12 -17 -28 -17q-15 0 -23.5 10t-8.5 25q0 9 2.5 26.5t2.5 23.5q5 68 5 123q0 101 -17.5 181t-48.5 138.5t-80 101t-105.5 69.5t-133 42.5t-154 21.5t-175.5 6h-224v-256q0 -26 -19 -45t-45 -19t-45 19 l-512 512q-19 19 -19 45t19 45l512 512q19 19 45 19t45 -19t19 -45v-256h224q713 0 875 -403q53 -134 53 -333z" />
-<glyph unicode="&#xf113;" horiz-adv-x="1664" d="M640 320q0 -40 -12.5 -82t-43 -76t-72.5 -34t-72.5 34t-43 76t-12.5 82t12.5 82t43 76t72.5 34t72.5 -34t43 -76t12.5 -82zM1280 320q0 -40 -12.5 -82t-43 -76t-72.5 -34t-72.5 34t-43 76t-12.5 82t12.5 82t43 76t72.5 34t72.5 -34t43 -76t12.5 -82zM1440 320 q0 120 -69 204t-187 84q-41 0 -195 -21q-71 -11 -157 -11t-157 11q-152 21 -195 21q-118 0 -187 -84t-69 -204q0 -88 32 -153.5t81 -103t122 -60t140 -29.5t149 -7h168q82 0 149 7t140 29.5t122 60t81 103t32 153.5zM1664 496q0 -207 -61 -331q-38 -77 -105.5 -133t-141 -86 t-170 -47.5t-171.5 -22t-167 -4.5q-78 0 -142 3t-147.5 12.5t-152.5 30t-137 51.5t-121 81t-86 115q-62 123 -62 331q0 237 136 396q-27 82 -27 170q0 116 51 218q108 0 190 -39.5t189 -123.5q147 35 309 35q148 0 280 -32q105 82 187 121t189 39q51 -102 51 -218 q0 -87 -27 -168q136 -160 136 -398z" />
-<glyph unicode="&#xf114;" horiz-adv-x="1664" d="M1536 224v704q0 40 -28 68t-68 28h-704q-40 0 -68 28t-28 68v64q0 40 -28 68t-68 28h-320q-40 0 -68 -28t-28 -68v-960q0 -40 28 -68t68 -28h1216q40 0 68 28t28 68zM1664 928v-704q0 -92 -66 -158t-158 -66h-1216q-92 0 -158 66t-66 158v960q0 92 66 158t158 66h320 q92 0 158 -66t66 -158v-32h672q92 0 158 -66t66 -158z" />
-<glyph unicode="&#xf115;" horiz-adv-x="1920" d="M1781 605q0 35 -53 35h-1088q-40 0 -85.5 -21.5t-71.5 -52.5l-294 -363q-18 -24 -18 -40q0 -35 53 -35h1088q40 0 86 22t71 53l294 363q18 22 18 39zM640 768h768v160q0 40 -28 68t-68 28h-576q-40 0 -68 28t-28 68v64q0 40 -28 68t-68 28h-320q-40 0 -68 -28t-28 -68 v-853l256 315q44 53 116 87.5t140 34.5zM1909 605q0 -62 -46 -120l-295 -363q-43 -53 -116 -87.5t-140 -34.5h-1088q-92 0 -158 66t-66 158v960q0 92 66 158t158 66h320q92 0 158 -66t66 -158v-32h544q92 0 158 -66t66 -158v-160h192q54 0 99 -24.5t67 -70.5q15 -32 15 -68z " />
-<glyph unicode="&#xf116;" horiz-adv-x="1152" d="M896 608v-64q0 -14 -9 -23t-23 -9h-224v-224q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v224h-224q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h224v224q0 14 9 23t23 9h64q14 0 23 -9t9 -23v-224h224q14 0 23 -9t9 -23zM1024 224v704q0 40 -28 68t-68 28h-704q-40 0 -68 -28 t-28 -68v-704q0 -40 28 -68t68 -28h704q40 0 68 28t28 68zM1152 928v-704q0 -92 -65.5 -158t-158.5 -66h-704q-93 0 -158.5 66t-65.5 158v704q0 93 65.5 158.5t158.5 65.5h704q93 0 158.5 -65.5t65.5 -158.5z" />
-<glyph unicode="&#xf117;" horiz-adv-x="1152" d="M928 1152q93 0 158.5 -65.5t65.5 -158.5v-704q0 -92 -65.5 -158t-158.5 -66h-704q-93 0 -158.5 66t-65.5 158v704q0 93 65.5 158.5t158.5 65.5h704zM1024 224v704q0 40 -28 68t-68 28h-704q-40 0 -68 -28t-28 -68v-704q0 -40 28 -68t68 -28h704q40 0 68 28t28 68z M864 640q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-576q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h576z" />
-<glyph unicode="&#xf118;" d="M1134 461q-37 -121 -138 -195t-228 -74t-228 74t-138 195q-8 25 4 48.5t38 31.5q25 8 48.5 -4t31.5 -38q25 -80 92.5 -129.5t151.5 -49.5t151.5 49.5t92.5 129.5q8 26 32 38t49 4t37 -31.5t4 -48.5zM640 896q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5 t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1152 896q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1408 640q0 130 -51 248.5t-136.5 204t-204 136.5t-248.5 51t-248.5 -51t-204 -136.5t-136.5 -204t-51 -248.5 t51 -248.5t136.5 -204t204 -136.5t248.5 -51t248.5 51t204 136.5t136.5 204t51 248.5zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
-<glyph unicode="&#xf119;" d="M1134 307q8 -25 -4 -48.5t-37 -31.5t-49 4t-32 38q-25 80 -92.5 129.5t-151.5 49.5t-151.5 -49.5t-92.5 -129.5q-8 -26 -31.5 -38t-48.5 -4q-26 8 -38 31.5t-4 48.5q37 121 138 195t228 74t228 -74t138 -195zM640 896q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5 t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1152 896q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1408 640q0 130 -51 248.5t-136.5 204t-204 136.5t-248.5 51t-248.5 -51t-204 -136.5t-136.5 -204 t-51 -248.5t51 -248.5t136.5 -204t204 -136.5t248.5 -51t248.5 51t204 136.5t136.5 204t51 248.5zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
-<glyph unicode="&#xf11a;" d="M1152 448q0 -26 -19 -45t-45 -19h-640q-26 0 -45 19t-19 45t19 45t45 19h640q26 0 45 -19t19 -45zM640 896q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1152 896q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5 t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1408 640q0 130 -51 248.5t-136.5 204t-204 136.5t-248.5 51t-248.5 -51t-204 -136.5t-136.5 -204t-51 -248.5t51 -248.5t136.5 -204t204 -136.5t248.5 -51t248.5 51t204 136.5t136.5 204t51 248.5zM1536 640 q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
-<glyph unicode="&#xf11b;" horiz-adv-x="1920" d="M832 448v128q0 14 -9 23t-23 9h-192v192q0 14 -9 23t-23 9h-128q-14 0 -23 -9t-9 -23v-192h-192q-14 0 -23 -9t-9 -23v-128q0 -14 9 -23t23 -9h192v-192q0 -14 9 -23t23 -9h128q14 0 23 9t9 23v192h192q14 0 23 9t9 23zM1408 384q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5 t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1664 640q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1920 512q0 -212 -150 -362t-362 -150q-192 0 -338 128h-220q-146 -128 -338 -128q-212 0 -362 150 t-150 362t150 362t362 150h896q212 0 362 -150t150 -362z" />
-<glyph unicode="&#xf11c;" horiz-adv-x="1920" d="M384 368v-96q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16h96q16 0 16 -16zM512 624v-96q0 -16 -16 -16h-224q-16 0 -16 16v96q0 16 16 16h224q16 0 16 -16zM384 880v-96q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16h96q16 0 16 -16zM1408 368v-96q0 -16 -16 -16 h-864q-16 0 -16 16v96q0 16 16 16h864q16 0 16 -16zM768 624v-96q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16h96q16 0 16 -16zM640 880v-96q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16h96q16 0 16 -16zM1024 624v-96q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16 h96q16 0 16 -16zM896 880v-96q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16h96q16 0 16 -16zM1280 624v-96q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16h96q16 0 16 -16zM1664 368v-96q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16h96q16 0 16 -16zM1152 880v-96 q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16h96q16 0 16 -16zM1408 880v-96q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16h96q16 0 16 -16zM1664 880v-352q0 -16 -16 -16h-224q-16 0 -16 16v96q0 16 16 16h112v240q0 16 16 16h96q16 0 16 -16zM1792 128v896h-1664v-896 h1664zM1920 1024v-896q0 -53 -37.5 -90.5t-90.5 -37.5h-1664q-53 0 -90.5 37.5t-37.5 90.5v896q0 53 37.5 90.5t90.5 37.5h1664q53 0 90.5 -37.5t37.5 -90.5z" />
-<glyph unicode="&#xf11d;" horiz-adv-x="1792" d="M1664 491v616q-169 -91 -306 -91q-82 0 -145 32q-100 49 -184 76.5t-178 27.5q-173 0 -403 -127v-599q245 113 433 113q55 0 103.5 -7.5t98 -26t77 -31t82.5 -39.5l28 -14q44 -22 101 -22q120 0 293 92zM320 1280q0 -35 -17.5 -64t-46.5 -46v-1266q0 -14 -9 -23t-23 -9 h-64q-14 0 -23 9t-9 23v1266q-29 17 -46.5 46t-17.5 64q0 53 37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1792 1216v-763q0 -39 -35 -57q-10 -5 -17 -9q-218 -116 -369 -116q-88 0 -158 35l-28 14q-64 33 -99 48t-91 29t-114 14q-102 0 -235.5 -44t-228.5 -102 q-15 -9 -33 -9q-16 0 -32 8q-32 19 -32 56v742q0 35 31 55q35 21 78.5 42.5t114 52t152.5 49.5t155 19q112 0 209 -31t209 -86q38 -19 89 -19q122 0 310 112q22 12 31 17q31 16 62 -2q31 -20 31 -55z" />
-<glyph unicode="&#xf11e;" horiz-adv-x="1792" d="M832 536v192q-181 -16 -384 -117v-185q205 96 384 110zM832 954v197q-172 -8 -384 -126v-189q215 111 384 118zM1664 491v184q-235 -116 -384 -71v224q-20 6 -39 15q-5 3 -33 17t-34.5 17t-31.5 15t-34.5 15.5t-32.5 13t-36 12.5t-35 8.5t-39.5 7.5t-39.5 4t-44 2 q-23 0 -49 -3v-222h19q102 0 192.5 -29t197.5 -82q19 -9 39 -15v-188q42 -17 91 -17q120 0 293 92zM1664 918v189q-169 -91 -306 -91q-45 0 -78 8v-196q148 -42 384 90zM320 1280q0 -35 -17.5 -64t-46.5 -46v-1266q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v1266 q-29 17 -46.5 46t-17.5 64q0 53 37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1792 1216v-763q0 -39 -35 -57q-10 -5 -17 -9q-218 -116 -369 -116q-88 0 -158 35l-28 14q-64 33 -99 48t-91 29t-114 14q-102 0 -235.5 -44t-228.5 -102q-15 -9 -33 -9q-16 0 -32 8 q-32 19 -32 56v742q0 35 31 55q35 21 78.5 42.5t114 52t152.5 49.5t155 19q112 0 209 -31t209 -86q38 -19 89 -19q122 0 310 112q22 12 31 17q31 16 62 -2q31 -20 31 -55z" />
-<glyph unicode="&#xf120;" horiz-adv-x="1664" d="M585 553l-466 -466q-10 -10 -23 -10t-23 10l-50 50q-10 10 -10 23t10 23l393 393l-393 393q-10 10 -10 23t10 23l50 50q10 10 23 10t23 -10l466 -466q10 -10 10 -23t-10 -23zM1664 96v-64q0 -14 -9 -23t-23 -9h-960q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h960q14 0 23 -9 t9 -23z" />
-<glyph unicode="&#xf121;" horiz-adv-x="1920" d="M617 137l-50 -50q-10 -10 -23 -10t-23 10l-466 466q-10 10 -10 23t10 23l466 466q10 10 23 10t23 -10l50 -50q10 -10 10 -23t-10 -23l-393 -393l393 -393q10 -10 10 -23t-10 -23zM1208 1204l-373 -1291q-4 -13 -15.5 -19.5t-23.5 -2.5l-62 17q-13 4 -19.5 15.5t-2.5 24.5 l373 1291q4 13 15.5 19.5t23.5 2.5l62 -17q13 -4 19.5 -15.5t2.5 -24.5zM1865 553l-466 -466q-10 -10 -23 -10t-23 10l-50 50q-10 10 -10 23t10 23l393 393l-393 393q-10 10 -10 23t10 23l50 50q10 10 23 10t23 -10l466 -466q10 -10 10 -23t-10 -23z" />
-<glyph unicode="&#xf122;" horiz-adv-x="1792" d="M640 454v-70q0 -42 -39 -59q-13 -5 -25 -5q-27 0 -45 19l-512 512q-19 19 -19 45t19 45l512 512q29 31 70 14q39 -17 39 -59v-69l-397 -398q-19 -19 -19 -45t19 -45zM1792 416q0 -58 -17 -133.5t-38.5 -138t-48 -125t-40.5 -90.5l-20 -40q-8 -17 -28 -17q-6 0 -9 1 q-25 8 -23 34q43 400 -106 565q-64 71 -170.5 110.5t-267.5 52.5v-251q0 -42 -39 -59q-13 -5 -25 -5q-27 0 -45 19l-512 512q-19 19 -19 45t19 45l512 512q29 31 70 14q39 -17 39 -59v-262q411 -28 599 -221q169 -173 169 -509z" />
-<glyph unicode="&#xf123;" horiz-adv-x="1664" d="M1186 579l257 250l-356 52l-66 10l-30 60l-159 322v-963l59 -31l318 -168l-60 355l-12 66zM1638 841l-363 -354l86 -500q5 -33 -6 -51.5t-34 -18.5q-17 0 -40 12l-449 236l-449 -236q-23 -12 -40 -12q-23 0 -34 18.5t-6 51.5l86 500l-364 354q-32 32 -23 59.5t54 34.5 l502 73l225 455q20 41 49 41q28 0 49 -41l225 -455l502 -73q45 -7 54 -34.5t-24 -59.5z" />
-<glyph unicode="&#xf124;" horiz-adv-x="1408" d="M1401 1187l-640 -1280q-17 -35 -57 -35q-5 0 -15 2q-22 5 -35.5 22.5t-13.5 39.5v576h-576q-22 0 -39.5 13.5t-22.5 35.5t4 42t29 30l1280 640q13 7 29 7q27 0 45 -19q15 -14 18.5 -34.5t-6.5 -39.5z" />
-<glyph unicode="&#xf125;" horiz-adv-x="1664" d="M557 256h595v595zM512 301l595 595h-595v-595zM1664 224v-192q0 -14 -9 -23t-23 -9h-224v-224q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v224h-864q-14 0 -23 9t-9 23v864h-224q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h224v224q0 14 9 23t23 9h192q14 0 23 -9t9 -23 v-224h851l246 247q10 9 23 9t23 -9q9 -10 9 -23t-9 -23l-247 -246v-851h224q14 0 23 -9t9 -23z" />
-<glyph unicode="&#xf126;" horiz-adv-x="1024" d="M288 64q0 40 -28 68t-68 28t-68 -28t-28 -68t28 -68t68 -28t68 28t28 68zM288 1216q0 40 -28 68t-68 28t-68 -28t-28 -68t28 -68t68 -28t68 28t28 68zM928 1088q0 40 -28 68t-68 28t-68 -28t-28 -68t28 -68t68 -28t68 28t28 68zM1024 1088q0 -52 -26 -96.5t-70 -69.5 q-2 -287 -226 -414q-68 -38 -203 -81q-128 -40 -169.5 -71t-41.5 -100v-26q44 -25 70 -69.5t26 -96.5q0 -80 -56 -136t-136 -56t-136 56t-56 136q0 52 26 96.5t70 69.5v820q-44 25 -70 69.5t-26 96.5q0 80 56 136t136 56t136 -56t56 -136q0 -52 -26 -96.5t-70 -69.5v-497 q54 26 154 57q55 17 87.5 29.5t70.5 31t59 39.5t40.5 51t28 69.5t8.5 91.5q-44 25 -70 69.5t-26 96.5q0 80 56 136t136 56t136 -56t56 -136z" />
-<glyph unicode="&#xf127;" horiz-adv-x="1664" d="M439 265l-256 -256q-10 -9 -23 -9q-12 0 -23 9q-9 10 -9 23t9 23l256 256q10 9 23 9t23 -9q9 -10 9 -23t-9 -23zM608 224v-320q0 -14 -9 -23t-23 -9t-23 9t-9 23v320q0 14 9 23t23 9t23 -9t9 -23zM384 448q0 -14 -9 -23t-23 -9h-320q-14 0 -23 9t-9 23t9 23t23 9h320 q14 0 23 -9t9 -23zM1648 320q0 -120 -85 -203l-147 -146q-83 -83 -203 -83q-121 0 -204 85l-334 335q-21 21 -42 56l239 18l273 -274q27 -27 68 -27.5t68 26.5l147 146q28 28 28 67q0 40 -28 68l-274 275l18 239q35 -21 56 -42l336 -336q84 -86 84 -204zM1031 1044l-239 -18 l-273 274q-28 28 -68 28q-39 0 -68 -27l-147 -146q-28 -28 -28 -67q0 -40 28 -68l274 -274l-18 -240q-35 21 -56 42l-336 336q-84 86 -84 204q0 120 85 203l147 146q83 83 203 83q121 0 204 -85l334 -335q21 -21 42 -56zM1664 960q0 -14 -9 -23t-23 -9h-320q-14 0 -23 9 t-9 23t9 23t23 9h320q14 0 23 -9t9 -23zM1120 1504v-320q0 -14 -9 -23t-23 -9t-23 9t-9 23v320q0 14 9 23t23 9t23 -9t9 -23zM1527 1353l-256 -256q-11 -9 -23 -9t-23 9q-9 10 -9 23t9 23l256 256q10 9 23 9t23 -9q9 -10 9 -23t-9 -23z" />
-<glyph unicode="&#xf128;" horiz-adv-x="1024" d="M704 280v-240q0 -16 -12 -28t-28 -12h-240q-16 0 -28 12t-12 28v240q0 16 12 28t28 12h240q16 0 28 -12t12 -28zM1020 880q0 -54 -15.5 -101t-35 -76.5t-55 -59.5t-57.5 -43.5t-61 -35.5q-41 -23 -68.5 -65t-27.5 -67q0 -17 -12 -32.5t-28 -15.5h-240q-15 0 -25.5 18.5 t-10.5 37.5v45q0 83 65 156.5t143 108.5q59 27 84 56t25 76q0 42 -46.5 74t-107.5 32q-65 0 -108 -29q-35 -25 -107 -115q-13 -16 -31 -16q-12 0 -25 8l-164 125q-13 10 -15.5 25t5.5 28q160 266 464 266q80 0 161 -31t146 -83t106 -127.5t41 -158.5z" />
-<glyph unicode="&#xf129;" horiz-adv-x="640" d="M640 192v-128q0 -26 -19 -45t-45 -19h-512q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h64v384h-64q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h384q26 0 45 -19t19 -45v-576h64q26 0 45 -19t19 -45zM512 1344v-192q0 -26 -19 -45t-45 -19h-256q-26 0 -45 19t-19 45v192 q0 26 19 45t45 19h256q26 0 45 -19t19 -45z" />
-<glyph unicode="&#xf12a;" horiz-adv-x="640" d="M512 288v-224q0 -26 -19 -45t-45 -19h-256q-26 0 -45 19t-19 45v224q0 26 19 45t45 19h256q26 0 45 -19t19 -45zM542 1344l-28 -768q-1 -26 -20.5 -45t-45.5 -19h-256q-26 0 -45.5 19t-20.5 45l-28 768q-1 26 17.5 45t44.5 19h320q26 0 44.5 -19t17.5 -45z" />
-<glyph unicode="&#xf12b;" d="M897 167v-167h-248l-159 252l-24 42q-8 9 -11 21h-3l-9 -21q-10 -20 -25 -44l-155 -250h-258v167h128l197 291l-185 272h-137v168h276l139 -228q2 -4 23 -42q8 -9 11 -21h3q3 9 11 21l25 42l140 228h257v-168h-125l-184 -267l204 -296h109zM1534 846v-206h-514l-3 27 q-4 28 -4 46q0 64 26 117t65 86.5t84 65t84 54.5t65 54t26 64q0 38 -29.5 62.5t-70.5 24.5q-51 0 -97 -39q-14 -11 -36 -38l-105 92q26 37 63 66q83 65 188 65q110 0 178 -59.5t68 -158.5q0 -56 -24.5 -103t-62 -76.5t-81.5 -58.5t-82 -50.5t-65.5 -51.5t-30.5 -63h232v80 h126z" />
-<glyph unicode="&#xf12c;" d="M897 167v-167h-248l-159 252l-24 42q-8 9 -11 21h-3l-9 -21q-10 -20 -25 -44l-155 -250h-258v167h128l197 291l-185 272h-137v168h276l139 -228q2 -4 23 -42q8 -9 11 -21h3q3 9 11 21l25 42l140 228h257v-168h-125l-184 -267l204 -296h109zM1536 -50v-206h-514l-4 27 q-3 45 -3 46q0 64 26 117t65 86.5t84 65t84 54.5t65 54t26 64q0 38 -29.5 62.5t-70.5 24.5q-51 0 -97 -39q-14 -11 -36 -38l-105 92q26 37 63 66q80 65 188 65q110 0 178 -59.5t68 -158.5q0 -66 -34.5 -118.5t-84 -86t-99.5 -62.5t-87 -63t-41 -73h232v80h126z" />
-<glyph unicode="&#xf12d;" horiz-adv-x="1920" d="M896 128l336 384h-768l-336 -384h768zM1909 1205q15 -34 9.5 -71.5t-30.5 -65.5l-896 -1024q-38 -44 -96 -44h-768q-38 0 -69.5 20.5t-47.5 54.5q-15 34 -9.5 71.5t30.5 65.5l896 1024q38 44 96 44h768q38 0 69.5 -20.5t47.5 -54.5z" />
-<glyph unicode="&#xf12e;" horiz-adv-x="1664" d="M1664 438q0 -81 -44.5 -135t-123.5 -54q-41 0 -77.5 17.5t-59 38t-56.5 38t-71 17.5q-110 0 -110 -124q0 -39 16 -115t15 -115v-5q-22 0 -33 -1q-34 -3 -97.5 -11.5t-115.5 -13.5t-98 -5q-61 0 -103 26.5t-42 83.5q0 37 17.5 71t38 56.5t38 59t17.5 77.5q0 79 -54 123.5 t-135 44.5q-84 0 -143 -45.5t-59 -127.5q0 -43 15 -83t33.5 -64.5t33.5 -53t15 -50.5q0 -45 -46 -89q-37 -35 -117 -35q-95 0 -245 24q-9 2 -27.5 4t-27.5 4l-13 2q-1 0 -3 1q-2 0 -2 1v1024q2 -1 17.5 -3.5t34 -5t21.5 -3.5q150 -24 245 -24q80 0 117 35q46 44 46 89 q0 22 -15 50.5t-33.5 53t-33.5 64.5t-15 83q0 82 59 127.5t144 45.5q80 0 134 -44.5t54 -123.5q0 -41 -17.5 -77.5t-38 -59t-38 -56.5t-17.5 -71q0 -57 42 -83.5t103 -26.5q64 0 180 15t163 17v-2q-1 -2 -3.5 -17.5t-5 -34t-3.5 -21.5q-24 -150 -24 -245q0 -80 35 -117 q44 -46 89 -46q22 0 50.5 15t53 33.5t64.5 33.5t83 15q82 0 127.5 -59t45.5 -143z" />
-<glyph unicode="&#xf130;" horiz-adv-x="1152" d="M1152 832v-128q0 -221 -147.5 -384.5t-364.5 -187.5v-132h256q26 0 45 -19t19 -45t-19 -45t-45 -19h-640q-26 0 -45 19t-19 45t19 45t45 19h256v132q-217 24 -364.5 187.5t-147.5 384.5v128q0 26 19 45t45 19t45 -19t19 -45v-128q0 -185 131.5 -316.5t316.5 -131.5 t316.5 131.5t131.5 316.5v128q0 26 19 45t45 19t45 -19t19 -45zM896 1216v-512q0 -132 -94 -226t-226 -94t-226 94t-94 226v512q0 132 94 226t226 94t226 -94t94 -226z" />
-<glyph unicode="&#xf131;" horiz-adv-x="1408" d="M271 591l-101 -101q-42 103 -42 214v128q0 26 19 45t45 19t45 -19t19 -45v-128q0 -53 15 -113zM1385 1193l-361 -361v-128q0 -132 -94 -226t-226 -94q-55 0 -109 19l-96 -96q97 -51 205 -51q185 0 316.5 131.5t131.5 316.5v128q0 26 19 45t45 19t45 -19t19 -45v-128 q0 -221 -147.5 -384.5t-364.5 -187.5v-132h256q26 0 45 -19t19 -45t-19 -45t-45 -19h-640q-26 0 -45 19t-19 45t19 45t45 19h256v132q-125 13 -235 81l-254 -254q-10 -10 -23 -10t-23 10l-82 82q-10 10 -10 23t10 23l1234 1234q10 10 23 10t23 -10l82 -82q10 -10 10 -23 t-10 -23zM1005 1325l-621 -621v512q0 132 94 226t226 94q102 0 184.5 -59t116.5 -152z" />
-<glyph unicode="&#xf132;" horiz-adv-x="1280" d="M1088 576v640h-448v-1137q119 63 213 137q235 184 235 360zM1280 1344v-768q0 -86 -33.5 -170.5t-83 -150t-118 -127.5t-126.5 -103t-121 -77.5t-89.5 -49.5t-42.5 -20q-12 -6 -26 -6t-26 6q-16 7 -42.5 20t-89.5 49.5t-121 77.5t-126.5 103t-118 127.5t-83 150 t-33.5 170.5v768q0 26 19 45t45 19h1152q26 0 45 -19t19 -45z" />
-<glyph unicode="&#xf133;" horiz-adv-x="1664" d="M128 -128h1408v1024h-1408v-1024zM512 1088v288q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-288q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM1280 1088v288q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-288q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM1664 1152v-1280 q0 -52 -38 -90t-90 -38h-1408q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h128v96q0 66 47 113t113 47h64q66 0 113 -47t47 -113v-96h384v96q0 66 47 113t113 47h64q66 0 113 -47t47 -113v-96h128q52 0 90 -38t38 -90z" />
-<glyph unicode="&#xf134;" horiz-adv-x="1408" d="M512 1344q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1408 1376v-320q0 -16 -12 -25q-8 -7 -20 -7q-4 0 -7 1l-448 96q-11 2 -18 11t-7 20h-256v-102q111 -23 183.5 -111t72.5 -203v-800q0 -26 -19 -45t-45 -19h-512q-26 0 -45 19t-19 45v800 q0 106 62.5 190.5t161.5 114.5v111h-32q-59 0 -115 -23.5t-91.5 -53t-66 -66.5t-40.5 -53.5t-14 -24.5q-17 -35 -57 -35q-16 0 -29 7q-23 12 -31.5 37t3.5 49q5 10 14.5 26t37.5 53.5t60.5 70t85 67t108.5 52.5q-25 42 -25 86q0 66 47 113t113 47t113 -47t47 -113 q0 -33 -14 -64h302q0 11 7 20t18 11l448 96q3 1 7 1q12 0 20 -7q12 -9 12 -25z" />
-<glyph unicode="&#xf135;" horiz-adv-x="1664" d="M1440 1088q0 40 -28 68t-68 28t-68 -28t-28 -68t28 -68t68 -28t68 28t28 68zM1664 1376q0 -249 -75.5 -430.5t-253.5 -360.5q-81 -80 -195 -176l-20 -379q-2 -16 -16 -26l-384 -224q-7 -4 -16 -4q-12 0 -23 9l-64 64q-13 14 -8 32l85 276l-281 281l-276 -85q-3 -1 -9 -1 q-14 0 -23 9l-64 64q-17 19 -5 39l224 384q10 14 26 16l379 20q96 114 176 195q188 187 358 258t431 71q14 0 24 -9.5t10 -22.5z" />
-<glyph unicode="&#xf136;" horiz-adv-x="1792" d="M1708 881l-188 -881h-304l181 849q4 21 1 43q-4 20 -16 35q-10 14 -28 24q-18 9 -40 9h-197l-205 -960h-303l204 960h-304l-205 -960h-304l272 1280h1139q157 0 245 -118q86 -116 52 -281z" />
-<glyph unicode="&#xf137;" d="M909 141l102 102q19 19 19 45t-19 45l-307 307l307 307q19 19 19 45t-19 45l-102 102q-19 19 -45 19t-45 -19l-454 -454q-19 -19 -19 -45t19 -45l454 -454q19 -19 45 -19t45 19zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5 t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
-<glyph unicode="&#xf138;" d="M717 141l454 454q19 19 19 45t-19 45l-454 454q-19 19 -45 19t-45 -19l-102 -102q-19 -19 -19 -45t19 -45l307 -307l-307 -307q-19 -19 -19 -45t19 -45l102 -102q19 -19 45 -19t45 19zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5 t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
-<glyph unicode="&#xf139;" d="M1165 397l102 102q19 19 19 45t-19 45l-454 454q-19 19 -45 19t-45 -19l-454 -454q-19 -19 -19 -45t19 -45l102 -102q19 -19 45 -19t45 19l307 307l307 -307q19 -19 45 -19t45 19zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5 t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
-<glyph unicode="&#xf13a;" d="M813 237l454 454q19 19 19 45t-19 45l-102 102q-19 19 -45 19t-45 -19l-307 -307l-307 307q-19 19 -45 19t-45 -19l-102 -102q-19 -19 -19 -45t19 -45l454 -454q19 -19 45 -19t45 19zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5 t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
-<glyph unicode="&#xf13b;" horiz-adv-x="1408" d="M1130 939l16 175h-884l47 -534h612l-22 -228l-197 -53l-196 53l-13 140h-175l22 -278l362 -100h4v1l359 99l50 544h-644l-15 181h674zM0 1408h1408l-128 -1438l-578 -162l-574 162z" />
-<glyph unicode="&#xf13c;" horiz-adv-x="1792" d="M275 1408h1505l-266 -1333l-804 -267l-698 267l71 356h297l-29 -147l422 -161l486 161l68 339h-1208l58 297h1209l38 191h-1208z" />
-<glyph unicode="&#xf13d;" horiz-adv-x="1792" d="M960 1280q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1792 352v-352q0 -22 -20 -30q-8 -2 -12 -2q-13 0 -23 9l-93 93q-119 -143 -318.5 -226.5t-429.5 -83.5t-429.5 83.5t-318.5 226.5l-93 -93q-9 -9 -23 -9q-4 0 -12 2q-20 8 -20 30v352 q0 14 9 23t23 9h352q22 0 30 -20q8 -19 -7 -35l-100 -100q67 -91 189.5 -153.5t271.5 -82.5v647h-192q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h192v163q-58 34 -93 92.5t-35 128.5q0 106 75 181t181 75t181 -75t75 -181q0 -70 -35 -128.5t-93 -92.5v-163h192q26 0 45 -19 t19 -45v-128q0 -26 -19 -45t-45 -19h-192v-647q149 20 271.5 82.5t189.5 153.5l-100 100q-15 16 -7 35q8 20 30 20h352q14 0 23 -9t9 -23z" />
-<glyph unicode="&#xf13e;" horiz-adv-x="1152" d="M1056 768q40 0 68 -28t28 -68v-576q0 -40 -28 -68t-68 -28h-960q-40 0 -68 28t-28 68v576q0 40 28 68t68 28h32v320q0 185 131.5 316.5t316.5 131.5t316.5 -131.5t131.5 -316.5q0 -26 -19 -45t-45 -19h-64q-26 0 -45 19t-19 45q0 106 -75 181t-181 75t-181 -75t-75 -181 v-320h736z" />
-<glyph unicode="&#xf140;" d="M1024 640q0 -106 -75 -181t-181 -75t-181 75t-75 181t75 181t181 75t181 -75t75 -181zM1152 640q0 159 -112.5 271.5t-271.5 112.5t-271.5 -112.5t-112.5 -271.5t112.5 -271.5t271.5 -112.5t271.5 112.5t112.5 271.5zM1280 640q0 -212 -150 -362t-362 -150t-362 150 t-150 362t150 362t362 150t362 -150t150 -362zM1408 640q0 130 -51 248.5t-136.5 204t-204 136.5t-248.5 51t-248.5 -51t-204 -136.5t-136.5 -204t-51 -248.5t51 -248.5t136.5 -204t204 -136.5t248.5 -51t248.5 51t204 136.5t136.5 204t51 248.5zM1536 640 q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
-<glyph unicode="&#xf141;" horiz-adv-x="1408" d="M384 800v-192q0 -40 -28 -68t-68 -28h-192q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h192q40 0 68 -28t28 -68zM896 800v-192q0 -40 -28 -68t-68 -28h-192q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h192q40 0 68 -28t28 -68zM1408 800v-192q0 -40 -28 -68t-68 -28h-192 q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h192q40 0 68 -28t28 -68z" />
-<glyph unicode="&#xf142;" horiz-adv-x="384" d="M384 288v-192q0 -40 -28 -68t-68 -28h-192q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h192q40 0 68 -28t28 -68zM384 800v-192q0 -40 -28 -68t-68 -28h-192q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h192q40 0 68 -28t28 -68zM384 1312v-192q0 -40 -28 -68t-68 -28h-192 q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h192q40 0 68 -28t28 -68z" />
-<glyph unicode="&#xf143;" d="M512 256q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM863 162q-13 232 -177 396t-396 177q-14 1 -24 -9t-10 -23v-128q0 -13 8.5 -22t21.5 -10q154 -11 264 -121t121 -264q1 -13 10 -21.5t22 -8.5h128q13 0 23 10 t9 24zM1247 161q-5 154 -56 297.5t-139.5 260t-205 205t-260 139.5t-297.5 56q-14 1 -23 -9q-10 -10 -10 -23v-128q0 -13 9 -22t22 -10q204 -7 378 -111.5t278.5 -278.5t111.5 -378q1 -13 10 -22t22 -9h128q13 0 23 10q11 9 9 23zM1536 1120v-960q0 -119 -84.5 -203.5 t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
-<glyph unicode="&#xf144;" d="M768 1408q209 0 385.5 -103t279.5 -279.5t103 -385.5t-103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103zM1152 585q32 18 32 55t-32 55l-544 320q-31 19 -64 1q-32 -19 -32 -56v-640q0 -37 32 -56 q16 -8 32 -8q17 0 32 9z" />
-<glyph unicode="&#xf145;" horiz-adv-x="1792" d="M1024 1084l316 -316l-572 -572l-316 316zM813 105l618 618q19 19 19 45t-19 45l-362 362q-18 18 -45 18t-45 -18l-618 -618q-19 -19 -19 -45t19 -45l362 -362q18 -18 45 -18t45 18zM1702 742l-907 -908q-37 -37 -90.5 -37t-90.5 37l-126 126q56 56 56 136t-56 136 t-136 56t-136 -56l-125 126q-37 37 -37 90.5t37 90.5l907 906q37 37 90.5 37t90.5 -37l125 -125q-56 -56 -56 -136t56 -136t136 -56t136 56l126 -125q37 -37 37 -90.5t-37 -90.5z" />
-<glyph unicode="&#xf146;" d="M1280 576v128q0 26 -19 45t-45 19h-896q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h896q26 0 45 19t19 45zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5 t84.5 -203.5z" />
-<glyph unicode="&#xf147;" horiz-adv-x="1408" d="M1152 736v-64q0 -14 -9 -23t-23 -9h-832q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h832q14 0 23 -9t9 -23zM1280 288v832q0 66 -47 113t-113 47h-832q-66 0 -113 -47t-47 -113v-832q0 -66 47 -113t113 -47h832q66 0 113 47t47 113zM1408 1120v-832q0 -119 -84.5 -203.5 t-203.5 -84.5h-832q-119 0 -203.5 84.5t-84.5 203.5v832q0 119 84.5 203.5t203.5 84.5h832q119 0 203.5 -84.5t84.5 -203.5z" />
-<glyph unicode="&#xf148;" horiz-adv-x="1024" d="M1018 933q-18 -37 -58 -37h-192v-864q0 -14 -9 -23t-23 -9h-704q-21 0 -29 18q-8 20 4 35l160 192q9 11 25 11h320v640h-192q-40 0 -58 37q-17 37 9 68l320 384q18 22 49 22t49 -22l320 -384q27 -32 9 -68z" />
-<glyph unicode="&#xf149;" horiz-adv-x="1024" d="M32 1280h704q13 0 22.5 -9.5t9.5 -23.5v-863h192q40 0 58 -37t-9 -69l-320 -384q-18 -22 -49 -22t-49 22l-320 384q-26 31 -9 69q18 37 58 37h192v640h-320q-14 0 -25 11l-160 192q-13 14 -4 34q9 19 29 19z" />
-<glyph unicode="&#xf14a;" d="M685 237l614 614q19 19 19 45t-19 45l-102 102q-19 19 -45 19t-45 -19l-467 -467l-211 211q-19 19 -45 19t-45 -19l-102 -102q-19 -19 -19 -45t19 -45l358 -358q19 -19 45 -19t45 19zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5 t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
-<glyph unicode="&#xf14b;" d="M404 428l152 -152l-52 -52h-56v96h-96v56zM818 818q14 -13 -3 -30l-291 -291q-17 -17 -30 -3q-14 13 3 30l291 291q17 17 30 3zM544 128l544 544l-288 288l-544 -544v-288h288zM1152 736l92 92q28 28 28 68t-28 68l-152 152q-28 28 -68 28t-68 -28l-92 -92zM1536 1120 v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
-<glyph unicode="&#xf14c;" d="M1280 608v480q0 26 -19 45t-45 19h-480q-42 0 -59 -39q-17 -41 14 -70l144 -144l-534 -534q-19 -19 -19 -45t19 -45l102 -102q19 -19 45 -19t45 19l534 534l144 -144q18 -19 45 -19q12 0 25 5q39 17 39 59zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960 q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
-<glyph unicode="&#xf14d;" d="M1005 435l352 352q19 19 19 45t-19 45l-352 352q-30 31 -69 14q-40 -17 -40 -59v-160q-119 0 -216 -19.5t-162.5 -51t-114 -79t-76.5 -95.5t-44.5 -109t-21.5 -111.5t-5 -110.5q0 -181 167 -404q10 -12 25 -12q7 0 13 3q22 9 19 33q-44 354 62 473q46 52 130 75.5 t224 23.5v-160q0 -42 40 -59q12 -5 24 -5q26 0 45 19zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
-<glyph unicode="&#xf14e;" d="M640 448l256 128l-256 128v-256zM1024 1039v-542l-512 -256v542zM1312 640q0 148 -73 273t-198 198t-273 73t-273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73t273 73t198 198t73 273zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103 t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
-<glyph unicode="&#xf150;" d="M1145 861q18 -35 -5 -66l-320 -448q-19 -27 -52 -27t-52 27l-320 448q-23 31 -5 66q17 35 57 35h640q40 0 57 -35zM1280 160v960q0 13 -9.5 22.5t-22.5 9.5h-960q-13 0 -22.5 -9.5t-9.5 -22.5v-960q0 -13 9.5 -22.5t22.5 -9.5h960q13 0 22.5 9.5t9.5 22.5zM1536 1120 v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
-<glyph unicode="&#xf151;" d="M1145 419q-17 -35 -57 -35h-640q-40 0 -57 35q-18 35 5 66l320 448q19 27 52 27t52 -27l320 -448q23 -31 5 -66zM1280 160v960q0 13 -9.5 22.5t-22.5 9.5h-960q-13 0 -22.5 -9.5t-9.5 -22.5v-960q0 -13 9.5 -22.5t22.5 -9.5h960q13 0 22.5 9.5t9.5 22.5zM1536 1120v-960 q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
-<glyph unicode="&#xf152;" d="M1088 640q0 -33 -27 -52l-448 -320q-31 -23 -66 -5q-35 17 -35 57v640q0 40 35 57q35 18 66 -5l448 -320q27 -19 27 -52zM1280 160v960q0 14 -9 23t-23 9h-960q-14 0 -23 -9t-9 -23v-960q0 -14 9 -23t23 -9h960q14 0 23 9t9 23zM1536 1120v-960q0 -119 -84.5 -203.5 t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
-<glyph unicode="&#xf153;" horiz-adv-x="1024" d="M976 229l35 -159q3 -12 -3 -22.5t-17 -14.5l-5 -1q-4 -2 -10.5 -3.5t-16 -4.5t-21.5 -5.5t-25.5 -5t-30 -5t-33.5 -4.5t-36.5 -3t-38.5 -1q-234 0 -409 130.5t-238 351.5h-95q-13 0 -22.5 9.5t-9.5 22.5v113q0 13 9.5 22.5t22.5 9.5h66q-2 57 1 105h-67q-14 0 -23 9 t-9 23v114q0 14 9 23t23 9h98q67 210 243.5 338t400.5 128q102 0 194 -23q11 -3 20 -15q6 -11 3 -24l-43 -159q-3 -13 -14 -19.5t-24 -2.5l-4 1q-4 1 -11.5 2.5l-17.5 3.5t-22.5 3.5t-26 3t-29 2.5t-29.5 1q-126 0 -226 -64t-150 -176h468q16 0 25 -12q10 -12 7 -26 l-24 -114q-5 -26 -32 -26h-488q-3 -37 0 -105h459q15 0 25 -12q9 -12 6 -27l-24 -112q-2 -11 -11 -18.5t-20 -7.5h-387q48 -117 149.5 -185.5t228.5 -68.5q18 0 36 1.5t33.5 3.5t29.5 4.5t24.5 5t18.5 4.5l12 3l5 2q13 5 26 -2q12 -7 15 -21z" />
-<glyph unicode="&#xf154;" horiz-adv-x="1024" d="M1020 399v-367q0 -14 -9 -23t-23 -9h-956q-14 0 -23 9t-9 23v150q0 13 9.5 22.5t22.5 9.5h97v383h-95q-14 0 -23 9.5t-9 22.5v131q0 14 9 23t23 9h95v223q0 171 123.5 282t314.5 111q185 0 335 -125q9 -8 10 -20.5t-7 -22.5l-103 -127q-9 -11 -22 -12q-13 -2 -23 7 q-5 5 -26 19t-69 32t-93 18q-85 0 -137 -47t-52 -123v-215h305q13 0 22.5 -9t9.5 -23v-131q0 -13 -9.5 -22.5t-22.5 -9.5h-305v-379h414v181q0 13 9 22.5t23 9.5h162q14 0 23 -9.5t9 -22.5z" />
-<glyph unicode="&#xf155;" horiz-adv-x="1024" d="M978 351q0 -153 -99.5 -263.5t-258.5 -136.5v-175q0 -14 -9 -23t-23 -9h-135q-13 0 -22.5 9.5t-9.5 22.5v175q-66 9 -127.5 31t-101.5 44.5t-74 48t-46.5 37.5t-17.5 18q-17 21 -2 41l103 135q7 10 23 12q15 2 24 -9l2 -2q113 -99 243 -125q37 -8 74 -8q81 0 142.5 43 t61.5 122q0 28 -15 53t-33.5 42t-58.5 37.5t-66 32t-80 32.5q-39 16 -61.5 25t-61.5 26.5t-62.5 31t-56.5 35.5t-53.5 42.5t-43.5 49t-35.5 58t-21 66.5t-8.5 78q0 138 98 242t255 134v180q0 13 9.5 22.5t22.5 9.5h135q14 0 23 -9t9 -23v-176q57 -6 110.5 -23t87 -33.5 t63.5 -37.5t39 -29t15 -14q17 -18 5 -38l-81 -146q-8 -15 -23 -16q-14 -3 -27 7q-3 3 -14.5 12t-39 26.5t-58.5 32t-74.5 26t-85.5 11.5q-95 0 -155 -43t-60 -111q0 -26 8.5 -48t29.5 -41.5t39.5 -33t56 -31t60.5 -27t70 -27.5q53 -20 81 -31.5t76 -35t75.5 -42.5t62 -50 t53 -63.5t31.5 -76.5t13 -94z" />
-<glyph unicode="&#xf156;" horiz-adv-x="898" d="M898 1066v-102q0 -14 -9 -23t-23 -9h-168q-23 -144 -129 -234t-276 -110q167 -178 459 -536q14 -16 4 -34q-8 -18 -29 -18h-195q-16 0 -25 12q-306 367 -498 571q-9 9 -9 22v127q0 13 9.5 22.5t22.5 9.5h112q132 0 212.5 43t102.5 125h-427q-14 0 -23 9t-9 23v102 q0 14 9 23t23 9h413q-57 113 -268 113h-145q-13 0 -22.5 9.5t-9.5 22.5v133q0 14 9 23t23 9h832q14 0 23 -9t9 -23v-102q0 -14 -9 -23t-23 -9h-233q47 -61 64 -144h171q14 0 23 -9t9 -23z" />
-<glyph unicode="&#xf157;" horiz-adv-x="1027" d="M603 0h-172q-13 0 -22.5 9t-9.5 23v330h-288q-13 0 -22.5 9t-9.5 23v103q0 13 9.5 22.5t22.5 9.5h288v85h-288q-13 0 -22.5 9t-9.5 23v104q0 13 9.5 22.5t22.5 9.5h214l-321 578q-8 16 0 32q10 16 28 16h194q19 0 29 -18l215 -425q19 -38 56 -125q10 24 30.5 68t27.5 61 l191 420q8 19 29 19h191q17 0 27 -16q9 -14 1 -31l-313 -579h215q13 0 22.5 -9.5t9.5 -22.5v-104q0 -14 -9.5 -23t-22.5 -9h-290v-85h290q13 0 22.5 -9.5t9.5 -22.5v-103q0 -14 -9.5 -23t-22.5 -9h-290v-330q0 -13 -9.5 -22.5t-22.5 -9.5z" />
-<glyph unicode="&#xf158;" horiz-adv-x="1664" d="M1664 352v-32q0 -132 -94 -226t-226 -94h-128q-132 0 -226 94t-94 226v480h-224q-2 -102 -14.5 -190.5t-30.5 -156t-48.5 -126.5t-57 -99.5t-67.5 -77.5t-69.5 -58.5t-74 -44t-69 -32t-65.5 -25.5q-4 -2 -32 -13q-8 -2 -12 -2q-22 0 -30 20l-71 178q-5 13 0 25t17 17 q7 3 20 7.5t18 6.5q31 12 46.5 18.5t44.5 20t45.5 26t42 32.5t40.5 42.5t34.5 53.5t30.5 68.5t22.5 83.5t17 103t6.5 123h-256q-14 0 -23 9t-9 23v160q0 14 9 23t23 9h1216q14 0 23 -9t9 -23v-160q0 -14 -9 -23t-23 -9h-224v-512q0 -26 19 -45t45 -19h128q26 0 45 19t19 45 v64q0 14 9 23t23 9h192q14 0 23 -9t9 -23zM1280 1376v-160q0 -14 -9 -23t-23 -9h-960q-14 0 -23 9t-9 23v160q0 14 9 23t23 9h960q14 0 23 -9t9 -23z" />
-<glyph unicode="&#xf159;" horiz-adv-x="1792" d="M514 341l81 299h-159l75 -300q1 -1 1 -3t1 -3q0 1 0.5 3.5t0.5 3.5zM630 768l35 128h-292l32 -128h225zM822 768h139l-35 128h-70zM1271 340l78 300h-162l81 -299q0 -1 0.5 -3.5t1.5 -3.5q0 1 0.5 3t0.5 3zM1382 768l33 128h-297l34 -128h230zM1792 736v-64q0 -14 -9 -23 t-23 -9h-213l-164 -616q-7 -24 -31 -24h-159q-24 0 -31 24l-166 616h-209l-167 -616q-7 -24 -31 -24h-159q-11 0 -19.5 7t-10.5 17l-160 616h-208q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h175l-33 128h-142q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h109l-89 344q-5 15 5 28 q10 12 26 12h137q26 0 31 -24l90 -360h359l97 360q7 24 31 24h126q24 0 31 -24l98 -360h365l93 360q5 24 31 24h137q16 0 26 -12q10 -13 5 -28l-91 -344h111q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-145l-34 -128h179q14 0 23 -9t9 -23z" />
-<glyph unicode="&#xf15a;" horiz-adv-x="1280" d="M1167 896q18 -182 -131 -258q117 -28 175 -103t45 -214q-7 -71 -32.5 -125t-64.5 -89t-97 -58.5t-121.5 -34.5t-145.5 -15v-255h-154v251q-80 0 -122 1v-252h-154v255q-18 0 -54 0.5t-55 0.5h-200l31 183h111q50 0 58 51v402h16q-6 1 -16 1v287q-13 68 -89 68h-111v164 l212 -1q64 0 97 1v252h154v-247q82 2 122 2v245h154v-252q79 -7 140 -22.5t113 -45t82.5 -78t36.5 -114.5zM952 351q0 36 -15 64t-37 46t-57.5 30.5t-65.5 18.5t-74 9t-69 3t-64.5 -1t-47.5 -1v-338q8 0 37 -0.5t48 -0.5t53 1.5t58.5 4t57 8.5t55.5 14t47.5 21t39.5 30 t24.5 40t9.5 51zM881 827q0 33 -12.5 58.5t-30.5 42t-48 28t-55 16.5t-61.5 8t-58 2.5t-54 -1t-39.5 -0.5v-307q5 0 34.5 -0.5t46.5 0t50 2t55 5.5t51.5 11t48.5 18.5t37 27t27 38.5t9 51z" />
-<glyph unicode="&#xf15b;" horiz-adv-x="1280" d="M1280 768v-800q0 -40 -28 -68t-68 -28h-1088q-40 0 -68 28t-28 68v1344q0 40 28 68t68 28h544v-544q0 -40 28 -68t68 -28h544zM1277 896h-509v509q82 -15 132 -65l312 -312q50 -50 65 -132z" />
-<glyph unicode="&#xf15c;" horiz-adv-x="1280" d="M1024 160v64q0 14 -9 23t-23 9h-704q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h704q14 0 23 9t9 23zM1024 416v64q0 14 -9 23t-23 9h-704q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h704q14 0 23 9t9 23zM1280 768v-800q0 -40 -28 -68t-68 -28h-1088q-40 0 -68 28 t-28 68v1344q0 40 28 68t68 28h544v-544q0 -40 28 -68t68 -28h544zM1277 896h-509v509q82 -15 132 -65l312 -312q50 -50 65 -132z" />
-<glyph unicode="&#xf15d;" horiz-adv-x="1664" d="M1191 1128h177l-72 218l-12 47q-2 16 -2 20h-4l-3 -20q0 -1 -3.5 -18t-7.5 -29zM736 96q0 -12 -10 -24l-319 -319q-10 -9 -23 -9q-12 0 -23 9l-320 320q-15 16 -7 35q8 20 30 20h192v1376q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-1376h192q14 0 23 -9t9 -23zM1572 -23 v-233h-584v90l369 529q12 18 21 27l11 9v3q-2 0 -6.5 -0.5t-7.5 -0.5q-12 -3 -30 -3h-232v-115h-120v229h567v-89l-369 -530q-6 -8 -21 -26l-11 -11v-2l14 2q9 2 30 2h248v119h121zM1661 874v-106h-288v106h75l-47 144h-243l-47 -144h75v-106h-287v106h70l230 662h162 l230 -662h70z" />
-<glyph unicode="&#xf15e;" horiz-adv-x="1664" d="M1191 104h177l-72 218l-12 47q-2 16 -2 20h-4l-3 -20q0 -1 -3.5 -18t-7.5 -29zM736 96q0 -12 -10 -24l-319 -319q-10 -9 -23 -9q-12 0 -23 9l-320 320q-15 16 -7 35q8 20 30 20h192v1376q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-1376h192q14 0 23 -9t9 -23zM1661 -150 v-106h-288v106h75l-47 144h-243l-47 -144h75v-106h-287v106h70l230 662h162l230 -662h70zM1572 1001v-233h-584v90l369 529q12 18 21 27l11 9v3q-2 0 -6.5 -0.5t-7.5 -0.5q-12 -3 -30 -3h-232v-115h-120v229h567v-89l-369 -530q-6 -8 -21 -26l-11 -10v-3l14 3q9 1 30 1h248 v119h121z" />
-<glyph unicode="&#xf160;" horiz-adv-x="1792" d="M736 96q0 -12 -10 -24l-319 -319q-10 -9 -23 -9q-12 0 -23 9l-320 320q-15 16 -7 35q8 20 30 20h192v1376q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-1376h192q14 0 23 -9t9 -23zM1792 -32v-192q0 -14 -9 -23t-23 -9h-832q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h832 q14 0 23 -9t9 -23zM1600 480v-192q0 -14 -9 -23t-23 -9h-640q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h640q14 0 23 -9t9 -23zM1408 992v-192q0 -14 -9 -23t-23 -9h-448q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h448q14 0 23 -9t9 -23zM1216 1504v-192q0 -14 -9 -23t-23 -9h-256 q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h256q14 0 23 -9t9 -23z" />
-<glyph unicode="&#xf161;" horiz-adv-x="1792" d="M1216 -32v-192q0 -14 -9 -23t-23 -9h-256q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h256q14 0 23 -9t9 -23zM736 96q0 -12 -10 -24l-319 -319q-10 -9 -23 -9q-12 0 -23 9l-320 320q-15 16 -7 35q8 20 30 20h192v1376q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-1376h192 q14 0 23 -9t9 -23zM1408 480v-192q0 -14 -9 -23t-23 -9h-448q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h448q14 0 23 -9t9 -23zM1600 992v-192q0 -14 -9 -23t-23 -9h-640q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h640q14 0 23 -9t9 -23zM1792 1504v-192q0 -14 -9 -23t-23 -9h-832 q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h832q14 0 23 -9t9 -23z" />
-<glyph unicode="&#xf162;" d="M1346 223q0 63 -44 116t-103 53q-52 0 -83 -37t-31 -94t36.5 -95t104.5 -38q50 0 85 27t35 68zM736 96q0 -12 -10 -24l-319 -319q-10 -9 -23 -9q-12 0 -23 9l-320 320q-15 16 -7 35q8 20 30 20h192v1376q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-1376h192q14 0 23 -9t9 -23 zM1486 165q0 -62 -13 -121.5t-41 -114t-68 -95.5t-98.5 -65.5t-127.5 -24.5q-62 0 -108 16q-24 8 -42 15l39 113q15 -7 31 -11q37 -13 75 -13q84 0 134.5 58.5t66.5 145.5h-2q-21 -23 -61.5 -37t-84.5 -14q-106 0 -173 71.5t-67 172.5q0 105 72 178t181 73q123 0 205 -94.5 t82 -252.5zM1456 882v-114h-469v114h167v432q0 7 0.5 19t0.5 17v16h-2l-7 -12q-8 -13 -26 -31l-62 -58l-82 86l192 185h123v-654h165z" />
-<glyph unicode="&#xf163;" d="M1346 1247q0 63 -44 116t-103 53q-52 0 -83 -37t-31 -94t36.5 -95t104.5 -38q50 0 85 27t35 68zM736 96q0 -12 -10 -24l-319 -319q-10 -9 -23 -9q-12 0 -23 9l-320 320q-15 16 -7 35q8 20 30 20h192v1376q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-1376h192q14 0 23 -9 t9 -23zM1456 -142v-114h-469v114h167v432q0 7 0.5 19t0.5 17v16h-2l-7 -12q-8 -13 -26 -31l-62 -58l-82 86l192 185h123v-654h165zM1486 1189q0 -62 -13 -121.5t-41 -114t-68 -95.5t-98.5 -65.5t-127.5 -24.5q-62 0 -108 16q-24 8 -42 15l39 113q15 -7 31 -11q37 -13 75 -13 q84 0 134.5 58.5t66.5 145.5h-2q-21 -23 -61.5 -37t-84.5 -14q-106 0 -173 71.5t-67 172.5q0 105 72 178t181 73q123 0 205 -94.5t82 -252.5z" />
-<glyph unicode="&#xf164;" horiz-adv-x="1664" d="M256 192q0 26 -19 45t-45 19q-27 0 -45.5 -19t-18.5 -45q0 -27 18.5 -45.5t45.5 -18.5q26 0 45 18.5t19 45.5zM416 704v-640q0 -26 -19 -45t-45 -19h-288q-26 0 -45 19t-19 45v640q0 26 19 45t45 19h288q26 0 45 -19t19 -45zM1600 704q0 -86 -55 -149q15 -44 15 -76 q3 -76 -43 -137q17 -56 0 -117q-15 -57 -54 -94q9 -112 -49 -181q-64 -76 -197 -78h-36h-76h-17q-66 0 -144 15.5t-121.5 29t-120.5 39.5q-123 43 -158 44q-26 1 -45 19.5t-19 44.5v641q0 25 18 43.5t43 20.5q24 2 76 59t101 121q68 87 101 120q18 18 31 48t17.5 48.5 t13.5 60.5q7 39 12.5 61t19.5 52t34 50q19 19 45 19q46 0 82.5 -10.5t60 -26t40 -40.5t24 -45t12 -50t5 -45t0.5 -39q0 -38 -9.5 -76t-19 -60t-27.5 -56q-3 -6 -10 -18t-11 -22t-8 -24h277q78 0 135 -57t57 -135z" />
-<glyph unicode="&#xf165;" horiz-adv-x="1664" d="M256 960q0 -26 -19 -45t-45 -19q-27 0 -45.5 19t-18.5 45q0 27 18.5 45.5t45.5 18.5q26 0 45 -18.5t19 -45.5zM416 448v640q0 26 -19 45t-45 19h-288q-26 0 -45 -19t-19 -45v-640q0 -26 19 -45t45 -19h288q26 0 45 19t19 45zM1545 597q55 -61 55 -149q-1 -78 -57.5 -135 t-134.5 -57h-277q4 -14 8 -24t11 -22t10 -18q18 -37 27 -57t19 -58.5t10 -76.5q0 -24 -0.5 -39t-5 -45t-12 -50t-24 -45t-40 -40.5t-60 -26t-82.5 -10.5q-26 0 -45 19q-20 20 -34 50t-19.5 52t-12.5 61q-9 42 -13.5 60.5t-17.5 48.5t-31 48q-33 33 -101 120q-49 64 -101 121 t-76 59q-25 2 -43 20.5t-18 43.5v641q0 26 19 44.5t45 19.5q35 1 158 44q77 26 120.5 39.5t121.5 29t144 15.5h17h76h36q133 -2 197 -78q58 -69 49 -181q39 -37 54 -94q17 -61 0 -117q46 -61 43 -137q0 -32 -15 -76z" />
-<glyph unicode="&#xf166;" d="M919 233v157q0 50 -29 50q-17 0 -33 -16v-224q16 -16 33 -16q29 0 29 49zM1103 355h66v34q0 51 -33 51t-33 -51v-34zM532 621v-70h-80v-423h-74v423h-78v70h232zM733 495v-367h-67v40q-39 -45 -76 -45q-33 0 -42 28q-6 16 -6 54v290h66v-270q0 -24 1 -26q1 -15 15 -15 q20 0 42 31v280h67zM985 384v-146q0 -52 -7 -73q-12 -42 -53 -42q-35 0 -68 41v-36h-67v493h67v-161q32 40 68 40q41 0 53 -42q7 -21 7 -74zM1236 255v-9q0 -29 -2 -43q-3 -22 -15 -40q-27 -40 -80 -40q-52 0 -81 38q-21 27 -21 86v129q0 59 20 86q29 38 80 38t78 -38 q21 -28 21 -86v-76h-133v-65q0 -51 34 -51q24 0 30 26q0 1 0.5 7t0.5 16.5v21.5h68zM785 1079v-156q0 -51 -32 -51t-32 51v156q0 52 32 52t32 -52zM1318 366q0 177 -19 260q-10 44 -43 73.5t-76 34.5q-136 15 -412 15q-275 0 -411 -15q-44 -5 -76.5 -34.5t-42.5 -73.5 q-20 -87 -20 -260q0 -176 20 -260q10 -43 42.5 -73t75.5 -35q137 -15 412 -15t412 15q43 5 75.5 35t42.5 73q20 84 20 260zM563 1017l90 296h-75l-51 -195l-53 195h-78l24 -69t23 -69q35 -103 46 -158v-201h74v201zM852 936v130q0 58 -21 87q-29 38 -78 38q-51 0 -78 -38 q-21 -29 -21 -87v-130q0 -58 21 -87q27 -38 78 -38q49 0 78 38q21 27 21 87zM1033 816h67v370h-67v-283q-22 -31 -42 -31q-15 0 -16 16q-1 2 -1 26v272h-67v-293q0 -37 6 -55q11 -27 43 -27q36 0 77 45v-40zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960 q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
-<glyph unicode="&#xf167;" d="M971 292v-211q0 -67 -39 -67q-23 0 -45 22v301q22 22 45 22q39 0 39 -67zM1309 291v-46h-90v46q0 68 45 68t45 -68zM343 509h107v94h-312v-94h105v-569h100v569zM631 -60h89v494h-89v-378q-30 -42 -57 -42q-18 0 -21 21q-1 3 -1 35v364h-89v-391q0 -49 8 -73 q12 -37 58 -37q48 0 102 61v-54zM1060 88v197q0 73 -9 99q-17 56 -71 56q-50 0 -93 -54v217h-89v-663h89v48q45 -55 93 -55q54 0 71 55q9 27 9 100zM1398 98v13h-91q0 -51 -2 -61q-7 -36 -40 -36q-46 0 -46 69v87h179v103q0 79 -27 116q-39 51 -106 51q-68 0 -107 -51 q-28 -37 -28 -116v-173q0 -79 29 -116q39 -51 108 -51q72 0 108 53q18 27 21 54q2 9 2 58zM790 1011v210q0 69 -43 69t-43 -69v-210q0 -70 43 -70t43 70zM1509 260q0 -234 -26 -350q-14 -59 -58 -99t-102 -46q-184 -21 -555 -21t-555 21q-58 6 -102.5 46t-57.5 99 q-26 112 -26 350q0 234 26 350q14 59 58 99t103 47q183 20 554 20t555 -20q58 -7 102.5 -47t57.5 -99q26 -112 26 -350zM511 1536h102l-121 -399v-271h-100v271q-14 74 -61 212q-37 103 -65 187h106l71 -263zM881 1203v-175q0 -81 -28 -118q-37 -51 -106 -51q-67 0 -105 51 q-28 38 -28 118v175q0 80 28 117q38 51 105 51q69 0 106 -51q28 -37 28 -117zM1216 1365v-499h-91v55q-53 -62 -103 -62q-46 0 -59 37q-8 24 -8 75v394h91v-367q0 -33 1 -35q3 -22 21 -22q27 0 57 43v381h91z" />
-<glyph unicode="&#xf168;" horiz-adv-x="1408" d="M597 869q-10 -18 -257 -456q-27 -46 -65 -46h-239q-21 0 -31 17t0 36l253 448q1 0 0 1l-161 279q-12 22 -1 37q9 15 32 15h239q40 0 66 -45zM1403 1511q11 -16 0 -37l-528 -934v-1l336 -615q11 -20 1 -37q-10 -15 -32 -15h-239q-42 0 -66 45l-339 622q18 32 531 942 q25 45 64 45h241q22 0 31 -15z" />
-<glyph unicode="&#xf169;" d="M685 771q0 1 -126 222q-21 34 -52 34h-184q-18 0 -26 -11q-7 -12 1 -29l125 -216v-1l-196 -346q-9 -14 0 -28q8 -13 24 -13h185q31 0 50 36zM1309 1268q-7 12 -24 12h-187q-30 0 -49 -35l-411 -729q1 -2 262 -481q20 -35 52 -35h184q18 0 25 12q8 13 -1 28l-260 476v1 l409 723q8 16 0 28zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
-<glyph unicode="&#xf16a;" horiz-adv-x="1792" d="M1280 640q0 37 -30 54l-512 320q-31 20 -65 2q-33 -18 -33 -56v-640q0 -38 33 -56q16 -8 31 -8q20 0 34 10l512 320q30 17 30 54zM1792 640q0 -96 -1 -150t-8.5 -136.5t-22.5 -147.5q-16 -73 -69 -123t-124 -58q-222 -25 -671 -25t-671 25q-71 8 -124.5 58t-69.5 123 q-14 65 -21.5 147.5t-8.5 136.5t-1 150t1 150t8.5 136.5t22.5 147.5q16 73 69 123t124 58q222 25 671 25t671 -25q71 -8 124.5 -58t69.5 -123q14 -65 21.5 -147.5t8.5 -136.5t1 -150z" />
-<glyph unicode="&#xf16b;" horiz-adv-x="1792" d="M402 829l494 -305l-342 -285l-490 319zM1388 274v-108l-490 -293v-1l-1 1l-1 -1v1l-489 293v108l147 -96l342 284v2l1 -1l1 1v-2l343 -284zM554 1418l342 -285l-494 -304l-338 270zM1390 829l338 -271l-489 -319l-343 285zM1239 1418l489 -319l-338 -270l-494 304z" />
-<glyph unicode="&#xf16c;" horiz-adv-x="1408" d="M928 135v-151l-707 -1v151zM1169 481v-701l-1 -35v-1h-1132l-35 1h-1v736h121v-618h928v618h120zM241 393l704 -65l-13 -150l-705 65zM309 709l683 -183l-39 -146l-683 183zM472 1058l609 -360l-77 -130l-609 360zM832 1389l398 -585l-124 -85l-399 584zM1285 1536 l121 -697l-149 -26l-121 697z" />
-<glyph unicode="&#xf16d;" d="M1362 110v648h-135q20 -63 20 -131q0 -126 -64 -232.5t-174 -168.5t-240 -62q-197 0 -337 135.5t-140 327.5q0 68 20 131h-141v-648q0 -26 17.5 -43.5t43.5 -17.5h1069q25 0 43 17.5t18 43.5zM1078 643q0 124 -90.5 211.5t-218.5 87.5q-127 0 -217.5 -87.5t-90.5 -211.5 t90.5 -211.5t217.5 -87.5q128 0 218.5 87.5t90.5 211.5zM1362 1003v165q0 28 -20 48.5t-49 20.5h-174q-29 0 -49 -20.5t-20 -48.5v-165q0 -29 20 -49t49 -20h174q29 0 49 20t20 49zM1536 1211v-1142q0 -81 -58 -139t-139 -58h-1142q-81 0 -139 58t-58 139v1142q0 81 58 139 t139 58h1142q81 0 139 -58t58 -139z" />
-<glyph unicode="&#xf16e;" d="M1248 1408q119 0 203.5 -84.5t84.5 -203.5v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960zM698 640q0 88 -62 150t-150 62t-150 -62t-62 -150t62 -150t150 -62t150 62t62 150zM1262 640q0 88 -62 150 t-150 62t-150 -62t-62 -150t62 -150t150 -62t150 62t62 150z" />
-<glyph unicode="&#xf170;" d="M768 914l201 -306h-402zM1133 384h94l-459 691l-459 -691h94l104 160h522zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
-<glyph unicode="&#xf171;" horiz-adv-x="1408" d="M815 677q8 -63 -50.5 -101t-111.5 -6q-39 17 -53.5 58t-0.5 82t52 58q36 18 72.5 12t64 -35.5t27.5 -67.5zM926 698q-14 107 -113 164t-197 13q-63 -28 -100.5 -88.5t-34.5 -129.5q4 -91 77.5 -155t165.5 -56q91 8 152 84t50 168zM1165 1240q-20 27 -56 44.5t-58 22 t-71 12.5q-291 47 -566 -2q-43 -7 -66 -12t-55 -22t-50 -43q30 -28 76 -45.5t73.5 -22t87.5 -11.5q228 -29 448 -1q63 8 89.5 12t72.5 21.5t75 46.5zM1222 205q-8 -26 -15.5 -76.5t-14 -84t-28.5 -70t-58 -56.5q-86 -48 -189.5 -71.5t-202 -22t-201.5 18.5q-46 8 -81.5 18 t-76.5 27t-73 43.5t-52 61.5q-25 96 -57 292l6 16l18 9q223 -148 506.5 -148t507.5 148q21 -6 24 -23t-5 -45t-8 -37zM1403 1166q-26 -167 -111 -655q-5 -30 -27 -56t-43.5 -40t-54.5 -31q-252 -126 -610 -88q-248 27 -394 139q-15 12 -25.5 26.5t-17 35t-9 34t-6 39.5 t-5.5 35q-9 50 -26.5 150t-28 161.5t-23.5 147.5t-22 158q3 26 17.5 48.5t31.5 37.5t45 30t46 22.5t48 18.5q125 46 313 64q379 37 676 -50q155 -46 215 -122q16 -20 16.5 -51t-5.5 -54z" />
-<glyph unicode="&#xf172;" d="M848 666q0 43 -41 66t-77 1q-43 -20 -42.5 -72.5t43.5 -70.5q39 -23 81 4t36 72zM928 682q8 -66 -36 -121t-110 -61t-119 40t-56 113q-2 49 25.5 93t72.5 64q70 31 141.5 -10t81.5 -118zM1100 1073q-20 -21 -53.5 -34t-53 -16t-63.5 -8q-155 -20 -324 0q-44 6 -63 9.5 t-52.5 16t-54.5 32.5q13 19 36 31t40 15.5t47 8.5q198 35 408 1q33 -5 51 -8.5t43 -16t39 -31.5zM1142 327q0 7 5.5 26.5t3 32t-17.5 16.5q-161 -106 -365 -106t-366 106l-12 -6l-5 -12q26 -154 41 -210q47 -81 204 -108q249 -46 428 53q34 19 49 51.5t22.5 85.5t12.5 71z M1272 1020q9 53 -8 75q-43 55 -155 88q-216 63 -487 36q-132 -12 -226 -46q-38 -15 -59.5 -25t-47 -34t-29.5 -54q8 -68 19 -138t29 -171t24 -137q1 -5 5 -31t7 -36t12 -27t22 -28q105 -80 284 -100q259 -28 440 63q24 13 39.5 23t31 29t19.5 40q48 267 80 473zM1536 1120 v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
-<glyph unicode="&#xf173;" horiz-adv-x="1024" d="M390 1408h219v-388h364v-241h-364v-394q0 -136 14 -172q13 -37 52 -60q50 -31 117 -31q117 0 232 76v-242q-102 -48 -178 -65q-77 -19 -173 -19q-105 0 -186 27q-78 25 -138 75q-58 51 -79 105q-22 54 -22 161v539h-170v217q91 30 155 84q64 55 103 132q39 78 54 196z " />
-<glyph unicode="&#xf174;" d="M1123 127v181q-88 -56 -174 -56q-51 0 -88 23q-29 17 -39 45q-11 30 -11 129v295h274v181h-274v291h-164q-11 -90 -40 -147t-78 -99q-48 -40 -116 -63v-163h127v-404q0 -78 17 -121q17 -42 59 -78q43 -37 104 -57q62 -20 140 -20q67 0 129 14q57 13 134 49zM1536 1120 v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
-<glyph unicode="&#xf175;" horiz-adv-x="768" d="M765 237q8 -19 -5 -35l-350 -384q-10 -10 -23 -10q-14 0 -24 10l-355 384q-13 16 -5 35q9 19 29 19h224v1248q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-1248h224q21 0 29 -19z" />
-<glyph unicode="&#xf176;" horiz-adv-x="768" d="M765 1043q-9 -19 -29 -19h-224v-1248q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v1248h-224q-21 0 -29 19t5 35l350 384q10 10 23 10q14 0 24 -10l355 -384q13 -16 5 -35z" />
-<glyph unicode="&#xf177;" horiz-adv-x="1792" d="M1792 736v-192q0 -14 -9 -23t-23 -9h-1248v-224q0 -21 -19 -29t-35 5l-384 350q-10 10 -10 23q0 14 10 24l384 354q16 14 35 6q19 -9 19 -29v-224h1248q14 0 23 -9t9 -23z" />
-<glyph unicode="&#xf178;" horiz-adv-x="1792" d="M1728 643q0 -14 -10 -24l-384 -354q-16 -14 -35 -6q-19 9 -19 29v224h-1248q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h1248v224q0 21 19 29t35 -5l384 -350q10 -10 10 -23z" />
-<glyph unicode="&#xf179;" horiz-adv-x="1408" d="M1393 321q-39 -125 -123 -250q-129 -196 -257 -196q-49 0 -140 32q-86 32 -151 32q-61 0 -142 -33q-81 -34 -132 -34q-152 0 -301 259q-147 261 -147 503q0 228 113 374q112 144 284 144q72 0 177 -30q104 -30 138 -30q45 0 143 34q102 34 173 34q119 0 213 -65 q52 -36 104 -100q-79 -67 -114 -118q-65 -94 -65 -207q0 -124 69 -223t158 -126zM1017 1494q0 -61 -29 -136q-30 -75 -93 -138q-54 -54 -108 -72q-37 -11 -104 -17q3 149 78 257q74 107 250 148q1 -3 2.5 -11t2.5 -11q0 -4 0.5 -10t0.5 -10z" />
-<glyph unicode="&#xf17a;" horiz-adv-x="1664" d="M682 530v-651l-682 94v557h682zM682 1273v-659h-682v565zM1664 530v-786l-907 125v661h907zM1664 1408v-794h-907v669z" />
-<glyph unicode="&#xf17b;" horiz-adv-x="1408" d="M493 1053q16 0 27.5 11.5t11.5 27.5t-11.5 27.5t-27.5 11.5t-27 -11.5t-11 -27.5t11 -27.5t27 -11.5zM915 1053q16 0 27 11.5t11 27.5t-11 27.5t-27 11.5t-27.5 -11.5t-11.5 -27.5t11.5 -27.5t27.5 -11.5zM103 869q42 0 72 -30t30 -72v-430q0 -43 -29.5 -73t-72.5 -30 t-73 30t-30 73v430q0 42 30 72t73 30zM1163 850v-666q0 -46 -32 -78t-77 -32h-75v-227q0 -43 -30 -73t-73 -30t-73 30t-30 73v227h-138v-227q0 -43 -30 -73t-73 -30q-42 0 -72 30t-30 73l-1 227h-74q-46 0 -78 32t-32 78v666h918zM931 1255q107 -55 171 -153.5t64 -215.5 h-925q0 117 64 215.5t172 153.5l-71 131q-7 13 5 20q13 6 20 -6l72 -132q95 42 201 42t201 -42l72 132q7 12 20 6q12 -7 5 -20zM1408 767v-430q0 -43 -30 -73t-73 -30q-42 0 -72 30t-30 73v430q0 43 30 72.5t72 29.5q43 0 73 -29.5t30 -72.5z" />
-<glyph unicode="&#xf17c;" d="M663 1125q-11 -1 -15.5 -10.5t-8.5 -9.5q-5 -1 -5 5q0 12 19 15h10zM750 1111q-4 -1 -11.5 6.5t-17.5 4.5q24 11 32 -2q3 -6 -3 -9zM399 684q-4 1 -6 -3t-4.5 -12.5t-5.5 -13.5t-10 -13q-7 -10 -1 -12q4 -1 12.5 7t12.5 18q1 3 2 7t2 6t1.5 4.5t0.5 4v3t-1 2.5t-3 2z M1254 325q0 18 -55 42q4 15 7.5 27.5t5 26t3 21.5t0.5 22.5t-1 19.5t-3.5 22t-4 20.5t-5 25t-5.5 26.5q-10 48 -47 103t-72 75q24 -20 57 -83q87 -162 54 -278q-11 -40 -50 -42q-31 -4 -38.5 18.5t-8 83.5t-11.5 107q-9 39 -19.5 69t-19.5 45.5t-15.5 24.5t-13 15t-7.5 7 q-14 62 -31 103t-29.5 56t-23.5 33t-15 40q-4 21 6 53.5t4.5 49.5t-44.5 25q-15 3 -44.5 18t-35.5 16q-8 1 -11 26t8 51t36 27q37 3 51 -30t4 -58q-11 -19 -2 -26.5t30 -0.5q13 4 13 36v37q-5 30 -13.5 50t-21 30.5t-23.5 15t-27 7.5q-107 -8 -89 -134q0 -15 -1 -15 q-9 9 -29.5 10.5t-33 -0.5t-15.5 5q1 57 -16 90t-45 34q-27 1 -41.5 -27.5t-16.5 -59.5q-1 -15 3.5 -37t13 -37.5t15.5 -13.5q10 3 16 14q4 9 -7 8q-7 0 -15.5 14.5t-9.5 33.5q-1 22 9 37t34 14q17 0 27 -21t9.5 -39t-1.5 -22q-22 -15 -31 -29q-8 -12 -27.5 -23.5 t-20.5 -12.5q-13 -14 -15.5 -27t7.5 -18q14 -8 25 -19.5t16 -19t18.5 -13t35.5 -6.5q47 -2 102 15q2 1 23 7t34.5 10.5t29.5 13t21 17.5q9 14 20 8q5 -3 6.5 -8.5t-3 -12t-16.5 -9.5q-20 -6 -56.5 -21.5t-45.5 -19.5q-44 -19 -70 -23q-25 -5 -79 2q-10 2 -9 -2t17 -19 q25 -23 67 -22q17 1 36 7t36 14t33.5 17.5t30 17t24.5 12t17.5 2.5t8.5 -11q0 -2 -1 -4.5t-4 -5t-6 -4.5t-8.5 -5t-9 -4.5t-10 -5t-9.5 -4.5q-28 -14 -67.5 -44t-66.5 -43t-49 -1q-21 11 -63 73q-22 31 -25 22q-1 -3 -1 -10q0 -25 -15 -56.5t-29.5 -55.5t-21 -58t11.5 -63 q-23 -6 -62.5 -90t-47.5 -141q-2 -18 -1.5 -69t-5.5 -59q-8 -24 -29 -3q-32 31 -36 94q-2 28 4 56q4 19 -1 18l-4 -5q-36 -65 10 -166q5 -12 25 -28t24 -20q20 -23 104 -90.5t93 -76.5q16 -15 17.5 -38t-14 -43t-45.5 -23q8 -15 29 -44.5t28 -54t7 -70.5q46 24 7 92 q-4 8 -10.5 16t-9.5 12t-2 6q3 5 13 9.5t20 -2.5q46 -52 166 -36q133 15 177 87q23 38 34 30q12 -6 10 -52q-1 -25 -23 -92q-9 -23 -6 -37.5t24 -15.5q3 19 14.5 77t13.5 90q2 21 -6.5 73.5t-7.5 97t23 70.5q15 18 51 18q1 37 34.5 53t72.5 10.5t60 -22.5zM626 1152 q3 17 -2.5 30t-11.5 15q-9 2 -9 -7q2 -5 5 -6q10 0 7 -15q-3 -20 8 -20q3 0 3 3zM1045 955q-2 8 -6.5 11.5t-13 5t-14.5 5.5q-5 3 -9.5 8t-7 8t-5.5 6.5t-4 4t-4 -1.5q-14 -16 7 -43.5t39 -31.5q9 -1 14.5 8t3.5 20zM867 1168q0 11 -5 19.5t-11 12.5t-9 3q-14 -1 -7 -7l4 -2 q14 -4 18 -31q0 -3 8 2zM921 1401q0 2 -2.5 5t-9 7t-9.5 6q-15 15 -24 15q-9 -1 -11.5 -7.5t-1 -13t-0.5 -12.5q-1 -4 -6 -10.5t-6 -9t3 -8.5q4 -3 8 0t11 9t15 9q1 1 9 1t15 2t9 7zM1486 60q20 -12 31 -24.5t12 -24t-2.5 -22.5t-15.5 -22t-23.5 -19.5t-30 -18.5 t-31.5 -16.5t-32 -15.5t-27 -13q-38 -19 -85.5 -56t-75.5 -64q-17 -16 -68 -19.5t-89 14.5q-18 9 -29.5 23.5t-16.5 25.5t-22 19.5t-47 9.5q-44 1 -130 1q-19 0 -57 -1.5t-58 -2.5q-44 -1 -79.5 -15t-53.5 -30t-43.5 -28.5t-53.5 -11.5q-29 1 -111 31t-146 43q-19 4 -51 9.5 t-50 9t-39.5 9.5t-33.5 14.5t-17 19.5q-10 23 7 66.5t18 54.5q1 16 -4 40t-10 42.5t-4.5 36.5t10.5 27q14 12 57 14t60 12q30 18 42 35t12 51q21 -73 -32 -106q-32 -20 -83 -15q-34 3 -43 -10q-13 -15 5 -57q2 -6 8 -18t8.5 -18t4.5 -17t1 -22q0 -15 -17 -49t-14 -48 q3 -17 37 -26q20 -6 84.5 -18.5t99.5 -20.5q24 -6 74 -22t82.5 -23t55.5 -4q43 6 64.5 28t23 48t-7.5 58.5t-19 52t-20 36.5q-121 190 -169 242q-68 74 -113 40q-11 -9 -15 15q-3 16 -2 38q1 29 10 52t24 47t22 42q8 21 26.5 72t29.5 78t30 61t39 54q110 143 124 195 q-12 112 -16 310q-2 90 24 151.5t106 104.5q39 21 104 21q53 1 106 -13.5t89 -41.5q57 -42 91.5 -121.5t29.5 -147.5q-5 -95 30 -214q34 -113 133 -218q55 -59 99.5 -163t59.5 -191q8 -49 5 -84.5t-12 -55.5t-20 -22q-10 -2 -23.5 -19t-27 -35.5t-40.5 -33.5t-61 -14 q-18 1 -31.5 5t-22.5 13.5t-13.5 15.5t-11.5 20.5t-9 19.5q-22 37 -41 30t-28 -49t7 -97q20 -70 1 -195q-10 -65 18 -100.5t73 -33t85 35.5q59 49 89.5 66.5t103.5 42.5q53 18 77 36.5t18.5 34.5t-25 28.5t-51.5 23.5q-33 11 -49.5 48t-15 72.5t15.5 47.5q1 -31 8 -56.5 t14.5 -40.5t20.5 -28.5t21 -19t21.5 -13t16.5 -9.5z" />
-<glyph unicode="&#xf17d;" d="M1024 36q-42 241 -140 498h-2l-2 -1q-16 -6 -43 -16.5t-101 -49t-137 -82t-131 -114.5t-103 -148l-15 11q184 -150 418 -150q132 0 256 52zM839 643q-21 49 -53 111q-311 -93 -673 -93q-1 -7 -1 -21q0 -124 44 -236.5t124 -201.5q50 89 123.5 166.5t142.5 124.5t130.5 81 t99.5 48l37 13q4 1 13 3.5t13 4.5zM732 855q-120 213 -244 378q-138 -65 -234 -186t-128 -272q302 0 606 80zM1416 536q-210 60 -409 29q87 -239 128 -469q111 75 185 189.5t96 250.5zM611 1277q-1 0 -2 -1q1 1 2 1zM1201 1132q-185 164 -433 164q-76 0 -155 -19 q131 -170 246 -382q69 26 130 60.5t96.5 61.5t65.5 57t37.5 40.5zM1424 647q-3 232 -149 410l-1 -1q-9 -12 -19 -24.5t-43.5 -44.5t-71 -60.5t-100 -65t-131.5 -64.5q25 -53 44 -95q2 -6 6.5 -17.5t7.5 -16.5q36 5 74.5 7t73.5 2t69 -1.5t64 -4t56.5 -5.5t48 -6.5t36.5 -6 t25 -4.5zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
-<glyph unicode="&#xf17e;" d="M1173 473q0 50 -19.5 91.5t-48.5 68.5t-73 49t-82.5 34t-87.5 23l-104 24q-30 7 -44 10.5t-35 11.5t-30 16t-16.5 21t-7.5 30q0 77 144 77q43 0 77 -12t54 -28.5t38 -33.5t40 -29t48 -12q47 0 75.5 32t28.5 77q0 55 -56 99.5t-142 67.5t-182 23q-68 0 -132 -15.5 t-119.5 -47t-89 -87t-33.5 -128.5q0 -61 19 -106.5t56 -75.5t80 -48.5t103 -32.5l146 -36q90 -22 112 -36q32 -20 32 -60q0 -39 -40 -64.5t-105 -25.5q-51 0 -91.5 16t-65 38.5t-45.5 45t-46 38.5t-54 16q-50 0 -75.5 -30t-25.5 -75q0 -92 122 -157.5t291 -65.5 q73 0 140 18.5t122.5 53.5t88.5 93.5t33 131.5zM1536 256q0 -159 -112.5 -271.5t-271.5 -112.5q-130 0 -234 80q-77 -16 -150 -16q-143 0 -273.5 55.5t-225 150t-150 225t-55.5 273.5q0 73 16 150q-80 104 -80 234q0 159 112.5 271.5t271.5 112.5q130 0 234 -80 q77 16 150 16q143 0 273.5 -55.5t225 -150t150 -225t55.5 -273.5q0 -73 -16 -150q80 -104 80 -234z" />
-<glyph unicode="&#xf180;" horiz-adv-x="1664" d="M1483 512l-587 -587q-52 -53 -127.5 -53t-128.5 53l-587 587q-53 53 -53 128t53 128l587 587q53 53 128 53t128 -53l265 -265l-398 -399l-188 188q-42 42 -99 42q-59 0 -100 -41l-120 -121q-42 -40 -42 -99q0 -58 42 -100l406 -408q30 -28 67 -37l6 -4h28q60 0 99 41 l619 619l2 -3q53 -53 53 -128t-53 -128zM1406 1138l120 -120q14 -15 14 -36t-14 -36l-730 -730q-17 -15 -37 -15v0q-4 0 -6 1q-18 2 -30 14l-407 408q-14 15 -14 36t14 35l121 120q13 15 35 15t36 -15l252 -252l574 575q15 15 36 15t36 -15z" />
-<glyph unicode="&#xf181;" d="M704 192v1024q0 14 -9 23t-23 9h-480q-14 0 -23 -9t-9 -23v-1024q0 -14 9 -23t23 -9h480q14 0 23 9t9 23zM1376 576v640q0 14 -9 23t-23 9h-480q-14 0 -23 -9t-9 -23v-640q0 -14 9 -23t23 -9h480q14 0 23 9t9 23zM1536 1344v-1408q0 -26 -19 -45t-45 -19h-1408 q-26 0 -45 19t-19 45v1408q0 26 19 45t45 19h1408q26 0 45 -19t19 -45z" />
-<glyph unicode="&#xf182;" horiz-adv-x="1280" d="M1280 480q0 -40 -28 -68t-68 -28q-51 0 -80 43l-227 341h-45v-132l247 -411q9 -15 9 -33q0 -26 -19 -45t-45 -19h-192v-272q0 -46 -33 -79t-79 -33h-160q-46 0 -79 33t-33 79v272h-192q-26 0 -45 19t-19 45q0 18 9 33l247 411v132h-45l-227 -341q-29 -43 -80 -43 q-40 0 -68 28t-28 68q0 29 16 53l256 384q73 107 176 107h384q103 0 176 -107l256 -384q16 -24 16 -53zM864 1280q0 -93 -65.5 -158.5t-158.5 -65.5t-158.5 65.5t-65.5 158.5t65.5 158.5t158.5 65.5t158.5 -65.5t65.5 -158.5z" />
-<glyph unicode="&#xf183;" horiz-adv-x="1024" d="M1024 832v-416q0 -40 -28 -68t-68 -28t-68 28t-28 68v352h-64v-912q0 -46 -33 -79t-79 -33t-79 33t-33 79v464h-64v-464q0 -46 -33 -79t-79 -33t-79 33t-33 79v912h-64v-352q0 -40 -28 -68t-68 -28t-68 28t-28 68v416q0 80 56 136t136 56h640q80 0 136 -56t56 -136z M736 1280q0 -93 -65.5 -158.5t-158.5 -65.5t-158.5 65.5t-65.5 158.5t65.5 158.5t158.5 65.5t158.5 -65.5t65.5 -158.5z" />
-<glyph unicode="&#xf184;" d="M773 234l350 473q16 22 24.5 59t-6 85t-61.5 79q-40 26 -83 25.5t-73.5 -17.5t-54.5 -45q-36 -40 -96 -40q-59 0 -95 40q-24 28 -54.5 45t-73.5 17.5t-84 -25.5q-46 -31 -60.5 -79t-6 -85t24.5 -59zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103 t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
-<glyph unicode="&#xf185;" horiz-adv-x="1792" d="M1472 640q0 117 -45.5 223.5t-123 184t-184 123t-223.5 45.5t-223.5 -45.5t-184 -123t-123 -184t-45.5 -223.5t45.5 -223.5t123 -184t184 -123t223.5 -45.5t223.5 45.5t184 123t123 184t45.5 223.5zM1748 363q-4 -15 -20 -20l-292 -96v-306q0 -16 -13 -26q-15 -10 -29 -4 l-292 94l-180 -248q-10 -13 -26 -13t-26 13l-180 248l-292 -94q-14 -6 -29 4q-13 10 -13 26v306l-292 96q-16 5 -20 20q-5 17 4 29l180 248l-180 248q-9 13 -4 29q4 15 20 20l292 96v306q0 16 13 26q15 10 29 4l292 -94l180 248q9 12 26 12t26 -12l180 -248l292 94 q14 6 29 -4q13 -10 13 -26v-306l292 -96q16 -5 20 -20q5 -16 -4 -29l-180 -248l180 -248q9 -12 4 -29z" />
-<glyph unicode="&#xf186;" d="M1262 233q-54 -9 -110 -9q-182 0 -337 90t-245 245t-90 337q0 192 104 357q-201 -60 -328.5 -229t-127.5 -384q0 -130 51 -248.5t136.5 -204t204 -136.5t248.5 -51q144 0 273.5 61.5t220.5 171.5zM1465 318q-94 -203 -283.5 -324.5t-413.5 -121.5q-156 0 -298 61 t-245 164t-164 245t-61 298q0 153 57.5 292.5t156 241.5t235.5 164.5t290 68.5q44 2 61 -39q18 -41 -15 -72q-86 -78 -131.5 -181.5t-45.5 -218.5q0 -148 73 -273t198 -198t273 -73q118 0 228 51q41 18 72 -13q14 -14 17.5 -34t-4.5 -38z" />
-<glyph unicode="&#xf187;" horiz-adv-x="1792" d="M1088 704q0 26 -19 45t-45 19h-256q-26 0 -45 -19t-19 -45t19 -45t45 -19h256q26 0 45 19t19 45zM1664 896v-960q0 -26 -19 -45t-45 -19h-1408q-26 0 -45 19t-19 45v960q0 26 19 45t45 19h1408q26 0 45 -19t19 -45zM1728 1344v-256q0 -26 -19 -45t-45 -19h-1536 q-26 0 -45 19t-19 45v256q0 26 19 45t45 19h1536q26 0 45 -19t19 -45z" />
-<glyph unicode="&#xf188;" horiz-adv-x="1664" d="M1632 576q0 -26 -19 -45t-45 -19h-224q0 -171 -67 -290l208 -209q19 -19 19 -45t-19 -45q-18 -19 -45 -19t-45 19l-198 197q-5 -5 -15 -13t-42 -28.5t-65 -36.5t-82 -29t-97 -13v896h-128v-896q-51 0 -101.5 13.5t-87 33t-66 39t-43.5 32.5l-15 14l-183 -207 q-20 -21 -48 -21q-24 0 -43 16q-19 18 -20.5 44.5t15.5 46.5l202 227q-58 114 -58 274h-224q-26 0 -45 19t-19 45t19 45t45 19h224v294l-173 173q-19 19 -19 45t19 45t45 19t45 -19l173 -173h844l173 173q19 19 45 19t45 -19t19 -45t-19 -45l-173 -173v-294h224q26 0 45 -19 t19 -45zM1152 1152h-640q0 133 93.5 226.5t226.5 93.5t226.5 -93.5t93.5 -226.5z" />
-<glyph unicode="&#xf189;" horiz-adv-x="1920" d="M1917 1016q23 -64 -150 -294q-24 -32 -65 -85q-78 -100 -90 -131q-17 -41 14 -81q17 -21 81 -82h1l1 -1l1 -1l2 -2q141 -131 191 -221q3 -5 6.5 -12.5t7 -26.5t-0.5 -34t-25 -27.5t-59 -12.5l-256 -4q-24 -5 -56 5t-52 22l-20 12q-30 21 -70 64t-68.5 77.5t-61 58 t-56.5 15.5q-3 -1 -8 -3.5t-17 -14.5t-21.5 -29.5t-17 -52t-6.5 -77.5q0 -15 -3.5 -27.5t-7.5 -18.5l-4 -5q-18 -19 -53 -22h-115q-71 -4 -146 16.5t-131.5 53t-103 66t-70.5 57.5l-25 24q-10 10 -27.5 30t-71.5 91t-106 151t-122.5 211t-130.5 272q-6 16 -6 27t3 16l4 6 q15 19 57 19l274 2q12 -2 23 -6.5t16 -8.5l5 -3q16 -11 24 -32q20 -50 46 -103.5t41 -81.5l16 -29q29 -60 56 -104t48.5 -68.5t41.5 -38.5t34 -14t27 5q2 1 5 5t12 22t13.5 47t9.5 81t0 125q-2 40 -9 73t-14 46l-6 12q-25 34 -85 43q-13 2 5 24q17 19 38 30q53 26 239 24 q82 -1 135 -13q20 -5 33.5 -13.5t20.5 -24t10.5 -32t3.5 -45.5t-1 -55t-2.5 -70.5t-1.5 -82.5q0 -11 -1 -42t-0.5 -48t3.5 -40.5t11.5 -39t22.5 -24.5q8 -2 17 -4t26 11t38 34.5t52 67t68 107.5q60 104 107 225q4 10 10 17.5t11 10.5l4 3l5 2.5t13 3t20 0.5l288 2 q39 5 64 -2.5t31 -16.5z" />
-<glyph unicode="&#xf18a;" horiz-adv-x="1792" d="M675 252q21 34 11 69t-45 50q-34 14 -73 1t-60 -46q-22 -34 -13 -68.5t43 -50.5t74.5 -2.5t62.5 47.5zM769 373q8 13 3.5 26.5t-17.5 18.5q-14 5 -28.5 -0.5t-21.5 -18.5q-17 -31 13 -45q14 -5 29 0.5t22 18.5zM943 266q-45 -102 -158 -150t-224 -12 q-107 34 -147.5 126.5t6.5 187.5q47 93 151.5 139t210.5 19q111 -29 158.5 -119.5t2.5 -190.5zM1255 426q-9 96 -89 170t-208.5 109t-274.5 21q-223 -23 -369.5 -141.5t-132.5 -264.5q9 -96 89 -170t208.5 -109t274.5 -21q223 23 369.5 141.5t132.5 264.5zM1563 422 q0 -68 -37 -139.5t-109 -137t-168.5 -117.5t-226 -83t-270.5 -31t-275 33.5t-240.5 93t-171.5 151t-65 199.5q0 115 69.5 245t197.5 258q169 169 341.5 236t246.5 -7q65 -64 20 -209q-4 -14 -1 -20t10 -7t14.5 0.5t13.5 3.5l6 2q139 59 246 59t153 -61q45 -63 0 -178 q-2 -13 -4.5 -20t4.5 -12.5t12 -7.5t17 -6q57 -18 103 -47t80 -81.5t34 -116.5zM1489 1046q42 -47 54.5 -108.5t-6.5 -117.5q-8 -23 -29.5 -34t-44.5 -4q-23 8 -34 29.5t-4 44.5q20 63 -24 111t-107 35q-24 -5 -45 8t-25 37q-5 24 8 44.5t37 25.5q60 13 119 -5.5t101 -65.5z M1670 1209q87 -96 112.5 -222.5t-13.5 -241.5q-9 -27 -34 -40t-52 -4t-40 34t-5 52q28 82 10 172t-80 158q-62 69 -148 95.5t-173 8.5q-28 -6 -52 9.5t-30 43.5t9.5 51.5t43.5 29.5q123 26 244 -11.5t208 -134.5z" />
-<glyph unicode="&#xf18b;" horiz-adv-x="1920" d="M805 163q-122 -67 -261 -67q-141 0 -261 67q98 61 167 149t94 191q25 -103 94 -191t167 -149zM453 1176v-344q0 -179 -89.5 -326t-234.5 -217q-129 152 -129 351q0 200 129.5 352t323.5 184zM958 991q-128 -152 -128 -351q0 -201 128 -351q-145 70 -234.5 218t-89.5 328 v341q196 -33 324 -185zM1638 163q-122 -67 -261 -67q-141 0 -261 67q98 61 167 149t94 191q25 -103 94 -191t167 -149zM1286 1176v-344q0 -179 -91 -326t-237 -217v0q133 154 133 351q0 195 -133 351q129 151 328 185zM1920 640q0 -201 -129 -351q-145 70 -234.5 218 t-89.5 328v341q194 -32 323.5 -184t129.5 -352z" />
-<glyph unicode="&#xf18c;" horiz-adv-x="1792" />
-<glyph unicode="&#xf18d;" horiz-adv-x="1792" />
-<glyph unicode="&#xf18e;" horiz-adv-x="1792" />
-<glyph unicode="&#xf500;" horiz-adv-x="1792" />
-</font>
-</defs></svg> 
\ No newline at end of file
diff --git a/fonts/fontawesome-webfont.ttf b/fonts/fontawesome-webfont.ttf
deleted file mode 100755
index d3659246..00000000
Binary files a/fonts/fontawesome-webfont.ttf and /dev/null differ
diff --git a/fonts/fontawesome-webfont.woff b/fonts/fontawesome-webfont.woff
deleted file mode 100755
index b9bd17e1..00000000
Binary files a/fonts/fontawesome-webfont.woff and /dev/null differ
diff --git a/generate_config.sh b/generate_config.sh
new file mode 100755
index 00000000..213bdcdb
--- /dev/null
+++ b/generate_config.sh
@@ -0,0 +1,59 @@
+#!/bin/bash
+
+if [[ -f mailcow.conf ]]; then
+  read -r -p "A config file exists and will be overwritten, are you sure you want to contine? [y/N] " response
+  case $response in
+    [yY][eE][sS]|[yY])
+      mv mailcow.conf mailcow.conf_backup
+      ;;
+    *)
+      exit 1
+    ;;
+  esac
+fi
+
+if [ -z "$MAILCOW_HOSTNAME" ]; then
+  read -p "Hostname (FQDN): " -ei "mx.example.org" MAILCOW_HOSTNAME
+fi
+
+if [ -z "$TZ" ]; then
+  read -p "Timezone: " -ei "Europe/Berlin" TZ
+fi
+
+cat << EOF > mailcow.conf
+# ------------------------------
+# mailcow web ui configuration
+# ------------------------------
+# example.org is _not_ a valid hostname, use a fqdn here.
+# Default admin user is "admin"
+# Default password is "moohoo"
+MAILCOW_HOSTNAME=${MAILCOW_HOSTNAME}
+
+# ------------------------------
+# SQL database configuration
+# ------------------------------
+DBNAME=mailcow
+DBUSER=mailcow
+
+# Please use long, random alphanumeric strings (A-Za-z0-9)
+DBPASS=$(</dev/urandom tr -dc A-Za-z0-9 | head -c 28)
+DBROOT=$(</dev/urandom tr -dc A-Za-z0-9 | head -c 28)
+
+# ------------------------------
+# Misc configuration
+# ------------------------------
+# You should leave that alone
+# Can also be 11.22.33.44:25 or 0.0.0.0:465 etc. for specific bindings
+HTTPS_PORT=443
+SMTP_PORT=25
+SMTPS_PORT=465
+SUBMISSION_PORT=587
+IMAP_PORT=143
+IMAPS_PORT=993
+POP_PORT=110
+POPS_PORT=995
+SIEVE_PORT=4190
+
+# Your timezone
+TZ=${TZ}
+EOF
diff --git a/img/favicon.ico b/img/favicon.ico
deleted file mode 100644
index e85006a3..00000000
Binary files a/img/favicon.ico and /dev/null differ
diff --git a/index.html b/index.html
deleted file mode 100644
index 1d5ee018..00000000
--- a/index.html
+++ /dev/null
@@ -1,207 +0,0 @@
-<!DOCTYPE html>
-<!--[if IE 8]><html class="no-js lt-ie9" lang="en" > <![endif]-->
-<!--[if gt IE 8]><!--> <html class="no-js" lang="en" > <!--<![endif]-->
-<head>
-  <meta charset="utf-8">
-  <meta http-equiv="X-UA-Compatible" content="IE=edge">
-  <meta name="viewport" content="width=device-width, initial-scale=1.0">
-  <meta name="description" content="None">
-  
-  <link rel="shortcut icon" href="./img/favicon.ico">
-  <title>Overview - mailcow: dockerized</title>
-  <link href='https://fonts.googleapis.com/css?family=Lato:400,700|Roboto+Slab:400,700|Inconsolata:400,700' rel='stylesheet' type='text/css'>
-
-  <link rel="stylesheet" href="./css/theme.css" type="text/css" />
-  <link rel="stylesheet" href="./css/theme_extra.css" type="text/css" />
-  <link rel="stylesheet" href="./css/highlight.css">
-  
-  <script>
-    // Current page data
-    var mkdocs_page_name = "Overview";
-    var mkdocs_page_input_path = "index.md";
-    var mkdocs_page_url = "/";
-  </script>
-  
-  <script src="./js/jquery-2.1.1.min.js"></script>
-  <script src="./js/modernizr-2.8.3.min.js"></script>
-  <script type="text/javascript" src="./js/highlight.pack.js"></script> 
-  
-</head>
-
-<body class="wy-body-for-nav" role="document">
-
-  <div class="wy-grid-for-nav">
-
-    
-    <nav data-toggle="wy-nav-shift" class="wy-nav-side stickynav">
-      <div class="wy-side-nav-search">
-        <a href="." class="icon icon-home"> mailcow: dockerized</a>
-        <div role="search">
-  <form id ="rtd-search-form" class="wy-form" action="./search.html" method="get">
-    <input type="text" name="q" placeholder="Search docs" />
-  </form>
-</div>
-      </div>
-
-      <div class="wy-menu wy-menu-vertical" data-spy="affix" role="navigation" aria-label="main navigation">
-	<ul class="current">
-          
-            <li>
-    <li class="toctree-l1 current">
-        <a class="current" href=".">Overview</a>
-        
-            <ul>
-            
-                <li class="toctree-l3"><a href="#mailcow-dockerized-">mailcow: dockerized - 🐮 + 🐋 = 💕</a></li>
-                
-            
-            </ul>
-        
-    </li>
-<li>
-          
-            <li>
-    <li class="toctree-l1 ">
-        <a class="" href="install/">Installation</a>
-        
-    </li>
-<li>
-          
-            <li>
-    <li class="toctree-l1 ">
-        <a class="" href="first_steps/">First Steps</a>
-        
-    </li>
-<li>
-          
-            <li>
-    <li class="toctree-l1 ">
-        <a class="" href="u_and_e/">Usage & Examples</a>
-        
-    </li>
-<li>
-          
-        </ul>
-      </div>
-      &nbsp;
-    </nav>
-
-    <section data-toggle="wy-nav-shift" class="wy-nav-content-wrap">
-
-      
-      <nav class="wy-nav-top" role="navigation" aria-label="top navigation">
-        <i data-toggle="wy-nav-top" class="fa fa-bars"></i>
-        <a href=".">mailcow: dockerized</a>
-      </nav>
-
-      
-      <div class="wy-nav-content">
-        <div class="rst-content">
-          <div role="navigation" aria-label="breadcrumbs navigation">
-  <ul class="wy-breadcrumbs">
-    <li><a href=".">Docs</a> &raquo;</li>
-    
-      
-    
-    <li>Overview</li>
-    <li class="wy-breadcrumbs-aside">
-      
-        <a href="https://github.com/andryyy/mailcow-dockerized/edit/master/docs/index.md"
-          class="icon icon-github"> Edit on GitHub</a>
-      
-    </li>
-  </ul>
-  <hr/>
-</div>
-          <div role="main">
-            <div class="section">
-              
-                <h1 id="mailcow-dockerized-">mailcow: dockerized - 🐮 + 🐋 = 💕</h1>
-<p><a href="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&amp;hosted_button_id=JWBSYHF4SMC68"><img alt="Donate" src="https://img.shields.io/badge/Donate-PayPal-green.svg" /></a></p>
-<p>mailcow dockerized comes with <strong>11 containers</strong> linked in <strong>one bridged network</strong>.</p>
-<ul>
-<li>Dovecot</li>
-<li>Memcached</li>
-<li>Redis</li>
-<li>MySQL</li>
-<li>Bind9 (Resolver) (formerly PDNS Recursor)</li>
-<li>PHP-FPM</li>
-<li>Postfix</li>
-<li>Nginx</li>
-<li>Rmilter</li>
-<li>Rspamd</li>
-<li>SOGo</li>
-</ul>
-<p><strong>6 volumes</strong> to keep dynamic data - take care of them!</p>
-<ul>
-<li>vmail-vol-1</li>
-<li>dkim-vol-1</li>
-<li>redis-vol-1</li>
-<li>mysql-vol-1</li>
-<li>rspamd-vol-1</li>
-<li>postfix-vol-1</li>
-</ul>
-<p>The integrated <strong>mailcow UI</strong> allows administrative work on your mail server instance as well as separated domain administrator and mailbox user access:</p>
-<ul>
-<li>DKIM key management</li>
-<li>Black- and whitelists per domain and per user</li>
-<li>Spam score managment per-user (reject spam, mark spam, greylist)</li>
-<li>Allow mailbox users to create temporary spam aliases</li>
-<li>Prepend mail tags to subject or move mail to subfolder (per-user)</li>
-<li>Allow mailbox users to toggle incoming and outgoing TLS enforcement</li>
-<li>Allow users to reset SOGo ActiveSync device caches</li>
-<li>imapsync to migrate or pull remote mailboxes regularly</li>
-<li>TFA: Yubi OTP and U2F USB (Google Chrome and derivates only)</li>
-<li>Add domains, mailboxes, aliases, domain aliases and SOGo resources</li>
-</ul>
-<p><em><a href="https://www.servercow.de">Looking for a farm to host your cow?</a></em> </p>
-              
-            </div>
-          </div>
-          <footer>
-  
-    <div class="rst-footer-buttons" role="navigation" aria-label="footer navigation">
-      
-        <a href="install/" class="btn btn-neutral float-right" title="Installation">Next <span class="icon icon-circle-arrow-right"></span></a>
-      
-      
-    </div>
-  
-
-  <hr/>
-
-  <div role="contentinfo">
-    <!-- Copyright etc -->
-    
-  </div>
-
-  Built with <a href="http://www.mkdocs.org">MkDocs</a> using a <a href="https://github.com/snide/sphinx_rtd_theme">theme</a> provided by <a href="https://readthedocs.org">Read the Docs</a>.
-</footer>
-	  
-        </div>
-      </div>
-
-    </section>
-    
-  </div>
-
-  <div class="rst-versions" role="note" style="cursor: pointer">
-    <span class="rst-current-version" data-toggle="rst-current-version">
-      
-          <a href="https://github.com/andryyy/mailcow-dockerized/" class="icon icon-github" style="float: left; color: #fcfcfc"> GitHub</a>
-      
-      
-      
-        <span style="margin-left: 15px"><a href="install/" style="color: #fcfcfc">Next &raquo;</a></span>
-      
-    </span>
-</div>
-    <script src="./js/theme.js"></script>
-
-</body>
-</html>
-
-<!--
-MkDocs version : 0.16.1
-Build Date UTC : 2017-03-02 10:21:46
--->
diff --git a/install/index.html b/install/index.html
deleted file mode 100644
index c44371cf..00000000
--- a/install/index.html
+++ /dev/null
@@ -1,227 +0,0 @@
-<!DOCTYPE html>
-<!--[if IE 8]><html class="no-js lt-ie9" lang="en" > <![endif]-->
-<!--[if gt IE 8]><!--> <html class="no-js" lang="en" > <!--<![endif]-->
-<head>
-  <meta charset="utf-8">
-  <meta http-equiv="X-UA-Compatible" content="IE=edge">
-  <meta name="viewport" content="width=device-width, initial-scale=1.0">
-  
-  
-  <link rel="shortcut icon" href="../img/favicon.ico">
-  <title>Installation - mailcow: dockerized</title>
-  <link href='https://fonts.googleapis.com/css?family=Lato:400,700|Roboto+Slab:400,700|Inconsolata:400,700' rel='stylesheet' type='text/css'>
-
-  <link rel="stylesheet" href="../css/theme.css" type="text/css" />
-  <link rel="stylesheet" href="../css/theme_extra.css" type="text/css" />
-  <link rel="stylesheet" href="../css/highlight.css">
-  
-  <script>
-    // Current page data
-    var mkdocs_page_name = "Installation";
-    var mkdocs_page_input_path = "install.md";
-    var mkdocs_page_url = "/install/";
-  </script>
-  
-  <script src="../js/jquery-2.1.1.min.js"></script>
-  <script src="../js/modernizr-2.8.3.min.js"></script>
-  <script type="text/javascript" src="../js/highlight.pack.js"></script> 
-  
-</head>
-
-<body class="wy-body-for-nav" role="document">
-
-  <div class="wy-grid-for-nav">
-
-    
-    <nav data-toggle="wy-nav-shift" class="wy-nav-side stickynav">
-      <div class="wy-side-nav-search">
-        <a href=".." class="icon icon-home"> mailcow: dockerized</a>
-        <div role="search">
-  <form id ="rtd-search-form" class="wy-form" action="../search.html" method="get">
-    <input type="text" name="q" placeholder="Search docs" />
-  </form>
-</div>
-      </div>
-
-      <div class="wy-menu wy-menu-vertical" data-spy="affix" role="navigation" aria-label="main navigation">
-	<ul class="current">
-          
-            <li>
-    <li class="toctree-l1 ">
-        <a class="" href="..">Overview</a>
-        
-    </li>
-<li>
-          
-            <li>
-    <li class="toctree-l1 current">
-        <a class="current" href="./">Installation</a>
-        
-            <ul>
-            
-                <li class="toctree-l3"><a href="#install-mailcow">Install mailcow</a></li>
-                
-            
-                <li class="toctree-l3"><a href="#update-mailcow">Update mailcow</a></li>
-                
-                    <li><a class="toctree-l4" href="#use-dev-branch-not-recommended">Use dev branch (not recommended)</a></li>
-                
-            
-            </ul>
-        
-    </li>
-<li>
-          
-            <li>
-    <li class="toctree-l1 ">
-        <a class="" href="../first_steps/">First Steps</a>
-        
-    </li>
-<li>
-          
-            <li>
-    <li class="toctree-l1 ">
-        <a class="" href="../u_and_e/">Usage & Examples</a>
-        
-    </li>
-<li>
-          
-        </ul>
-      </div>
-      &nbsp;
-    </nav>
-
-    <section data-toggle="wy-nav-shift" class="wy-nav-content-wrap">
-
-      
-      <nav class="wy-nav-top" role="navigation" aria-label="top navigation">
-        <i data-toggle="wy-nav-top" class="fa fa-bars"></i>
-        <a href="..">mailcow: dockerized</a>
-      </nav>
-
-      
-      <div class="wy-nav-content">
-        <div class="rst-content">
-          <div role="navigation" aria-label="breadcrumbs navigation">
-  <ul class="wy-breadcrumbs">
-    <li><a href="..">Docs</a> &raquo;</li>
-    
-      
-    
-    <li>Installation</li>
-    <li class="wy-breadcrumbs-aside">
-      
-        <a href="https://github.com/andryyy/mailcow-dockerized/edit/master/docs/install.md"
-          class="icon icon-github"> Edit on GitHub</a>
-      
-    </li>
-  </ul>
-  <hr/>
-</div>
-          <div role="main">
-            <div class="section">
-              
-                <h1 id="install-mailcow">Install mailcow</h1>
-<ol>
-<li>
-<p>You need Docker.</p>
-<ul>
-<li>Most systems can install Docker by running <code>wget -qO- https://get.docker.com/ | sh</code></li>
-</ul>
-</li>
-<li>
-<p>You need Docker Compose.</p>
-<ul>
-<li>Learn <a href="https://docs.docker.com/compose/install/">how to install Docker Compose</a> - or:</li>
-<li><code>curl -L "https://github.com/docker/compose/releases/download/composer_version/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose &amp;&amp; chmod +x /usr/local/bin/docker-compose</code></li>
-</ul>
-</li>
-<li>
-<p>Clone the master branch of the repository and run <code>./generate_config.sh</code> to generate a file "mailcow.conf". You will be asked for a hostname and a timezone:</p>
-<ul>
-<li><code>git clone https://github.com/andryyy/mailcow-dockerized &amp;&amp; cd mailcow-dockerized</code></li>
-<li><code>./generate_config.sh</code></li>
-<li>Open and check "mailcow.conf" if you need or want to make changes to ports (for example changing the default HTTPS port)</li>
-</ul>
-</li>
-<li>
-<p>Run the composer file.</p>
-<ul>
-<li><code>docker-compose up -d</code></li>
-</ul>
-</li>
-</ol>
-<p>Done.</p>
-<p>You can now access <strong>https://${MAILCOW_HOSTNAME}</strong> with the default credentials <code>admin</code> + password <code>moohoo</code>.</p>
-<p>It may take a while for MySQL to warm up, so please wait half a minute.</p>
-<p>The database will be initialized right after a connection to MySQL can be established.</p>
-<h1 id="update-mailcow">Update mailcow</h1>
-<p>There is no update routine.</p>
-<p>You need to refresh your pulled repository clone by running <code>git pull</code> - this will likely fail due to changes to your local configuration. But that's why we use git! :-)</p>
-<p>Whatever file has local changes, add and commit it to your repository clone. For example:</p>
-<pre><code>git add data/conf/postfix/main.cf data/conf/dovecot/dovecot.conf
-git commit -m &quot;My changes to main.cf and dovecot.conf
-</code></pre>
-
-<p>Try running <code>git pull</code> again and resolve conflicts, if any.</p>
-<p>Now update all images, apply changes to containers and restart all services:</p>
-<pre><code>docker-compose pull
-docker-compose up -d --remove-orphans
-docker-compose restart
-</code></pre>
-
-<h2 id="use-dev-branch-not-recommended">Use dev branch (not recommended)</h2>
-<p>When you checkout the dev branch, you will most likely end up using the "master" images with code base of "dev".
-If there were critical changes to the images in dev, mailcow will not work.</p>
-<p>But you can still build the images by yourself:</p>
-<pre><code>docker-compose up -d --build
-</code></pre>
-              
-            </div>
-          </div>
-          <footer>
-  
-    <div class="rst-footer-buttons" role="navigation" aria-label="footer navigation">
-      
-        <a href="../first_steps/" class="btn btn-neutral float-right" title="First Steps">Next <span class="icon icon-circle-arrow-right"></span></a>
-      
-      
-        <a href=".." class="btn btn-neutral" title="Overview"><span class="icon icon-circle-arrow-left"></span> Previous</a>
-      
-    </div>
-  
-
-  <hr/>
-
-  <div role="contentinfo">
-    <!-- Copyright etc -->
-    
-  </div>
-
-  Built with <a href="http://www.mkdocs.org">MkDocs</a> using a <a href="https://github.com/snide/sphinx_rtd_theme">theme</a> provided by <a href="https://readthedocs.org">Read the Docs</a>.
-</footer>
-	  
-        </div>
-      </div>
-
-    </section>
-    
-  </div>
-
-  <div class="rst-versions" role="note" style="cursor: pointer">
-    <span class="rst-current-version" data-toggle="rst-current-version">
-      
-          <a href="https://github.com/andryyy/mailcow-dockerized/" class="icon icon-github" style="float: left; color: #fcfcfc"> GitHub</a>
-      
-      
-        <span><a href=".." style="color: #fcfcfc;">&laquo; Previous</a></span>
-      
-      
-        <span style="margin-left: 15px"><a href="../first_steps/" style="color: #fcfcfc">Next &raquo;</a></span>
-      
-    </span>
-</div>
-    <script src="../js/theme.js"></script>
-
-</body>
-</html>
diff --git a/js/highlight.pack.js b/js/highlight.pack.js
deleted file mode 100644
index a5818dfb..00000000
--- a/js/highlight.pack.js
+++ /dev/null
@@ -1,2 +0,0 @@
-!function(e){"undefined"!=typeof exports?e(exports):(window.hljs=e({}),"function"==typeof define&&define.amd&&define([],function(){return window.hljs}))}(function(e){function n(e){return e.replace(/&/gm,"&amp;").replace(/</gm,"&lt;").replace(/>/gm,"&gt;")}function t(e){return e.nodeName.toLowerCase()}function r(e,n){var t=e&&e.exec(n);return t&&0==t.index}function a(e){var n=(e.className+" "+(e.parentNode?e.parentNode.className:"")).split(/\s+/);return n=n.map(function(e){return e.replace(/^lang(uage)?-/,"")}),n.filter(function(e){return N(e)||/no(-?)highlight|plain|text/.test(e)})[0]}function i(e,n){var t,r={};for(t in e)r[t]=e[t];if(n)for(t in n)r[t]=n[t];return r}function o(e){var n=[];return function r(e,a){for(var i=e.firstChild;i;i=i.nextSibling)3==i.nodeType?a+=i.nodeValue.length:1==i.nodeType&&(n.push({event:"start",offset:a,node:i}),a=r(i,a),t(i).match(/br|hr|img|input/)||n.push({event:"stop",offset:a,node:i}));return a}(e,0),n}function u(e,r,a){function i(){return e.length&&r.length?e[0].offset!=r[0].offset?e[0].offset<r[0].offset?e:r:"start"==r[0].event?e:r:e.length?e:r}function o(e){function r(e){return" "+e.nodeName+'="'+n(e.value)+'"'}l+="<"+t(e)+Array.prototype.map.call(e.attributes,r).join("")+">"}function u(e){l+="</"+t(e)+">"}function c(e){("start"==e.event?o:u)(e.node)}for(var s=0,l="",f=[];e.length||r.length;){var g=i();if(l+=n(a.substr(s,g[0].offset-s)),s=g[0].offset,g==e){f.reverse().forEach(u);do c(g.splice(0,1)[0]),g=i();while(g==e&&g.length&&g[0].offset==s);f.reverse().forEach(o)}else"start"==g[0].event?f.push(g[0].node):f.pop(),c(g.splice(0,1)[0])}return l+n(a.substr(s))}function c(e){function n(e){return e&&e.source||e}function t(t,r){return new RegExp(n(t),"m"+(e.cI?"i":"")+(r?"g":""))}function r(a,o){if(!a.compiled){if(a.compiled=!0,a.k=a.k||a.bK,a.k){var u={},c=function(n,t){e.cI&&(t=t.toLowerCase()),t.split(" ").forEach(function(e){var t=e.split("|");u[t[0]]=[n,t[1]?Number(t[1]):1]})};"string"==typeof a.k?c("keyword",a.k):Object.keys(a.k).forEach(function(e){c(e,a.k[e])}),a.k=u}a.lR=t(a.l||/\b\w+\b/,!0),o&&(a.bK&&(a.b="\\b("+a.bK.split(" ").join("|")+")\\b"),a.b||(a.b=/\B|\b/),a.bR=t(a.b),a.e||a.eW||(a.e=/\B|\b/),a.e&&(a.eR=t(a.e)),a.tE=n(a.e)||"",a.eW&&o.tE&&(a.tE+=(a.e?"|":"")+o.tE)),a.i&&(a.iR=t(a.i)),void 0===a.r&&(a.r=1),a.c||(a.c=[]);var s=[];a.c.forEach(function(e){e.v?e.v.forEach(function(n){s.push(i(e,n))}):s.push("self"==e?a:e)}),a.c=s,a.c.forEach(function(e){r(e,a)}),a.starts&&r(a.starts,o);var l=a.c.map(function(e){return e.bK?"\\.?("+e.b+")\\.?":e.b}).concat([a.tE,a.i]).map(n).filter(Boolean);a.t=l.length?t(l.join("|"),!0):{exec:function(){return null}}}}r(e)}function s(e,t,a,i){function o(e,n){for(var t=0;t<n.c.length;t++)if(r(n.c[t].bR,e))return n.c[t]}function u(e,n){if(r(e.eR,n)){for(;e.endsParent&&e.parent;)e=e.parent;return e}return e.eW?u(e.parent,n):void 0}function f(e,n){return!a&&r(n.iR,e)}function g(e,n){var t=E.cI?n[0].toLowerCase():n[0];return e.k.hasOwnProperty(t)&&e.k[t]}function p(e,n,t,r){var a=r?"":x.classPrefix,i='<span class="'+a,o=t?"":"</span>";return i+=e+'">',i+n+o}function d(){if(!L.k)return n(y);var e="",t=0;L.lR.lastIndex=0;for(var r=L.lR.exec(y);r;){e+=n(y.substr(t,r.index-t));var a=g(L,r);a?(B+=a[1],e+=p(a[0],n(r[0]))):e+=n(r[0]),t=L.lR.lastIndex,r=L.lR.exec(y)}return e+n(y.substr(t))}function h(){if(L.sL&&!w[L.sL])return n(y);var e=L.sL?s(L.sL,y,!0,M[L.sL]):l(y);return L.r>0&&(B+=e.r),"continuous"==L.subLanguageMode&&(M[L.sL]=e.top),p(e.language,e.value,!1,!0)}function b(){return void 0!==L.sL?h():d()}function v(e,t){var r=e.cN?p(e.cN,"",!0):"";e.rB?(k+=r,y=""):e.eB?(k+=n(t)+r,y=""):(k+=r,y=t),L=Object.create(e,{parent:{value:L}})}function m(e,t){if(y+=e,void 0===t)return k+=b(),0;var r=o(t,L);if(r)return k+=b(),v(r,t),r.rB?0:t.length;var a=u(L,t);if(a){var i=L;i.rE||i.eE||(y+=t),k+=b();do L.cN&&(k+="</span>"),B+=L.r,L=L.parent;while(L!=a.parent);return i.eE&&(k+=n(t)),y="",a.starts&&v(a.starts,""),i.rE?0:t.length}if(f(t,L))throw new Error('Illegal lexeme "'+t+'" for mode "'+(L.cN||"<unnamed>")+'"');return y+=t,t.length||1}var E=N(e);if(!E)throw new Error('Unknown language: "'+e+'"');c(E);var R,L=i||E,M={},k="";for(R=L;R!=E;R=R.parent)R.cN&&(k=p(R.cN,"",!0)+k);var y="",B=0;try{for(var C,j,I=0;;){if(L.t.lastIndex=I,C=L.t.exec(t),!C)break;j=m(t.substr(I,C.index-I),C[0]),I=C.index+j}for(m(t.substr(I)),R=L;R.parent;R=R.parent)R.cN&&(k+="</span>");return{r:B,value:k,language:e,top:L}}catch(S){if(-1!=S.message.indexOf("Illegal"))return{r:0,value:n(t)};throw S}}function l(e,t){t=t||x.languages||Object.keys(w);var r={r:0,value:n(e)},a=r;return t.forEach(function(n){if(N(n)){var t=s(n,e,!1);t.language=n,t.r>a.r&&(a=t),t.r>r.r&&(a=r,r=t)}}),a.language&&(r.second_best=a),r}function f(e){return x.tabReplace&&(e=e.replace(/^((<[^>]+>|\t)+)/gm,function(e,n){return n.replace(/\t/g,x.tabReplace)})),x.useBR&&(e=e.replace(/\n/g,"<br>")),e}function g(e,n,t){var r=n?E[n]:t,a=[e.trim()];return e.match(/\bhljs\b/)||a.push("hljs"),-1===e.indexOf(r)&&a.push(r),a.join(" ").trim()}function p(e){var n=a(e);if(!/no(-?)highlight|plain|text/.test(n)){var t;x.useBR?(t=document.createElementNS("http://www.w3.org/1999/xhtml","div"),t.innerHTML=e.innerHTML.replace(/\n/g,"").replace(/<br[ \/]*>/g,"\n")):t=e;var r=t.textContent,i=n?s(n,r,!0):l(r),c=o(t);if(c.length){var p=document.createElementNS("http://www.w3.org/1999/xhtml","div");p.innerHTML=i.value,i.value=u(c,o(p),r)}i.value=f(i.value),e.innerHTML=i.value,e.className=g(e.className,n,i.language),e.result={language:i.language,re:i.r},i.second_best&&(e.second_best={language:i.second_best.language,re:i.second_best.r})}}function d(e){x=i(x,e)}function h(){if(!h.called){h.called=!0;var e=document.querySelectorAll("pre code");Array.prototype.forEach.call(e,p)}}function b(){addEventListener("DOMContentLoaded",h,!1),addEventListener("load",h,!1)}function v(n,t){var r=w[n]=t(e);r.aliases&&r.aliases.forEach(function(e){E[e]=n})}function m(){return Object.keys(w)}function N(e){return w[e]||w[E[e]]}var x={classPrefix:"hljs-",tabReplace:null,useBR:!1,languages:void 0},w={},E={};return e.highlight=s,e.highlightAuto=l,e.fixMarkup=f,e.highlightBlock=p,e.configure=d,e.initHighlighting=h,e.initHighlightingOnLoad=b,e.registerLanguage=v,e.listLanguages=m,e.getLanguage=N,e.inherit=i,e.IR="[a-zA-Z]\\w*",e.UIR="[a-zA-Z_]\\w*",e.NR="\\b\\d+(\\.\\d+)?",e.CNR="\\b(0[xX][a-fA-F0-9]+|(\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",e.BNR="\\b(0b[01]+)",e.RSR="!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~",e.BE={b:"\\\\[\\s\\S]",r:0},e.ASM={cN:"string",b:"'",e:"'",i:"\\n",c:[e.BE]},e.QSM={cN:"string",b:'"',e:'"',i:"\\n",c:[e.BE]},e.PWM={b:/\b(a|an|the|are|I|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such)\b/},e.C=function(n,t,r){var a=e.inherit({cN:"comment",b:n,e:t,c:[]},r||{});return a.c.push(e.PWM),a},e.CLCM=e.C("//","$"),e.CBCM=e.C("/\\*","\\*/"),e.HCM=e.C("#","$"),e.NM={cN:"number",b:e.NR,r:0},e.CNM={cN:"number",b:e.CNR,r:0},e.BNM={cN:"number",b:e.BNR,r:0},e.CSSNM={cN:"number",b:e.NR+"(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?",r:0},e.RM={cN:"regexp",b:/\//,e:/\/[gimuy]*/,i:/\n/,c:[e.BE,{b:/\[/,e:/\]/,r:0,c:[e.BE]}]},e.TM={cN:"title",b:e.IR,r:0},e.UTM={cN:"title",b:e.UIR,r:0},e});hljs.registerLanguage("objectivec",function(e){var t={cN:"built_in",b:"(AV|CA|CF|CG|CI|MK|MP|NS|UI)\\w+"},i={keyword:"int float while char export sizeof typedef const struct for union unsigned long volatile static bool mutable if do return goto void enum else break extern asm case short default double register explicit signed typename this switch continue wchar_t inline readonly assign readwrite self @synchronized id typeof nonatomic super unichar IBOutlet IBAction strong weak copy in out inout bycopy byref oneway __strong __weak __block __autoreleasing @private @protected @public @try @property @end @throw @catch @finally @autoreleasepool @synthesize @dynamic @selector @optional @required",literal:"false true FALSE TRUE nil YES NO NULL",built_in:"BOOL dispatch_once_t dispatch_queue_t dispatch_sync dispatch_async dispatch_once"},o=/[a-zA-Z@][a-zA-Z0-9_]*/,n="@interface @class @protocol @implementation";return{aliases:["m","mm","objc","obj-c"],k:i,l:o,i:"</",c:[t,e.CLCM,e.CBCM,e.CNM,e.QSM,{cN:"string",v:[{b:'@"',e:'"',i:"\\n",c:[e.BE]},{b:"'",e:"[^\\\\]'",i:"[^\\\\][^']"}]},{cN:"preprocessor",b:"#",e:"$",c:[{cN:"title",v:[{b:'"',e:'"'},{b:"<",e:">"}]}]},{cN:"class",b:"("+n.split(" ").join("|")+")\\b",e:"({|$)",eE:!0,k:n,l:o,c:[e.UTM]},{cN:"variable",b:"\\."+e.UIR,r:0}]}});hljs.registerLanguage("sql",function(e){var t=e.C("--","$");return{cI:!0,i:/[<>]/,c:[{cN:"operator",bK:"begin end start commit rollback savepoint lock alter create drop rename call delete do handler insert load replace select truncate update set show pragma grant merge describe use explain help declare prepare execute deallocate savepoint release unlock purge reset change stop analyze cache flush optimize repair kill install uninstall checksum restore check backup revoke",e:/;/,eW:!0,k:{keyword:"abs absolute acos action add adddate addtime aes_decrypt aes_encrypt after aggregate all allocate alter analyze and any are as asc ascii asin assertion at atan atan2 atn2 authorization authors avg backup before begin benchmark between bin binlog bit_and bit_count bit_length bit_or bit_xor both by cache call cascade cascaded case cast catalog ceil ceiling chain change changed char_length character_length charindex charset check checksum checksum_agg choose close coalesce coercibility collate collation collationproperty column columns columns_updated commit compress concat concat_ws concurrent connect connection connection_id consistent constraint constraints continue contributors conv convert convert_tz corresponding cos cot count count_big crc32 create cross cume_dist curdate current current_date current_time current_timestamp current_user cursor curtime data database databases datalength date_add date_format date_sub dateadd datediff datefromparts datename datepart datetime2fromparts datetimeoffsetfromparts day dayname dayofmonth dayofweek dayofyear deallocate declare decode default deferrable deferred degrees delayed delete des_decrypt des_encrypt des_key_file desc describe descriptor diagnostics difference disconnect distinct distinctrow div do domain double drop dumpfile each else elt enclosed encode encrypt end end-exec engine engines eomonth errors escape escaped event eventdata events except exception exec execute exists exp explain export_set extended external extract fast fetch field fields find_in_set first first_value floor flush for force foreign format found found_rows from from_base64 from_days from_unixtime full function get get_format get_lock getdate getutcdate global go goto grant grants greatest group group_concat grouping grouping_id gtid_subset gtid_subtract handler having help hex high_priority hosts hour ident_current ident_incr ident_seed identified identity if ifnull ignore iif ilike immediate in index indicator inet6_aton inet6_ntoa inet_aton inet_ntoa infile initially inner innodb input insert install instr intersect into is is_free_lock is_ipv4 is_ipv4_compat is_ipv4_mapped is_not is_not_null is_used_lock isdate isnull isolation join key kill language last last_day last_insert_id last_value lcase lead leading least leaves left len lenght level like limit lines ln load load_file local localtime localtimestamp locate lock log log10 log2 logfile logs low_priority lower lpad ltrim make_set makedate maketime master master_pos_wait match matched max md5 medium merge microsecond mid min minute mod mode module month monthname mutex name_const names national natural nchar next no no_write_to_binlog not now nullif nvarchar oct octet_length of old_password on only open optimize option optionally or ord order outer outfile output pad parse partial partition password patindex percent_rank percentile_cont percentile_disc period_add period_diff pi plugin position pow power pragma precision prepare preserve primary prior privileges procedure procedure_analyze processlist profile profiles public publishingservername purge quarter query quick quote quotename radians rand read references regexp relative relaylog release release_lock rename repair repeat replace replicate reset restore restrict return returns reverse revoke right rlike rollback rollup round row row_count rows rpad rtrim savepoint schema scroll sec_to_time second section select serializable server session session_user set sha sha1 sha2 share show sign sin size slave sleep smalldatetimefromparts snapshot some soname soundex sounds_like space sql sql_big_result sql_buffer_result sql_cache sql_calc_found_rows sql_no_cache sql_small_result sql_variant_property sqlstate sqrt square start starting status std stddev stddev_pop stddev_samp stdev stdevp stop str str_to_date straight_join strcmp string stuff subdate substr substring subtime subtring_index sum switchoffset sysdate sysdatetime sysdatetimeoffset system_user sysutcdatetime table tables tablespace tan temporary terminated tertiary_weights then time time_format time_to_sec timediff timefromparts timestamp timestampadd timestampdiff timezone_hour timezone_minute to to_base64 to_days to_seconds todatetimeoffset trailing transaction translation trigger trigger_nestlevel triggers trim truncate try_cast try_convert try_parse ucase uncompress uncompressed_length unhex unicode uninstall union unique unix_timestamp unknown unlock update upgrade upped upper usage use user user_resources using utc_date utc_time utc_timestamp uuid uuid_short validate_password_strength value values var var_pop var_samp variables variance varp version view warnings week weekday weekofyear weight_string when whenever where with work write xml xor year yearweek zon",literal:"true false null",built_in:"array bigint binary bit blob boolean char character date dec decimal float int integer interval number numeric real serial smallint varchar varying int8 serial8 text"},c:[{cN:"string",b:"'",e:"'",c:[e.BE,{b:"''"}]},{cN:"string",b:'"',e:'"',c:[e.BE,{b:'""'}]},{cN:"string",b:"`",e:"`",c:[e.BE]},e.CNM,e.CBCM,t]},e.CBCM,t]}});hljs.registerLanguage("javascript",function(e){return{aliases:["js"],k:{keyword:"in of if for while finally var new function do return void else break catch instanceof with throw case default try this switch continue typeof delete let yield const export super debugger as await",literal:"true false null undefined NaN Infinity",built_in:"eval isFinite isNaN parseFloat parseInt decodeURI decodeURIComponent encodeURI encodeURIComponent escape unescape Object Function Boolean Error EvalError InternalError RangeError ReferenceError StopIteration SyntaxError TypeError URIError Number Math Date String RegExp Array Float32Array Float64Array Int16Array Int32Array Int8Array Uint16Array Uint32Array Uint8Array Uint8ClampedArray ArrayBuffer DataView JSON Intl arguments require module console window document Symbol Set Map WeakSet WeakMap Proxy Reflect Promise"},c:[{cN:"pi",r:10,v:[{b:/^\s*('|")use strict('|")/},{b:/^\s*('|")use asm('|")/}]},e.ASM,e.QSM,{cN:"string",b:"`",e:"`",c:[e.BE,{cN:"subst",b:"\\$\\{",e:"\\}"}]},e.CLCM,e.CBCM,{cN:"number",b:"\\b(0[xXbBoO][a-fA-F0-9]+|(\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",r:0},{b:"("+e.RSR+"|\\b(case|return|throw)\\b)\\s*",k:"return throw case",c:[e.CLCM,e.CBCM,e.RM,{b:/</,e:/>\s*[);\]]/,r:0,sL:"xml"}],r:0},{cN:"function",bK:"function",e:/\{/,eE:!0,c:[e.inherit(e.TM,{b:/[A-Za-z$_][0-9A-Za-z$_]*/}),{cN:"params",b:/\(/,e:/\)/,c:[e.CLCM,e.CBCM],i:/["'\(]/}],i:/\[|%/},{b:/\$[(.]/},{b:"\\."+e.IR,r:0},{bK:"import",e:"[;$]",k:"import from as",c:[e.ASM,e.QSM]},{cN:"class",bK:"class",e:/[{;=]/,eE:!0,i:/[:"\[\]]/,c:[{bK:"extends"},e.UTM]}]}});hljs.registerLanguage("scss",function(e){{var t="[a-zA-Z-][a-zA-Z0-9_-]*",i={cN:"variable",b:"(\\$"+t+")\\b"},r={cN:"function",b:t+"\\(",rB:!0,eE:!0,e:"\\("},o={cN:"hexcolor",b:"#[0-9A-Fa-f]+"};({cN:"attribute",b:"[A-Z\\_\\.\\-]+",e:":",eE:!0,i:"[^\\s]",starts:{cN:"value",eW:!0,eE:!0,c:[r,o,e.CSSNM,e.QSM,e.ASM,e.CBCM,{cN:"important",b:"!important"}]}})}return{cI:!0,i:"[=/|']",c:[e.CLCM,e.CBCM,r,{cN:"id",b:"\\#[A-Za-z0-9_-]+",r:0},{cN:"class",b:"\\.[A-Za-z0-9_-]+",r:0},{cN:"attr_selector",b:"\\[",e:"\\]",i:"$"},{cN:"tag",b:"\\b(a|abbr|acronym|address|area|article|aside|audio|b|base|big|blockquote|body|br|button|canvas|caption|cite|code|col|colgroup|command|datalist|dd|del|details|dfn|div|dl|dt|em|embed|fieldset|figcaption|figure|footer|form|frame|frameset|(h[1-6])|head|header|hgroup|hr|html|i|iframe|img|input|ins|kbd|keygen|label|legend|li|link|map|mark|meta|meter|nav|noframes|noscript|object|ol|optgroup|option|output|p|param|pre|progress|q|rp|rt|ruby|samp|script|section|select|small|span|strike|strong|style|sub|sup|table|tbody|td|textarea|tfoot|th|thead|time|title|tr|tt|ul|var|video)\\b",r:0},{cN:"pseudo",b:":(visited|valid|root|right|required|read-write|read-only|out-range|optional|only-of-type|only-child|nth-of-type|nth-last-of-type|nth-last-child|nth-child|not|link|left|last-of-type|last-child|lang|invalid|indeterminate|in-range|hover|focus|first-of-type|first-line|first-letter|first-child|first|enabled|empty|disabled|default|checked|before|after|active)"},{cN:"pseudo",b:"::(after|before|choices|first-letter|first-line|repeat-index|repeat-item|selection|value)"},i,{cN:"attribute",b:"\\b(z-index|word-wrap|word-spacing|word-break|width|widows|white-space|visibility|vertical-align|unicode-bidi|transition-timing-function|transition-property|transition-duration|transition-delay|transition|transform-style|transform-origin|transform|top|text-underline-position|text-transform|text-shadow|text-rendering|text-overflow|text-indent|text-decoration-style|text-decoration-line|text-decoration-color|text-decoration|text-align-last|text-align|tab-size|table-layout|right|resize|quotes|position|pointer-events|perspective-origin|perspective|page-break-inside|page-break-before|page-break-after|padding-top|padding-right|padding-left|padding-bottom|padding|overflow-y|overflow-x|overflow-wrap|overflow|outline-width|outline-style|outline-offset|outline-color|outline|orphans|order|opacity|object-position|object-fit|normal|none|nav-up|nav-right|nav-left|nav-index|nav-down|min-width|min-height|max-width|max-height|mask|marks|margin-top|margin-right|margin-left|margin-bottom|margin|list-style-type|list-style-position|list-style-image|list-style|line-height|letter-spacing|left|justify-content|initial|inherit|ime-mode|image-orientation|image-resolution|image-rendering|icon|hyphens|height|font-weight|font-variant-ligatures|font-variant|font-style|font-stretch|font-size-adjust|font-size|font-language-override|font-kerning|font-feature-settings|font-family|font|float|flex-wrap|flex-shrink|flex-grow|flex-flow|flex-direction|flex-basis|flex|filter|empty-cells|display|direction|cursor|counter-reset|counter-increment|content|column-width|column-span|column-rule-width|column-rule-style|column-rule-color|column-rule|column-gap|column-fill|column-count|columns|color|clip-path|clip|clear|caption-side|break-inside|break-before|break-after|box-sizing|box-shadow|box-decoration-break|bottom|border-width|border-top-width|border-top-style|border-top-right-radius|border-top-left-radius|border-top-color|border-top|border-style|border-spacing|border-right-width|border-right-style|border-right-color|border-right|border-radius|border-left-width|border-left-style|border-left-color|border-left|border-image-width|border-image-source|border-image-slice|border-image-repeat|border-image-outset|border-image|border-color|border-collapse|border-bottom-width|border-bottom-style|border-bottom-right-radius|border-bottom-left-radius|border-bottom-color|border-bottom|border|background-size|background-repeat|background-position|background-origin|background-image|background-color|background-clip|background-attachment|background-blend-mode|background|backface-visibility|auto|animation-timing-function|animation-play-state|animation-name|animation-iteration-count|animation-fill-mode|animation-duration|animation-direction|animation-delay|animation|align-self|align-items|align-content)\\b",i:"[^\\s]"},{cN:"value",b:"\\b(whitespace|wait|w-resize|visible|vertical-text|vertical-ideographic|uppercase|upper-roman|upper-alpha|underline|transparent|top|thin|thick|text|text-top|text-bottom|tb-rl|table-header-group|table-footer-group|sw-resize|super|strict|static|square|solid|small-caps|separate|se-resize|scroll|s-resize|rtl|row-resize|ridge|right|repeat|repeat-y|repeat-x|relative|progress|pointer|overline|outside|outset|oblique|nowrap|not-allowed|normal|none|nw-resize|no-repeat|no-drop|newspaper|ne-resize|n-resize|move|middle|medium|ltr|lr-tb|lowercase|lower-roman|lower-alpha|loose|list-item|line|line-through|line-edge|lighter|left|keep-all|justify|italic|inter-word|inter-ideograph|inside|inset|inline|inline-block|inherit|inactive|ideograph-space|ideograph-parenthesis|ideograph-numeric|ideograph-alpha|horizontal|hidden|help|hand|groove|fixed|ellipsis|e-resize|double|dotted|distribute|distribute-space|distribute-letter|distribute-all-lines|disc|disabled|default|decimal|dashed|crosshair|collapse|col-resize|circle|char|center|capitalize|break-word|break-all|bottom|both|bolder|bold|block|bidi-override|below|baseline|auto|always|all-scroll|absolute|table|table-cell)\\b"},{cN:"value",b:":",e:";",c:[r,i,o,e.CSSNM,e.QSM,e.ASM,{cN:"important",b:"!important"}]},{cN:"at_rule",b:"@",e:"[{;]",k:"mixin include extend for if else each while charset import debug media page content font-face namespace warn",c:[r,i,e.QSM,e.ASM,o,e.CSSNM,{cN:"preprocessor",b:"\\s[A-Za-z0-9_.-]+",r:0}]}]}});hljs.registerLanguage("mel",function(e){return{k:"int float string vector matrix if else switch case default while do for in break continue global proc return about abs addAttr addAttributeEditorNodeHelp addDynamic addNewShelfTab addPP addPanelCategory addPrefixToName advanceToNextDrivenKey affectedNet affects aimConstraint air alias aliasAttr align alignCtx alignCurve alignSurface allViewFit ambientLight angle angleBetween animCone animCurveEditor animDisplay animView annotate appendStringArray applicationName applyAttrPreset applyTake arcLenDimContext arcLengthDimension arclen arrayMapper art3dPaintCtx artAttrCtx artAttrPaintVertexCtx artAttrSkinPaintCtx artAttrTool artBuildPaintMenu artFluidAttrCtx artPuttyCtx artSelectCtx artSetPaintCtx artUserPaintCtx assignCommand assignInputDevice assignViewportFactories attachCurve attachDeviceAttr attachSurface attrColorSliderGrp attrCompatibility attrControlGrp attrEnumOptionMenu attrEnumOptionMenuGrp attrFieldGrp attrFieldSliderGrp attrNavigationControlGrp attrPresetEditWin attributeExists attributeInfo attributeMenu attributeQuery autoKeyframe autoPlace bakeClip bakeFluidShading bakePartialHistory bakeResults bakeSimulation basename basenameEx batchRender bessel bevel bevelPlus binMembership bindSkin blend2 blendShape blendShapeEditor blendShapePanel blendTwoAttr blindDataType boneLattice boundary boxDollyCtx boxZoomCtx bufferCurve buildBookmarkMenu buildKeyframeMenu button buttonManip CBG cacheFile cacheFileCombine cacheFileMerge cacheFileTrack camera cameraView canCreateManip canvas capitalizeString catch catchQuiet ceil changeSubdivComponentDisplayLevel changeSubdivRegion channelBox character characterMap characterOutlineEditor characterize chdir checkBox checkBoxGrp checkDefaultRenderGlobals choice circle circularFillet clamp clear clearCache clip clipEditor clipEditorCurrentTimeCtx clipSchedule clipSchedulerOutliner clipTrimBefore closeCurve closeSurface cluster cmdFileOutput cmdScrollFieldExecuter cmdScrollFieldReporter cmdShell coarsenSubdivSelectionList collision color colorAtPoint colorEditor colorIndex colorIndexSliderGrp colorSliderButtonGrp colorSliderGrp columnLayout commandEcho commandLine commandPort compactHairSystem componentEditor compositingInterop computePolysetVolume condition cone confirmDialog connectAttr connectControl connectDynamic connectJoint connectionInfo constrain constrainValue constructionHistory container containsMultibyte contextInfo control convertFromOldLayers convertIffToPsd convertLightmap convertSolidTx convertTessellation convertUnit copyArray copyFlexor copyKey copySkinWeights cos cpButton cpCache cpClothSet cpCollision cpConstraint cpConvClothToMesh cpForces cpGetSolverAttr cpPanel cpProperty cpRigidCollisionFilter cpSeam cpSetEdit cpSetSolverAttr cpSolver cpSolverTypes cpTool cpUpdateClothUVs createDisplayLayer createDrawCtx createEditor createLayeredPsdFile createMotionField createNewShelf createNode createRenderLayer createSubdivRegion cross crossProduct ctxAbort ctxCompletion ctxEditMode ctxTraverse currentCtx currentTime currentTimeCtx currentUnit curve curveAddPtCtx curveCVCtx curveEPCtx curveEditorCtx curveIntersect curveMoveEPCtx curveOnSurface curveSketchCtx cutKey cycleCheck cylinder dagPose date defaultLightListCheckBox defaultNavigation defineDataServer defineVirtualDevice deformer deg_to_rad delete deleteAttr deleteShadingGroupsAndMaterials deleteShelfTab deleteUI deleteUnusedBrushes delrandstr detachCurve detachDeviceAttr detachSurface deviceEditor devicePanel dgInfo dgdirty dgeval dgtimer dimWhen directKeyCtx directionalLight dirmap dirname disable disconnectAttr disconnectJoint diskCache displacementToPoly displayAffected displayColor displayCull displayLevelOfDetail displayPref displayRGBColor displaySmoothness displayStats displayString displaySurface distanceDimContext distanceDimension doBlur dolly dollyCtx dopeSheetEditor dot dotProduct doubleProfileBirailSurface drag dragAttrContext draggerContext dropoffLocator duplicate duplicateCurve duplicateSurface dynCache dynControl dynExport dynExpression dynGlobals dynPaintEditor dynParticleCtx dynPref dynRelEdPanel dynRelEditor dynamicLoad editAttrLimits editDisplayLayerGlobals editDisplayLayerMembers editRenderLayerAdjustment editRenderLayerGlobals editRenderLayerMembers editor editorTemplate effector emit emitter enableDevice encodeString endString endsWith env equivalent equivalentTol erf error eval evalDeferred evalEcho event exactWorldBoundingBox exclusiveLightCheckBox exec executeForEachObject exists exp expression expressionEditorListen extendCurve extendSurface extrude fcheck fclose feof fflush fgetline fgetword file fileBrowserDialog fileDialog fileExtension fileInfo filetest filletCurve filter filterCurve filterExpand filterStudioImport findAllIntersections findAnimCurves findKeyframe findMenuItem findRelatedSkinCluster finder firstParentOf fitBspline flexor floatEq floatField floatFieldGrp floatScrollBar floatSlider floatSlider2 floatSliderButtonGrp floatSliderGrp floor flow fluidCacheInfo fluidEmitter fluidVoxelInfo flushUndo fmod fontDialog fopen formLayout format fprint frameLayout fread freeFormFillet frewind fromNativePath fwrite gamma gauss geometryConstraint getApplicationVersionAsFloat getAttr getClassification getDefaultBrush getFileList getFluidAttr getInputDeviceRange getMayaPanelTypes getModifiers getPanel getParticleAttr getPluginResource getenv getpid glRender glRenderEditor globalStitch gmatch goal gotoBindPose grabColor gradientControl gradientControlNoAttr graphDollyCtx graphSelectContext graphTrackCtx gravity grid gridLayout group groupObjectsByName HfAddAttractorToAS HfAssignAS HfBuildEqualMap HfBuildFurFiles HfBuildFurImages HfCancelAFR HfConnectASToHF HfCreateAttractor HfDeleteAS HfEditAS HfPerformCreateAS HfRemoveAttractorFromAS HfSelectAttached HfSelectAttractors HfUnAssignAS hardenPointCurve hardware hardwareRenderPanel headsUpDisplay headsUpMessage help helpLine hermite hide hilite hitTest hotBox hotkey hotkeyCheck hsv_to_rgb hudButton hudSlider hudSliderButton hwReflectionMap hwRender hwRenderLoad hyperGraph hyperPanel hyperShade hypot iconTextButton iconTextCheckBox iconTextRadioButton iconTextRadioCollection iconTextScrollList iconTextStaticLabel ikHandle ikHandleCtx ikHandleDisplayScale ikSolver ikSplineHandleCtx ikSystem ikSystemInfo ikfkDisplayMethod illustratorCurves image imfPlugins inheritTransform insertJoint insertJointCtx insertKeyCtx insertKnotCurve insertKnotSurface instance instanceable instancer intField intFieldGrp intScrollBar intSlider intSliderGrp interToUI internalVar intersect iprEngine isAnimCurve isConnected isDirty isParentOf isSameObject isTrue isValidObjectName isValidString isValidUiName isolateSelect itemFilter itemFilterAttr itemFilterRender itemFilterType joint jointCluster jointCtx jointDisplayScale jointLattice keyTangent keyframe keyframeOutliner keyframeRegionCurrentTimeCtx keyframeRegionDirectKeyCtx keyframeRegionDollyCtx keyframeRegionInsertKeyCtx keyframeRegionMoveKeyCtx keyframeRegionScaleKeyCtx keyframeRegionSelectKeyCtx keyframeRegionSetKeyCtx keyframeRegionTrackCtx keyframeStats lassoContext lattice latticeDeformKeyCtx launch launchImageEditor layerButton layeredShaderPort layeredTexturePort layout layoutDialog lightList lightListEditor lightListPanel lightlink lineIntersection linearPrecision linstep listAnimatable listAttr listCameras listConnections listDeviceAttachments listHistory listInputDeviceAxes listInputDeviceButtons listInputDevices listMenuAnnotation listNodeTypes listPanelCategories listRelatives listSets listTransforms listUnselected listerEditor loadFluid loadNewShelf loadPlugin loadPluginLanguageResources loadPrefObjects localizedPanelLabel lockNode loft log longNameOf lookThru ls lsThroughFilter lsType lsUI Mayatomr mag makeIdentity makeLive makePaintable makeRoll makeSingleSurface makeTubeOn makebot manipMoveContext manipMoveLimitsCtx manipOptions manipRotateContext manipRotateLimitsCtx manipScaleContext manipScaleLimitsCtx marker match max memory menu menuBarLayout menuEditor menuItem menuItemToShelf menuSet menuSetPref messageLine min minimizeApp mirrorJoint modelCurrentTimeCtx modelEditor modelPanel mouse movIn movOut move moveIKtoFK moveKeyCtx moveVertexAlongDirection multiProfileBirailSurface mute nParticle nameCommand nameField namespace namespaceInfo newPanelItems newton nodeCast nodeIconButton nodeOutliner nodePreset nodeType noise nonLinear normalConstraint normalize nurbsBoolean nurbsCopyUVSet nurbsCube nurbsEditUV nurbsPlane nurbsSelect nurbsSquare nurbsToPoly nurbsToPolygonsPref nurbsToSubdiv nurbsToSubdivPref nurbsUVSet nurbsViewDirectionVector objExists objectCenter objectLayer objectType objectTypeUI obsoleteProc oceanNurbsPreviewPlane offsetCurve offsetCurveOnSurface offsetSurface openGLExtension openMayaPref optionMenu optionMenuGrp optionVar orbit orbitCtx orientConstraint outlinerEditor outlinerPanel overrideModifier paintEffectsDisplay pairBlend palettePort paneLayout panel panelConfiguration panelHistory paramDimContext paramDimension paramLocator parent parentConstraint particle particleExists particleInstancer particleRenderInfo partition pasteKey pathAnimation pause pclose percent performanceOptions pfxstrokes pickWalk picture pixelMove planarSrf plane play playbackOptions playblast plugAttr plugNode pluginInfo pluginResourceUtil pointConstraint pointCurveConstraint pointLight pointMatrixMult pointOnCurve pointOnSurface pointPosition poleVectorConstraint polyAppend polyAppendFacetCtx polyAppendVertex polyAutoProjection polyAverageNormal polyAverageVertex polyBevel polyBlendColor polyBlindData polyBoolOp polyBridgeEdge polyCacheMonitor polyCheck polyChipOff polyClipboard polyCloseBorder polyCollapseEdge polyCollapseFacet polyColorBlindData polyColorDel polyColorPerVertex polyColorSet polyCompare polyCone polyCopyUV polyCrease polyCreaseCtx polyCreateFacet polyCreateFacetCtx polyCube polyCut polyCutCtx polyCylinder polyCylindricalProjection polyDelEdge polyDelFacet polyDelVertex polyDuplicateAndConnect polyDuplicateEdge polyEditUV polyEditUVShell polyEvaluate polyExtrudeEdge polyExtrudeFacet polyExtrudeVertex polyFlipEdge polyFlipUV polyForceUV polyGeoSampler polyHelix polyInfo polyInstallAction polyLayoutUV polyListComponentConversion polyMapCut polyMapDel polyMapSew polyMapSewMove polyMergeEdge polyMergeEdgeCtx polyMergeFacet polyMergeFacetCtx polyMergeUV polyMergeVertex polyMirrorFace polyMoveEdge polyMoveFacet polyMoveFacetUV polyMoveUV polyMoveVertex polyNormal polyNormalPerVertex polyNormalizeUV polyOptUvs polyOptions polyOutput polyPipe polyPlanarProjection polyPlane polyPlatonicSolid polyPoke polyPrimitive polyPrism polyProjection polyPyramid polyQuad polyQueryBlindData polyReduce polySelect polySelectConstraint polySelectConstraintMonitor polySelectCtx polySelectEditCtx polySeparate polySetToFaceNormal polySewEdge polyShortestPathCtx polySmooth polySoftEdge polySphere polySphericalProjection polySplit polySplitCtx polySplitEdge polySplitRing polySplitVertex polyStraightenUVBorder polySubdivideEdge polySubdivideFacet polyToSubdiv polyTorus polyTransfer polyTriangulate polyUVSet polyUnite polyWedgeFace popen popupMenu pose pow preloadRefEd print progressBar progressWindow projFileViewer projectCurve projectTangent projectionContext projectionManip promptDialog propModCtx propMove psdChannelOutliner psdEditTextureFile psdExport psdTextureFile putenv pwd python querySubdiv quit rad_to_deg radial radioButton radioButtonGrp radioCollection radioMenuItemCollection rampColorPort rand randomizeFollicles randstate rangeControl readTake rebuildCurve rebuildSurface recordAttr recordDevice redo reference referenceEdit referenceQuery refineSubdivSelectionList refresh refreshAE registerPluginResource rehash reloadImage removeJoint removeMultiInstance removePanelCategory rename renameAttr renameSelectionList renameUI render renderGlobalsNode renderInfo renderLayerButton renderLayerParent renderLayerPostProcess renderLayerUnparent renderManip renderPartition renderQualityNode renderSettings renderThumbnailUpdate renderWindowEditor renderWindowSelectContext renderer reorder reorderDeformers requires reroot resampleFluid resetAE resetPfxToPolyCamera resetTool resolutionNode retarget reverseCurve reverseSurface revolve rgb_to_hsv rigidBody rigidSolver roll rollCtx rootOf rot rotate rotationInterpolation roundConstantRadius rowColumnLayout rowLayout runTimeCommand runup sampleImage saveAllShelves saveAttrPreset saveFluid saveImage saveInitialState saveMenu savePrefObjects savePrefs saveShelf saveToolSettings scale scaleBrushBrightness scaleComponents scaleConstraint scaleKey scaleKeyCtx sceneEditor sceneUIReplacement scmh scriptCtx scriptEditorInfo scriptJob scriptNode scriptTable scriptToShelf scriptedPanel scriptedPanelType scrollField scrollLayout sculpt searchPathArray seed selLoadSettings select selectContext selectCurveCV selectKey selectKeyCtx selectKeyframeRegionCtx selectMode selectPref selectPriority selectType selectedNodes selectionConnection separator setAttr setAttrEnumResource setAttrMapping setAttrNiceNameResource setConstraintRestPosition setDefaultShadingGroup setDrivenKeyframe setDynamic setEditCtx setEditor setFluidAttr setFocus setInfinity setInputDeviceMapping setKeyCtx setKeyPath setKeyframe setKeyframeBlendshapeTargetWts setMenuMode setNodeNiceNameResource setNodeTypeFlag setParent setParticleAttr setPfxToPolyCamera setPluginResource setProject setStampDensity setStartupMessage setState setToolTo setUITemplate setXformManip sets shadingConnection shadingGeometryRelCtx shadingLightRelCtx shadingNetworkCompare shadingNode shapeCompare shelfButton shelfLayout shelfTabLayout shellField shortNameOf showHelp showHidden showManipCtx showSelectionInTitle showShadingGroupAttrEditor showWindow sign simplify sin singleProfileBirailSurface size sizeBytes skinCluster skinPercent smoothCurve smoothTangentSurface smoothstep snap2to2 snapKey snapMode snapTogetherCtx snapshot soft softMod softModCtx sort sound soundControl source spaceLocator sphere sphrand spotLight spotLightPreviewPort spreadSheetEditor spring sqrt squareSurface srtContext stackTrace startString startsWith stitchAndExplodeShell stitchSurface stitchSurfacePoints strcmp stringArrayCatenate stringArrayContains stringArrayCount stringArrayInsertAtIndex stringArrayIntersector stringArrayRemove stringArrayRemoveAtIndex stringArrayRemoveDuplicates stringArrayRemoveExact stringArrayToString stringToStringArray strip stripPrefixFromName stroke subdAutoProjection subdCleanTopology subdCollapse subdDuplicateAndConnect subdEditUV subdListComponentConversion subdMapCut subdMapSewMove subdMatchTopology subdMirror subdToBlind subdToPoly subdTransferUVsToCache subdiv subdivCrease subdivDisplaySmoothness substitute substituteAllString substituteGeometry substring surface surfaceSampler surfaceShaderList swatchDisplayPort switchTable symbolButton symbolCheckBox sysFile system tabLayout tan tangentConstraint texLatticeDeformContext texManipContext texMoveContext texMoveUVShellContext texRotateContext texScaleContext texSelectContext texSelectShortestPathCtx texSmudgeUVContext texWinToolCtx text textCurves textField textFieldButtonGrp textFieldGrp textManip textScrollList textToShelf textureDisplacePlane textureHairColor texturePlacementContext textureWindow threadCount threePointArcCtx timeControl timePort timerX toNativePath toggle toggleAxis toggleWindowVisibility tokenize tokenizeList tolerance tolower toolButton toolCollection toolDropped toolHasOptions toolPropertyWindow torus toupper trace track trackCtx transferAttributes transformCompare transformLimits translator trim trunc truncateFluidCache truncateHairCache tumble tumbleCtx turbulence twoPointArcCtx uiRes uiTemplate unassignInputDevice undo undoInfo ungroup uniform unit unloadPlugin untangleUV untitledFileName untrim upAxis updateAE userCtx uvLink uvSnapshot validateShelfName vectorize view2dToolCtx viewCamera viewClipPlane viewFit viewHeadOn viewLookAt viewManip viewPlace viewSet visor volumeAxis vortex waitCursor warning webBrowser webBrowserPrefs whatIs window windowPref wire wireContext workspace wrinkle wrinkleContext writeTake xbmLangPathList xform",i:"</",c:[e.CNM,e.ASM,e.QSM,{cN:"string",b:"`",e:"`",c:[e.BE]},{cN:"variable",v:[{b:"\\$\\d"},{b:"[\\$\\%\\@](\\^\\w\\b|#\\w+|[^\\s\\w{]|{\\w+}|\\w+)"},{b:"\\*(\\^\\w\\b|#\\w+|[^\\s\\w{]|{\\w+}|\\w+)",r:0}]},e.CLCM,e.CBCM]}});hljs.registerLanguage("d",function(e){var r={keyword:"abstract alias align asm assert auto body break byte case cast catch class const continue debug default delete deprecated do else enum export extern final finally for foreach foreach_reverse|10 goto if immutable import in inout int interface invariant is lazy macro mixin module new nothrow out override package pragma private protected public pure ref return scope shared static struct super switch synchronized template this throw try typedef typeid typeof union unittest version void volatile while with __FILE__ __LINE__ __gshared|10 __thread __traits __DATE__ __EOF__ __TIME__ __TIMESTAMP__ __VENDOR__ __VERSION__",built_in:"bool cdouble cent cfloat char creal dchar delegate double dstring float function idouble ifloat ireal long real short string ubyte ucent uint ulong ushort wchar wstring",literal:"false null true"},t="(0|[1-9][\\d_]*)",a="(0|[1-9][\\d_]*|\\d[\\d_]*|[\\d_]+?\\d)",i="0[bB][01_]+",n="([\\da-fA-F][\\da-fA-F_]*|_[\\da-fA-F][\\da-fA-F_]*)",c="0[xX]"+n,_="([eE][+-]?"+a+")",d="("+a+"(\\.\\d*|"+_+")|\\d+\\."+a+a+"|\\."+t+_+"?)",o="(0[xX]("+n+"\\."+n+"|\\.?"+n+")[pP][+-]?"+a+")",s="("+t+"|"+i+"|"+c+")",l="("+o+"|"+d+")",u="\\\\(['\"\\?\\\\abfnrtv]|u[\\dA-Fa-f]{4}|[0-7]{1,3}|x[\\dA-Fa-f]{2}|U[\\dA-Fa-f]{8})|&[a-zA-Z\\d]{2,};",b={cN:"number",b:"\\b"+s+"(L|u|U|Lu|LU|uL|UL)?",r:0},f={cN:"number",b:"\\b("+l+"([fF]|L|i|[fF]i|Li)?|"+s+"(i|[fF]i|Li))",r:0},g={cN:"string",b:"'("+u+"|.)",e:"'",i:"."},h={b:u,r:0},p={cN:"string",b:'"',c:[h],e:'"[cwd]?'},w={cN:"string",b:'[rq]"',e:'"[cwd]?',r:5},N={cN:"string",b:"`",e:"`[cwd]?"},A={cN:"string",b:'x"[\\da-fA-F\\s\\n\\r]*"[cwd]?',r:10},F={cN:"string",b:'q"\\{',e:'\\}"'},m={cN:"shebang",b:"^#!",e:"$",r:5},y={cN:"preprocessor",b:"#(line)",e:"$",r:5},L={cN:"keyword",b:"@[a-zA-Z_][a-zA-Z_\\d]*"},v=e.C("\\/\\+","\\+\\/",{c:["self"],r:10});return{l:e.UIR,k:r,c:[e.CLCM,e.CBCM,v,A,p,w,N,F,f,b,g,m,y,L]}});hljs.registerLanguage("ruleslanguage",function(T){return{k:{keyword:"BILL_PERIOD BILL_START BILL_STOP RS_EFFECTIVE_START RS_EFFECTIVE_STOP RS_JURIS_CODE RS_OPCO_CODE INTDADDATTRIBUTE|5 INTDADDVMSG|5 INTDBLOCKOP|5 INTDBLOCKOPNA|5 INTDCLOSE|5 INTDCOUNT|5 INTDCOUNTSTATUSCODE|5 INTDCREATEMASK|5 INTDCREATEDAYMASK|5 INTDCREATEFACTORMASK|5 INTDCREATEHANDLE|5 INTDCREATEOVERRIDEDAYMASK|5 INTDCREATEOVERRIDEMASK|5 INTDCREATESTATUSCODEMASK|5 INTDCREATETOUPERIOD|5 INTDDELETE|5 INTDDIPTEST|5 INTDEXPORT|5 INTDGETERRORCODE|5 INTDGETERRORMESSAGE|5 INTDISEQUAL|5 INTDJOIN|5 INTDLOAD|5 INTDLOADACTUALCUT|5 INTDLOADDATES|5 INTDLOADHIST|5 INTDLOADLIST|5 INTDLOADLISTDATES|5 INTDLOADLISTENERGY|5 INTDLOADLISTHIST|5 INTDLOADRELATEDCHANNEL|5 INTDLOADSP|5 INTDLOADSTAGING|5 INTDLOADUOM|5 INTDLOADUOMDATES|5 INTDLOADUOMHIST|5 INTDLOADVERSION|5 INTDOPEN|5 INTDREADFIRST|5 INTDREADNEXT|5 INTDRECCOUNT|5 INTDRELEASE|5 INTDREPLACE|5 INTDROLLAVG|5 INTDROLLPEAK|5 INTDSCALAROP|5 INTDSCALE|5 INTDSETATTRIBUTE|5 INTDSETDSTPARTICIPANT|5 INTDSETSTRING|5 INTDSETVALUE|5 INTDSETVALUESTATUS|5 INTDSHIFTSTARTTIME|5 INTDSMOOTH|5 INTDSORT|5 INTDSPIKETEST|5 INTDSUBSET|5 INTDTOU|5 INTDTOURELEASE|5 INTDTOUVALUE|5 INTDUPDATESTATS|5 INTDVALUE|5 STDEV INTDDELETEEX|5 INTDLOADEXACTUAL|5 INTDLOADEXCUT|5 INTDLOADEXDATES|5 INTDLOADEX|5 INTDLOADEXRELATEDCHANNEL|5 INTDSAVEEX|5 MVLOAD|5 MVLOADACCT|5 MVLOADACCTDATES|5 MVLOADACCTHIST|5 MVLOADDATES|5 MVLOADHIST|5 MVLOADLIST|5 MVLOADLISTDATES|5 MVLOADLISTHIST|5 IF FOR NEXT DONE SELECT END CALL ABORT CLEAR CHANNEL FACTOR LIST NUMBER OVERRIDE SET WEEK DISTRIBUTIONNODE ELSE WHEN THEN OTHERWISE IENUM CSV INCLUDE LEAVE RIDER SAVE DELETE NOVALUE SECTION WARN SAVE_UPDATE DETERMINANT LABEL REPORT REVENUE EACH IN FROM TOTAL CHARGE BLOCK AND OR CSV_FILE RATE_CODE AUXILIARY_DEMAND UIDACCOUNT RS BILL_PERIOD_SELECT HOURS_PER_MONTH INTD_ERROR_STOP SEASON_SCHEDULE_NAME ACCOUNTFACTOR ARRAYUPPERBOUND CALLSTOREDPROC GETADOCONNECTION GETCONNECT GETDATASOURCE GETQUALIFIER GETUSERID HASVALUE LISTCOUNT LISTOP LISTUPDATE LISTVALUE PRORATEFACTOR RSPRORATE SETBINPATH SETDBMONITOR WQ_OPEN BILLINGHOURS DATE DATEFROMFLOAT DATETIMEFROMSTRING DATETIMETOSTRING DATETOFLOAT DAY DAYDIFF DAYNAME DBDATETIME HOUR MINUTE MONTH MONTHDIFF MONTHHOURS MONTHNAME ROUNDDATE SAMEWEEKDAYLASTYEAR SECOND WEEKDAY WEEKDIFF YEAR YEARDAY YEARSTR COMPSUM HISTCOUNT HISTMAX HISTMIN HISTMINNZ HISTVALUE MAXNRANGE MAXRANGE MINRANGE COMPIKVA COMPKVA COMPKVARFROMKQKW COMPLF IDATTR FLAG LF2KW LF2KWH MAXKW POWERFACTOR READING2USAGE AVGSEASON MAXSEASON MONTHLYMERGE SEASONVALUE SUMSEASON ACCTREADDATES ACCTTABLELOAD CONFIGADD CONFIGGET CREATEOBJECT CREATEREPORT EMAILCLIENT EXPBLKMDMUSAGE EXPMDMUSAGE EXPORT_USAGE FACTORINEFFECT GETUSERSPECIFIEDSTOP INEFFECT ISHOLIDAY RUNRATE SAVE_PROFILE SETREPORTTITLE USEREXIT WATFORRUNRATE TO TABLE ACOS ASIN ATAN ATAN2 BITAND CEIL COS COSECANT COSH COTANGENT DIVQUOT DIVREM EXP FABS FLOOR FMOD FREPM FREXPN LOG LOG10 MAX MAXN MIN MINNZ MODF POW ROUND ROUND2VALUE ROUNDINT SECANT SIN SINH SQROOT TAN TANH FLOAT2STRING FLOAT2STRINGNC INSTR LEFT LEN LTRIM MID RIGHT RTRIM STRING STRINGNC TOLOWER TOUPPER TRIM NUMDAYS READ_DATE STAGING",built_in:"IDENTIFIER OPTIONS XML_ELEMENT XML_OP XML_ELEMENT_OF DOMDOCCREATE DOMDOCLOADFILE DOMDOCLOADXML DOMDOCSAVEFILE DOMDOCGETROOT DOMDOCADDPI DOMNODEGETNAME DOMNODEGETTYPE DOMNODEGETVALUE DOMNODEGETCHILDCT DOMNODEGETFIRSTCHILD DOMNODEGETSIBLING DOMNODECREATECHILDELEMENT DOMNODESETATTRIBUTE DOMNODEGETCHILDELEMENTCT DOMNODEGETFIRSTCHILDELEMENT DOMNODEGETSIBLINGELEMENT DOMNODEGETATTRIBUTECT DOMNODEGETATTRIBUTEI DOMNODEGETATTRIBUTEBYNAME DOMNODEGETBYNAME"},c:[T.CLCM,T.CBCM,T.ASM,T.QSM,T.CNM,{cN:"array",b:"#[a-zA-Z .]+"}]}});hljs.registerLanguage("actionscript",function(e){var a="[a-zA-Z_$][a-zA-Z0-9_$]*",c="([*]|[a-zA-Z_$][a-zA-Z0-9_$]*)",t={cN:"rest_arg",b:"[.]{3}",e:a,r:10};return{aliases:["as"],k:{keyword:"as break case catch class const continue default delete do dynamic each else extends final finally for function get if implements import in include instanceof interface internal is namespace native new override package private protected public return set static super switch this throw try typeof use var void while with",literal:"true false null undefined"},c:[e.ASM,e.QSM,e.CLCM,e.CBCM,e.CNM,{cN:"package",bK:"package",e:"{",c:[e.TM]},{cN:"class",bK:"class interface",e:"{",eE:!0,c:[{bK:"extends implements"},e.TM]},{cN:"preprocessor",bK:"import include",e:";"},{cN:"function",bK:"function",e:"[{;]",eE:!0,i:"\\S",c:[e.TM,{cN:"params",b:"\\(",e:"\\)",c:[e.ASM,e.QSM,e.CLCM,e.CBCM,t]},{cN:"type",b:":",e:c,r:10}]}]}});hljs.registerLanguage("coffeescript",function(e){var c={keyword:"in if for while finally new do return else break catch instanceof throw try this switch continue typeof delete debugger super then unless until loop of by when and or is isnt not",literal:"true false null undefined yes no on off",reserved:"case default function var void with const let enum export import native __hasProp __extends __slice __bind __indexOf",built_in:"npm require console print module global window document"},n="[A-Za-z$_][0-9A-Za-z$_]*",t={cN:"subst",b:/#\{/,e:/}/,k:c},r=[e.BNM,e.inherit(e.CNM,{starts:{e:"(\\s*/)?",r:0}}),{cN:"string",v:[{b:/'''/,e:/'''/,c:[e.BE]},{b:/'/,e:/'/,c:[e.BE]},{b:/"""/,e:/"""/,c:[e.BE,t]},{b:/"/,e:/"/,c:[e.BE,t]}]},{cN:"regexp",v:[{b:"///",e:"///",c:[t,e.HCM]},{b:"//[gim]*",r:0},{b:/\/(?![ *])(\\\/|.)*?\/[gim]*(?=\W|$)/}]},{cN:"property",b:"@"+n},{b:"`",e:"`",eB:!0,eE:!0,sL:"javascript"}];t.c=r;var i=e.inherit(e.TM,{b:n}),s="(\\(.*\\))?\\s*\\B[-=]>",o={cN:"params",b:"\\([^\\(]",rB:!0,c:[{b:/\(/,e:/\)/,k:c,c:["self"].concat(r)}]};return{aliases:["coffee","cson","iced"],k:c,i:/\/\*/,c:r.concat([e.C("###","###"),e.HCM,{cN:"function",b:"^\\s*"+n+"\\s*=\\s*"+s,e:"[-=]>",rB:!0,c:[i,o]},{b:/[:\(,=]\s*/,r:0,c:[{cN:"function",b:s,e:"[-=]>",rB:!0,c:[o]}]},{cN:"class",bK:"class",e:"$",i:/[:="\[\]]/,c:[{bK:"extends",eW:!0,i:/[:="\[\]]/,c:[i]},i]},{cN:"attribute",b:n+":",e:":",rB:!0,rE:!0,r:0}])}});hljs.registerLanguage("tex",function(c){var e={cN:"command",b:"\\\\[a-zA-Zа-яА-я]+[\\*]?"},m={cN:"command",b:"\\\\[^a-zA-Zа-яА-я0-9]"},r={cN:"special",b:"[{}\\[\\]\\&#~]",r:0};return{c:[{b:"\\\\[a-zA-Zа-яА-я]+[\\*]? *= *-?\\d*\\.?\\d+(pt|pc|mm|cm|in|dd|cc|ex|em)?",rB:!0,c:[e,m,{cN:"number",b:" *=",e:"-?\\d*\\.?\\d+(pt|pc|mm|cm|in|dd|cc|ex|em)?",eB:!0}],r:10},e,m,r,{cN:"formula",b:"\\$\\$",e:"\\$\\$",c:[e,m,r],r:0},{cN:"formula",b:"\\$",e:"\\$",c:[e,m,r],r:0},c.C("%","$",{r:0})]}});hljs.registerLanguage("go",function(e){var t={keyword:"break default func interface select case map struct chan else goto package switch const fallthrough if range type continue for import return var go defer",constant:"true false iota nil",typename:"bool byte complex64 complex128 float32 float64 int8 int16 int32 int64 string uint8 uint16 uint32 uint64 int uint uintptr rune",built_in:"append cap close complex copy imag len make new panic print println real recover delete"};return{aliases:["golang"],k:t,i:"</",c:[e.CLCM,e.CBCM,e.QSM,{cN:"string",b:"'",e:"[^\\\\]'"},{cN:"string",b:"`",e:"`"},{cN:"number",b:e.CNR+"[dflsi]?",r:0},e.CNM]}});hljs.registerLanguage("vbscript-html",function(s){return{sL:"xml",subLanguageMode:"continuous",c:[{b:"<%",e:"%>",sL:"vbscript"}]}});hljs.registerLanguage("haskell",function(e){var c=[e.C("--","$"),e.C("{-","-}",{c:["self"]})],a={cN:"pragma",b:"{-#",e:"#-}"},i={cN:"preprocessor",b:"^#",e:"$"},n={cN:"type",b:"\\b[A-Z][\\w']*",r:0},t={cN:"container",b:"\\(",e:"\\)",i:'"',c:[a,i,{cN:"type",b:"\\b[A-Z][\\w]*(\\((\\.\\.|,|\\w+)\\))?"},e.inherit(e.TM,{b:"[_a-z][\\w']*"})].concat(c)},l={cN:"container",b:"{",e:"}",c:t.c};return{aliases:["hs"],k:"let in if then else case of where do module import hiding qualified type data newtype deriving class instance as default infix infixl infixr foreign export ccall stdcall cplusplus jvm dotnet safe unsafe family forall mdo proc rec",c:[{cN:"module",b:"\\bmodule\\b",e:"where",k:"module where",c:[t].concat(c),i:"\\W\\.|;"},{cN:"import",b:"\\bimport\\b",e:"$",k:"import|0 qualified as hiding",c:[t].concat(c),i:"\\W\\.|;"},{cN:"class",b:"^(\\s*)?(class|instance)\\b",e:"where",k:"class family instance where",c:[n,t].concat(c)},{cN:"typedef",b:"\\b(data|(new)?type)\\b",e:"$",k:"data family type newtype deriving",c:[a,n,t,l].concat(c)},{cN:"default",bK:"default",e:"$",c:[n,t].concat(c)},{cN:"infix",bK:"infix infixl infixr",e:"$",c:[e.CNM].concat(c)},{cN:"foreign",b:"\\bforeign\\b",e:"$",k:"foreign import export ccall stdcall cplusplus jvm dotnet safe unsafe",c:[n,e.QSM].concat(c)},{cN:"shebang",b:"#!\\/usr\\/bin\\/env runhaskell",e:"$"},a,i,e.QSM,e.CNM,n,e.inherit(e.TM,{b:"^[_a-z][\\w']*"}),{b:"->|<-"}].concat(c)}});hljs.registerLanguage("scilab",function(e){var n=[e.CNM,{cN:"string",b:"'|\"",e:"'|\"",c:[e.BE,{b:"''"}]}];return{aliases:["sci"],k:{keyword:"abort break case clear catch continue do elseif else endfunction end for functionglobal if pause return resume select try then while%f %F %t %T %pi %eps %inf %nan %e %i %z %s",built_in:"abs and acos asin atan ceil cd chdir clearglobal cosh cos cumprod deff disp errorexec execstr exists exp eye gettext floor fprintf fread fsolve imag isdef isemptyisinfisnan isvector lasterror length load linspace list listfiles log10 log2 logmax min msprintf mclose mopen ones or pathconvert poly printf prod pwd rand realround sinh sin size gsort sprintf sqrt strcat strcmps tring sum system tanh tantype typename warning zeros matrix"},i:'("|#|/\\*|\\s+/\\w+)',c:[{cN:"function",bK:"function endfunction",e:"$",k:"function endfunction|10",c:[e.UTM,{cN:"params",b:"\\(",e:"\\)"}]},{cN:"transposed_variable",b:"[a-zA-Z_][a-zA-Z_0-9]*('+[\\.']*|[\\.']+)",e:"",r:0},{cN:"matrix",b:"\\[",e:"\\]'*[\\.']*",r:0,c:n},e.C("//","$")].concat(n)}});hljs.registerLanguage("profile",function(e){return{c:[e.CNM,{cN:"built_in",b:"{",e:"}$",eB:!0,eE:!0,c:[e.ASM,e.QSM],r:0},{cN:"filename",b:"[a-zA-Z_][\\da-zA-Z_]+\\.[\\da-zA-Z_]{1,3}",e:":",eE:!0},{cN:"header",b:"(ncalls|tottime|cumtime)",e:"$",k:"ncalls tottime|10 cumtime|10 filename",r:10},{cN:"summary",b:"function calls",e:"$",c:[e.CNM],r:10},e.ASM,e.QSM,{cN:"function",b:"\\(",e:"\\)$",c:[e.UTM],r:0}]}});hljs.registerLanguage("thrift",function(e){var t="bool byte i16 i32 i64 double string binary";return{k:{keyword:"namespace const typedef struct enum service exception void oneway set list map required optional",built_in:t,literal:"true false"},c:[e.QSM,e.NM,e.CLCM,e.CBCM,{cN:"class",bK:"struct enum service exception",e:/\{/,i:/\n/,c:[e.inherit(e.TM,{starts:{eW:!0,eE:!0}})]},{b:"\\b(set|list|map)\\s*<",e:">",k:t,c:["self"]}]}});hljs.registerLanguage("matlab",function(e){var a=[e.CNM,{cN:"string",b:"'",e:"'",c:[e.BE,{b:"''"}]}],s={r:0,c:[{cN:"operator",b:/'['\.]*/}]};return{k:{keyword:"break case catch classdef continue else elseif end enumerated events for function global if methods otherwise parfor persistent properties return spmd switch try while",built_in:"sin sind sinh asin asind asinh cos cosd cosh acos acosd acosh tan tand tanh atan atand atan2 atanh sec secd sech asec asecd asech csc cscd csch acsc acscd acsch cot cotd coth acot acotd acoth hypot exp expm1 log log1p log10 log2 pow2 realpow reallog realsqrt sqrt nthroot nextpow2 abs angle complex conj imag real unwrap isreal cplxpair fix floor ceil round mod rem sign airy besselj bessely besselh besseli besselk beta betainc betaln ellipj ellipke erf erfc erfcx erfinv expint gamma gammainc gammaln psi legendre cross dot factor isprime primes gcd lcm rat rats perms nchoosek factorial cart2sph cart2pol pol2cart sph2cart hsv2rgb rgb2hsv zeros ones eye repmat rand randn linspace logspace freqspace meshgrid accumarray size length ndims numel disp isempty isequal isequalwithequalnans cat reshape diag blkdiag tril triu fliplr flipud flipdim rot90 find sub2ind ind2sub bsxfun ndgrid permute ipermute shiftdim circshift squeeze isscalar isvector ans eps realmax realmin pi i inf nan isnan isinf isfinite j why compan gallery hadamard hankel hilb invhilb magic pascal rosser toeplitz vander wilkinson"},i:'(//|"|#|/\\*|\\s+/\\w+)',c:[{cN:"function",bK:"function",e:"$",c:[e.UTM,{cN:"params",b:"\\(",e:"\\)"},{cN:"params",b:"\\[",e:"\\]"}]},{b:/[a-zA-Z_][a-zA-Z_0-9]*'['\.]*/,rB:!0,r:0,c:[{b:/[a-zA-Z_][a-zA-Z_0-9]*/,r:0},s.c[0]]},{cN:"matrix",b:"\\[",e:"\\]",c:a,r:0,starts:s},{cN:"cell",b:"\\{",e:/}/,c:a,r:0,starts:s},{b:/\)/,r:0,starts:s},e.C("^\\s*\\%\\{\\s*$","^\\s*\\%\\}\\s*$"),e.C("\\%","$")].concat(a)}});hljs.registerLanguage("vbscript",function(e){return{aliases:["vbs"],cI:!0,k:{keyword:"call class const dim do loop erase execute executeglobal exit for each next function if then else on error option explicit new private property let get public randomize redim rem select case set stop sub while wend with end to elseif is or xor and not class_initialize class_terminate default preserve in me byval byref step resume goto",built_in:"lcase month vartype instrrev ubound setlocale getobject rgb getref string weekdayname rnd dateadd monthname now day minute isarray cbool round formatcurrency conversions csng timevalue second year space abs clng timeserial fixs len asc isempty maths dateserial atn timer isobject filter weekday datevalue ccur isdate instr datediff formatdatetime replace isnull right sgn array snumeric log cdbl hex chr lbound msgbox ucase getlocale cos cdate cbyte rtrim join hour oct typename trim strcomp int createobject loadpicture tan formatnumber mid scriptenginebuildversion scriptengine split scriptengineminorversion cint sin datepart ltrim sqr scriptenginemajorversion time derived eval date formatpercent exp inputbox left ascw chrw regexp server response request cstr err",literal:"true false null nothing empty"},i:"//",c:[e.inherit(e.QSM,{c:[{b:'""'}]}),e.C(/'/,/$/,{r:0}),e.CNM]}});hljs.registerLanguage("capnproto",function(t){return{aliases:["capnp"],k:{keyword:"struct enum interface union group import using const annotation extends in of on as with from fixed",built_in:"Void Bool Int8 Int16 Int32 Int64 UInt8 UInt16 UInt32 UInt64 Float32 Float64 Text Data AnyPointer AnyStruct Capability List",literal:"true false"},c:[t.QSM,t.NM,t.HCM,{cN:"shebang",b:/@0x[\w\d]{16};/,i:/\n/},{cN:"number",b:/@\d+\b/},{cN:"class",bK:"struct enum",e:/\{/,i:/\n/,c:[t.inherit(t.TM,{starts:{eW:!0,eE:!0}})]},{cN:"class",bK:"interface",e:/\{/,i:/\n/,c:[t.inherit(t.TM,{starts:{eW:!0,eE:!0}})]}]}});hljs.registerLanguage("xl",function(e){var t="ObjectLoader Animate MovieCredits Slides Filters Shading Materials LensFlare Mapping VLCAudioVideo StereoDecoder PointCloud NetworkAccess RemoteControl RegExp ChromaKey Snowfall NodeJS Speech Charts",o={keyword:"if then else do while until for loop import with is as where when by data constant",literal:"true false nil",type:"integer real text name boolean symbol infix prefix postfix block tree",built_in:"in mod rem and or xor not abs sign floor ceil sqrt sin cos tan asin acos atan exp expm1 log log2 log10 log1p pi at",module:t,id:"text_length text_range text_find text_replace contains page slide basic_slide title_slide title subtitle fade_in fade_out fade_at clear_color color line_color line_width texture_wrap texture_transform texture scale_?x scale_?y scale_?z? translate_?x translate_?y translate_?z? rotate_?x rotate_?y rotate_?z? rectangle circle ellipse sphere path line_to move_to quad_to curve_to theme background contents locally time mouse_?x mouse_?y mouse_buttons"},a={cN:"constant",b:"[A-Z][A-Z_0-9]+",r:0},r={cN:"variable",b:"([A-Z][a-z_0-9]+)+",r:0},i={cN:"id",b:"[a-z][a-z_0-9]+",r:0},l={cN:"string",b:'"',e:'"',i:"\\n"},n={cN:"string",b:"'",e:"'",i:"\\n"},s={cN:"string",b:"<<",e:">>"},c={cN:"number",b:"[0-9]+#[0-9A-Z_]+(\\.[0-9-A-Z_]+)?#?([Ee][+-]?[0-9]+)?",r:10},_={cN:"import",bK:"import",e:"$",k:{keyword:"import",module:t},r:0,c:[l]},d={cN:"function",b:"[a-z].*->"};return{aliases:["tao"],l:/[a-zA-Z][a-zA-Z0-9_?]*/,k:o,c:[e.CLCM,e.CBCM,l,n,s,d,_,a,r,i,c,e.NM]}});hljs.registerLanguage("scala",function(e){var t={cN:"annotation",b:"@[A-Za-z]+"},a={cN:"string",b:'u?r?"""',e:'"""',r:10},r={cN:"symbol",b:"'\\w[\\w\\d_]*(?!')"},c={cN:"type",b:"\\b[A-Z][A-Za-z0-9_]*",r:0},i={cN:"title",b:/[^0-9\n\t "'(),.`{}\[\]:;][^\n\t "'(),.`{}\[\]:;]+|[^0-9\n\t "'(),.`{}\[\]:;=]/,r:0},l={cN:"class",bK:"class object trait type",e:/[:={\[(\n;]/,c:[{cN:"keyword",bK:"extends with",r:10},i]},n={cN:"function",bK:"def val",e:/[:={\[(\n;]/,c:[i]};return{k:{literal:"true false null",keyword:"type yield lazy override def with val var sealed abstract private trait object if forSome for while throw finally protected extends import final return else break new catch super class case package default try this match continue throws implicit"},c:[e.CLCM,e.CBCM,a,e.QSM,r,c,n,l,e.CNM,t]}});hljs.registerLanguage("elixir",function(e){var n="[a-zA-Z_][a-zA-Z0-9_]*(\\!|\\?)?",r="[a-zA-Z_]\\w*[!?=]?|[-+~]\\@|<<|>>|=~|===?|<=>|[<>]=?|\\*\\*|[-/+%^&*~`|]|\\[\\]=?",b="and false then defined module in return redo retry end for true self when next until do begin unless nil break not case cond alias while ensure or include use alias fn quote",c={cN:"subst",b:"#\\{",e:"}",l:n,k:b},a={cN:"string",c:[e.BE,c],v:[{b:/'/,e:/'/},{b:/"/,e:/"/}]},i={cN:"function",bK:"def defp defmacro",e:/\B\b/,c:[e.inherit(e.TM,{b:n,endsParent:!0})]},s=e.inherit(i,{cN:"class",bK:"defmodule defrecord",e:/\bdo\b|$|;/}),l=[a,e.HCM,s,i,{cN:"constant",b:"(\\b[A-Z_]\\w*(.)?)+",r:0},{cN:"symbol",b:":",c:[a,{b:r}],r:0},{cN:"symbol",b:n+":",r:0},{cN:"number",b:"(\\b0[0-7_]+)|(\\b0x[0-9a-fA-F_]+)|(\\b[1-9][0-9_]*(\\.[0-9_]+)?)|[0_]\\b",r:0},{cN:"variable",b:"(\\$\\W)|((\\$|\\@\\@?)(\\w+))"},{b:"->"},{b:"("+e.RSR+")\\s*",c:[e.HCM,{cN:"regexp",i:"\\n",c:[e.BE,c],v:[{b:"/",e:"/[a-z]*"},{b:"%r\\[",e:"\\][a-z]*"}]}],r:0}];return c.c=l,{l:n,k:b,c:l}});hljs.registerLanguage("sml",function(e){return{aliases:["ml"],k:{keyword:"abstype and andalso as case datatype do else end eqtype exception fn fun functor handle if in include infix infixr let local nonfix of op open orelse raise rec sharing sig signature struct structure then type val with withtype where while",built_in:"array bool char exn int list option order real ref string substring vector unit word",literal:"true false NONE SOME LESS EQUAL GREATER nil"},i:/\/\/|>>/,l:"[a-z_]\\w*!?",c:[{cN:"literal",b:"\\[(\\|\\|)?\\]|\\(\\)"},e.C("\\(\\*","\\*\\)",{c:["self"]}),{cN:"symbol",b:"'[A-Za-z_](?!')[\\w']*"},{cN:"tag",b:"`[A-Z][\\w']*"},{cN:"type",b:"\\b[A-Z][\\w']*",r:0},{b:"[a-z_]\\w*'[\\w']*"},e.inherit(e.ASM,{cN:"char",r:0}),e.inherit(e.QSM,{i:null}),{cN:"number",b:"\\b(0[xX][a-fA-F0-9_]+[Lln]?|0[oO][0-7_]+[Lln]?|0[bB][01_]+[Lln]?|[0-9][0-9_]*([Lln]|(\\.[0-9_]*)?([eE][-+]?[0-9_]+)?)?)",r:0},{b:/[-=]>/}]}});hljs.registerLanguage("apache",function(e){var r={cN:"number",b:"[\\$%]\\d+"};return{aliases:["apacheconf"],cI:!0,c:[e.HCM,{cN:"tag",b:"</?",e:">"},{cN:"keyword",b:/\w+/,r:0,k:{common:"order deny allow setenv rewriterule rewriteengine rewritecond documentroot sethandler errordocument loadmodule options header listen serverroot servername"},starts:{e:/$/,r:0,k:{literal:"on off all"},c:[{cN:"sqbracket",b:"\\s\\[",e:"\\]$"},{cN:"cbracket",b:"[\\$%]\\{",e:"\\}",c:["self",r]},r,e.QSM]}}],i:/\S/}});hljs.registerLanguage("dockerfile",function(n){return{aliases:["docker"],cI:!0,k:{built_ins:"from maintainer cmd expose add copy entrypoint volume user workdir onbuild run env"},c:[n.HCM,{k:{built_in:"run cmd entrypoint volume add copy workdir onbuild"},b:/^ *(onbuild +)?(run|cmd|entrypoint|volume|add|copy|workdir) +/,starts:{e:/[^\\]\n/,sL:"bash",subLanguageMode:"continuous"}},{k:{built_in:"from maintainer expose env user onbuild"},b:/^ *(onbuild +)?(from|maintainer|expose|env|user|onbuild) +/,e:/[^\\]\n/,c:[n.ASM,n.QSM,n.NM,n.HCM]}]}});hljs.registerLanguage("markdown",function(e){return{aliases:["md","mkdown","mkd"],c:[{cN:"header",v:[{b:"^#{1,6}",e:"$"},{b:"^.+?\\n[=-]{2,}$"}]},{b:"<",e:">",sL:"xml",r:0},{cN:"bullet",b:"^([*+-]|(\\d+\\.))\\s+"},{cN:"strong",b:"[*_]{2}.+?[*_]{2}"},{cN:"emphasis",v:[{b:"\\*.+?\\*"},{b:"_.+?_",r:0}]},{cN:"blockquote",b:"^>\\s+",e:"$"},{cN:"code",v:[{b:"`.+?`"},{b:"^( {4}|	)",e:"$",r:0}]},{cN:"horizontal_rule",b:"^[-\\*]{3,}",e:"$"},{b:"\\[.+?\\][\\(\\[].*?[\\)\\]]",rB:!0,c:[{cN:"link_label",b:"\\[",e:"\\]",eB:!0,rE:!0,r:0},{cN:"link_url",b:"\\]\\(",e:"\\)",eB:!0,eE:!0},{cN:"link_reference",b:"\\]\\[",e:"\\]",eB:!0,eE:!0}],r:10},{b:"^\\[.+\\]:",rB:!0,c:[{cN:"link_reference",b:"\\[",e:"\\]:",eB:!0,eE:!0,starts:{cN:"link_url",e:"$"}}]}]}});hljs.registerLanguage("haml",function(s){return{cI:!0,c:[{cN:"doctype",b:"^!!!( (5|1\\.1|Strict|Frameset|Basic|Mobile|RDFa|XML\\b.*))?$",r:10},s.C("^\\s*(!=#|=#|-#|/).*$",!1,{r:0}),{b:"^\\s*(-|=|!=)(?!#)",starts:{e:"\\n",sL:"ruby"}},{cN:"tag",b:"^\\s*%",c:[{cN:"title",b:"\\w+"},{cN:"value",b:"[#\\.]\\w+"},{b:"{\\s*",e:"\\s*}",eE:!0,c:[{b:":\\w+\\s*=>",e:",\\s+",rB:!0,eW:!0,c:[{cN:"symbol",b:":\\w+"},{cN:"string",b:'"',e:'"'},{cN:"string",b:"'",e:"'"},{b:"\\w+",r:0}]}]},{b:"\\(\\s*",e:"\\s*\\)",eE:!0,c:[{b:"\\w+\\s*=",e:"\\s+",rB:!0,eW:!0,c:[{cN:"attribute",b:"\\w+",r:0},{cN:"string",b:'"',e:'"'},{cN:"string",b:"'",e:"'"},{b:"\\w+",r:0}]}]}]},{cN:"bullet",b:"^\\s*[=~]\\s*",r:0},{b:"#{",starts:{e:"}",sL:"ruby"}}]}});hljs.registerLanguage("fortran",function(e){var t={cN:"params",b:"\\(",e:"\\)"},n={constant:".False. .True.",type:"integer real character complex logical dimension allocatable|10 parameter external implicit|10 none double precision assign intent optional pointer target in out common equivalence data",keyword:"kind do while private call intrinsic where elsewhere type endtype endmodule endselect endinterface end enddo endif if forall endforall only contains default return stop then public subroutine|10 function program .and. .or. .not. .le. .eq. .ge. .gt. .lt. goto save else use module select case access blank direct exist file fmt form formatted iostat name named nextrec number opened rec recl sequential status unformatted unit continue format pause cycle exit c_null_char c_alert c_backspace c_form_feed flush wait decimal round iomsg synchronous nopass non_overridable pass protected volatile abstract extends import non_intrinsic value deferred generic final enumerator class associate bind enum c_int c_short c_long c_long_long c_signed_char c_size_t c_int8_t c_int16_t c_int32_t c_int64_t c_int_least8_t c_int_least16_t c_int_least32_t c_int_least64_t c_int_fast8_t c_int_fast16_t c_int_fast32_t c_int_fast64_t c_intmax_t C_intptr_t c_float c_double c_long_double c_float_complex c_double_complex c_long_double_complex c_bool c_char c_null_ptr c_null_funptr c_new_line c_carriage_return c_horizontal_tab c_vertical_tab iso_c_binding c_loc c_funloc c_associated  c_f_pointer c_ptr c_funptr iso_fortran_env character_storage_size error_unit file_storage_size input_unit iostat_end iostat_eor numeric_storage_size output_unit c_f_procpointer ieee_arithmetic ieee_support_underflow_control ieee_get_underflow_mode ieee_set_underflow_mode newunit contiguous pad position action delim readwrite eor advance nml interface procedure namelist include sequence elemental pure",built_in:"alog alog10 amax0 amax1 amin0 amin1 amod cabs ccos cexp clog csin csqrt dabs dacos dasin datan datan2 dcos dcosh ddim dexp dint dlog dlog10 dmax1 dmin1 dmod dnint dsign dsin dsinh dsqrt dtan dtanh float iabs idim idint idnint ifix isign max0 max1 min0 min1 sngl algama cdabs cdcos cdexp cdlog cdsin cdsqrt cqabs cqcos cqexp cqlog cqsin cqsqrt dcmplx dconjg derf derfc dfloat dgamma dimag dlgama iqint qabs qacos qasin qatan qatan2 qcmplx qconjg qcos qcosh qdim qerf qerfc qexp qgamma qimag qlgama qlog qlog10 qmax1 qmin1 qmod qnint qsign qsin qsinh qsqrt qtan qtanh abs acos aimag aint anint asin atan atan2 char cmplx conjg cos cosh exp ichar index int log log10 max min nint sign sin sinh sqrt tan tanh print write dim lge lgt lle llt mod nullify allocate deallocate adjustl adjustr all allocated any associated bit_size btest ceiling count cshift date_and_time digits dot_product eoshift epsilon exponent floor fraction huge iand ibclr ibits ibset ieor ior ishft ishftc lbound len_trim matmul maxexponent maxloc maxval merge minexponent minloc minval modulo mvbits nearest pack present product radix random_number random_seed range repeat reshape rrspacing scale scan selected_int_kind selected_real_kind set_exponent shape size spacing spread sum system_clock tiny transpose trim ubound unpack verify achar iachar transfer dble entry dprod cpu_time command_argument_count get_command get_command_argument get_environment_variable is_iostat_end ieee_arithmetic ieee_support_underflow_control ieee_get_underflow_mode ieee_set_underflow_mode is_iostat_eor move_alloc new_line selected_char_kind same_type_as extends_type_ofacosh asinh atanh bessel_j0 bessel_j1 bessel_jn bessel_y0 bessel_y1 bessel_yn erf erfc erfc_scaled gamma log_gamma hypot norm2 atomic_define atomic_ref execute_command_line leadz trailz storage_size merge_bits bge bgt ble blt dshiftl dshiftr findloc iall iany iparity image_index lcobound ucobound maskl maskr num_images parity popcnt poppar shifta shiftl shiftr this_image"};return{cI:!0,aliases:["f90","f95"],k:n,c:[e.inherit(e.ASM,{cN:"string",r:0}),e.inherit(e.QSM,{cN:"string",r:0}),{cN:"function",bK:"subroutine function program",i:"[${=\\n]",c:[e.UTM,t]},e.C("!","$",{r:0}),{cN:"number",b:"(?=\\b|\\+|\\-|\\.)(?=\\.\\d|\\d)(?:\\d+)?(?:\\.?\\d*)(?:[de][+-]?\\d+)?\\b\\.?",r:0}]}});hljs.registerLanguage("smali",function(r){var t=["add","and","cmp","cmpg","cmpl","const","div","double","float","goto","if","int","long","move","mul","neg","new","nop","not","or","rem","return","shl","shr","sput","sub","throw","ushr","xor"],n=["aget","aput","array","check","execute","fill","filled","goto/16","goto/32","iget","instance","invoke","iput","monitor","packed","sget","sparse"],s=["transient","constructor","abstract","final","synthetic","public","private","protected","static","bridge","system"];return{aliases:["smali"],c:[{cN:"string",b:'"',e:'"',r:0},r.C("#","$",{r:0}),{cN:"keyword",b:"\\s*\\.end\\s[a-zA-Z0-9]*",r:1},{cN:"keyword",b:"^[ ]*\\.[a-zA-Z]*",r:0},{cN:"keyword",b:"\\s:[a-zA-Z_0-9]*",r:0},{cN:"keyword",b:"\\s("+s.join("|")+")",r:1},{cN:"keyword",b:"\\[",r:0},{cN:"instruction",b:"\\s("+t.join("|")+")\\s",r:1},{cN:"instruction",b:"\\s("+t.join("|")+")((\\-|/)[a-zA-Z0-9]+)+\\s",r:10},{cN:"instruction",b:"\\s("+n.join("|")+")((\\-|/)[a-zA-Z0-9]+)*\\s",r:10},{cN:"class",b:"L[^(;:\n]*;",r:0},{cN:"function",b:'( |->)[^(\n ;"]*\\(',r:0},{cN:"function",b:"\\)",r:0},{cN:"variable",b:"[vp][0-9]+",r:0}]}});hljs.registerLanguage("julia",function(r){var e={keyword:"in abstract baremodule begin bitstype break catch ccall const continue do else elseif end export finally for function global if immutable import importall let local macro module quote return try type typealias using while",literal:"true false ANY ARGS CPU_CORES C_NULL DL_LOAD_PATH DevNull ENDIAN_BOM ENV I|0 Inf Inf16 Inf32 InsertionSort JULIA_HOME LOAD_PATH MS_ASYNC MS_INVALIDATE MS_SYNC MergeSort NaN NaN16 NaN32 OS_NAME QuickSort RTLD_DEEPBIND RTLD_FIRST RTLD_GLOBAL RTLD_LAZY RTLD_LOCAL RTLD_NODELETE RTLD_NOLOAD RTLD_NOW RoundDown RoundFromZero RoundNearest RoundToZero RoundUp STDERR STDIN STDOUT VERSION WORD_SIZE catalan cglobal e eu eulergamma golden im nothing pi γ π φ",built_in:"ASCIIString AbstractArray AbstractRNG AbstractSparseArray Any ArgumentError Array Associative Base64Pipe Bidiagonal BigFloat BigInt BitArray BitMatrix BitVector Bool BoundsError Box CFILE Cchar Cdouble Cfloat Char CharString Cint Clong Clonglong ClusterManager Cmd Coff_t Colon Complex Complex128 Complex32 Complex64 Condition Cptrdiff_t Cshort Csize_t Cssize_t Cuchar Cuint Culong Culonglong Cushort Cwchar_t DArray DataType DenseArray Diagonal Dict DimensionMismatch DirectIndexString Display DivideError DomainError EOFError EachLine Enumerate ErrorException Exception Expr Factorization FileMonitor FileOffset Filter Float16 Float32 Float64 FloatRange FloatingPoint Function GetfieldNode GotoNode Hermitian IO IOBuffer IOStream IPv4 IPv6 InexactError Int Int128 Int16 Int32 Int64 Int8 IntSet Integer InterruptException IntrinsicFunction KeyError LabelNode LambdaStaticData LineNumberNode LoadError LocalProcess MIME MathConst MemoryError MersenneTwister Method MethodError MethodTable Module NTuple NewvarNode Nothing Number ObjectIdDict OrdinalRange OverflowError ParseError PollingFileWatcher ProcessExitedException ProcessGroup Ptr QuoteNode Range Range1 Ranges Rational RawFD Real Regex RegexMatch RemoteRef RepString RevString RopeString RoundingMode Set SharedArray Signed SparseMatrixCSC StackOverflowError Stat StatStruct StepRange String SubArray SubString SymTridiagonal Symbol SymbolNode Symmetric SystemError Task TextDisplay Timer TmStruct TopNode Triangular Tridiagonal Type TypeConstructor TypeError TypeName TypeVar UTF16String UTF32String UTF8String UdpSocket Uint Uint128 Uint16 Uint32 Uint64 Uint8 UndefRefError UndefVarError UniformScaling UnionType UnitRange Unsigned Vararg VersionNumber WString WeakKeyDict WeakRef Woodbury Zip"},t="[A-Za-z_\\u00A1-\\uFFFF][A-Za-z_0-9\\u00A1-\\uFFFF]*",o={l:t,k:e},n={cN:"type-annotation",b:/::/},a={cN:"subtype",b:/<:/},i={cN:"number",b:/(\b0x[\d_]*(\.[\d_]*)?|0x\.\d[\d_]*)p[-+]?\d+|\b0[box][a-fA-F0-9][a-fA-F0-9_]*|(\b\d[\d_]*(\.[\d_]*)?|\.\d[\d_]*)([eEfF][-+]?\d+)?/,r:0},l={cN:"char",b:/'(.|\\[xXuU][a-zA-Z0-9]+)'/},c={cN:"subst",b:/\$\(/,e:/\)/,k:e},u={cN:"variable",b:"\\$"+t},d={cN:"string",c:[r.BE,c,u],v:[{b:/\w*"/,e:/"\w*/},{b:/\w*"""/,e:/"""\w*/}]},g={cN:"string",c:[r.BE,c,u],b:"`",e:"`"},s={cN:"macrocall",b:"@"+t},S={cN:"comment",v:[{b:"#=",e:"=#",r:10},{b:"#",e:"$"}]};return o.c=[i,l,n,a,d,g,s,S,r.HCM],c.c=o.c,o});hljs.registerLanguage("delphi",function(e){var r="exports register file shl array record property for mod while set ally label uses raise not stored class safecall var interface or private static exit index inherited to else stdcall override shr asm far resourcestring finalization packed virtual out and protected library do xorwrite goto near function end div overload object unit begin string on inline repeat until destructor write message program with read initialization except default nil if case cdecl in downto threadvar of try pascal const external constructor type public then implementation finally published procedure",t=[e.CLCM,e.C(/\{/,/\}/,{r:0}),e.C(/\(\*/,/\*\)/,{r:10})],i={cN:"string",b:/'/,e:/'/,c:[{b:/''/}]},c={cN:"string",b:/(#\d+)+/},o={b:e.IR+"\\s*=\\s*class\\s*\\(",rB:!0,c:[e.TM]},n={cN:"function",bK:"function constructor destructor procedure",e:/[:;]/,k:"function constructor|10 destructor|10 procedure|10",c:[e.TM,{cN:"params",b:/\(/,e:/\)/,k:r,c:[i,c]}].concat(t)};return{cI:!0,k:r,i:/"|\$[G-Zg-z]|\/\*|<\/|\|/,c:[i,c,e.NM,o,n].concat(t)}});hljs.registerLanguage("brainfuck",function(r){var n={cN:"literal",b:"[\\+\\-]",r:0};return{aliases:["bf"],c:[r.C("[^\\[\\]\\.,\\+\\-<> \r\n]","[\\[\\]\\.,\\+\\-<> \r\n]",{rE:!0,r:0}),{cN:"title",b:"[\\[\\]]",r:0},{cN:"string",b:"[\\.,]",r:0},{b:/\+\+|\-\-/,rB:!0,c:[n]},n]}});hljs.registerLanguage("ini",function(e){return{cI:!0,i:/\S/,c:[e.C(";","$"),{cN:"title",b:"^\\[",e:"\\]"},{cN:"setting",b:"^[a-z0-9\\[\\]_-]+[ \\t]*=[ \\t]*",e:"$",c:[{cN:"value",eW:!0,k:"on off true false yes no",c:[e.QSM,e.NM],r:0}]}]}});hljs.registerLanguage("json",function(e){var t={literal:"true false null"},i=[e.QSM,e.CNM],l={cN:"value",e:",",eW:!0,eE:!0,c:i,k:t},c={b:"{",e:"}",c:[{cN:"attribute",b:'\\s*"',e:'"\\s*:\\s*',eB:!0,eE:!0,c:[e.BE],i:"\\n",starts:l}],i:"\\S"},n={b:"\\[",e:"\\]",c:[e.inherit(l,{cN:null})],i:"\\S"};return i.splice(i.length,0,c,n),{c:i,k:t,i:"\\S"}});hljs.registerLanguage("powershell",function(e){var t={b:"`[\\s\\S]",r:0},r={cN:"variable",v:[{b:/\$[\w\d][\w\d_:]*/}]},o={cN:"string",b:/"/,e:/"/,c:[t,r,{cN:"variable",b:/\$[A-z]/,e:/[^A-z]/}]},a={cN:"string",b:/'/,e:/'/};return{aliases:["ps"],l:/-?[A-z\.\-]+/,cI:!0,k:{keyword:"if else foreach return function do while until elseif begin for trap data dynamicparam end break throw param continue finally in switch exit filter try process catch",literal:"$null $true $false",built_in:"Add-Content Add-History Add-Member Add-PSSnapin Clear-Content Clear-Item Clear-Item Property Clear-Variable Compare-Object ConvertFrom-SecureString Convert-Path ConvertTo-Html ConvertTo-SecureString Copy-Item Copy-ItemProperty Export-Alias Export-Clixml Export-Console Export-Csv ForEach-Object Format-Custom Format-List Format-Table Format-Wide Get-Acl Get-Alias Get-AuthenticodeSignature Get-ChildItem Get-Command Get-Content Get-Credential Get-Culture Get-Date Get-EventLog Get-ExecutionPolicy Get-Help Get-History Get-Host Get-Item Get-ItemProperty Get-Location Get-Member Get-PfxCertificate Get-Process Get-PSDrive Get-PSProvider Get-PSSnapin Get-Service Get-TraceSource Get-UICulture Get-Unique Get-Variable Get-WmiObject Group-Object Import-Alias Import-Clixml Import-Csv Invoke-Expression Invoke-History Invoke-Item Join-Path Measure-Command Measure-Object Move-Item Move-ItemProperty New-Alias New-Item New-ItemProperty New-Object New-PSDrive New-Service New-TimeSpan New-Variable Out-Default Out-File Out-Host Out-Null Out-Printer Out-String Pop-Location Push-Location Read-Host Remove-Item Remove-ItemProperty Remove-PSDrive Remove-PSSnapin Remove-Variable Rename-Item Rename-ItemProperty Resolve-Path Restart-Service Resume-Service Select-Object Select-String Set-Acl Set-Alias Set-AuthenticodeSignature Set-Content Set-Date Set-ExecutionPolicy Set-Item Set-ItemProperty Set-Location Set-PSDebug Set-Service Set-TraceSource Set-Variable Sort-Object Split-Path Start-Service Start-Sleep Start-Transcript Stop-Process Stop-Service Stop-Transcript Suspend-Service Tee-Object Test-Path Trace-Command Update-FormatData Update-TypeData Where-Object Write-Debug Write-Error Write-Host Write-Output Write-Progress Write-Verbose Write-Warning",operator:"-ne -eq -lt -gt -ge -le -not -like -notlike -match -notmatch -contains -notcontains -in -notin -replace"},c:[e.HCM,e.NM,o,a,r]}});hljs.registerLanguage("gradle",function(e){return{cI:!0,k:{keyword:"task project allprojects subprojects artifacts buildscript configurations dependencies repositories sourceSets description delete from into include exclude source classpath destinationDir includes options sourceCompatibility targetCompatibility group flatDir doLast doFirst flatten todir fromdir ant def abstract break case catch continue default do else extends final finally for if implements instanceof native new private protected public return static switch synchronized throw throws transient try volatile while strictfp package import false null super this true antlrtask checkstyle codenarc copy boolean byte char class double float int interface long short void compile runTime file fileTree abs any append asList asWritable call collect compareTo count div dump each eachByte eachFile eachLine every find findAll flatten getAt getErr getIn getOut getText grep immutable inject inspect intersect invokeMethods isCase join leftShift minus multiply newInputStream newOutputStream newPrintWriter newReader newWriter next plus pop power previous print println push putAt read readBytes readLines reverse reverseEach round size sort splitEachLine step subMap times toInteger toList tokenize upto waitForOrKill withPrintWriter withReader withStream withWriter withWriterAppend write writeLine"},c:[e.CLCM,e.CBCM,e.ASM,e.QSM,e.NM,e.RM]}});hljs.registerLanguage("erb",function(e){return{sL:"xml",subLanguageMode:"continuous",c:[e.C("<%#","%>"),{b:"<%[%=-]?",e:"[%-]?%>",sL:"ruby",eB:!0,eE:!0}]}});hljs.registerLanguage("swift",function(e){var i={keyword:"class deinit enum extension func import init let protocol static struct subscript typealias var break case continue default do else fallthrough if in for return switch where while as dynamicType is new super self Self Type __COLUMN__ __FILE__ __FUNCTION__ __LINE__ associativity didSet get infix inout left mutating none nonmutating operator override postfix precedence prefix right set unowned unowned safe unsafe weak willSet",literal:"true false nil",built_in:"abs advance alignof alignofValue assert bridgeFromObjectiveC bridgeFromObjectiveCUnconditional bridgeToObjectiveC bridgeToObjectiveCUnconditional c contains count countElements countLeadingZeros debugPrint debugPrintln distance dropFirst dropLast dump encodeBitsAsWords enumerate equal false filter find getBridgedObjectiveCType getVaList indices insertionSort isBridgedToObjectiveC isBridgedVerbatimToObjectiveC isUniquelyReferenced join lexicographicalCompare map max maxElement min minElement nil numericCast partition posix print println quickSort reduce reflect reinterpretCast reverse roundUpToAlignment sizeof sizeofValue sort split startsWith strideof strideofValue swap swift toString transcode true underestimateCount unsafeReflect withExtendedLifetime withObjectAtPlusZero withUnsafePointer withUnsafePointerToObject withUnsafePointers withVaList"},t={cN:"type",b:"\\b[A-Z][\\w']*",r:0},n=e.C("/\\*","\\*/",{c:["self"]}),r={cN:"subst",b:/\\\(/,e:"\\)",k:i,c:[]},s={cN:"number",b:"\\b([\\d_]+(\\.[\\deE_]+)?|0x[a-fA-F0-9_]+(\\.[a-fA-F0-9p_]+)?|0b[01_]+|0o[0-7_]+)\\b",r:0},o=e.inherit(e.QSM,{c:[r,e.BE]});return r.c=[s],{k:i,c:[o,e.CLCM,n,t,s,{cN:"func",bK:"func",e:"{",eE:!0,c:[e.inherit(e.TM,{b:/[A-Za-z$_][0-9A-Za-z$_]*/,i:/\(/}),{cN:"generics",b:/</,e:/>/,i:/>/},{cN:"params",b:/\(/,e:/\)/,endsParent:!0,k:i,c:["self",s,o,e.CBCM,{b:":"}],i:/["']/}],i:/\[|%/},{cN:"class",bK:"struct protocol class extension enum",k:i,e:"\\{",eE:!0,c:[e.inherit(e.TM,{b:/[A-Za-z$_][0-9A-Za-z$_]*/})]},{cN:"preprocessor",b:"(@assignment|@class_protocol|@exported|@final|@lazy|@noreturn|@NSCopying|@NSManaged|@objc|@optional|@required|@auto_closure|@noreturn|@IBAction|@IBDesignable|@IBInspectable|@IBOutlet|@infix|@prefix|@postfix)"}]}});hljs.registerLanguage("lisp",function(b){var e="[a-zA-Z_\\-\\+\\*\\/\\<\\=\\>\\&\\#][a-zA-Z0-9_\\-\\+\\*\\/\\<\\=\\>\\&\\#!]*",c="\\|[^]*?\\|",r="(\\-|\\+)?\\d+(\\.\\d+|\\/\\d+)?((d|e|f|l|s|D|E|F|L|S)(\\+|\\-)?\\d+)?",a={cN:"shebang",b:"^#!",e:"$"},i={cN:"literal",b:"\\b(t{1}|nil)\\b"},l={cN:"number",v:[{b:r,r:0},{b:"#(b|B)[0-1]+(/[0-1]+)?"},{b:"#(o|O)[0-7]+(/[0-7]+)?"},{b:"#(x|X)[0-9a-fA-F]+(/[0-9a-fA-F]+)?"},{b:"#(c|C)\\("+r+" +"+r,e:"\\)"}]},t=b.inherit(b.QSM,{i:null}),d=b.C(";","$",{r:0}),n={cN:"variable",b:"\\*",e:"\\*"},u={cN:"keyword",b:"[:&]"+e},N={b:e,r:0},o={b:c},s={b:"\\(",e:"\\)",c:["self",i,t,l,N]},v={cN:"quoted",c:[l,t,n,u,s,N],v:[{b:"['`]\\(",e:"\\)"},{b:"\\(quote ",e:"\\)",k:"quote"},{b:"'"+c}]},f={cN:"quoted",v:[{b:"'"+e},{b:"#'"+e+"(::"+e+")*"}]},g={cN:"list",b:"\\(\\s*",e:"\\)"},q={eW:!0,r:0};return g.c=[{cN:"keyword",v:[{b:e},{b:c}]},q],q.c=[v,f,g,i,l,t,d,n,u,o,N],{i:/\S/,c:[l,a,i,t,d,v,f,g,N]}});hljs.registerLanguage("rsl",function(e){return{k:{keyword:"float color point normal vector matrix while for if do return else break extern continue",built_in:"abs acos ambient area asin atan atmosphere attribute calculatenormal ceil cellnoise clamp comp concat cos degrees depth Deriv diffuse distance Du Dv environment exp faceforward filterstep floor format fresnel incident length lightsource log match max min mod noise normalize ntransform opposite option phong pnoise pow printf ptlined radians random reflect refract renderinfo round setcomp setxcomp setycomp setzcomp shadow sign sin smoothstep specular specularbrdf spline sqrt step tan texture textureinfo trace transform vtransform xcomp ycomp zcomp"},i:"</",c:[e.CLCM,e.CBCM,e.QSM,e.ASM,e.CNM,{cN:"preprocessor",b:"#",e:"$"},{cN:"shader",bK:"surface displacement light volume imager",e:"\\("},{cN:"shading",bK:"illuminate illuminance gather",e:"\\("}]}});hljs.registerLanguage("scheme",function(e){var t="[^\\(\\)\\[\\]\\{\\}\",'`;#|\\\\\\s]+",r="(\\-|\\+)?\\d+([./]\\d+)?",i=r+"[+\\-]"+r+"i",a={built_in:"case-lambda call/cc class define-class exit-handler field import inherit init-field interface let*-values let-values let/ec mixin opt-lambda override protect provide public rename require require-for-syntax syntax syntax-case syntax-error unit/sig unless when with-syntax and begin call-with-current-continuation call-with-input-file call-with-output-file case cond define define-syntax delay do dynamic-wind else for-each if lambda let let* let-syntax letrec letrec-syntax map or syntax-rules ' * + , ,@ - ... / ; < <= = => > >= ` abs acos angle append apply asin assoc assq assv atan boolean? caar cadr call-with-input-file call-with-output-file call-with-values car cdddar cddddr cdr ceiling char->integer char-alphabetic? char-ci<=? char-ci<? char-ci=? char-ci>=? char-ci>? char-downcase char-lower-case? char-numeric? char-ready? char-upcase char-upper-case? char-whitespace? char<=? char<? char=? char>=? char>? char? close-input-port close-output-port complex? cons cos current-input-port current-output-port denominator display eof-object? eq? equal? eqv? eval even? exact->inexact exact? exp expt floor force gcd imag-part inexact->exact inexact? input-port? integer->char integer? interaction-environment lcm length list list->string list->vector list-ref list-tail list? load log magnitude make-polar make-rectangular make-string make-vector max member memq memv min modulo negative? newline not null-environment null? number->string number? numerator odd? open-input-file open-output-file output-port? pair? peek-char port? positive? procedure? quasiquote quote quotient rational? rationalize read read-char real-part real? remainder reverse round scheme-report-environment set! set-car! set-cdr! sin sqrt string string->list string->number string->symbol string-append string-ci<=? string-ci<? string-ci=? string-ci>=? string-ci>? string-copy string-fill! string-length string-ref string-set! string<=? string<? string=? string>=? string>? string? substring symbol->string symbol? tan transcript-off transcript-on truncate values vector vector->list vector-fill! vector-length vector-ref vector-set! with-input-from-file with-output-to-file write write-char zero?"},n={cN:"shebang",b:"^#!",e:"$"},c={cN:"literal",b:"(#t|#f|#\\\\"+t+"|#\\\\.)"},l={cN:"number",v:[{b:r,r:0},{b:i,r:0},{b:"#b[0-1]+(/[0-1]+)?"},{b:"#o[0-7]+(/[0-7]+)?"},{b:"#x[0-9a-f]+(/[0-9a-f]+)?"}]},s=e.QSM,o=[e.C(";","$",{r:0}),e.C("#\\|","\\|#")],u={b:t,r:0},p={cN:"variable",b:"'"+t},d={eW:!0,r:0},g={cN:"list",v:[{b:"\\(",e:"\\)"},{b:"\\[",e:"\\]"}],c:[{cN:"keyword",b:t,l:t,k:a},d]};return d.c=[c,l,s,u,p,g].concat(o),{i:/\S/,c:[n,l,s,p,g].concat(o)}});hljs.registerLanguage("stata",function(e){return{aliases:["do","ado"],cI:!0,k:"if else in foreach for forv forva forval forvalu forvalue forvalues by bys bysort xi quietly qui capture about ac ac_7 acprplot acprplot_7 adjust ado adopath adoupdate alpha ameans an ano anov anova anova_estat anova_terms anovadef aorder ap app appe appen append arch arch_dr arch_estat arch_p archlm areg areg_p args arima arima_dr arima_estat arima_p as asmprobit asmprobit_estat asmprobit_lf asmprobit_mfx__dlg asmprobit_p ass asse asser assert avplot avplot_7 avplots avplots_7 bcskew0 bgodfrey binreg bip0_lf biplot bipp_lf bipr_lf bipr_p biprobit bitest bitesti bitowt blogit bmemsize boot bootsamp bootstrap bootstrap_8 boxco_l boxco_p boxcox boxcox_6 boxcox_p bprobit br break brier bro brow brows browse brr brrstat bs bs_7 bsampl_w bsample bsample_7 bsqreg bstat bstat_7 bstat_8 bstrap bstrap_7 ca ca_estat ca_p cabiplot camat canon canon_8 canon_8_p canon_estat canon_p cap caprojection capt captu captur capture cat cc cchart cchart_7 cci cd censobs_table centile cf char chdir checkdlgfiles checkestimationsample checkhlpfiles checksum chelp ci cii cl class classutil clear cli clis clist clo clog clog_lf clog_p clogi clogi_sw clogit clogit_lf clogit_p clogitp clogl_sw cloglog clonevar clslistarray cluster cluster_measures cluster_stop cluster_tree cluster_tree_8 clustermat cmdlog cnr cnre cnreg cnreg_p cnreg_sw cnsreg codebook collaps4 collapse colormult_nb colormult_nw compare compress conf confi confir confirm conren cons const constr constra constrai constrain constraint continue contract copy copyright copysource cor corc corr corr2data corr_anti corr_kmo corr_smc corre correl correla correlat correlate corrgram cou coun count cox cox_p cox_sw coxbase coxhaz coxvar cprplot cprplot_7 crc cret cretu cretur creturn cross cs cscript cscript_log csi ct ct_is ctset ctst_5 ctst_st cttost cumsp cumsp_7 cumul cusum cusum_7 cutil d datasig datasign datasigna datasignat datasignatu datasignatur datasignature datetof db dbeta de dec deco decod decode deff des desc descr descri describ describe destring dfbeta dfgls dfuller di di_g dir dirstats dis discard disp disp_res disp_s displ displa display distinct do doe doed doedi doedit dotplot dotplot_7 dprobit drawnorm drop ds ds_util dstdize duplicates durbina dwstat dydx e ed edi edit egen eivreg emdef en enc enco encod encode eq erase ereg ereg_lf ereg_p ereg_sw ereghet ereghet_glf ereghet_glf_sh ereghet_gp ereghet_ilf ereghet_ilf_sh ereghet_ip eret eretu eretur ereturn err erro error est est_cfexist est_cfname est_clickable est_expand est_hold est_table est_unhold est_unholdok estat estat_default estat_summ estat_vce_only esti estimates etodow etof etomdy ex exi exit expand expandcl fac fact facto factor factor_estat factor_p factor_pca_rotated factor_rotate factormat fcast fcast_compute fcast_graph fdades fdadesc fdadescr fdadescri fdadescrib fdadescribe fdasav fdasave fdause fh_st file open file read file close file filefilter fillin find_hlp_file findfile findit findit_7 fit fl fli flis flist for5_0 form forma format fpredict frac_154 frac_adj frac_chk frac_cox frac_ddp frac_dis frac_dv frac_in frac_mun frac_pp frac_pq frac_pv frac_wgt frac_xo fracgen fracplot fracplot_7 fracpoly fracpred fron_ex fron_hn fron_p fron_tn fron_tn2 frontier ftodate ftoe ftomdy ftowdate g gamhet_glf gamhet_gp gamhet_ilf gamhet_ip gamma gamma_d2 gamma_p gamma_sw gammahet gdi_hexagon gdi_spokes ge gen gene gener genera generat generate genrank genstd genvmean gettoken gl gladder gladder_7 glim_l01 glim_l02 glim_l03 glim_l04 glim_l05 glim_l06 glim_l07 glim_l08 glim_l09 glim_l10 glim_l11 glim_l12 glim_lf glim_mu glim_nw1 glim_nw2 glim_nw3 glim_p glim_v1 glim_v2 glim_v3 glim_v4 glim_v5 glim_v6 glim_v7 glm glm_6 glm_p glm_sw glmpred glo glob globa global glogit glogit_8 glogit_p gmeans gnbre_lf gnbreg gnbreg_5 gnbreg_p gomp_lf gompe_sw gomper_p gompertz gompertzhet gomphet_glf gomphet_glf_sh gomphet_gp gomphet_ilf gomphet_ilf_sh gomphet_ip gphdot gphpen gphprint gprefs gprobi_p gprobit gprobit_8 gr gr7 gr_copy gr_current gr_db gr_describe gr_dir gr_draw gr_draw_replay gr_drop gr_edit gr_editviewopts gr_example gr_example2 gr_export gr_print gr_qscheme gr_query gr_read gr_rename gr_replay gr_save gr_set gr_setscheme gr_table gr_undo gr_use graph graph7 grebar greigen greigen_7 greigen_8 grmeanby grmeanby_7 gs_fileinfo gs_filetype gs_graphinfo gs_stat gsort gwood h hadimvo hareg hausman haver he heck_d2 heckma_p heckman heckp_lf heckpr_p heckprob hel help hereg hetpr_lf hetpr_p hetprob hettest hexdump hilite hist hist_7 histogram hlogit hlu hmeans hotel hotelling hprobit hreg hsearch icd9 icd9_ff icd9p iis impute imtest inbase include inf infi infil infile infix inp inpu input ins insheet insp inspe inspec inspect integ inten intreg intreg_7 intreg_p intrg2_ll intrg_ll intrg_ll2 ipolate iqreg ir irf irf_create irfm iri is_svy is_svysum isid istdize ivprob_1_lf ivprob_lf ivprobit ivprobit_p ivreg ivreg_footnote ivtob_1_lf ivtob_lf ivtobit ivtobit_p jackknife jacknife jknife jknife_6 jknife_8 jkstat joinby kalarma1 kap kap_3 kapmeier kappa kapwgt kdensity kdensity_7 keep ksm ksmirnov ktau kwallis l la lab labe label labelbook ladder levels levelsof leverage lfit lfit_p li lincom line linktest lis list lloghet_glf lloghet_glf_sh lloghet_gp lloghet_ilf lloghet_ilf_sh lloghet_ip llogi_sw llogis_p llogist llogistic llogistichet lnorm_lf lnorm_sw lnorma_p lnormal lnormalhet lnormhet_glf lnormhet_glf_sh lnormhet_gp lnormhet_ilf lnormhet_ilf_sh lnormhet_ip lnskew0 loadingplot loc loca local log logi logis_lf logistic logistic_p logit logit_estat logit_p loglogs logrank loneway lookfor lookup lowess lowess_7 lpredict lrecomp lroc lroc_7 lrtest ls lsens lsens_7 lsens_x lstat ltable ltable_7 ltriang lv lvr2plot lvr2plot_7 m ma mac macr macro makecns man manova manova_estat manova_p manovatest mantel mark markin markout marksample mat mat_capp mat_order mat_put_rr mat_rapp mata mata_clear mata_describe mata_drop mata_matdescribe mata_matsave mata_matuse mata_memory mata_mlib mata_mosave mata_rename mata_which matalabel matcproc matlist matname matr matri matrix matrix_input__dlg matstrik mcc mcci md0_ md1_ md1debug_ md2_ md2debug_ mds mds_estat mds_p mdsconfig mdslong mdsmat mdsshepard mdytoe mdytof me_derd mean means median memory memsize meqparse mer merg merge mfp mfx mhelp mhodds minbound mixed_ll mixed_ll_reparm mkassert mkdir mkmat mkspline ml ml_5 ml_adjs ml_bhhhs ml_c_d ml_check ml_clear ml_cnt ml_debug ml_defd ml_e0 ml_e0_bfgs ml_e0_cycle ml_e0_dfp ml_e0i ml_e1 ml_e1_bfgs ml_e1_bhhh ml_e1_cycle ml_e1_dfp ml_e2 ml_e2_cycle ml_ebfg0 ml_ebfr0 ml_ebfr1 ml_ebh0q ml_ebhh0 ml_ebhr0 ml_ebr0i ml_ecr0i ml_edfp0 ml_edfr0 ml_edfr1 ml_edr0i ml_eds ml_eer0i ml_egr0i ml_elf ml_elf_bfgs ml_elf_bhhh ml_elf_cycle ml_elf_dfp ml_elfi ml_elfs ml_enr0i ml_enrr0 ml_erdu0 ml_erdu0_bfgs ml_erdu0_bhhh ml_erdu0_bhhhq ml_erdu0_cycle ml_erdu0_dfp ml_erdu0_nrbfgs ml_exde ml_footnote ml_geqnr ml_grad0 ml_graph ml_hbhhh ml_hd0 ml_hold ml_init ml_inv ml_log ml_max ml_mlout ml_mlout_8 ml_model ml_nb0 ml_opt ml_p ml_plot ml_query ml_rdgrd ml_repor ml_s_e ml_score ml_searc ml_technique ml_unhold mleval mlf_ mlmatbysum mlmatsum mlog mlogi mlogit mlogit_footnote mlogit_p mlopts mlsum mlvecsum mnl0_ mor more mov move mprobit mprobit_lf mprobit_p mrdu0_ mrdu1_ mvdecode mvencode mvreg mvreg_estat n nbreg nbreg_al nbreg_lf nbreg_p nbreg_sw nestreg net newey newey_7 newey_p news nl nl_7 nl_9 nl_9_p nl_p nl_p_7 nlcom nlcom_p nlexp2 nlexp2_7 nlexp2a nlexp2a_7 nlexp3 nlexp3_7 nlgom3 nlgom3_7 nlgom4 nlgom4_7 nlinit nllog3 nllog3_7 nllog4 nllog4_7 nlog_rd nlogit nlogit_p nlogitgen nlogittree nlpred no nobreak noi nois noisi noisil noisily note notes notes_dlg nptrend numlabel numlist odbc old_ver olo olog ologi ologi_sw ologit ologit_p ologitp on one onew onewa oneway op_colnm op_comp op_diff op_inv op_str opr opro oprob oprob_sw oprobi oprobi_p oprobit oprobitp opts_exclusive order orthog orthpoly ou out outf outfi outfil outfile outs outsh outshe outshee outsheet ovtest pac pac_7 palette parse parse_dissim pause pca pca_8 pca_display pca_estat pca_p pca_rotate pcamat pchart pchart_7 pchi pchi_7 pcorr pctile pentium pergram pergram_7 permute permute_8 personal peto_st pkcollapse pkcross pkequiv pkexamine pkexamine_7 pkshape pksumm pksumm_7 pl plo plot plugin pnorm pnorm_7 poisgof poiss_lf poiss_sw poisso_p poisson poisson_estat post postclose postfile postutil pperron pr prais prais_e prais_e2 prais_p predict predictnl preserve print pro prob probi probit probit_estat probit_p proc_time procoverlay procrustes procrustes_estat procrustes_p profiler prog progr progra program prop proportion prtest prtesti pwcorr pwd q\\s qby qbys qchi qchi_7 qladder qladder_7 qnorm qnorm_7 qqplot qqplot_7 qreg qreg_c qreg_p qreg_sw qu quadchk quantile quantile_7 que quer query range ranksum ratio rchart rchart_7 rcof recast reclink recode reg reg3 reg3_p regdw regr regre regre_p2 regres regres_p regress regress_estat regriv_p remap ren rena renam rename renpfix repeat replace report reshape restore ret retu retur return rm rmdir robvar roccomp roccomp_7 roccomp_8 rocf_lf rocfit rocfit_8 rocgold rocplot rocplot_7 roctab roctab_7 rolling rologit rologit_p rot rota rotat rotate rotatemat rreg rreg_p ru run runtest rvfplot rvfplot_7 rvpplot rvpplot_7 sa safesum sample sampsi sav save savedresults saveold sc sca scal scala scalar scatter scm_mine sco scob_lf scob_p scobi_sw scobit scor score scoreplot scoreplot_help scree screeplot screeplot_help sdtest sdtesti se search separate seperate serrbar serrbar_7 serset set set_defaults sfrancia sh she shel shell shewhart shewhart_7 signestimationsample signrank signtest simul simul_7 simulate simulate_8 sktest sleep slogit slogit_d2 slogit_p smooth snapspan so sor sort spearman spikeplot spikeplot_7 spikeplt spline_x split sqreg sqreg_p sret sretu sretur sreturn ssc st st_ct st_hc st_hcd st_hcd_sh st_is st_issys st_note st_promo st_set st_show st_smpl st_subid stack statsby statsby_8 stbase stci stci_7 stcox stcox_estat stcox_fr stcox_fr_ll stcox_p stcox_sw stcoxkm stcoxkm_7 stcstat stcurv stcurve stcurve_7 stdes stem stepwise stereg stfill stgen stir stjoin stmc stmh stphplot stphplot_7 stphtest stphtest_7 stptime strate strate_7 streg streg_sw streset sts sts_7 stset stsplit stsum sttocc sttoct stvary stweib su suest suest_8 sum summ summa summar summari summariz summarize sunflower sureg survcurv survsum svar svar_p svmat svy svy_disp svy_dreg svy_est svy_est_7 svy_estat svy_get svy_gnbreg_p svy_head svy_header svy_heckman_p svy_heckprob_p svy_intreg_p svy_ivreg_p svy_logistic_p svy_logit_p svy_mlogit_p svy_nbreg_p svy_ologit_p svy_oprobit_p svy_poisson_p svy_probit_p svy_regress_p svy_sub svy_sub_7 svy_x svy_x_7 svy_x_p svydes svydes_8 svygen svygnbreg svyheckman svyheckprob svyintreg svyintreg_7 svyintrg svyivreg svylc svylog_p svylogit svymarkout svymarkout_8 svymean svymlog svymlogit svynbreg svyolog svyologit svyoprob svyoprobit svyopts svypois svypois_7 svypoisson svyprobit svyprobt svyprop svyprop_7 svyratio svyreg svyreg_p svyregress svyset svyset_7 svyset_8 svytab svytab_7 svytest svytotal sw sw_8 swcnreg swcox swereg swilk swlogis swlogit swologit swoprbt swpois swprobit swqreg swtobit swweib symmetry symmi symplot symplot_7 syntax sysdescribe sysdir sysuse szroeter ta tab tab1 tab2 tab_or tabd tabdi tabdis tabdisp tabi table tabodds tabodds_7 tabstat tabu tabul tabula tabulat tabulate te tempfile tempname tempvar tes test testnl testparm teststd tetrachoric time_it timer tis tob tobi tobit tobit_p tobit_sw token tokeni tokeniz tokenize tostring total translate translator transmap treat_ll treatr_p treatreg trim trnb_cons trnb_mean trpoiss_d2 trunc_ll truncr_p truncreg tsappend tset tsfill tsline tsline_ex tsreport tsrevar tsrline tsset tssmooth tsunab ttest ttesti tut_chk tut_wait tutorial tw tware_st two twoway twoway__fpfit_serset twoway__function_gen twoway__histogram_gen twoway__ipoint_serset twoway__ipoints_serset twoway__kdensity_gen twoway__lfit_serset twoway__normgen_gen twoway__pci_serset twoway__qfit_serset twoway__scatteri_serset twoway__sunflower_gen twoway_ksm_serset ty typ type typeof u unab unabbrev unabcmd update us use uselabel var var_mkcompanion var_p varbasic varfcast vargranger varirf varirf_add varirf_cgraph varirf_create varirf_ctable varirf_describe varirf_dir varirf_drop varirf_erase varirf_graph varirf_ograph varirf_rename varirf_set varirf_table varlist varlmar varnorm varsoc varstable varstable_w varstable_w2 varwle vce vec vec_fevd vec_mkphi vec_p vec_p_w vecirf_create veclmar veclmar_w vecnorm vecnorm_w vecrank vecstable verinst vers versi versio version view viewsource vif vwls wdatetof webdescribe webseek webuse weib1_lf weib2_lf weib_lf weib_lf0 weibhet_glf weibhet_glf_sh weibhet_glfa weibhet_glfa_sh weibhet_gp weibhet_ilf weibhet_ilf_sh weibhet_ilfa weibhet_ilfa_sh weibhet_ip weibu_sw weibul_p weibull weibull_c weibull_s weibullhet wh whelp whi which whil while wilc_st wilcoxon win wind windo window winexec wntestb wntestb_7 wntestq xchart xchart_7 xcorr xcorr_7 xi xi_6 xmlsav xmlsave xmluse xpose xsh xshe xshel xshell xt_iis xt_tis xtab_p xtabond xtbin_p xtclog xtcloglog xtcloglog_8 xtcloglog_d2 xtcloglog_pa_p xtcloglog_re_p xtcnt_p xtcorr xtdata xtdes xtfront_p xtfrontier xtgee xtgee_elink xtgee_estat xtgee_makeivar xtgee_p xtgee_plink xtgls xtgls_p xthaus xthausman xtht_p xthtaylor xtile xtint_p xtintreg xtintreg_8 xtintreg_d2 xtintreg_p xtivp_1 xtivp_2 xtivreg xtline xtline_ex xtlogit xtlogit_8 xtlogit_d2 xtlogit_fe_p xtlogit_pa_p xtlogit_re_p xtmixed xtmixed_estat xtmixed_p xtnb_fe xtnb_lf xtnbreg xtnbreg_pa_p xtnbreg_refe_p xtpcse xtpcse_p xtpois xtpoisson xtpoisson_d2 xtpoisson_pa_p xtpoisson_refe_p xtpred xtprobit xtprobit_8 xtprobit_d2 xtprobit_re_p xtps_fe xtps_lf xtps_ren xtps_ren_8 xtrar_p xtrc xtrc_p xtrchh xtrefe_p xtreg xtreg_be xtreg_fe xtreg_ml xtreg_pa_p xtreg_re xtregar xtrere_p xtset xtsf_ll xtsf_llti xtsum xttab xttest0 xttobit xttobit_8 xttobit_p xttrans yx yxview__barlike_draw yxview_area_draw yxview_bar_draw yxview_dot_draw yxview_dropline_draw yxview_function_draw yxview_iarrow_draw yxview_ilabels_draw yxview_normal_draw yxview_pcarrow_draw yxview_pcbarrow_draw yxview_pccapsym_draw yxview_pcscatter_draw yxview_pcspike_draw yxview_rarea_draw yxview_rbar_draw yxview_rbarm_draw yxview_rcap_draw yxview_rcapsym_draw yxview_rconnected_draw yxview_rline_draw yxview_rscatter_draw yxview_rspike_draw yxview_spike_draw yxview_sunflower_draw zap_s zinb zinb_llf zinb_plf zip zip_llf zip_p zip_plf zt_ct_5 zt_hc_5 zt_hcd_5 zt_is_5 zt_iss_5 zt_sho_5 zt_smp_5 ztbase_5 ztcox_5 ztdes_5 ztereg_5 ztfill_5 ztgen_5 ztir_5 ztjoin_5 ztnb ztnb_p ztp ztp_p zts_5 ztset_5 ztspli_5 ztsum_5 zttoct_5 ztvary_5 ztweib_5",c:[{cN:"label",v:[{b:"\\$\\{?[a-zA-Z0-9_]+\\}?"},{b:"`[a-zA-Z0-9_]+'"}]},{cN:"string",v:[{b:'`"[^\r\n]*?"\''},{b:'"[^\r\n"]*"'}]},{cN:"literal",v:[{b:"\\b(abs|acos|asin|atan|atan2|atanh|ceil|cloglog|comb|cos|digamma|exp|floor|invcloglog|invlogit|ln|lnfact|lnfactorial|lngamma|log|log10|max|min|mod|reldif|round|sign|sin|sqrt|sum|tan|tanh|trigamma|trunc|betaden|Binomial|binorm|binormal|chi2|chi2tail|dgammapda|dgammapdada|dgammapdadx|dgammapdx|dgammapdxdx|F|Fden|Ftail|gammaden|gammap|ibeta|invbinomial|invchi2|invchi2tail|invF|invFtail|invgammap|invibeta|invnchi2|invnFtail|invnibeta|invnorm|invnormal|invttail|nbetaden|nchi2|nFden|nFtail|nibeta|norm|normal|normalden|normd|npnchi2|tden|ttail|uniform|abbrev|char|index|indexnot|length|lower|ltrim|match|plural|proper|real|regexm|regexr|regexs|reverse|rtrim|string|strlen|strlower|strltrim|strmatch|strofreal|strpos|strproper|strreverse|strrtrim|strtrim|strupper|subinstr|subinword|substr|trim|upper|word|wordcount|_caller|autocode|byteorder|chop|clip|cond|e|epsdouble|epsfloat|group|inlist|inrange|irecode|matrix|maxbyte|maxdouble|maxfloat|maxint|maxlong|mi|minbyte|mindouble|minfloat|minint|minlong|missing|r|recode|replay|return|s|scalar|d|date|day|dow|doy|halfyear|mdy|month|quarter|week|year|d|daily|dofd|dofh|dofm|dofq|dofw|dofy|h|halfyearly|hofd|m|mofd|monthly|q|qofd|quarterly|tin|twithin|w|weekly|wofd|y|yearly|yh|ym|yofd|yq|yw|cholesky|colnumb|colsof|corr|det|diag|diag0cnt|el|get|hadamard|I|inv|invsym|issym|issymmetric|J|matmissing|matuniform|mreldif|nullmat|rownumb|rowsof|sweep|syminv|trace|vec|vecdiag)(?=\\(|$)"}]},e.C("^[ 	]*\\*.*$",!1),e.CLCM,e.CBCM]}});hljs.registerLanguage("asciidoc",function(e){return{aliases:["adoc"],c:[e.C("^/{4,}\\n","\\n/{4,}$",{r:10}),e.C("^//","$",{r:0}),{cN:"title",b:"^\\.\\w.*$"},{b:"^[=\\*]{4,}\\n",e:"\\n^[=\\*]{4,}$",r:10},{cN:"header",b:"^(={1,5}) .+?( \\1)?$",r:10},{cN:"header",b:"^[^\\[\\]\\n]+?\\n[=\\-~\\^\\+]{2,}$",r:10},{cN:"attribute",b:"^:.+?:",e:"\\s",eE:!0,r:10},{cN:"attribute",b:"^\\[.+?\\]$",r:0},{cN:"blockquote",b:"^_{4,}\\n",e:"\\n_{4,}$",r:10},{cN:"code",b:"^[\\-\\.]{4,}\\n",e:"\\n[\\-\\.]{4,}$",r:10},{b:"^\\+{4,}\\n",e:"\\n\\+{4,}$",c:[{b:"<",e:">",sL:"xml",r:0}],r:10},{cN:"bullet",b:"^(\\*+|\\-+|\\.+|[^\\n]+?::)\\s+"},{cN:"label",b:"^(NOTE|TIP|IMPORTANT|WARNING|CAUTION):\\s+",r:10},{cN:"strong",b:"\\B\\*(?![\\*\\s])",e:"(\\n{2}|\\*)",c:[{b:"\\\\*\\w",r:0}]},{cN:"emphasis",b:"\\B'(?!['\\s])",e:"(\\n{2}|')",c:[{b:"\\\\'\\w",r:0}],r:0},{cN:"emphasis",b:"_(?![_\\s])",e:"(\\n{2}|_)",r:0},{cN:"smartquote",v:[{b:"``.+?''"},{b:"`.+?'"}]},{cN:"code",b:"(`.+?`|\\+.+?\\+)",r:0},{cN:"code",b:"^[ \\t]",e:"$",r:0},{cN:"horizontal_rule",b:"^'{3,}[ \\t]*$",r:10},{b:"(link:)?(http|https|ftp|file|irc|image:?):\\S+\\[.*?\\]",rB:!0,c:[{b:"(link|image:?):",r:0},{cN:"link_url",b:"\\w",e:"[^\\[]+",r:0},{cN:"link_label",b:"\\[",e:"\\]",eB:!0,eE:!0,r:0}],r:10}]}});hljs.registerLanguage("php",function(e){var c={cN:"variable",b:"\\$+[a-zA-Z_-ÿ][a-zA-Z0-9_-ÿ]*"},i={cN:"preprocessor",b:/<\?(php)?|\?>/},a={cN:"string",c:[e.BE,i],v:[{b:'b"',e:'"'},{b:"b'",e:"'"},e.inherit(e.ASM,{i:null}),e.inherit(e.QSM,{i:null})]},n={v:[e.BNM,e.CNM]};return{aliases:["php3","php4","php5","php6"],cI:!0,k:"and include_once list abstract global private echo interface as static endswitch array null if endwhile or const for endforeach self var while isset public protected exit foreach throw elseif include __FILE__ empty require_once do xor return parent clone use __CLASS__ __LINE__ else break print eval new catch __METHOD__ case exception default die require __FUNCTION__ enddeclare final try switch continue endfor endif declare unset true false trait goto instanceof insteadof __DIR__ __NAMESPACE__ yield finally",c:[e.CLCM,e.HCM,e.C("/\\*","\\*/",{c:[{cN:"phpdoc",b:"\\s@[A-Za-z]+"},i]}),e.C("__halt_compiler.+?;",!1,{eW:!0,k:"__halt_compiler",l:e.UIR}),{cN:"string",b:"<<<['\"]?\\w+['\"]?$",e:"^\\w+;",c:[e.BE]},i,c,{b:/(::|->)+[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/},{cN:"function",bK:"function",e:/[;{]/,eE:!0,i:"\\$|\\[|%",c:[e.UTM,{cN:"params",b:"\\(",e:"\\)",c:["self",c,e.CBCM,a,n]}]},{cN:"class",bK:"class interface",e:"{",eE:!0,i:/[:\(\$"]/,c:[{bK:"extends implements"},e.UTM]},{bK:"namespace",e:";",i:/[\.']/,c:[e.UTM]},{bK:"use",e:";",c:[e.UTM]},{b:"=>"},a,n]}});hljs.registerLanguage("java",function(e){var a=e.UIR+"(<"+e.UIR+">)?",t="false synchronized int abstract float private char boolean static null if const for true while long strictfp finally protected import native final void enum else break transient catch instanceof byte super volatile case assert short package default double public try this switch continue throws protected public private",c="(\\b(0b[01_]+)|\\b0[xX][a-fA-F0-9_]+|(\\b[\\d_]+(\\.[\\d_]*)?|\\.[\\d_]+)([eE][-+]?\\d+)?)[lLfF]?",r={cN:"number",b:c,r:0};return{aliases:["jsp"],k:t,i:/<\//,c:[{cN:"javadoc",b:"/\\*\\*",e:"\\*/",r:0,c:[{cN:"javadoctag",b:"(^|\\s)@[A-Za-z]+"}]},e.CLCM,e.CBCM,e.ASM,e.QSM,{cN:"class",bK:"class interface",e:/[{;=]/,eE:!0,k:"class interface",i:/[:"\[\]]/,c:[{bK:"extends implements"},e.UTM]},{bK:"new throw return",r:0},{cN:"function",b:"("+a+"\\s+)+"+e.UIR+"\\s*\\(",rB:!0,e:/[{;=]/,eE:!0,k:t,c:[{b:e.UIR+"\\s*\\(",rB:!0,r:0,c:[e.UTM]},{cN:"params",b:/\(/,e:/\)/,k:t,r:0,c:[e.ASM,e.QSM,e.CNM,e.CBCM]},e.CLCM,e.CBCM]},r,{cN:"annotation",b:"@[A-Za-z]+"}]}});hljs.registerLanguage("glsl",function(e){return{k:{keyword:"atomic_uint attribute bool break bvec2 bvec3 bvec4 case centroid coherent const continue default discard dmat2 dmat2x2 dmat2x3 dmat2x4 dmat3 dmat3x2 dmat3x3 dmat3x4 dmat4 dmat4x2 dmat4x3 dmat4x4 do double dvec2 dvec3 dvec4 else flat float for highp if iimage1D iimage1DArray iimage2D iimage2DArray iimage2DMS iimage2DMSArray iimage2DRect iimage3D iimageBuffer iimageCube iimageCubeArray image1D image1DArray image2D image2DArray image2DMS image2DMSArray image2DRect image3D imageBuffer imageCube imageCubeArray in inout int invariant isampler1D isampler1DArray isampler2D isampler2DArray isampler2DMS isampler2DMSArray isampler2DRect isampler3D isamplerBuffer isamplerCube isamplerCubeArray ivec2 ivec3 ivec4 layout lowp mat2 mat2x2 mat2x3 mat2x4 mat3 mat3x2 mat3x3 mat3x4 mat4 mat4x2 mat4x3 mat4x4 mediump noperspective out patch precision readonly restrict return sample sampler1D sampler1DArray sampler1DArrayShadow sampler1DShadow sampler2D sampler2DArray sampler2DArrayShadow sampler2DMS sampler2DMSArray sampler2DRect sampler2DRectShadow sampler2DShadow sampler3D samplerBuffer samplerCube samplerCubeArray samplerCubeArrayShadow samplerCubeShadow smooth struct subroutine switch uimage1D uimage1DArray uimage2D uimage2DArray uimage2DMS uimage2DMSArray uimage2DRect uimage3D uimageBuffer uimageCube uimageCubeArray uint uniform usampler1D usampler1DArray usampler2D usampler2DArray usampler2DMS usampler2DMSArray usampler2DRect usampler3D usamplerBuffer usamplerCube usamplerCubeArray uvec2 uvec3 uvec4 varying vec2 vec3 vec4 void volatile while writeonly",built_in:"gl_BackColor gl_BackLightModelProduct gl_BackLightProduct gl_BackMaterial gl_BackSecondaryColor gl_ClipDistance gl_ClipPlane gl_ClipVertex gl_Color gl_DepthRange gl_EyePlaneQ gl_EyePlaneR gl_EyePlaneS gl_EyePlaneT gl_Fog gl_FogCoord gl_FogFragCoord gl_FragColor gl_FragCoord gl_FragData gl_FragDepth gl_FrontColor gl_FrontFacing gl_FrontLightModelProduct gl_FrontLightProduct gl_FrontMaterial gl_FrontSecondaryColor gl_InstanceID gl_InvocationID gl_Layer gl_LightModel gl_LightSource gl_MaxAtomicCounterBindings gl_MaxAtomicCounterBufferSize gl_MaxClipDistances gl_MaxClipPlanes gl_MaxCombinedAtomicCounterBuffers gl_MaxCombinedAtomicCounters gl_MaxCombinedImageUniforms gl_MaxCombinedImageUnitsAndFragmentOutputs gl_MaxCombinedTextureImageUnits gl_MaxDrawBuffers gl_MaxFragmentAtomicCounterBuffers gl_MaxFragmentAtomicCounters gl_MaxFragmentImageUniforms gl_MaxFragmentInputComponents gl_MaxFragmentUniformComponents gl_MaxFragmentUniformVectors gl_MaxGeometryAtomicCounterBuffers gl_MaxGeometryAtomicCounters gl_MaxGeometryImageUniforms gl_MaxGeometryInputComponents gl_MaxGeometryOutputComponents gl_MaxGeometryOutputVertices gl_MaxGeometryTextureImageUnits gl_MaxGeometryTotalOutputComponents gl_MaxGeometryUniformComponents gl_MaxGeometryVaryingComponents gl_MaxImageSamples gl_MaxImageUnits gl_MaxLights gl_MaxPatchVertices gl_MaxProgramTexelOffset gl_MaxTessControlAtomicCounterBuffers gl_MaxTessControlAtomicCounters gl_MaxTessControlImageUniforms gl_MaxTessControlInputComponents gl_MaxTessControlOutputComponents gl_MaxTessControlTextureImageUnits gl_MaxTessControlTotalOutputComponents gl_MaxTessControlUniformComponents gl_MaxTessEvaluationAtomicCounterBuffers gl_MaxTessEvaluationAtomicCounters gl_MaxTessEvaluationImageUniforms gl_MaxTessEvaluationInputComponents gl_MaxTessEvaluationOutputComponents gl_MaxTessEvaluationTextureImageUnits gl_MaxTessEvaluationUniformComponents gl_MaxTessGenLevel gl_MaxTessPatchComponents gl_MaxTextureCoords gl_MaxTextureImageUnits gl_MaxTextureUnits gl_MaxVaryingComponents gl_MaxVaryingFloats gl_MaxVaryingVectors gl_MaxVertexAtomicCounterBuffers gl_MaxVertexAtomicCounters gl_MaxVertexAttribs gl_MaxVertexImageUniforms gl_MaxVertexOutputComponents gl_MaxVertexTextureImageUnits gl_MaxVertexUniformComponents gl_MaxVertexUniformVectors gl_MaxViewports gl_MinProgramTexelOffsetgl_ModelViewMatrix gl_ModelViewMatrixInverse gl_ModelViewMatrixInverseTranspose gl_ModelViewMatrixTranspose gl_ModelViewProjectionMatrix gl_ModelViewProjectionMatrixInverse gl_ModelViewProjectionMatrixInverseTranspose gl_ModelViewProjectionMatrixTranspose gl_MultiTexCoord0 gl_MultiTexCoord1 gl_MultiTexCoord2 gl_MultiTexCoord3 gl_MultiTexCoord4 gl_MultiTexCoord5 gl_MultiTexCoord6 gl_MultiTexCoord7 gl_Normal gl_NormalMatrix gl_NormalScale gl_ObjectPlaneQ gl_ObjectPlaneR gl_ObjectPlaneS gl_ObjectPlaneT gl_PatchVerticesIn gl_PerVertex gl_Point gl_PointCoord gl_PointSize gl_Position gl_PrimitiveID gl_PrimitiveIDIn gl_ProjectionMatrix gl_ProjectionMatrixInverse gl_ProjectionMatrixInverseTranspose gl_ProjectionMatrixTranspose gl_SampleID gl_SampleMask gl_SampleMaskIn gl_SamplePosition gl_SecondaryColor gl_TessCoord gl_TessLevelInner gl_TessLevelOuter gl_TexCoord gl_TextureEnvColor gl_TextureMatrixInverseTranspose gl_TextureMatrixTranspose gl_Vertex gl_VertexID gl_ViewportIndex gl_in gl_out EmitStreamVertex EmitVertex EndPrimitive EndStreamPrimitive abs acos acosh all any asin asinh atan atanh atomicCounter atomicCounterDecrement atomicCounterIncrement barrier bitCount bitfieldExtract bitfieldInsert bitfieldReverse ceil clamp cos cosh cross dFdx dFdy degrees determinant distance dot equal exp exp2 faceforward findLSB findMSB floatBitsToInt floatBitsToUint floor fma fract frexp ftransform fwidth greaterThan greaterThanEqual imageAtomicAdd imageAtomicAnd imageAtomicCompSwap imageAtomicExchange imageAtomicMax imageAtomicMin imageAtomicOr imageAtomicXor imageLoad imageStore imulExtended intBitsToFloat interpolateAtCentroid interpolateAtOffset interpolateAtSample inverse inversesqrt isinf isnan ldexp length lessThan lessThanEqual log log2 matrixCompMult max memoryBarrier min mix mod modf noise1 noise2 noise3 noise4 normalize not notEqual outerProduct packDouble2x32 packHalf2x16 packSnorm2x16 packSnorm4x8 packUnorm2x16 packUnorm4x8 pow radians reflect refract round roundEven shadow1D shadow1DLod shadow1DProj shadow1DProjLod shadow2D shadow2DLod shadow2DProj shadow2DProjLod sign sin sinh smoothstep sqrt step tan tanh texelFetch texelFetchOffset texture texture1D texture1DLod texture1DProj texture1DProjLod texture2D texture2DLod texture2DProj texture2DProjLod texture3D texture3DLod texture3DProj texture3DProjLod textureCube textureCubeLod textureGather textureGatherOffset textureGatherOffsets textureGrad textureGradOffset textureLod textureLodOffset textureOffset textureProj textureProjGrad textureProjGradOffset textureProjLod textureProjLodOffset textureProjOffset textureQueryLod textureSize transpose trunc uaddCarry uintBitsToFloat umulExtended unpackDouble2x32 unpackHalf2x16 unpackSnorm2x16 unpackSnorm4x8 unpackUnorm2x16 unpackUnorm4x8 usubBorrow gl_TextureMatrix gl_TextureMatrixInverse",literal:"true false"},i:'"',c:[e.CLCM,e.CBCM,e.CNM,{cN:"preprocessor",b:"#",e:"$"}]}});hljs.registerLanguage("lua",function(e){var t="\\[=*\\[",a="\\]=*\\]",r={b:t,e:a,c:["self"]},n=[e.C("--(?!"+t+")","$"),e.C("--"+t,a,{c:[r],r:10})];return{l:e.UIR,k:{keyword:"and break do else elseif end false for if in local nil not or repeat return then true until while",built_in:"_G _VERSION assert collectgarbage dofile error getfenv getmetatable ipairs load loadfile loadstring module next pairs pcall print rawequal rawget rawset require select setfenv setmetatable tonumber tostring type unpack xpcall coroutine debug io math os package string table"},c:n.concat([{cN:"function",bK:"function",e:"\\)",c:[e.inherit(e.TM,{b:"([_a-zA-Z]\\w*\\.)*([_a-zA-Z]\\w*:)?[_a-zA-Z]\\w*"}),{cN:"params",b:"\\(",eW:!0,c:n}].concat(n)},e.CNM,e.ASM,e.QSM,{cN:"string",b:t,e:a,c:[r],r:5}])}});hljs.registerLanguage("protobuf",function(e){return{k:{keyword:"package import option optional required repeated group",built_in:"double float int32 int64 uint32 uint64 sint32 sint64 fixed32 fixed64 sfixed32 sfixed64 bool string bytes",literal:"true false"},c:[e.QSM,e.NM,e.CLCM,{cN:"class",bK:"message enum service",e:/\{/,i:/\n/,c:[e.inherit(e.TM,{starts:{eW:!0,eE:!0}})]},{cN:"function",bK:"rpc",e:/;/,eE:!0,k:"rpc returns"},{cN:"constant",b:/^\s*[A-Z_]+/,e:/\s*=/,eE:!0}]}});hljs.registerLanguage("gcode",function(e){var N="[A-Z_][A-Z0-9_.]*",i="\\%",c={literal:"",built_in:"",keyword:"IF DO WHILE ENDWHILE CALL ENDIF SUB ENDSUB GOTO REPEAT ENDREPEAT EQ LT GT NE GE LE OR XOR"},r={cN:"preprocessor",b:"([O])([0-9]+)"},l=[e.CLCM,e.CBCM,e.C(/\(/,/\)/),e.inherit(e.CNM,{b:"([-+]?([0-9]*\\.?[0-9]+\\.?))|"+e.CNR}),e.inherit(e.ASM,{i:null}),e.inherit(e.QSM,{i:null}),{cN:"keyword",b:"([G])([0-9]+\\.?[0-9]?)"},{cN:"title",b:"([M])([0-9]+\\.?[0-9]?)"},{cN:"title",b:"(VC|VS|#)",e:"(\\d+)"},{cN:"title",b:"(VZOFX|VZOFY|VZOFZ)"},{cN:"built_in",b:"(ATAN|ABS|ACOS|ASIN|SIN|COS|EXP|FIX|FUP|ROUND|LN|TAN)(\\[)",e:"([-+]?([0-9]*\\.?[0-9]+\\.?))(\\])"},{cN:"label",v:[{b:"N",e:"\\d+",i:"\\W"}]}];return{aliases:["nc"],cI:!0,l:N,k:c,c:[{cN:"preprocessor",b:i},r].concat(l)}});hljs.registerLanguage("vim",function(e){return{l:/[!#@\w]+/,k:{keyword:"N|0 P|0 X|0 a|0 ab abc abo al am an|0 ar arga argd arge argdo argg argl argu as au aug aun b|0 bN ba bad bd be bel bf bl bm bn bo bp br brea breaka breakd breakl bro bufdo buffers bun bw c|0 cN cNf ca cabc caddb cad caddf cal cat cb cc ccl cd ce cex cf cfir cgetb cgete cg changes chd che checkt cl cla clo cm cmapc cme cn cnew cnf cno cnorea cnoreme co col colo com comc comp con conf cope cp cpf cq cr cs cst cu cuna cunme cw d|0 delm deb debugg delc delf dif diffg diffo diffp diffpu diffs diffthis dig di dl dell dj dli do doautoa dp dr ds dsp e|0 ea ec echoe echoh echom echon el elsei em en endfo endf endt endw ene ex exe exi exu f|0 files filet fin fina fini fir fix fo foldc foldd folddoc foldo for fu g|0 go gr grepa gu gv ha h|0 helpf helpg helpt hi hid his i|0 ia iabc if ij il im imapc ime ino inorea inoreme int is isp iu iuna iunme j|0 ju k|0 keepa kee keepj lN lNf l|0 lad laddb laddf la lan lat lb lc lch lcl lcs le lefta let lex lf lfir lgetb lgete lg lgr lgrepa lh ll lla lli lmak lm lmapc lne lnew lnf ln loadk lo loc lockv lol lope lp lpf lr ls lt lu lua luad luaf lv lvimgrepa lw m|0 ma mak map mapc marks mat me menut mes mk mks mksp mkv mkvie mod mz mzf nbc nb nbs n|0 new nm nmapc nme nn nnoreme noa no noh norea noreme norm nu nun nunme ol o|0 om omapc ome on ono onoreme opt ou ounme ow p|0 profd prof pro promptr pc ped pe perld po popu pp pre prev ps pt ptN ptf ptj ptl ptn ptp ptr pts pu pw py3 python3 py3d py3f py pyd pyf q|0 quita qa r|0 rec red redi redr redraws reg res ret retu rew ri rightb rub rubyd rubyf rund ru rv s|0 sN san sa sal sav sb sbN sba sbf sbl sbm sbn sbp sbr scrip scripte scs se setf setg setl sf sfir sh sim sig sil sl sla sm smap smapc sme sn sni sno snor snoreme sor so spelld spe spelli spellr spellu spellw sp spr sre st sta startg startr star stopi stj sts sun sunm sunme sus sv sw sy synti sync t|0 tN tabN tabc tabdo tabe tabf tabfir tabl tabm tabnew tabn tabo tabp tabr tabs tab ta tags tc tcld tclf te tf th tj tl tm tn to tp tr try ts tu u|0 undoj undol una unh unl unlo unm unme uns up v|0 ve verb vert vim vimgrepa vi viu vie vm vmapc vme vne vn vnoreme vs vu vunme windo w|0 wN wa wh wi winc winp wn wp wq wqa ws wu wv x|0 xa xmapc xm xme xn xnoreme xu xunme y|0 z|0 ~ Next Print append abbreviate abclear aboveleft all amenu anoremenu args argadd argdelete argedit argglobal arglocal argument ascii autocmd augroup aunmenu buffer bNext ball badd bdelete behave belowright bfirst blast bmodified bnext botright bprevious brewind break breakadd breakdel breaklist browse bunload bwipeout change cNext cNfile cabbrev cabclear caddbuffer caddexpr caddfile call catch cbuffer cclose center cexpr cfile cfirst cgetbuffer cgetexpr cgetfile chdir checkpath checktime clist clast close cmap cmapclear cmenu cnext cnewer cnfile cnoremap cnoreabbrev cnoremenu copy colder colorscheme command comclear compiler continue confirm copen cprevious cpfile cquit crewind cscope cstag cunmap cunabbrev cunmenu cwindow delete delmarks debug debuggreedy delcommand delfunction diffupdate diffget diffoff diffpatch diffput diffsplit digraphs display deletel djump dlist doautocmd doautoall deletep drop dsearch dsplit edit earlier echo echoerr echohl echomsg else elseif emenu endif endfor endfunction endtry endwhile enew execute exit exusage file filetype find finally finish first fixdel fold foldclose folddoopen folddoclosed foldopen function global goto grep grepadd gui gvim hardcopy help helpfind helpgrep helptags highlight hide history insert iabbrev iabclear ijump ilist imap imapclear imenu inoremap inoreabbrev inoremenu intro isearch isplit iunmap iunabbrev iunmenu join jumps keepalt keepmarks keepjumps lNext lNfile list laddexpr laddbuffer laddfile last language later lbuffer lcd lchdir lclose lcscope left leftabove lexpr lfile lfirst lgetbuffer lgetexpr lgetfile lgrep lgrepadd lhelpgrep llast llist lmake lmap lmapclear lnext lnewer lnfile lnoremap loadkeymap loadview lockmarks lockvar lolder lopen lprevious lpfile lrewind ltag lunmap luado luafile lvimgrep lvimgrepadd lwindow move mark make mapclear match menu menutranslate messages mkexrc mksession mkspell mkvimrc mkview mode mzscheme mzfile nbclose nbkey nbsart next nmap nmapclear nmenu nnoremap nnoremenu noautocmd noremap nohlsearch noreabbrev noremenu normal number nunmap nunmenu oldfiles open omap omapclear omenu only onoremap onoremenu options ounmap ounmenu ownsyntax print profdel profile promptfind promptrepl pclose pedit perl perldo pop popup ppop preserve previous psearch ptag ptNext ptfirst ptjump ptlast ptnext ptprevious ptrewind ptselect put pwd py3do py3file python pydo pyfile quit quitall qall read recover redo redir redraw redrawstatus registers resize retab return rewind right rightbelow ruby rubydo rubyfile rundo runtime rviminfo substitute sNext sandbox sargument sall saveas sbuffer sbNext sball sbfirst sblast sbmodified sbnext sbprevious sbrewind scriptnames scriptencoding scscope set setfiletype setglobal setlocal sfind sfirst shell simalt sign silent sleep slast smagic smapclear smenu snext sniff snomagic snoremap snoremenu sort source spelldump spellgood spellinfo spellrepall spellundo spellwrong split sprevious srewind stop stag startgreplace startreplace startinsert stopinsert stjump stselect sunhide sunmap sunmenu suspend sview swapname syntax syntime syncbind tNext tabNext tabclose tabedit tabfind tabfirst tablast tabmove tabnext tabonly tabprevious tabrewind tag tcl tcldo tclfile tearoff tfirst throw tjump tlast tmenu tnext topleft tprevious trewind tselect tunmenu undo undojoin undolist unabbreviate unhide unlet unlockvar unmap unmenu unsilent update vglobal version verbose vertical vimgrep vimgrepadd visual viusage view vmap vmapclear vmenu vnew vnoremap vnoremenu vsplit vunmap vunmenu write wNext wall while winsize wincmd winpos wnext wprevious wqall wsverb wundo wviminfo xit xall xmapclear xmap xmenu xnoremap xnoremenu xunmap xunmenu yank",built_in:"abs acos add and append argc argidx argv asin atan atan2 browse browsedir bufexists buflisted bufloaded bufname bufnr bufwinnr byte2line byteidx call ceil changenr char2nr cindent clearmatches col complete complete_add complete_check confirm copy cos cosh count cscope_connection cursor deepcopy delete did_filetype diff_filler diff_hlID empty escape eval eventhandler executable exists exp expand extend feedkeys filereadable filewritable filter finddir findfile float2nr floor fmod fnameescape fnamemodify foldclosed foldclosedend foldlevel foldtext foldtextresult foreground function garbagecollect get getbufline getbufvar getchar getcharmod getcmdline getcmdpos getcmdtype getcwd getfontname getfperm getfsize getftime getftype getline getloclist getmatches getpid getpos getqflist getreg getregtype gettabvar gettabwinvar getwinposx getwinposy getwinvar glob globpath has has_key haslocaldir hasmapto histadd histdel histget histnr hlexists hlID hostname iconv indent index input inputdialog inputlist inputrestore inputsave inputsecret insert invert isdirectory islocked items join keys len libcall libcallnr line line2byte lispindent localtime log log10 luaeval map maparg mapcheck match matchadd matcharg matchdelete matchend matchlist matchstr max min mkdir mode mzeval nextnonblank nr2char or pathshorten pow prevnonblank printf pumvisible py3eval pyeval range readfile reltime reltimestr remote_expr remote_foreground remote_peek remote_read remote_send remove rename repeat resolve reverse round screenattr screenchar screencol screenrow search searchdecl searchpair searchpairpos searchpos server2client serverlist setbufvar setcmdpos setline setloclist setmatches setpos setqflist setreg settabvar settabwinvar setwinvar sha256 shellescape shiftwidth simplify sin sinh sort soundfold spellbadword spellsuggest split sqrt str2float str2nr strchars strdisplaywidth strftime stridx string strlen strpart strridx strtrans strwidth submatch substitute synconcealed synID synIDattr synIDtrans synstack system tabpagebuflist tabpagenr tabpagewinnr tagfiles taglist tan tanh tempname tolower toupper tr trunc type undofile undotree values virtcol visualmode wildmenumode winbufnr wincol winheight winline winnr winrestcmd winrestview winsaveview winwidth writefile xor"},i:/[{:]/,c:[e.NM,e.ASM,{cN:"string",b:/"((\\")|[^"\n])*("|\n)/},{cN:"variable",b:/[bwtglsav]:[\w\d_]*/},{cN:"function",bK:"function function!",e:"$",r:0,c:[e.TM,{cN:"params",b:"\\(",e:"\\)"}]}]}});hljs.registerLanguage("processing",function(e){return{k:{keyword:"BufferedReader PVector PFont PImage PGraphics HashMap boolean byte char color double float int long String Array FloatDict FloatList IntDict IntList JSONArray JSONObject Object StringDict StringList Table TableRow XML false synchronized int abstract float private char boolean static null if const for true while long throw strictfp finally protected import native final return void enum else break transient new catch instanceof byte super volatile case assert short package default double public try this switch continue throws protected public private",constant:"P2D P3D HALF_PI PI QUARTER_PI TAU TWO_PI",variable:"displayHeight displayWidth mouseY mouseX mousePressed pmouseX pmouseY key keyCode pixels focused frameCount frameRate height width",title:"setup draw",built_in:"size createGraphics beginDraw createShape loadShape PShape arc ellipse line point quad rect triangle bezier bezierDetail bezierPoint bezierTangent curve curveDetail curvePoint curveTangent curveTightness shape shapeMode beginContour beginShape bezierVertex curveVertex endContour endShape quadraticVertex vertex ellipseMode noSmooth rectMode smooth strokeCap strokeJoin strokeWeight mouseClicked mouseDragged mouseMoved mousePressed mouseReleased mouseWheel keyPressed keyPressedkeyReleased keyTyped print println save saveFrame day hour millis minute month second year background clear colorMode fill noFill noStroke stroke alpha blue brightness color green hue lerpColor red saturation modelX modelY modelZ screenX screenY screenZ ambient emissive shininess specular add createImage beginCamera camera endCamera frustum ortho perspective printCamera printProjection cursor frameRate noCursor exit loop noLoop popStyle pushStyle redraw binary boolean byte char float hex int str unbinary unhex join match matchAll nf nfc nfp nfs split splitTokens trim append arrayCopy concat expand reverse shorten sort splice subset box sphere sphereDetail createInput createReader loadBytes loadJSONArray loadJSONObject loadStrings loadTable loadXML open parseXML saveTable selectFolder selectInput beginRaw beginRecord createOutput createWriter endRaw endRecord PrintWritersaveBytes saveJSONArray saveJSONObject saveStream saveStrings saveXML selectOutput popMatrix printMatrix pushMatrix resetMatrix rotate rotateX rotateY rotateZ scale shearX shearY translate ambientLight directionalLight lightFalloff lights lightSpecular noLights normal pointLight spotLight image imageMode loadImage noTint requestImage tint texture textureMode textureWrap blend copy filter get loadPixels set updatePixels blendMode loadShader PShaderresetShader shader createFont loadFont text textFont textAlign textLeading textMode textSize textWidth textAscent textDescent abs ceil constrain dist exp floor lerp log mag map max min norm pow round sq sqrt acos asin atan atan2 cos degrees radians sin tan noise noiseDetail noiseSeed random randomGaussian randomSeed"},c:[e.CLCM,e.CBCM,e.ASM,e.QSM,e.CNM]}});hljs.registerLanguage("mizar",function(e){return{k:"environ vocabularies notations constructors definitions registrations theorems schemes requirements begin end definition registration cluster existence pred func defpred deffunc theorem proof let take assume then thus hence ex for st holds consider reconsider such that and in provided of as from be being by means equals implies iff redefine define now not or attr is mode suppose per cases set thesis contradiction scheme reserve struct correctness compatibility coherence symmetry assymetry reflexivity irreflexivity connectedness uniqueness commutativity idempotence involutiveness projectivity",c:[e.C("::","$")]}});hljs.registerLanguage("vbnet",function(e){return{aliases:["vb"],cI:!0,k:{keyword:"addhandler addressof alias and andalso aggregate ansi as assembly auto binary by byref byval call case catch class compare const continue custom declare default delegate dim distinct do each equals else elseif end enum erase error event exit explicit finally for friend from function get global goto group handles if implements imports in inherits interface into is isfalse isnot istrue join key let lib like loop me mid mod module mustinherit mustoverride mybase myclass namespace narrowing new next not notinheritable notoverridable of off on operator option optional or order orelse overloads overridable overrides paramarray partial preserve private property protected public raiseevent readonly redim rem removehandler resume return select set shadows shared skip static step stop structure strict sub synclock take text then throw to try unicode until using when where while widening with withevents writeonly xor",built_in:"boolean byte cbool cbyte cchar cdate cdec cdbl char cint clng cobj csbyte cshort csng cstr ctype date decimal directcast double gettype getxmlnamespace iif integer long object sbyte short single string trycast typeof uinteger ulong ushort",literal:"true false nothing"},i:"//|{|}|endif|gosub|variant|wend",c:[e.inherit(e.QSM,{c:[{b:'""'}]}),e.C("'","$",{rB:!0,c:[{cN:"xmlDocTag",b:"'''|<!--|-->",c:[e.PWM]},{cN:"xmlDocTag",b:"</?",e:">",c:[e.PWM]}]}),e.CNM,{cN:"preprocessor",b:"#",e:"$",k:"if else elseif end region externalsource"}]}});hljs.registerLanguage("q",function(e){var s={keyword:"do while select delete by update from",constant:"0b 1b",built_in:"neg not null string reciprocal floor ceiling signum mod xbar xlog and or each scan over prior mmu lsq inv md5 ltime gtime count first var dev med cov cor all any rand sums prds mins maxs fills deltas ratios avgs differ prev next rank reverse iasc idesc asc desc msum mcount mavg mdev xrank mmin mmax xprev rotate distinct group where flip type key til get value attr cut set upsert raze union inter except cross sv vs sublist enlist read0 read1 hopen hclose hdel hsym hcount peach system ltrim rtrim trim lower upper ssr view tables views cols xcols keys xkey xcol xasc xdesc fkeys meta lj aj aj0 ij pj asof uj ww wj wj1 fby xgroup ungroup ej save load rsave rload show csv parse eval min max avg wavg wsum sin cos tan sum",typename:"`float `double int `timestamp `timespan `datetime `time `boolean `symbol `char `byte `short `long `real `month `date `minute `second `guid"};return{aliases:["k","kdb"],k:s,l:/\b(`?)[A-Za-z0-9_]+\b/,c:[e.CLCM,e.QSM,e.CNM]}});hljs.registerLanguage("livescript",function(e){var t={keyword:"in if for while finally new do return else break catch instanceof throw try this switch continue typeof delete debugger case default function var with then unless until loop of by when and or is isnt not it that otherwise from to til fallthrough super case default function var void const let enum export import native __hasProp __extends __slice __bind __indexOf",literal:"true false null undefined yes no on off it that void",built_in:"npm require console print module global window document"},s="[A-Za-z$_](?:-[0-9A-Za-z$_]|[0-9A-Za-z$_])*",i=e.inherit(e.TM,{b:s}),n={cN:"subst",b:/#\{/,e:/}/,k:t},r={cN:"subst",b:/#[A-Za-z$_]/,e:/(?:\-[0-9A-Za-z$_]|[0-9A-Za-z$_])*/,k:t},c=[e.BNM,{cN:"number",b:"(\\b0[xX][a-fA-F0-9_]+)|(\\b\\d(\\d|_\\d)*(\\.(\\d(\\d|_\\d)*)?)?(_*[eE]([-+]\\d(_\\d|\\d)*)?)?[_a-z]*)",r:0,starts:{e:"(\\s*/)?",r:0}},{cN:"string",v:[{b:/'''/,e:/'''/,c:[e.BE]},{b:/'/,e:/'/,c:[e.BE]},{b:/"""/,e:/"""/,c:[e.BE,n,r]},{b:/"/,e:/"/,c:[e.BE,n,r]},{b:/\\/,e:/(\s|$)/,eE:!0}]},{cN:"pi",v:[{b:"//",e:"//[gim]*",c:[n,e.HCM]},{b:/\/(?![ *])(\\\/|.)*?\/[gim]*(?=\W|$)/}]},{cN:"property",b:"@"+s},{b:"``",e:"``",eB:!0,eE:!0,sL:"javascript"}];n.c=c;var a={cN:"params",b:"\\(",rB:!0,c:[{b:/\(/,e:/\)/,k:t,c:["self"].concat(c)}]};return{aliases:["ls"],k:t,i:/\/\*/,c:c.concat([e.C("\\/\\*","\\*\\/"),e.HCM,{cN:"function",c:[i,a],rB:!0,v:[{b:"("+s+"\\s*(?:=|:=)\\s*)?(\\(.*\\))?\\s*\\B\\->\\*?",e:"\\->\\*?"},{b:"("+s+"\\s*(?:=|:=)\\s*)?!?(\\(.*\\))?\\s*\\B[-~]{1,2}>\\*?",e:"[-~]{1,2}>\\*?"},{b:"("+s+"\\s*(?:=|:=)\\s*)?(\\(.*\\))?\\s*\\B!?[-~]{1,2}>\\*?",e:"!?[-~]{1,2}>\\*?"}]},{cN:"class",bK:"class",e:"$",i:/[:="\[\]]/,c:[{bK:"extends",eW:!0,i:/[:="\[\]]/,c:[i]},i]},{cN:"attribute",b:s+":",e:":",rB:!0,rE:!0,r:0}])}});hljs.registerLanguage("haxe",function(e){var r="([*]|[a-zA-Z_$][a-zA-Z0-9_$]*)";return{aliases:["hx"],k:{keyword:"break callback case cast catch class continue default do dynamic else enum extends extern for function here if implements import in inline interface never new override package private public return static super switch this throw trace try typedef untyped using var while",literal:"true false null"},c:[e.ASM,e.QSM,e.CLCM,e.CBCM,e.CNM,{cN:"class",bK:"class interface",e:"{",eE:!0,c:[{bK:"extends implements"},e.TM]},{cN:"preprocessor",b:"#",e:"$",k:"if else elseif end error"},{cN:"function",bK:"function",e:"[{;]",eE:!0,i:"\\S",c:[e.TM,{cN:"params",b:"\\(",e:"\\)",c:[e.ASM,e.QSM,e.CLCM,e.CBCM]},{cN:"type",b:":",e:r,r:10}]}]}});hljs.registerLanguage("monkey",function(e){var n={cN:"number",r:0,v:[{b:"[$][a-fA-F0-9]+"},e.NM]};return{cI:!0,k:{keyword:"public private property continue exit extern new try catch eachin not abstract final select case default const local global field end if then else elseif endif while wend repeat until forever for to step next return module inline throw",built_in:"DebugLog DebugStop Error Print ACos ACosr ASin ASinr ATan ATan2 ATan2r ATanr Abs Abs Ceil Clamp Clamp Cos Cosr Exp Floor Log Max Max Min Min Pow Sgn Sgn Sin Sinr Sqrt Tan Tanr Seed PI HALFPI TWOPI",literal:"true false null and or shl shr mod"},c:[e.C("#rem","#end"),e.C("'","$",{r:0}),{cN:"function",bK:"function method",e:"[(=:]|$",i:/\n/,c:[e.UTM]},{cN:"class",bK:"class interface",e:"$",c:[{bK:"extends implements"},e.UTM]},{cN:"variable",b:"\\b(self|super)\\b"},{cN:"preprocessor",bK:"import",e:"$"},{cN:"preprocessor",b:"\\s*#",e:"$",k:"if else elseif endif end then"},{cN:"pi",b:"^\\s*strict\\b"},{bK:"alias",e:"=",c:[e.UTM]},e.QSM,n]}});hljs.registerLanguage("bash",function(e){var t={cN:"variable",v:[{b:/\$[\w\d#@][\w\d_]*/},{b:/\$\{(.*?)}/}]},s={cN:"string",b:/"/,e:/"/,c:[e.BE,t,{cN:"variable",b:/\$\(/,e:/\)/,c:[e.BE]}]},a={cN:"string",b:/'/,e:/'/};return{aliases:["sh","zsh"],l:/-?[a-z\.]+/,k:{keyword:"if then else elif fi for while in do done case esac function",literal:"true false",built_in:"break cd continue eval exec exit export getopts hash pwd readonly return shift test times trap umask unset alias bind builtin caller command declare echo enable help let local logout mapfile printf read readarray source type typeset ulimit unalias set shopt autoload bg bindkey bye cap chdir clone comparguments compcall compctl compdescribe compfiles compgroups compquote comptags comptry compvalues dirs disable disown echotc echoti emulate fc fg float functions getcap getln history integer jobs kill limit log noglob popd print pushd pushln rehash sched setcap setopt stat suspend ttyctl unfunction unhash unlimit unsetopt vared wait whence where which zcompile zformat zftp zle zmodload zparseopts zprof zpty zregexparse zsocket zstyle ztcp",operator:"-ne -eq -lt -gt -f -d -e -s -l -a"},c:[{cN:"shebang",b:/^#![^\n]+sh\s*$/,r:10},{cN:"function",b:/\w[\w\d_]*\s*\(\s*\)\s*\{/,rB:!0,c:[e.inherit(e.TM,{b:/\w[\w\d_]*/})],r:0},e.HCM,e.NM,s,a,t]}});hljs.registerLanguage("erlang",function(e){var r="[a-z'][a-zA-Z0-9_']*",c="("+r+":"+r+"|"+r+")",a={keyword:"after and andalso|10 band begin bnot bor bsl bzr bxor case catch cond div end fun if let not of orelse|10 query receive rem try when xor",literal:"false true"},n=e.C("%","$"),i={cN:"number",b:"\\b(\\d+#[a-fA-F0-9]+|\\d+(\\.\\d+)?([eE][-+]?\\d+)?)",r:0},b={b:"fun\\s+"+r+"/\\d+"},d={b:c+"\\(",e:"\\)",rB:!0,r:0,c:[{cN:"function_name",b:c,r:0},{b:"\\(",e:"\\)",eW:!0,rE:!0,r:0}]},o={cN:"tuple",b:"{",e:"}",r:0},t={cN:"variable",b:"\\b_([A-Z][A-Za-z0-9_]*)?",r:0},l={cN:"variable",b:"[A-Z][a-zA-Z0-9_]*",r:0},f={b:"#"+e.UIR,r:0,rB:!0,c:[{cN:"record_name",b:"#"+e.UIR,r:0},{b:"{",e:"}",r:0}]},s={bK:"fun receive if try case",e:"end",k:a};s.c=[n,b,e.inherit(e.ASM,{cN:""}),s,d,e.QSM,i,o,t,l,f];var u=[n,b,s,d,e.QSM,i,o,t,l,f];d.c[1].c=u,o.c=u,f.c[1].c=u;var v={cN:"params",b:"\\(",e:"\\)",c:u};return{aliases:["erl"],k:a,i:"(</|\\*=|\\+=|-=|/\\*|\\*/|\\(\\*|\\*\\))",c:[{cN:"function",b:"^"+r+"\\s*\\(",e:"->",rB:!0,i:"\\(|#|//|/\\*|\\\\|:|;",c:[v,e.inherit(e.TM,{b:r})],starts:{e:";|\\.",k:a,c:u}},n,{cN:"pp",b:"^-",e:"\\.",r:0,eE:!0,rB:!0,l:"-"+e.IR,k:"-module -record -undef -export -ifdef -ifndef -author -copyright -doc -vsn -import -include -include_lib -compile -define -else -endif -file -behaviour -behavior -spec",c:[v]},i,e.QSM,f,t,l,o,{b:/\.$/}]}});hljs.registerLanguage("kotlin",function(e){var a="val var get set class trait object public open private protected final enum if else do while for when break continue throw try catch finally import package is as in return fun override default companion reified inline volatile transient native";return{k:{typename:"Byte Short Char Int Long Boolean Float Double Void Unit Nothing",literal:"true false null",keyword:a},c:[e.CLCM,{cN:"javadoc",b:"/\\*\\*",e:"\\*//*",r:0,c:[{cN:"javadoctag",b:"(^|\\s)@[A-Za-z]+"}]},e.CBCM,{cN:"type",b:/</,e:/>/,rB:!0,eE:!1,r:0},{cN:"function",bK:"fun",e:"[(]|$",rB:!0,eE:!0,k:a,i:/fun\s+(<.*>)?[^\s\(]+(\s+[^\s\(]+)\s*=/,r:5,c:[{b:e.UIR+"\\s*\\(",rB:!0,r:0,c:[e.UTM]},{cN:"type",b:/</,e:/>/,k:"reified",r:0},{cN:"params",b:/\(/,e:/\)/,k:a,r:0,i:/\([^\(,\s:]+,/,c:[{cN:"typename",b:/:\s*/,e:/\s*[=\)]/,eB:!0,rE:!0,r:0}]},e.CLCM,e.CBCM]},{cN:"class",bK:"class trait",e:/[:\{(]|$/,eE:!0,i:"extends implements",c:[e.UTM,{cN:"type",b:/</,e:/>/,eB:!0,eE:!0,r:0},{cN:"typename",b:/[,:]\s*/,e:/[<\(,]|$/,eB:!0,rE:!0}]},{cN:"variable",bK:"var val",e:/\s*[=:$]/,eE:!0},e.QSM,{cN:"shebang",b:"^#!/usr/bin/env",e:"$",i:"\n"},e.CNM]}});hljs.registerLanguage("stylus",function(t){var e={cN:"variable",b:"\\$"+t.IR},o={cN:"hexcolor",b:"#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})",r:10},i=["charset","css","debug","extend","font-face","for","import","include","media","mixin","page","warn","while"],r=["after","before","first-letter","first-line","active","first-child","focus","hover","lang","link","visited"],n=["a","abbr","address","article","aside","audio","b","blockquote","body","button","canvas","caption","cite","code","dd","del","details","dfn","div","dl","dt","em","fieldset","figcaption","figure","footer","form","h1","h2","h3","h4","h5","h6","header","hgroup","html","i","iframe","img","input","ins","kbd","label","legend","li","mark","menu","nav","object","ol","p","q","quote","samp","section","span","strong","summary","sup","table","tbody","td","textarea","tfoot","th","thead","time","tr","ul","var","video"],a="[\\.\\s\\n\\[\\:,]",l=["align-content","align-items","align-self","animation","animation-delay","animation-direction","animation-duration","animation-fill-mode","animation-iteration-count","animation-name","animation-play-state","animation-timing-function","auto","backface-visibility","background","background-attachment","background-clip","background-color","background-image","background-origin","background-position","background-repeat","background-size","border","border-bottom","border-bottom-color","border-bottom-left-radius","border-bottom-right-radius","border-bottom-style","border-bottom-width","border-collapse","border-color","border-image","border-image-outset","border-image-repeat","border-image-slice","border-image-source","border-image-width","border-left","border-left-color","border-left-style","border-left-width","border-radius","border-right","border-right-color","border-right-style","border-right-width","border-spacing","border-style","border-top","border-top-color","border-top-left-radius","border-top-right-radius","border-top-style","border-top-width","border-width","bottom","box-decoration-break","box-shadow","box-sizing","break-after","break-before","break-inside","caption-side","clear","clip","clip-path","color","column-count","column-fill","column-gap","column-rule","column-rule-color","column-rule-style","column-rule-width","column-span","column-width","columns","content","counter-increment","counter-reset","cursor","direction","display","empty-cells","filter","flex","flex-basis","flex-direction","flex-flow","flex-grow","flex-shrink","flex-wrap","float","font","font-family","font-feature-settings","font-kerning","font-language-override","font-size","font-size-adjust","font-stretch","font-style","font-variant","font-variant-ligatures","font-weight","height","hyphens","icon","image-orientation","image-rendering","image-resolution","ime-mode","inherit","initial","justify-content","left","letter-spacing","line-height","list-style","list-style-image","list-style-position","list-style-type","margin","margin-bottom","margin-left","margin-right","margin-top","marks","mask","max-height","max-width","min-height","min-width","nav-down","nav-index","nav-left","nav-right","nav-up","none","normal","object-fit","object-position","opacity","order","orphans","outline","outline-color","outline-offset","outline-style","outline-width","overflow","overflow-wrap","overflow-x","overflow-y","padding","padding-bottom","padding-left","padding-right","padding-top","page-break-after","page-break-before","page-break-inside","perspective","perspective-origin","pointer-events","position","quotes","resize","right","tab-size","table-layout","text-align","text-align-last","text-decoration","text-decoration-color","text-decoration-line","text-decoration-style","text-indent","text-overflow","text-rendering","text-shadow","text-transform","text-underline-position","top","transform","transform-origin","transform-style","transition","transition-delay","transition-duration","transition-property","transition-timing-function","unicode-bidi","vertical-align","visibility","white-space","widows","width","word-break","word-spacing","word-wrap","z-index"],d=["\\{","\\}","\\?","(\\bReturn\\b)","(\\bEnd\\b)","(\\bend\\b)",";","#\\s","\\*\\s","===\\s","\\|","%"];return{aliases:["styl"],cI:!1,i:"("+d.join("|")+")",k:"if else for in",c:[t.QSM,t.ASM,t.CLCM,t.CBCM,o,{b:"\\.[a-zA-Z][a-zA-Z0-9_-]*"+a,rB:!0,c:[{cN:"class",b:"\\.[a-zA-Z][a-zA-Z0-9_-]*"}]},{b:"\\#[a-zA-Z][a-zA-Z0-9_-]*"+a,rB:!0,c:[{cN:"id",b:"\\#[a-zA-Z][a-zA-Z0-9_-]*"}]},{b:"\\b("+n.join("|")+")"+a,rB:!0,c:[{cN:"tag",b:"\\b[a-zA-Z][a-zA-Z0-9_-]*"}]},{cN:"pseudo",b:"&?:?:\\b("+r.join("|")+")"+a},{cN:"at_rule",b:"@("+i.join("|")+")\\b"},e,t.CSSNM,t.NM,{cN:"function",b:"\\b[a-zA-Z][a-zA-Z0-9_-]*\\(.*\\)",i:"[\\n]",rB:!0,c:[{cN:"title",b:"\\b[a-zA-Z][a-zA-Z0-9_-]*"},{cN:"params",b:/\(/,e:/\)/,c:[o,e,t.ASM,t.CSSNM,t.NM,t.QSM]}]},{cN:"attribute",b:"\\b("+l.reverse().join("|")+")\\b"}]}});hljs.registerLanguage("css",function(e){var c="[a-zA-Z-][a-zA-Z0-9_-]*",a={cN:"function",b:c+"\\(",rB:!0,eE:!0,e:"\\("},r={cN:"rule",b:/[A-Z\_\.\-]+\s*:/,rB:!0,e:";",eW:!0,c:[{cN:"attribute",b:/\S/,e:":",eE:!0,starts:{cN:"value",eW:!0,eE:!0,c:[a,e.CSSNM,e.QSM,e.ASM,e.CBCM,{cN:"hexcolor",b:"#[0-9A-Fa-f]+"},{cN:"important",b:"!important"}]}}]};return{cI:!0,i:/[=\/|']/,c:[e.CBCM,r,{cN:"id",b:/\#[A-Za-z0-9_-]+/},{cN:"class",b:/\.[A-Za-z0-9_-]+/,r:0},{cN:"attr_selector",b:/\[/,e:/\]/,i:"$"},{cN:"pseudo",b:/:(:)?[a-zA-Z0-9\_\-\+\(\)"']+/},{cN:"at_rule",b:"@(font-face|page)",l:"[a-z-]+",k:"font-face page"},{cN:"at_rule",b:"@",e:"[{;]",c:[{cN:"keyword",b:/\S+/},{b:/\s/,eW:!0,eE:!0,r:0,c:[a,e.ASM,e.QSM,e.CSSNM]}]},{cN:"tag",b:c,r:0},{cN:"rules",b:"{",e:"}",i:/\S/,r:0,c:[e.CBCM,r]}]}});hljs.registerLanguage("puppet",function(e){var s="augeas computer cron exec file filebucket host interface k5login macauthorization mailalias maillist mcx mount nagios_command nagios_contact nagios_contactgroup nagios_host nagios_hostdependency nagios_hostescalation nagios_hostextinfo nagios_hostgroup nagios_service firewall nagios_servicedependency nagios_serviceescalation nagios_serviceextinfo nagios_servicegroup nagios_timeperiod notify package resources router schedule scheduled_task selboolean selmodule service ssh_authorized_key sshkey stage tidy user vlan yumrepo zfs zone zpool",r="alias audit before loglevel noop require subscribe tag owner ensure group mode name|0 changes context force incl lens load_path onlyif provider returns root show_diff type_check en_address ip_address realname command environment hour monute month monthday special target weekday creates cwd ogoutput refresh refreshonly tries try_sleep umask backup checksum content ctime force ignore links mtime purge recurse recurselimit replace selinux_ignore_defaults selrange selrole seltype seluser source souirce_permissions sourceselect validate_cmd validate_replacement allowdupe attribute_membership auth_membership forcelocal gid ia_load_module members system host_aliases ip allowed_trunk_vlans description device_url duplex encapsulation etherchannel native_vlan speed principals allow_root auth_class auth_type authenticate_user k_of_n mechanisms rule session_owner shared options device fstype enable hasrestart directory present absent link atboot blockdevice device dump pass remounts poller_tag use message withpath adminfile allow_virtual allowcdrom category configfiles flavor install_options instance package_settings platform responsefile status uninstall_options vendor unless_system_user unless_uid binary control flags hasstatus manifest pattern restart running start stop allowdupe auths expiry gid groups home iterations key_membership keys managehome membership password password_max_age password_min_age profile_membership profiles project purge_ssh_keys role_membership roles salt shell uid baseurl cost descr enabled enablegroups exclude failovermethod gpgcheck gpgkey http_caching include includepkgs keepalive metadata_expire metalink mirrorlist priority protect proxy proxy_password proxy_username repo_gpgcheck s3_enabled skip_if_unavailable sslcacert sslclientcert sslclientkey sslverify mounted",a={keyword:"and case class default define else elsif false if in import enherits node or true undef unless main settings $string "+s,literal:r,built_in:"architecture augeasversion blockdevices boardmanufacturer boardproductname boardserialnumber cfkey dhcp_servers domain ec2_ ec2_userdata facterversion filesystems ldom fqdn gid hardwareisa hardwaremodel hostname id|0 interfaces ipaddress ipaddress_ ipaddress6 ipaddress6_ iphostnumber is_virtual kernel kernelmajversion kernelrelease kernelversion kernelrelease kernelversion lsbdistcodename lsbdistdescription lsbdistid lsbdistrelease lsbmajdistrelease lsbminordistrelease lsbrelease macaddress macaddress_ macosx_buildversion macosx_productname macosx_productversion macosx_productverson_major macosx_productversion_minor manufacturer memoryfree memorysize netmask metmask_ network_ operatingsystem operatingsystemmajrelease operatingsystemrelease osfamily partitions path physicalprocessorcount processor processorcount productname ps puppetversion rubysitedir rubyversion selinux selinux_config_mode selinux_config_policy selinux_current_mode selinux_current_mode selinux_enforced selinux_policyversion serialnumber sp_ sshdsakey sshecdsakey sshrsakey swapencrypted swapfree swapsize timezone type uniqueid uptime uptime_days uptime_hours uptime_seconds uuid virtual vlans xendomains zfs_version zonenae zones zpool_version"},i=e.C("#","$"),o={cN:"string",c:[e.BE],v:[{b:/'/,e:/'/},{b:/"/,e:/"/}]},n=[o,i,{cN:"keyword",bK:"class",e:"$|;",i:/=/,c:[e.inherit(e.TM,{b:"(::)?[A-Za-z_]\\w*(::\\w+)*"}),i,o]},{cN:"keyword",b:"([a-zA-Z_(::)]+ *\\{)",c:[o,i],r:0},{cN:"keyword",b:"(\\}|\\{)",r:0},{cN:"function",b:"[a-zA-Z_]+\\s*=>"},{cN:"constant",b:"(::)?(\\b[A-Z][a-z_]*(::)?)+",r:0},{cN:"number",b:"(\\b0[0-7_]+)|(\\b0x[0-9a-fA-F_]+)|(\\b[1-9][0-9_]*(\\.[0-9_]+)?)|[0_]\\b",r:0}];return{aliases:["pp"],k:a,c:n}});hljs.registerLanguage("nimrod",function(t){return{aliases:["nim"],k:{keyword:"addr and as asm bind block break|0 case|0 cast const|0 continue|0 converter discard distinct|10 div do elif else|0 end|0 enum|0 except export finally for from generic if|0 import|0 in include|0 interface is isnot|10 iterator|10 let|0 macro method|10 mixin mod nil not notin|10 object|0 of or out proc|10 ptr raise ref|10 return shl shr static template|10 try|0 tuple type|0 using|0 var|0 when while|0 with without xor yield",literal:"shared guarded stdin stdout stderr result|10 true false"},c:[{cN:"decorator",b:/{\./,e:/\.}/,r:10},{cN:"string",b:/[a-zA-Z]\w*"/,e:/"/,c:[{b:/""/}]},{cN:"string",b:/([a-zA-Z]\w*)?"""/,e:/"""/},t.QSM,{cN:"type",b:/\b[A-Z]\w+\b/,r:0},{cN:"type",b:/\b(int|int8|int16|int32|int64|uint|uint8|uint16|uint32|uint64|float|float32|float64|bool|char|string|cstring|pointer|expr|stmt|void|auto|any|range|array|openarray|varargs|seq|set|clong|culong|cchar|cschar|cshort|cint|csize|clonglong|cfloat|cdouble|clongdouble|cuchar|cushort|cuint|culonglong|cstringarray|semistatic)\b/},{cN:"number",b:/\b(0[xX][0-9a-fA-F][_0-9a-fA-F]*)('?[iIuU](8|16|32|64))?/,r:0},{cN:"number",b:/\b(0o[0-7][_0-7]*)('?[iIuUfF](8|16|32|64))?/,r:0},{cN:"number",b:/\b(0(b|B)[01][_01]*)('?[iIuUfF](8|16|32|64))?/,r:0},{cN:"number",b:/\b(\d[_\d]*)('?[iIuUfF](8|16|32|64))?/,r:0},t.HCM]}});hljs.registerLanguage("smalltalk",function(a){var r="[a-z][a-zA-Z0-9_]*",s={cN:"char",b:"\\$.{1}"},c={cN:"symbol",b:"#"+a.UIR};return{aliases:["st"],k:"self super nil true false thisContext",c:[a.C('"','"'),a.ASM,{cN:"class",b:"\\b[A-Z][A-Za-z0-9_]*",r:0},{cN:"method",b:r+":",r:0},a.CNM,c,s,{cN:"localvars",b:"\\|[ ]*"+r+"([ ]+"+r+")*[ ]*\\|",rB:!0,e:/\|/,i:/\S/,c:[{b:"(\\|[ ]*)?"+r}]},{cN:"array",b:"\\#\\(",e:"\\)",c:[a.ASM,s,a.CNM,c]}]}});hljs.registerLanguage("x86asm",function(s){return{cI:!0,l:"\\.?"+s.IR,k:{keyword:"lock rep repe repz repne repnz xaquire xrelease bnd nobnd aaa aad aam aas adc add and arpl bb0_reset bb1_reset bound bsf bsr bswap bt btc btr bts call cbw cdq cdqe clc cld cli clts cmc cmp cmpsb cmpsd cmpsq cmpsw cmpxchg cmpxchg486 cmpxchg8b cmpxchg16b cpuid cpu_read cpu_write cqo cwd cwde daa das dec div dmint emms enter equ f2xm1 fabs fadd faddp fbld fbstp fchs fclex fcmovb fcmovbe fcmove fcmovnb fcmovnbe fcmovne fcmovnu fcmovu fcom fcomi fcomip fcomp fcompp fcos fdecstp fdisi fdiv fdivp fdivr fdivrp femms feni ffree ffreep fiadd ficom ficomp fidiv fidivr fild fimul fincstp finit fist fistp fisttp fisub fisubr fld fld1 fldcw fldenv fldl2e fldl2t fldlg2 fldln2 fldpi fldz fmul fmulp fnclex fndisi fneni fninit fnop fnsave fnstcw fnstenv fnstsw fpatan fprem fprem1 fptan frndint frstor fsave fscale fsetpm fsin fsincos fsqrt fst fstcw fstenv fstp fstsw fsub fsubp fsubr fsubrp ftst fucom fucomi fucomip fucomp fucompp fxam fxch fxtract fyl2x fyl2xp1 hlt ibts icebp idiv imul in inc incbin insb insd insw int int01 int1 int03 int3 into invd invpcid invlpg invlpga iret iretd iretq iretw jcxz jecxz jrcxz jmp jmpe lahf lar lds lea leave les lfence lfs lgdt lgs lidt lldt lmsw loadall loadall286 lodsb lodsd lodsq lodsw loop loope loopne loopnz loopz lsl lss ltr mfence monitor mov movd movq movsb movsd movsq movsw movsx movsxd movzx mul mwait neg nop not or out outsb outsd outsw packssdw packsswb packuswb paddb paddd paddsb paddsiw paddsw paddusb paddusw paddw pand pandn pause paveb pavgusb pcmpeqb pcmpeqd pcmpeqw pcmpgtb pcmpgtd pcmpgtw pdistib pf2id pfacc pfadd pfcmpeq pfcmpge pfcmpgt pfmax pfmin pfmul pfrcp pfrcpit1 pfrcpit2 pfrsqit1 pfrsqrt pfsub pfsubr pi2fd pmachriw pmaddwd pmagw pmulhriw pmulhrwa pmulhrwc pmulhw pmullw pmvgezb pmvlzb pmvnzb pmvzb pop popa popad popaw popf popfd popfq popfw por prefetch prefetchw pslld psllq psllw psrad psraw psrld psrlq psrlw psubb psubd psubsb psubsiw psubsw psubusb psubusw psubw punpckhbw punpckhdq punpckhwd punpcklbw punpckldq punpcklwd push pusha pushad pushaw pushf pushfd pushfq pushfw pxor rcl rcr rdshr rdmsr rdpmc rdtsc rdtscp ret retf retn rol ror rdm rsdc rsldt rsm rsts sahf sal salc sar sbb scasb scasd scasq scasw sfence sgdt shl shld shr shrd sidt sldt skinit smi smint smintold smsw stc std sti stosb stosd stosq stosw str sub svdc svldt svts swapgs syscall sysenter sysexit sysret test ud0 ud1 ud2b ud2 ud2a umov verr verw fwait wbinvd wrshr wrmsr xadd xbts xchg xlatb xlat xor cmove cmovz cmovne cmovnz cmova cmovnbe cmovae cmovnb cmovb cmovnae cmovbe cmovna cmovg cmovnle cmovge cmovnl cmovl cmovnge cmovle cmovng cmovc cmovnc cmovo cmovno cmovs cmovns cmovp cmovpe cmovnp cmovpo je jz jne jnz ja jnbe jae jnb jb jnae jbe jna jg jnle jge jnl jl jnge jle jng jc jnc jo jno js jns jpo jnp jpe jp sete setz setne setnz seta setnbe setae setnb setnc setb setnae setcset setbe setna setg setnle setge setnl setl setnge setle setng sets setns seto setno setpe setp setpo setnp addps addss andnps andps cmpeqps cmpeqss cmpleps cmpless cmpltps cmpltss cmpneqps cmpneqss cmpnleps cmpnless cmpnltps cmpnltss cmpordps cmpordss cmpunordps cmpunordss cmpps cmpss comiss cvtpi2ps cvtps2pi cvtsi2ss cvtss2si cvttps2pi cvttss2si divps divss ldmxcsr maxps maxss minps minss movaps movhps movlhps movlps movhlps movmskps movntps movss movups mulps mulss orps rcpps rcpss rsqrtps rsqrtss shufps sqrtps sqrtss stmxcsr subps subss ucomiss unpckhps unpcklps xorps fxrstor fxrstor64 fxsave fxsave64 xgetbv xsetbv xsave xsave64 xsaveopt xsaveopt64 xrstor xrstor64 prefetchnta prefetcht0 prefetcht1 prefetcht2 maskmovq movntq pavgb pavgw pextrw pinsrw pmaxsw pmaxub pminsw pminub pmovmskb pmulhuw psadbw pshufw pf2iw pfnacc pfpnacc pi2fw pswapd maskmovdqu clflush movntdq movnti movntpd movdqa movdqu movdq2q movq2dq paddq pmuludq pshufd pshufhw pshuflw pslldq psrldq psubq punpckhqdq punpcklqdq addpd addsd andnpd andpd cmpeqpd cmpeqsd cmplepd cmplesd cmpltpd cmpltsd cmpneqpd cmpneqsd cmpnlepd cmpnlesd cmpnltpd cmpnltsd cmpordpd cmpordsd cmpunordpd cmpunordsd cmppd comisd cvtdq2pd cvtdq2ps cvtpd2dq cvtpd2pi cvtpd2ps cvtpi2pd cvtps2dq cvtps2pd cvtsd2si cvtsd2ss cvtsi2sd cvtss2sd cvttpd2pi cvttpd2dq cvttps2dq cvttsd2si divpd divsd maxpd maxsd minpd minsd movapd movhpd movlpd movmskpd movupd mulpd mulsd orpd shufpd sqrtpd sqrtsd subpd subsd ucomisd unpckhpd unpcklpd xorpd addsubpd addsubps haddpd haddps hsubpd hsubps lddqu movddup movshdup movsldup clgi stgi vmcall vmclear vmfunc vmlaunch vmload vmmcall vmptrld vmptrst vmread vmresume vmrun vmsave vmwrite vmxoff vmxon invept invvpid pabsb pabsw pabsd palignr phaddw phaddd phaddsw phsubw phsubd phsubsw pmaddubsw pmulhrsw pshufb psignb psignw psignd extrq insertq movntsd movntss lzcnt blendpd blendps blendvpd blendvps dppd dpps extractps insertps movntdqa mpsadbw packusdw pblendvb pblendw pcmpeqq pextrb pextrd pextrq phminposuw pinsrb pinsrd pinsrq pmaxsb pmaxsd pmaxud pmaxuw pminsb pminsd pminud pminuw pmovsxbw pmovsxbd pmovsxbq pmovsxwd pmovsxwq pmovsxdq pmovzxbw pmovzxbd pmovzxbq pmovzxwd pmovzxwq pmovzxdq pmuldq pmulld ptest roundpd roundps roundsd roundss crc32 pcmpestri pcmpestrm pcmpistri pcmpistrm pcmpgtq popcnt getsec pfrcpv pfrsqrtv movbe aesenc aesenclast aesdec aesdeclast aesimc aeskeygenassist vaesenc vaesenclast vaesdec vaesdeclast vaesimc vaeskeygenassist vaddpd vaddps vaddsd vaddss vaddsubpd vaddsubps vandpd vandps vandnpd vandnps vblendpd vblendps vblendvpd vblendvps vbroadcastss vbroadcastsd vbroadcastf128 vcmpeq_ospd vcmpeqpd vcmplt_ospd vcmpltpd vcmple_ospd vcmplepd vcmpunord_qpd vcmpunordpd vcmpneq_uqpd vcmpneqpd vcmpnlt_uspd vcmpnltpd vcmpnle_uspd vcmpnlepd vcmpord_qpd vcmpordpd vcmpeq_uqpd vcmpnge_uspd vcmpngepd vcmpngt_uspd vcmpngtpd vcmpfalse_oqpd vcmpfalsepd vcmpneq_oqpd vcmpge_ospd vcmpgepd vcmpgt_ospd vcmpgtpd vcmptrue_uqpd vcmptruepd vcmplt_oqpd vcmple_oqpd vcmpunord_spd vcmpneq_uspd vcmpnlt_uqpd vcmpnle_uqpd vcmpord_spd vcmpeq_uspd vcmpnge_uqpd vcmpngt_uqpd vcmpfalse_ospd vcmpneq_ospd vcmpge_oqpd vcmpgt_oqpd vcmptrue_uspd vcmppd vcmpeq_osps vcmpeqps vcmplt_osps vcmpltps vcmple_osps vcmpleps vcmpunord_qps vcmpunordps vcmpneq_uqps vcmpneqps vcmpnlt_usps vcmpnltps vcmpnle_usps vcmpnleps vcmpord_qps vcmpordps vcmpeq_uqps vcmpnge_usps vcmpngeps vcmpngt_usps vcmpngtps vcmpfalse_oqps vcmpfalseps vcmpneq_oqps vcmpge_osps vcmpgeps vcmpgt_osps vcmpgtps vcmptrue_uqps vcmptrueps vcmplt_oqps vcmple_oqps vcmpunord_sps vcmpneq_usps vcmpnlt_uqps vcmpnle_uqps vcmpord_sps vcmpeq_usps vcmpnge_uqps vcmpngt_uqps vcmpfalse_osps vcmpneq_osps vcmpge_oqps vcmpgt_oqps vcmptrue_usps vcmpps vcmpeq_ossd vcmpeqsd vcmplt_ossd vcmpltsd vcmple_ossd vcmplesd vcmpunord_qsd vcmpunordsd vcmpneq_uqsd vcmpneqsd vcmpnlt_ussd vcmpnltsd vcmpnle_ussd vcmpnlesd vcmpord_qsd vcmpordsd vcmpeq_uqsd vcmpnge_ussd vcmpngesd vcmpngt_ussd vcmpngtsd vcmpfalse_oqsd vcmpfalsesd vcmpneq_oqsd vcmpge_ossd vcmpgesd vcmpgt_ossd vcmpgtsd vcmptrue_uqsd vcmptruesd vcmplt_oqsd vcmple_oqsd vcmpunord_ssd vcmpneq_ussd vcmpnlt_uqsd vcmpnle_uqsd vcmpord_ssd vcmpeq_ussd vcmpnge_uqsd vcmpngt_uqsd vcmpfalse_ossd vcmpneq_ossd vcmpge_oqsd vcmpgt_oqsd vcmptrue_ussd vcmpsd vcmpeq_osss vcmpeqss vcmplt_osss vcmpltss vcmple_osss vcmpless vcmpunord_qss vcmpunordss vcmpneq_uqss vcmpneqss vcmpnlt_usss vcmpnltss vcmpnle_usss vcmpnless vcmpord_qss vcmpordss vcmpeq_uqss vcmpnge_usss vcmpngess vcmpngt_usss vcmpngtss vcmpfalse_oqss vcmpfalsess vcmpneq_oqss vcmpge_osss vcmpgess vcmpgt_osss vcmpgtss vcmptrue_uqss vcmptruess vcmplt_oqss vcmple_oqss vcmpunord_sss vcmpneq_usss vcmpnlt_uqss vcmpnle_uqss vcmpord_sss vcmpeq_usss vcmpnge_uqss vcmpngt_uqss vcmpfalse_osss vcmpneq_osss vcmpge_oqss vcmpgt_oqss vcmptrue_usss vcmpss vcomisd vcomiss vcvtdq2pd vcvtdq2ps vcvtpd2dq vcvtpd2ps vcvtps2dq vcvtps2pd vcvtsd2si vcvtsd2ss vcvtsi2sd vcvtsi2ss vcvtss2sd vcvtss2si vcvttpd2dq vcvttps2dq vcvttsd2si vcvttss2si vdivpd vdivps vdivsd vdivss vdppd vdpps vextractf128 vextractps vhaddpd vhaddps vhsubpd vhsubps vinsertf128 vinsertps vlddqu vldqqu vldmxcsr vmaskmovdqu vmaskmovps vmaskmovpd vmaxpd vmaxps vmaxsd vmaxss vminpd vminps vminsd vminss vmovapd vmovaps vmovd vmovq vmovddup vmovdqa vmovqqa vmovdqu vmovqqu vmovhlps vmovhpd vmovhps vmovlhps vmovlpd vmovlps vmovmskpd vmovmskps vmovntdq vmovntqq vmovntdqa vmovntpd vmovntps vmovsd vmovshdup vmovsldup vmovss vmovupd vmovups vmpsadbw vmulpd vmulps vmulsd vmulss vorpd vorps vpabsb vpabsw vpabsd vpacksswb vpackssdw vpackuswb vpackusdw vpaddb vpaddw vpaddd vpaddq vpaddsb vpaddsw vpaddusb vpaddusw vpalignr vpand vpandn vpavgb vpavgw vpblendvb vpblendw vpcmpestri vpcmpestrm vpcmpistri vpcmpistrm vpcmpeqb vpcmpeqw vpcmpeqd vpcmpeqq vpcmpgtb vpcmpgtw vpcmpgtd vpcmpgtq vpermilpd vpermilps vperm2f128 vpextrb vpextrw vpextrd vpextrq vphaddw vphaddd vphaddsw vphminposuw vphsubw vphsubd vphsubsw vpinsrb vpinsrw vpinsrd vpinsrq vpmaddwd vpmaddubsw vpmaxsb vpmaxsw vpmaxsd vpmaxub vpmaxuw vpmaxud vpminsb vpminsw vpminsd vpminub vpminuw vpminud vpmovmskb vpmovsxbw vpmovsxbd vpmovsxbq vpmovsxwd vpmovsxwq vpmovsxdq vpmovzxbw vpmovzxbd vpmovzxbq vpmovzxwd vpmovzxwq vpmovzxdq vpmulhuw vpmulhrsw vpmulhw vpmullw vpmulld vpmuludq vpmuldq vpor vpsadbw vpshufb vpshufd vpshufhw vpshuflw vpsignb vpsignw vpsignd vpslldq vpsrldq vpsllw vpslld vpsllq vpsraw vpsrad vpsrlw vpsrld vpsrlq vptest vpsubb vpsubw vpsubd vpsubq vpsubsb vpsubsw vpsubusb vpsubusw vpunpckhbw vpunpckhwd vpunpckhdq vpunpckhqdq vpunpcklbw vpunpcklwd vpunpckldq vpunpcklqdq vpxor vrcpps vrcpss vrsqrtps vrsqrtss vroundpd vroundps vroundsd vroundss vshufpd vshufps vsqrtpd vsqrtps vsqrtsd vsqrtss vstmxcsr vsubpd vsubps vsubsd vsubss vtestps vtestpd vucomisd vucomiss vunpckhpd vunpckhps vunpcklpd vunpcklps vxorpd vxorps vzeroall vzeroupper pclmullqlqdq pclmulhqlqdq pclmullqhqdq pclmulhqhqdq pclmulqdq vpclmullqlqdq vpclmulhqlqdq vpclmullqhqdq vpclmulhqhqdq vpclmulqdq vfmadd132ps vfmadd132pd vfmadd312ps vfmadd312pd vfmadd213ps vfmadd213pd vfmadd123ps vfmadd123pd vfmadd231ps vfmadd231pd vfmadd321ps vfmadd321pd vfmaddsub132ps vfmaddsub132pd vfmaddsub312ps vfmaddsub312pd vfmaddsub213ps vfmaddsub213pd vfmaddsub123ps vfmaddsub123pd vfmaddsub231ps vfmaddsub231pd vfmaddsub321ps vfmaddsub321pd vfmsub132ps vfmsub132pd vfmsub312ps vfmsub312pd vfmsub213ps vfmsub213pd vfmsub123ps vfmsub123pd vfmsub231ps vfmsub231pd vfmsub321ps vfmsub321pd vfmsubadd132ps vfmsubadd132pd vfmsubadd312ps vfmsubadd312pd vfmsubadd213ps vfmsubadd213pd vfmsubadd123ps vfmsubadd123pd vfmsubadd231ps vfmsubadd231pd vfmsubadd321ps vfmsubadd321pd vfnmadd132ps vfnmadd132pd vfnmadd312ps vfnmadd312pd vfnmadd213ps vfnmadd213pd vfnmadd123ps vfnmadd123pd vfnmadd231ps vfnmadd231pd vfnmadd321ps vfnmadd321pd vfnmsub132ps vfnmsub132pd vfnmsub312ps vfnmsub312pd vfnmsub213ps vfnmsub213pd vfnmsub123ps vfnmsub123pd vfnmsub231ps vfnmsub231pd vfnmsub321ps vfnmsub321pd vfmadd132ss vfmadd132sd vfmadd312ss vfmadd312sd vfmadd213ss vfmadd213sd vfmadd123ss vfmadd123sd vfmadd231ss vfmadd231sd vfmadd321ss vfmadd321sd vfmsub132ss vfmsub132sd vfmsub312ss vfmsub312sd vfmsub213ss vfmsub213sd vfmsub123ss vfmsub123sd vfmsub231ss vfmsub231sd vfmsub321ss vfmsub321sd vfnmadd132ss vfnmadd132sd vfnmadd312ss vfnmadd312sd vfnmadd213ss vfnmadd213sd vfnmadd123ss vfnmadd123sd vfnmadd231ss vfnmadd231sd vfnmadd321ss vfnmadd321sd vfnmsub132ss vfnmsub132sd vfnmsub312ss vfnmsub312sd vfnmsub213ss vfnmsub213sd vfnmsub123ss vfnmsub123sd vfnmsub231ss vfnmsub231sd vfnmsub321ss vfnmsub321sd rdfsbase rdgsbase rdrand wrfsbase wrgsbase vcvtph2ps vcvtps2ph adcx adox rdseed clac stac xstore xcryptecb xcryptcbc xcryptctr xcryptcfb xcryptofb montmul xsha1 xsha256 llwpcb slwpcb lwpval lwpins vfmaddpd vfmaddps vfmaddsd vfmaddss vfmaddsubpd vfmaddsubps vfmsubaddpd vfmsubaddps vfmsubpd vfmsubps vfmsubsd vfmsubss vfnmaddpd vfnmaddps vfnmaddsd vfnmaddss vfnmsubpd vfnmsubps vfnmsubsd vfnmsubss vfrczpd vfrczps vfrczsd vfrczss vpcmov vpcomb vpcomd vpcomq vpcomub vpcomud vpcomuq vpcomuw vpcomw vphaddbd vphaddbq vphaddbw vphadddq vphaddubd vphaddubq vphaddubw vphaddudq vphadduwd vphadduwq vphaddwd vphaddwq vphsubbw vphsubdq vphsubwd vpmacsdd vpmacsdqh vpmacsdql vpmacssdd vpmacssdqh vpmacssdql vpmacsswd vpmacssww vpmacswd vpmacsww vpmadcsswd vpmadcswd vpperm vprotb vprotd vprotq vprotw vpshab vpshad vpshaq vpshaw vpshlb vpshld vpshlq vpshlw vbroadcasti128 vpblendd vpbroadcastb vpbroadcastw vpbroadcastd vpbroadcastq vpermd vpermpd vpermps vpermq vperm2i128 vextracti128 vinserti128 vpmaskmovd vpmaskmovq vpsllvd vpsllvq vpsravd vpsrlvd vpsrlvq vgatherdpd vgatherqpd vgatherdps vgatherqps vpgatherdd vpgatherqd vpgatherdq vpgatherqq xabort xbegin xend xtest andn bextr blci blcic blsi blsic blcfill blsfill blcmsk blsmsk blsr blcs bzhi mulx pdep pext rorx sarx shlx shrx tzcnt tzmsk t1mskc valignd valignq vblendmpd vblendmps vbroadcastf32x4 vbroadcastf64x4 vbroadcasti32x4 vbroadcasti64x4 vcompresspd vcompressps vcvtpd2udq vcvtps2udq vcvtsd2usi vcvtss2usi vcvttpd2udq vcvttps2udq vcvttsd2usi vcvttss2usi vcvtudq2pd vcvtudq2ps vcvtusi2sd vcvtusi2ss vexpandpd vexpandps vextractf32x4 vextractf64x4 vextracti32x4 vextracti64x4 vfixupimmpd vfixupimmps vfixupimmsd vfixupimmss vgetexppd vgetexpps vgetexpsd vgetexpss vgetmantpd vgetmantps vgetmantsd vgetmantss vinsertf32x4 vinsertf64x4 vinserti32x4 vinserti64x4 vmovdqa32 vmovdqa64 vmovdqu32 vmovdqu64 vpabsq vpandd vpandnd vpandnq vpandq vpblendmd vpblendmq vpcmpltd vpcmpled vpcmpneqd vpcmpnltd vpcmpnled vpcmpd vpcmpltq vpcmpleq vpcmpneqq vpcmpnltq vpcmpnleq vpcmpq vpcmpequd vpcmpltud vpcmpleud vpcmpnequd vpcmpnltud vpcmpnleud vpcmpud vpcmpequq vpcmpltuq vpcmpleuq vpcmpnequq vpcmpnltuq vpcmpnleuq vpcmpuq vpcompressd vpcompressq vpermi2d vpermi2pd vpermi2ps vpermi2q vpermt2d vpermt2pd vpermt2ps vpermt2q vpexpandd vpexpandq vpmaxsq vpmaxuq vpminsq vpminuq vpmovdb vpmovdw vpmovqb vpmovqd vpmovqw vpmovsdb vpmovsdw vpmovsqb vpmovsqd vpmovsqw vpmovusdb vpmovusdw vpmovusqb vpmovusqd vpmovusqw vpord vporq vprold vprolq vprolvd vprolvq vprord vprorq vprorvd vprorvq vpscatterdd vpscatterdq vpscatterqd vpscatterqq vpsraq vpsravq vpternlogd vpternlogq vptestmd vptestmq vptestnmd vptestnmq vpxord vpxorq vrcp14pd vrcp14ps vrcp14sd vrcp14ss vrndscalepd vrndscaleps vrndscalesd vrndscaless vrsqrt14pd vrsqrt14ps vrsqrt14sd vrsqrt14ss vscalefpd vscalefps vscalefsd vscalefss vscatterdpd vscatterdps vscatterqpd vscatterqps vshuff32x4 vshuff64x2 vshufi32x4 vshufi64x2 kandnw kandw kmovw knotw kortestw korw kshiftlw kshiftrw kunpckbw kxnorw kxorw vpbroadcastmb2q vpbroadcastmw2d vpconflictd vpconflictq vplzcntd vplzcntq vexp2pd vexp2ps vrcp28pd vrcp28ps vrcp28sd vrcp28ss vrsqrt28pd vrsqrt28ps vrsqrt28sd vrsqrt28ss vgatherpf0dpd vgatherpf0dps vgatherpf0qpd vgatherpf0qps vgatherpf1dpd vgatherpf1dps vgatherpf1qpd vgatherpf1qps vscatterpf0dpd vscatterpf0dps vscatterpf0qpd vscatterpf0qps vscatterpf1dpd vscatterpf1dps vscatterpf1qpd vscatterpf1qps prefetchwt1 bndmk bndcl bndcu bndcn bndmov bndldx bndstx sha1rnds4 sha1nexte sha1msg1 sha1msg2 sha256rnds2 sha256msg1 sha256msg2 hint_nop0 hint_nop1 hint_nop2 hint_nop3 hint_nop4 hint_nop5 hint_nop6 hint_nop7 hint_nop8 hint_nop9 hint_nop10 hint_nop11 hint_nop12 hint_nop13 hint_nop14 hint_nop15 hint_nop16 hint_nop17 hint_nop18 hint_nop19 hint_nop20 hint_nop21 hint_nop22 hint_nop23 hint_nop24 hint_nop25 hint_nop26 hint_nop27 hint_nop28 hint_nop29 hint_nop30 hint_nop31 hint_nop32 hint_nop33 hint_nop34 hint_nop35 hint_nop36 hint_nop37 hint_nop38 hint_nop39 hint_nop40 hint_nop41 hint_nop42 hint_nop43 hint_nop44 hint_nop45 hint_nop46 hint_nop47 hint_nop48 hint_nop49 hint_nop50 hint_nop51 hint_nop52 hint_nop53 hint_nop54 hint_nop55 hint_nop56 hint_nop57 hint_nop58 hint_nop59 hint_nop60 hint_nop61 hint_nop62 hint_nop63",literal:"ip eip rip al ah bl bh cl ch dl dh sil dil bpl spl r8b r9b r10b r11b r12b r13b r14b r15b ax bx cx dx si di bp sp r8w r9w r10w r11w r12w r13w r14w r15w eax ebx ecx edx esi edi ebp esp eip r8d r9d r10d r11d r12d r13d r14d r15d rax rbx rcx rdx rsi rdi rbp rsp r8 r9 r10 r11 r12 r13 r14 r15 cs ds es fs gs ss st st0 st1 st2 st3 st4 st5 st6 st7 mm0 mm1 mm2 mm3 mm4 mm5 mm6 mm7 xmm0  xmm1  xmm2  xmm3  xmm4  xmm5  xmm6  xmm7  xmm8  xmm9 xmm10  xmm11 xmm12 xmm13 xmm14 xmm15 xmm16 xmm17 xmm18 xmm19 xmm20 xmm21 xmm22 xmm23 xmm24 xmm25 xmm26 xmm27 xmm28 xmm29 xmm30 xmm31 ymm0  ymm1  ymm2  ymm3  ymm4  ymm5  ymm6  ymm7  ymm8  ymm9 ymm10  ymm11 ymm12 ymm13 ymm14 ymm15 ymm16 ymm17 ymm18 ymm19 ymm20 ymm21 ymm22 ymm23 ymm24 ymm25 ymm26 ymm27 ymm28 ymm29 ymm30 ymm31 zmm0  zmm1  zmm2  zmm3  zmm4  zmm5  zmm6  zmm7  zmm8  zmm9 zmm10  zmm11 zmm12 zmm13 zmm14 zmm15 zmm16 zmm17 zmm18 zmm19 zmm20 zmm21 zmm22 zmm23 zmm24 zmm25 zmm26 zmm27 zmm28 zmm29 zmm30 zmm31 k0 k1 k2 k3 k4 k5 k6 k7 bnd0 bnd1 bnd2 bnd3 cr0 cr1 cr2 cr3 cr4 cr8 dr0 dr1 dr2 dr3 dr8 tr3 tr4 tr5 tr6 tr7 r0 r1 r2 r3 r4 r5 r6 r7 r0b r1b r2b r3b r4b r5b r6b r7b r0w r1w r2w r3w r4w r5w r6w r7w r0d r1d r2d r3d r4d r5d r6d r7d r0h r1h r2h r3h r0l r1l r2l r3l r4l r5l r6l r7l r8l r9l r10l r11l r12l r13l r14l r15l",pseudo:"db dw dd dq dt ddq do dy dz resb resw resd resq rest resdq reso resy resz incbin equ times",preprocessor:"%define %xdefine %+ %undef %defstr %deftok %assign %strcat %strlen %substr %rotate %elif %else %endif %ifmacro %ifctx %ifidn %ifidni %ifid %ifnum %ifstr %iftoken %ifempty %ifenv %error %warning %fatal %rep %endrep %include %push %pop %repl %pathsearch %depend %use %arg %stacksize %local %line %comment %endcomment .nolist byte word dword qword nosplit rel abs seg wrt strict near far a32 ptr __FILE__ __LINE__ __SECT__  __BITS__ __OUTPUT_FORMAT__ __DATE__ __TIME__ __DATE_NUM__ __TIME_NUM__ __UTC_DATE__ __UTC_TIME__ __UTC_DATE_NUM__ __UTC_TIME_NUM__  __PASS__ struc endstruc istruc at iend align alignb sectalign daz nodaz up down zero default option assume public ",built_in:"bits use16 use32 use64 default section segment absolute extern global common cpu float __utf16__ __utf16le__ __utf16be__ __utf32__ __utf32le__ __utf32be__ __float8__ __float16__ __float32__ __float64__ __float80m__ __float80e__ __float128l__ __float128h__ __Infinity__ __QNaN__ __SNaN__ Inf NaN QNaN SNaN float8 float16 float32 float64 float80m float80e float128l float128h __FLOAT_DAZ__ __FLOAT_ROUND__ __FLOAT__"},c:[s.C(";","$",{r:0}),{cN:"number",b:"\\b(?:([0-9][0-9_]*)?\\.[0-9_]*(?:[eE][+-]?[0-9_]+)?|(0[Xx])?[0-9][0-9_]*\\.?[0-9_]*(?:[pP](?:[+-]?[0-9_]+)?)?)\\b",r:0},{cN:"number",b:"\\$[0-9][0-9A-Fa-f]*",r:0},{cN:"number",b:"\\b(?:[0-9A-Fa-f][0-9A-Fa-f_]*[HhXx]|[0-9][0-9_]*[DdTt]?|[0-7][0-7_]*[QqOo]|[0-1][0-1_]*[BbYy])\\b"},{cN:"number",b:"\\b(?:0[HhXx][0-9A-Fa-f_]+|0[DdTt][0-9_]+|0[QqOo][0-7_]+|0[BbYy][0-1_]+)\\b"},s.QSM,{cN:"string",b:"'",e:"[^\\\\]'",r:0},{cN:"string",b:"`",e:"[^\\\\]`",r:0},{cN:"string",b:"\\.[A-Za-z0-9]+",r:0},{cN:"label",b:"^\\s*[A-Za-z._?][A-Za-z0-9_$#@~.?]*(:|\\s+label)",r:0},{cN:"label",b:"^\\s*%%[A-Za-z0-9_$#@~.?]*:",r:0},{cN:"argument",b:"%[0-9]+",r:0},{cN:"built_in",b:"%!S+",r:0}]}});hljs.registerLanguage("roboconf",function(e){var n="[a-zA-Z-_][^\n{\r\n]+\\{";return{aliases:["graph","instances"],cI:!0,k:"import",c:[{cN:"facet",b:"^facet "+n,e:"}",k:"facet installer exports children extends",c:[e.HCM]},{cN:"instance-of",b:"^instance of "+n,e:"}",k:"name count channels instance-data instance-state instance of",c:[{cN:"keyword",b:"[a-zA-Z-_]+( |	)*:"},e.HCM]},{cN:"component",b:"^"+n,e:"}",l:"\\(?[a-zA-Z]+\\)?",k:"installer exports children extends imports facets alias (optional)",c:[{cN:"string",b:"\\.[a-zA-Z-_]+",e:"\\s|,|;",eE:!0},e.HCM]},e.HCM]}});hljs.registerLanguage("ruby",function(e){var c="[a-zA-Z_]\\w*[!?=]?|[-+~]\\@|<<|>>|=~|===?|<=>|[<>]=?|\\*\\*|[-/+%^&*~`|]|\\[\\]=?",r="and false then defined module in return redo if BEGIN retry end for true self when next until do begin unless END rescue nil else break undef not super class case require yield alias while ensure elsif or include attr_reader attr_writer attr_accessor",b={cN:"yardoctag",b:"@[A-Za-z]+"},a={cN:"value",b:"#<",e:">"},n=[e.C("#","$",{c:[b]}),e.C("^\\=begin","^\\=end",{c:[b],r:10}),e.C("^__END__","\\n$")],s={cN:"subst",b:"#\\{",e:"}",k:r},t={cN:"string",c:[e.BE,s],v:[{b:/'/,e:/'/},{b:/"/,e:/"/},{b:/`/,e:/`/},{b:"%[qQwWx]?\\(",e:"\\)"},{b:"%[qQwWx]?\\[",e:"\\]"},{b:"%[qQwWx]?{",e:"}"},{b:"%[qQwWx]?<",e:">"},{b:"%[qQwWx]?/",e:"/"},{b:"%[qQwWx]?%",e:"%"},{b:"%[qQwWx]?-",e:"-"},{b:"%[qQwWx]?\\|",e:"\\|"},{b:/\B\?(\\\d{1,3}|\\x[A-Fa-f0-9]{1,2}|\\u[A-Fa-f0-9]{4}|\\?\S)\b/}]},i={cN:"params",b:"\\(",e:"\\)",k:r},d=[t,a,{cN:"class",bK:"class module",e:"$|;",i:/=/,c:[e.inherit(e.TM,{b:"[A-Za-z_]\\w*(::\\w+)*(\\?|\\!)?"}),{cN:"inheritance",b:"<\\s*",c:[{cN:"parent",b:"("+e.IR+"::)?"+e.IR}]}].concat(n)},{cN:"function",bK:"def",e:" |$|;",r:0,c:[e.inherit(e.TM,{b:c}),i].concat(n)},{cN:"constant",b:"(::)?(\\b[A-Z]\\w*(::)?)+",r:0},{cN:"symbol",b:e.UIR+"(\\!|\\?)?:",r:0},{cN:"symbol",b:":",c:[t,{b:c}],r:0},{cN:"number",b:"(\\b0[0-7_]+)|(\\b0x[0-9a-fA-F_]+)|(\\b[1-9][0-9_]*(\\.[0-9_]+)?)|[0_]\\b",r:0},{cN:"variable",b:"(\\$\\W)|((\\$|\\@\\@?)(\\w+))"},{b:"("+e.RSR+")\\s*",c:[a,{cN:"regexp",c:[e.BE,s],i:/\n/,v:[{b:"/",e:"/[a-z]*"},{b:"%r{",e:"}[a-z]*"},{b:"%r\\(",e:"\\)[a-z]*"},{b:"%r!",e:"![a-z]*"},{b:"%r\\[",e:"\\][a-z]*"}]}].concat(n),r:0}].concat(n);s.c=d,i.c=d;var o="[>?]>",l="[\\w#]+\\(\\w+\\):\\d+:\\d+>",u="(\\w+-)?\\d+\\.\\d+\\.\\d(p\\d+)?[^>]+>",N=[{b:/^\s*=>/,cN:"status",starts:{e:"$",c:d}},{cN:"prompt",b:"^("+o+"|"+l+"|"+u+")",starts:{e:"$",c:d}}];return{aliases:["rb","gemspec","podspec","thor","irb"],k:r,c:n.concat(N).concat(d)}});hljs.registerLanguage("typescript",function(e){return{aliases:["ts"],k:{keyword:"in if for while finally var new function|0 do return void else break catch instanceof with throw case default try this switch continue typeof delete let yield const class public private get set super interface extendsstatic constructor implements enum export import declare type protected",literal:"true false null undefined NaN Infinity",built_in:"eval isFinite isNaN parseFloat parseInt decodeURI decodeURIComponent encodeURI encodeURIComponent escape unescape Object Function Boolean Error EvalError InternalError RangeError ReferenceError StopIteration SyntaxError TypeError URIError Number Math Date String RegExp Array Float32Array Float64Array Int16Array Int32Array Int8Array Uint16Array Uint32Array Uint8Array Uint8ClampedArray ArrayBuffer DataView JSON Intl arguments require module console window document any number boolean string void"},c:[{cN:"pi",b:/^\s*('|")use strict('|")/,r:0},e.ASM,e.QSM,e.CLCM,e.CBCM,e.CNM,{b:"("+e.RSR+"|\\b(case|return|throw)\\b)\\s*",k:"return throw case",c:[e.CLCM,e.CBCM,e.RM,{b:/</,e:/>;/,r:0,sL:"xml"}],r:0},{cN:"function",bK:"function",e:/\{/,eE:!0,c:[e.inherit(e.TM,{b:/[A-Za-z$_][0-9A-Za-z$_]*/}),{cN:"params",b:/\(/,e:/\)/,c:[e.CLCM,e.CBCM],i:/["'\(]/}],i:/\[|%/,r:0},{cN:"constructor",bK:"constructor",e:/\{/,eE:!0,r:10},{cN:"module",bK:"module",e:/\{/,eE:!0},{cN:"interface",bK:"interface",e:/\{/,eE:!0},{b:/\$[(.]/},{b:"\\."+e.IR,r:0}]}});hljs.registerLanguage("handlebars",function(e){var a="each in with if else unless bindattr action collection debugger log outlet template unbound view yield";return{aliases:["hbs","html.hbs","html.handlebars"],cI:!0,sL:"xml",subLanguageMode:"continuous",c:[{cN:"expression",b:"{{",e:"}}",c:[{cN:"begin-block",b:"#[a-zA-Z- .]+",k:a},{cN:"string",b:'"',e:'"'},{cN:"end-block",b:"\\/[a-zA-Z- .]+",k:a},{cN:"variable",b:"[a-zA-Z-.]+",k:a}]}]}});hljs.registerLanguage("mercury",function(e){var i={keyword:"module use_module import_module include_module end_module initialise mutable initialize finalize finalise interface implementation pred mode func type inst solver any_pred any_func is semidet det nondet multi erroneous failure cc_nondet cc_multi typeclass instance where pragma promise external trace atomic or_else require_complete_switch require_det require_semidet require_multi require_nondet require_cc_multi require_cc_nondet require_erroneous require_failure",pragma:"inline no_inline type_spec source_file fact_table obsolete memo loop_check minimal_model terminates does_not_terminate check_termination promise_equivalent_clauses",preprocessor:"foreign_proc foreign_decl foreign_code foreign_type foreign_import_module foreign_export_enum foreign_export foreign_enum may_call_mercury will_not_call_mercury thread_safe not_thread_safe maybe_thread_safe promise_pure promise_semipure tabled_for_io local untrailed trailed attach_to_io_state can_pass_as_mercury_type stable will_not_throw_exception may_modify_trail will_not_modify_trail may_duplicate may_not_duplicate affects_liveness does_not_affect_liveness doesnt_affect_liveness no_sharing unknown_sharing sharing",built_in:"some all not if then else true fail false try catch catch_any semidet_true semidet_false semidet_fail impure_true impure semipure"},r={cN:"label",b:"XXX",e:"$",eW:!0,r:0},t=e.inherit(e.CLCM,{b:"%"}),_=e.inherit(e.CBCM,{r:0});t.c.push(r),_.c.push(r);var n={cN:"number",b:"0'.\\|0[box][0-9a-fA-F]*"},a=e.inherit(e.ASM,{r:0}),o=e.inherit(e.QSM,{r:0}),l={cN:"constant",b:"\\\\[abfnrtv]\\|\\\\x[0-9a-fA-F]*\\\\\\|%[-+# *.0-9]*[dioxXucsfeEgGp]",r:0};o.c.push(l);var s={cN:"built_in",v:[{b:"<=>"},{b:"<=",r:0},{b:"=>",r:0},{b:"/\\\\"},{b:"\\\\/"}]},c={cN:"built_in",v:[{b:":-\\|-->"},{b:"=",r:0}]};return{aliases:["m","moo"],k:i,c:[s,c,t,_,n,e.NM,a,o,{b:/:-/}]}});hljs.registerLanguage("fix",function(u){return{c:[{b:/[^\u2401\u0001]+/,e:/[\u2401\u0001]/,eE:!0,rB:!0,rE:!1,c:[{b:/([^\u2401\u0001=]+)/,e:/=([^\u2401\u0001=]+)/,rE:!0,rB:!1,cN:"attribute"},{b:/=/,e:/([\u2401\u0001])/,eE:!0,eB:!0,cN:"string"}]}],cI:!0}});hljs.registerLanguage("clojure",function(e){var t={built_in:"def cond apply if-not if-let if not not= = < > <= >= == + / * - rem quot neg? pos? delay? symbol? keyword? true? false? integer? empty? coll? list? set? ifn? fn? associative? sequential? sorted? counted? reversible? number? decimal? class? distinct? isa? float? rational? reduced? ratio? odd? even? char? seq? vector? string? map? nil? contains? zero? instance? not-every? not-any? libspec? -> ->> .. . inc compare do dotimes mapcat take remove take-while drop letfn drop-last take-last drop-while while intern condp case reduced cycle split-at split-with repeat replicate iterate range merge zipmap declare line-seq sort comparator sort-by dorun doall nthnext nthrest partition eval doseq await await-for let agent atom send send-off release-pending-sends add-watch mapv filterv remove-watch agent-error restart-agent set-error-handler error-handler set-error-mode! error-mode shutdown-agents quote var fn loop recur throw try monitor-enter monitor-exit defmacro defn defn- macroexpand macroexpand-1 for dosync and or when when-not when-let comp juxt partial sequence memoize constantly complement identity assert peek pop doto proxy defstruct first rest cons defprotocol cast coll deftype defrecord last butlast sigs reify second ffirst fnext nfirst nnext defmulti defmethod meta with-meta ns in-ns create-ns import refer keys select-keys vals key val rseq name namespace promise into transient persistent! conj! assoc! dissoc! pop! disj! use class type num float double short byte boolean bigint biginteger bigdec print-method print-dup throw-if printf format load compile get-in update-in pr pr-on newline flush read slurp read-line subvec with-open memfn time re-find re-groups rand-int rand mod locking assert-valid-fdecl alias resolve ref deref refset swap! reset! set-validator! compare-and-set! alter-meta! reset-meta! commute get-validator alter ref-set ref-history-count ref-min-history ref-max-history ensure sync io! new next conj set! to-array future future-call into-array aset gen-class reduce map filter find empty hash-map hash-set sorted-map sorted-map-by sorted-set sorted-set-by vec vector seq flatten reverse assoc dissoc list disj get union difference intersection extend extend-type extend-protocol int nth delay count concat chunk chunk-buffer chunk-append chunk-first chunk-rest max min dec unchecked-inc-int unchecked-inc unchecked-dec-inc unchecked-dec unchecked-negate unchecked-add-int unchecked-add unchecked-subtract-int unchecked-subtract chunk-next chunk-cons chunked-seq? prn vary-meta lazy-seq spread list* str find-keyword keyword symbol gensym force rationalize"},r="a-zA-Z_\\-!.?+*=<>&#'",n="["+r+"]["+r+"0-9/;:]*",a="[-+]?\\d+(\\.\\d+)?",o={b:n,r:0},s={cN:"number",b:a,r:0},i=e.inherit(e.QSM,{i:null}),c=e.C(";","$",{r:0}),d={cN:"literal",b:/\b(true|false|nil)\b/},l={cN:"collection",b:"[\\[\\{]",e:"[\\]\\}]"},m={cN:"comment",b:"\\^"+n},p=e.C("\\^\\{","\\}"),u={cN:"attribute",b:"[:]"+n},f={cN:"list",b:"\\(",e:"\\)"},h={eW:!0,r:0},y={k:t,l:n,cN:"keyword",b:n,starts:h},b=[f,i,m,p,c,u,l,s,d,o];return f.c=[e.C("comment",""),y,h],h.c=b,l.c=b,{aliases:["clj"],i:/\S/,c:[f,i,m,p,c,u,l,s,d]}});hljs.registerLanguage("perl",function(e){var t="getpwent getservent quotemeta msgrcv scalar kill dbmclose undef lc ma syswrite tr send umask sysopen shmwrite vec qx utime local oct semctl localtime readpipe do return format read sprintf dbmopen pop getpgrp not getpwnam rewinddir qqfileno qw endprotoent wait sethostent bless s|0 opendir continue each sleep endgrent shutdown dump chomp connect getsockname die socketpair close flock exists index shmgetsub for endpwent redo lstat msgctl setpgrp abs exit select print ref gethostbyaddr unshift fcntl syscall goto getnetbyaddr join gmtime symlink semget splice x|0 getpeername recv log setsockopt cos last reverse gethostbyname getgrnam study formline endhostent times chop length gethostent getnetent pack getprotoent getservbyname rand mkdir pos chmod y|0 substr endnetent printf next open msgsnd readdir use unlink getsockopt getpriority rindex wantarray hex system getservbyport endservent int chr untie rmdir prototype tell listen fork shmread ucfirst setprotoent else sysseek link getgrgid shmctl waitpid unpack getnetbyname reset chdir grep split require caller lcfirst until warn while values shift telldir getpwuid my getprotobynumber delete and sort uc defined srand accept package seekdir getprotobyname semop our rename seek if q|0 chroot sysread setpwent no crypt getc chown sqrt write setnetent setpriority foreach tie sin msgget map stat getlogin unless elsif truncate exec keys glob tied closedirioctl socket readlink eval xor readline binmode setservent eof ord bind alarm pipe atan2 getgrent exp time push setgrent gt lt or ne m|0 break given say state when",r={cN:"subst",b:"[$@]\\{",e:"\\}",k:t},s={b:"->{",e:"}"},n={cN:"variable",v:[{b:/\$\d/},{b:/[\$%@](\^\w\b|#\w+(::\w+)*|{\w+}|\w+(::\w*)*)/},{b:/[\$%@][^\s\w{]/,r:0}]},i=e.C("^(__END__|__DATA__)","\\n$",{r:5}),o=[e.BE,r,n],a=[n,e.HCM,i,e.C("^\\=\\w","\\=cut",{eW:!0}),s,{cN:"string",c:o,v:[{b:"q[qwxr]?\\s*\\(",e:"\\)",r:5},{b:"q[qwxr]?\\s*\\[",e:"\\]",r:5},{b:"q[qwxr]?\\s*\\{",e:"\\}",r:5},{b:"q[qwxr]?\\s*\\|",e:"\\|",r:5},{b:"q[qwxr]?\\s*\\<",e:"\\>",r:5},{b:"qw\\s+q",e:"q",r:5},{b:"'",e:"'",c:[e.BE]},{b:'"',e:'"'},{b:"`",e:"`",c:[e.BE]},{b:"{\\w+}",c:[],r:0},{b:"-?\\w+\\s*\\=\\>",c:[],r:0}]},{cN:"number",b:"(\\b0[0-7_]+)|(\\b0x[0-9a-fA-F_]+)|(\\b[1-9][0-9_]*(\\.[0-9_]+)?)|[0_]\\b",r:0},{b:"(\\/\\/|"+e.RSR+"|\\b(split|return|print|reverse|grep)\\b)\\s*",k:"split return print reverse grep",r:0,c:[e.HCM,i,{cN:"regexp",b:"(s|tr|y)/(\\\\.|[^/])*/(\\\\.|[^/])*/[a-z]*",r:10},{cN:"regexp",b:"(m|qr)?/",e:"/[a-z]*",c:[e.BE],r:0}]},{cN:"sub",bK:"sub",e:"(\\s*\\(.*?\\))?[;{]",r:5},{cN:"operator",b:"-\\w\\b",r:0}];return r.c=a,s.c=a,{aliases:["pl"],k:t,c:a}});hljs.registerLanguage("twig",function(e){var t={cN:"params",b:"\\(",e:"\\)"},a="attribute block constant cycle date dump include max min parent random range source template_from_string",r={cN:"function",bK:a,r:0,c:[t]},c={cN:"filter",b:/\|[A-Za-z_]+:?/,k:"abs batch capitalize convert_encoding date date_modify default escape first format join json_encode keys last length lower merge nl2br number_format raw replace reverse round slice sort split striptags title trim upper url_encode",c:[r]},n="autoescape block do embed extends filter flush for if import include macro sandbox set spaceless use verbatim";return n=n+" "+n.split(" ").map(function(e){return"end"+e}).join(" "),{aliases:["craftcms"],cI:!0,sL:"xml",subLanguageMode:"continuous",c:[e.C(/\{#/,/#}/),{cN:"template_tag",b:/\{%/,e:/%}/,k:n,c:[c,r]},{cN:"variable",b:/\{\{/,e:/}}/,c:[c,r]}]}});hljs.registerLanguage("livecodeserver",function(e){var r={cN:"variable",b:"\\b[gtps][A-Z]+[A-Za-z0-9_\\-]*\\b|\\$_[A-Z]+",r:0},t=[e.CBCM,e.HCM,e.C("--","$"),e.C("[^:]//","$")],a=e.inherit(e.TM,{v:[{b:"\\b_*rig[A-Z]+[A-Za-z0-9_\\-]*"},{b:"\\b_[a-z0-9\\-]+"}]}),o=e.inherit(e.TM,{b:"\\b([A-Za-z0-9_\\-]+)\\b"});return{cI:!1,k:{keyword:"$_COOKIE $_FILES $_GET $_GET_BINARY $_GET_RAW $_POST $_POST_BINARY $_POST_RAW $_SESSION $_SERVER codepoint codepoints segment segments codeunit codeunits sentence sentences trueWord trueWords paragraph after byte bytes english the until http forever descending using line real8 with seventh for stdout finally element word words fourth before black ninth sixth characters chars stderr uInt1 uInt1s uInt2 uInt2s stdin string lines relative rel any fifth items from middle mid at else of catch then third it file milliseconds seconds second secs sec int1 int1s int4 int4s internet int2 int2s normal text item last long detailed effective uInt4 uInt4s repeat end repeat URL in try into switch to words https token binfile each tenth as ticks tick system real4 by dateItems without char character ascending eighth whole dateTime numeric short first ftp integer abbreviated abbr abbrev private case while if",constant:"SIX TEN FORMFEED NINE ZERO NONE SPACE FOUR FALSE COLON CRLF PI COMMA ENDOFFILE EOF EIGHT FIVE QUOTE EMPTY ONE TRUE RETURN CR LINEFEED RIGHT BACKSLASH NULL SEVEN TAB THREE TWO six ten formfeed nine zero none space four false colon crlf pi comma endoffile eof eight five quote empty one true return cr linefeed right backslash null seven tab three two RIVERSION RISTATE FILE_READ_MODE FILE_WRITE_MODE FILE_WRITE_MODE DIR_WRITE_MODE FILE_READ_UMASK FILE_WRITE_UMASK DIR_READ_UMASK DIR_WRITE_UMASK",operator:"div mod wrap and or bitAnd bitNot bitOr bitXor among not in a an within contains ends with begins the keys of keys",built_in:"put abs acos aliasReference annuity arrayDecode arrayEncode asin atan atan2 average avg avgDev base64Decode base64Encode baseConvert binaryDecode binaryEncode byteOffset byteToNum cachedURL cachedURLs charToNum cipherNames codepointOffset codepointProperty codepointToNum codeunitOffset commandNames compound compress constantNames cos date dateFormat decompress directories diskSpace DNSServers exp exp1 exp2 exp10 extents files flushEvents folders format functionNames geometricMean global globals hasMemory harmonicMean hostAddress hostAddressToName hostName hostNameToAddress isNumber ISOToMac itemOffset keys len length libURLErrorData libUrlFormData libURLftpCommand libURLLastHTTPHeaders libURLLastRHHeaders libUrlMultipartFormAddPart libUrlMultipartFormData libURLVersion lineOffset ln ln1 localNames log log2 log10 longFilePath lower macToISO matchChunk matchText matrixMultiply max md5Digest median merge millisec millisecs millisecond milliseconds min monthNames nativeCharToNum normalizeText num number numToByte numToChar numToCodepoint numToNativeChar offset open openfiles openProcesses openProcessIDs openSockets paragraphOffset paramCount param params peerAddress pendingMessages platform popStdDev populationStandardDeviation populationVariance popVariance processID random randomBytes replaceText result revCreateXMLTree revCreateXMLTreeFromFile revCurrentRecord revCurrentRecordIsFirst revCurrentRecordIsLast revDatabaseColumnCount revDatabaseColumnIsNull revDatabaseColumnLengths revDatabaseColumnNames revDatabaseColumnNamed revDatabaseColumnNumbered revDatabaseColumnTypes revDatabaseConnectResult revDatabaseCursors revDatabaseID revDatabaseTableNames revDatabaseType revDataFromQuery revdb_closeCursor revdb_columnbynumber revdb_columncount revdb_columnisnull revdb_columnlengths revdb_columnnames revdb_columntypes revdb_commit revdb_connect revdb_connections revdb_connectionerr revdb_currentrecord revdb_cursorconnection revdb_cursorerr revdb_cursors revdb_dbtype revdb_disconnect revdb_execute revdb_iseof revdb_isbof revdb_movefirst revdb_movelast revdb_movenext revdb_moveprev revdb_query revdb_querylist revdb_recordcount revdb_rollback revdb_tablenames revGetDatabaseDriverPath revNumberOfRecords revOpenDatabase revOpenDatabases revQueryDatabase revQueryDatabaseBlob revQueryResult revQueryIsAtStart revQueryIsAtEnd revUnixFromMacPath revXMLAttribute revXMLAttributes revXMLAttributeValues revXMLChildContents revXMLChildNames revXMLCreateTreeFromFileWithNamespaces revXMLCreateTreeWithNamespaces revXMLDataFromXPathQuery revXMLEvaluateXPath revXMLFirstChild revXMLMatchingNode revXMLNextSibling revXMLNodeContents revXMLNumberOfChildren revXMLParent revXMLPreviousSibling revXMLRootNode revXMLRPC_CreateRequest revXMLRPC_Documents revXMLRPC_Error revXMLRPC_GetHost revXMLRPC_GetMethod revXMLRPC_GetParam revXMLText revXMLRPC_Execute revXMLRPC_GetParamCount revXMLRPC_GetParamNode revXMLRPC_GetParamType revXMLRPC_GetPath revXMLRPC_GetPort revXMLRPC_GetProtocol revXMLRPC_GetRequest revXMLRPC_GetResponse revXMLRPC_GetSocket revXMLTree revXMLTrees revXMLValidateDTD revZipDescribeItem revZipEnumerateItems revZipOpenArchives round sampVariance sec secs seconds sentenceOffset sha1Digest shell shortFilePath sin specialFolderPath sqrt standardDeviation statRound stdDev sum sysError systemVersion tan tempName textDecode textEncode tick ticks time to tokenOffset toLower toUpper transpose truewordOffset trunc uniDecode uniEncode upper URLDecode URLEncode URLStatus uuid value variableNames variance version waitDepth weekdayNames wordOffset xsltApplyStylesheet xsltApplyStylesheetFromFile xsltLoadStylesheet xsltLoadStylesheetFromFile add breakpoint cancel clear local variable file word line folder directory URL close socket process combine constant convert create new alias folder directory decrypt delete variable word line folder directory URL dispatch divide do encrypt filter get include intersect kill libURLDownloadToFile libURLFollowHttpRedirects libURLftpUpload libURLftpUploadFile libURLresetAll libUrlSetAuthCallback libURLSetCustomHTTPHeaders libUrlSetExpect100 libURLSetFTPListCommand libURLSetFTPMode libURLSetFTPStopTime libURLSetStatusCallback load multiply socket prepare process post seek rel relative read from process rename replace require resetAll resolve revAddXMLNode revAppendXML revCloseCursor revCloseDatabase revCommitDatabase revCopyFile revCopyFolder revCopyXMLNode revDeleteFolder revDeleteXMLNode revDeleteAllXMLTrees revDeleteXMLTree revExecuteSQL revGoURL revInsertXMLNode revMoveFolder revMoveToFirstRecord revMoveToLastRecord revMoveToNextRecord revMoveToPreviousRecord revMoveToRecord revMoveXMLNode revPutIntoXMLNode revRollBackDatabase revSetDatabaseDriverPath revSetXMLAttribute revXMLRPC_AddParam revXMLRPC_DeleteAllDocuments revXMLAddDTD revXMLRPC_Free revXMLRPC_FreeAll revXMLRPC_DeleteDocument revXMLRPC_DeleteParam revXMLRPC_SetHost revXMLRPC_SetMethod revXMLRPC_SetPort revXMLRPC_SetProtocol revXMLRPC_SetSocket revZipAddItemWithData revZipAddItemWithFile revZipAddUncompressedItemWithData revZipAddUncompressedItemWithFile revZipCancel revZipCloseArchive revZipDeleteItem revZipExtractItemToFile revZipExtractItemToVariable revZipSetProgressCallback revZipRenameItem revZipReplaceItemWithData revZipReplaceItemWithFile revZipOpenArchive send set sort split start stop subtract union unload wait write"},c:[r,{cN:"keyword",b:"\\bend\\sif\\b"},{cN:"function",bK:"function",e:"$",c:[r,o,e.ASM,e.QSM,e.BNM,e.CNM,a]},{cN:"function",bK:"end",e:"$",c:[o,a]},{cN:"command",bK:"command on",e:"$",c:[r,o,e.ASM,e.QSM,e.BNM,e.CNM,a]},{cN:"command",bK:"end",e:"$",c:[o,a]},{cN:"preprocessor",b:"<\\?rev|<\\?lc|<\\?livecode",r:10},{cN:"preprocessor",b:"<\\?"},{cN:"preprocessor",b:"\\?>"},e.ASM,e.QSM,e.BNM,e.CNM,a].concat(t),i:";$|^\\[|^="}});hljs.registerLanguage("step21",function(e){var r="[A-Z_][A-Z0-9_.]*",i="END-ISO-10303-21;",l={literal:"",built_in:"",keyword:"HEADER ENDSEC DATA"},s={cN:"preprocessor",b:"ISO-10303-21;",r:10},t=[e.CLCM,e.CBCM,e.C("/\\*\\*!","\\*/"),e.CNM,e.inherit(e.ASM,{i:null}),e.inherit(e.QSM,{i:null}),{cN:"string",b:"'",e:"'"},{cN:"label",v:[{b:"#",e:"\\d+",i:"\\W"}]}];return{aliases:["p21","step","stp"],cI:!0,l:r,k:l,c:[{cN:"preprocessor",b:i,r:10},s].concat(t)}});hljs.registerLanguage("cpp",function(t){var i={keyword:"false int float while private char catch export virtual operator sizeof dynamic_cast|10 typedef const_cast|10 const struct for static_cast|10 union namespace unsigned long volatile static protected bool template mutable if public friend do goto auto void enum else break extern using true class asm case typeid short reinterpret_cast|10 default double register explicit signed typename try this switch continue wchar_t inline delete alignof char16_t char32_t constexpr decltype noexcept nullptr static_assert thread_local restrict _Bool complex _Complex _Imaginary intmax_t uintmax_t int8_t uint8_t int16_t uint16_t int32_t uint32_t  int64_t uint64_t int_least8_t uint_least8_t int_least16_t uint_least16_t int_least32_t uint_least32_t int_least64_t uint_least64_t int_fast8_t uint_fast8_t int_fast16_t uint_fast16_t int_fast32_t uint_fast32_t int_fast64_t uint_fast64_t intptr_t uintptr_t atomic_bool atomic_char atomic_schar atomic_uchar atomic_short atomic_ushort atomic_int atomic_uint atomic_long atomic_ulong atomic_llong atomic_ullong atomic_wchar_t atomic_char16_t atomic_char32_t atomic_intmax_t atomic_uintmax_t atomic_intptr_t atomic_uintptr_t atomic_size_t atomic_ptrdiff_t atomic_int_least8_t atomic_int_least16_t atomic_int_least32_t atomic_int_least64_t atomic_uint_least8_t atomic_uint_least16_t atomic_uint_least32_t atomic_uint_least64_t atomic_int_fast8_t atomic_int_fast16_t atomic_int_fast32_t atomic_int_fast64_t atomic_uint_fast8_t atomic_uint_fast16_t atomic_uint_fast32_t atomic_uint_fast64_t",built_in:"std string cin cout cerr clog stringstream istringstream ostringstream auto_ptr deque list queue stack vector map set bitset multiset multimap unordered_set unordered_map unordered_multiset unordered_multimap array shared_ptr abort abs acos asin atan2 atan calloc ceil cosh cos exit exp fabs floor fmod fprintf fputs free frexp fscanf isalnum isalpha iscntrl isdigit isgraph islower isprint ispunct isspace isupper isxdigit tolower toupper labs ldexp log10 log malloc memchr memcmp memcpy memset modf pow printf putchar puts scanf sinh sin snprintf sprintf sqrt sscanf strcat strchr strcmp strcpy strcspn strlen strncat strncmp strncpy strpbrk strrchr strspn strstr tanh tan vfprintf vprintf vsprintf"};return{aliases:["c","cc","h","c++","h++","hpp"],k:i,i:"</",c:[t.CLCM,t.CBCM,t.QSM,{cN:"string",b:"'\\\\?.",e:"'",i:"."},{cN:"number",b:"\\b(\\d+(\\.\\d*)?|\\.\\d+)(u|U|l|L|ul|UL|f|F)"},t.CNM,{cN:"preprocessor",b:"#",e:"$",k:"if else elif endif define undef warning error line pragma",c:[{b:/\\\n/,r:0},{b:'include\\s*[<"]',e:'[>"]',k:"include",i:"\\n"},t.CLCM]},{b:"\\b(deque|list|queue|stack|vector|map|set|bitset|multiset|multimap|unordered_map|unordered_set|unordered_multiset|unordered_multimap|array)\\s*<",e:">",k:i,c:["self"]},{b:t.IR+"::",k:i},{bK:"new throw return else",r:0},{cN:"function",b:"("+t.IR+"\\s+)+"+t.IR+"\\s*\\(",rB:!0,e:/[{;=]/,eE:!0,k:i,c:[{b:t.IR+"\\s*\\(",rB:!0,c:[t.TM],r:0},{cN:"params",b:/\(/,e:/\)/,k:i,r:0,c:[t.CBCM]},t.CLCM,t.CBCM]}]}});hljs.registerLanguage("vala",function(e){return{k:{keyword:"char uchar unichar int uint long ulong short ushort int8 int16 int32 int64 uint8 uint16 uint32 uint64 float double bool struct enum string void weak unowned owned async signal static abstract interface override while do for foreach else switch case break default return try catch public private protected internal using new this get set const stdout stdin stderr var",built_in:"DBus GLib CCode Gee Object",literal:"false true null"},c:[{cN:"class",bK:"class interface delegate namespace",e:"{",eE:!0,i:"[^,:\\n\\s\\.]",c:[e.UTM]},e.CLCM,e.CBCM,{cN:"string",b:'"""',e:'"""',r:5},e.ASM,e.QSM,e.CNM,{cN:"preprocessor",b:"^#",e:"$",r:2},{cN:"constant",b:" [A-Z_]+ ",r:0}]}});hljs.registerLanguage("http",function(t){return{aliases:["https"],i:"\\S",c:[{cN:"status",b:"^HTTP/[0-9\\.]+",e:"$",c:[{cN:"number",b:"\\b\\d{3}\\b"}]},{cN:"request",b:"^[A-Z]+ (.*?) HTTP/[0-9\\.]+$",rB:!0,e:"$",c:[{cN:"string",b:" ",e:" ",eB:!0,eE:!0}]},{cN:"attribute",b:"^\\w",e:": ",eE:!0,i:"\\n|\\s|=",starts:{cN:"string",e:"$"}},{b:"\\n\\n",starts:{sL:"",eW:!0}}]}});hljs.registerLanguage("avrasm",function(r){return{cI:!0,l:"\\.?"+r.IR,k:{keyword:"adc add adiw and andi asr bclr bld brbc brbs brcc brcs break breq brge brhc brhs brid brie brlo brlt brmi brne brpl brsh brtc brts brvc brvs bset bst call cbi cbr clc clh cli cln clr cls clt clv clz com cp cpc cpi cpse dec eicall eijmp elpm eor fmul fmuls fmulsu icall ijmp in inc jmp ld ldd ldi lds lpm lsl lsr mov movw mul muls mulsu neg nop or ori out pop push rcall ret reti rjmp rol ror sbc sbr sbrc sbrs sec seh sbi sbci sbic sbis sbiw sei sen ser ses set sev sez sleep spm st std sts sub subi swap tst wdr",built_in:"r0 r1 r2 r3 r4 r5 r6 r7 r8 r9 r10 r11 r12 r13 r14 r15 r16 r17 r18 r19 r20 r21 r22 r23 r24 r25 r26 r27 r28 r29 r30 r31 x|0 xh xl y|0 yh yl z|0 zh zl ucsr1c udr1 ucsr1a ucsr1b ubrr1l ubrr1h ucsr0c ubrr0h tccr3c tccr3a tccr3b tcnt3h tcnt3l ocr3ah ocr3al ocr3bh ocr3bl ocr3ch ocr3cl icr3h icr3l etimsk etifr tccr1c ocr1ch ocr1cl twcr twdr twar twsr twbr osccal xmcra xmcrb eicra spmcsr spmcr portg ddrg ping portf ddrf sreg sph spl xdiv rampz eicrb eimsk gimsk gicr eifr gifr timsk tifr mcucr mcucsr tccr0 tcnt0 ocr0 assr tccr1a tccr1b tcnt1h tcnt1l ocr1ah ocr1al ocr1bh ocr1bl icr1h icr1l tccr2 tcnt2 ocr2 ocdr wdtcr sfior eearh eearl eedr eecr porta ddra pina portb ddrb pinb portc ddrc pinc portd ddrd pind spdr spsr spcr udr0 ucsr0a ucsr0b ubrr0l acsr admux adcsr adch adcl porte ddre pine pinf",preprocessor:".byte .cseg .db .def .device .dseg .dw .endmacro .equ .eseg .exit .include .list .listmac .macro .nolist .org .set"},c:[r.CBCM,r.C(";","$",{r:0}),r.CNM,r.BNM,{cN:"number",b:"\\b(\\$[a-zA-Z0-9]+|0o[0-7]+)"},r.QSM,{cN:"string",b:"'",e:"[^\\\\]'",i:"[^\\\\][^']"},{cN:"label",b:"^[A-Za-z0-9_.$]+:"},{cN:"preprocessor",b:"#",e:"$"},{cN:"localvars",b:"@[0-9]+"}]}});hljs.registerLanguage("aspectj",function(e){var t="false synchronized int abstract float private char boolean static null if const for true while long throw strictfp finally protected import native final return void enum else extends implements break transient new catch instanceof byte super volatile case assert short package default double public try this switch continue throws privileged aspectOf adviceexecution proceed cflowbelow cflow initialization preinitialization staticinitialization withincode target within execution getWithinTypeName handler thisJoinPoint thisJoinPointStaticPart thisEnclosingJoinPointStaticPart declare parents warning error soft precedence thisAspectInstance",i="get set args call";return{k:t,i:/<\//,c:[{cN:"javadoc",b:"/\\*\\*",e:"\\*/",r:0,c:[{cN:"javadoctag",b:"(^|\\s)@[A-Za-z]+"}]},e.CLCM,e.CBCM,e.ASM,e.QSM,{cN:"aspect",bK:"aspect",e:/[{;=]/,eE:!0,i:/[:;"\[\]]/,c:[{bK:"extends implements pertypewithin perthis pertarget percflowbelow percflow issingleton"},e.UTM,{b:/\([^\)]*/,e:/[)]+/,k:t+" "+i,eE:!1}]},{cN:"class",bK:"class interface",e:/[{;=]/,eE:!0,r:0,k:"class interface",i:/[:"\[\]]/,c:[{bK:"extends implements"},e.UTM]},{bK:"pointcut after before around throwing returning",e:/[)]/,eE:!1,i:/["\[\]]/,c:[{b:e.UIR+"\\s*\\(",rB:!0,c:[e.UTM]}]},{b:/[:]/,rB:!0,e:/[{;]/,r:0,eE:!1,k:t,i:/["\[\]]/,c:[{b:e.UIR+"\\s*\\(",k:t+" "+i},e.QSM]},{bK:"new throw",r:0},{cN:"function",b:/\w+ +\w+(\.)?\w+\s*\([^\)]*\)\s*((throws)[\w\s,]+)?[\{;]/,rB:!0,e:/[{;=]/,k:t,eE:!0,c:[{b:e.UIR+"\\s*\\(",rB:!0,r:0,c:[e.UTM]},{cN:"params",b:/\(/,e:/\)/,r:0,k:t,c:[e.ASM,e.QSM,e.CNM,e.CBCM]},e.CLCM,e.CBCM]},e.CNM,{cN:"annotation",b:"@[A-Za-z]+"}]}});hljs.registerLanguage("rib",function(e){return{k:"ArchiveRecord AreaLightSource Atmosphere Attribute AttributeBegin AttributeEnd Basis Begin Blobby Bound Clipping ClippingPlane Color ColorSamples ConcatTransform Cone CoordinateSystem CoordSysTransform CropWindow Curves Cylinder DepthOfField Detail DetailRange Disk Displacement Display End ErrorHandler Exposure Exterior Format FrameAspectRatio FrameBegin FrameEnd GeneralPolygon GeometricApproximation Geometry Hider Hyperboloid Identity Illuminate Imager Interior LightSource MakeCubeFaceEnvironment MakeLatLongEnvironment MakeShadow MakeTexture Matte MotionBegin MotionEnd NuPatch ObjectBegin ObjectEnd ObjectInstance Opacity Option Orientation Paraboloid Patch PatchMesh Perspective PixelFilter PixelSamples PixelVariance Points PointsGeneralPolygons PointsPolygons Polygon Procedural Projection Quantize ReadArchive RelativeDetail ReverseOrientation Rotate Scale ScreenWindow ShadingInterpolation ShadingRate Shutter Sides Skew SolidBegin SolidEnd Sphere SubdivisionMesh Surface TextureCoordinates Torus Transform TransformBegin TransformEnd TransformPoints Translate TrimCurve WorldBegin WorldEnd",i:"</",c:[e.HCM,e.CNM,e.ASM,e.QSM]}});hljs.registerLanguage("python",function(e){var r={cN:"prompt",b:/^(>>>|\.\.\.) /},b={cN:"string",c:[e.BE],v:[{b:/(u|b)?r?'''/,e:/'''/,c:[r],r:10},{b:/(u|b)?r?"""/,e:/"""/,c:[r],r:10},{b:/(u|r|ur)'/,e:/'/,r:10},{b:/(u|r|ur)"/,e:/"/,r:10},{b:/(b|br)'/,e:/'/},{b:/(b|br)"/,e:/"/},e.ASM,e.QSM]},l={cN:"number",r:0,v:[{b:e.BNR+"[lLjJ]?"},{b:"\\b(0o[0-7]+)[lLjJ]?"},{b:e.CNR+"[lLjJ]?"}]},c={cN:"params",b:/\(/,e:/\)/,c:["self",r,l,b]};return{aliases:["py","gyp"],k:{keyword:"and elif is global as in if from raise for except finally print import pass return exec else break not with class assert yield try while continue del or def lambda nonlocal|10 None True False",built_in:"Ellipsis NotImplemented"},i:/(<\/|->|\?)/,c:[r,l,b,e.HCM,{v:[{cN:"function",bK:"def",r:10},{cN:"class",bK:"class"}],e:/:/,i:/[${=;\n,]/,c:[e.UTM,c]},{cN:"decorator",b:/@/,e:/$/},{b:/\b(print|exec)\(/}]}});hljs.registerLanguage("axapta",function(e){return{k:"false int abstract private char boolean static null if for true while long throw finally protected final return void enum else break new catch byte super case short default double public try this switch continue reverse firstfast firstonly forupdate nofetch sum avg minof maxof count order group by asc desc index hint like dispaly edit client server ttsbegin ttscommit str real date container anytype common div mod",c:[e.CLCM,e.CBCM,e.ASM,e.QSM,e.CNM,{cN:"preprocessor",b:"#",e:"$"},{cN:"class",bK:"class interface",e:"{",eE:!0,i:":",c:[{bK:"extends implements"},e.UTM]}]}});hljs.registerLanguage("nix",function(e){var t={keyword:"rec with let in inherit assert if else then",constant:"true false or and null",built_in:"import abort baseNameOf dirOf isNull builtins map removeAttrs throw toString derivation"},i={cN:"subst",b:/\$\{/,e:/}/,k:t},r={cN:"variable",b:/[a-zA-Z0-9-_]+(\s*=)/},n={cN:"string",b:"''",e:"''",c:[i]},s={cN:"string",b:'"',e:'"',c:[i]},a=[e.NM,e.HCM,e.CBCM,n,s,r];return i.c=a,{aliases:["nixos"],k:t,c:a}});hljs.registerLanguage("diff",function(e){return{aliases:["patch"],c:[{cN:"chunk",r:10,v:[{b:/^@@ +\-\d+,\d+ +\+\d+,\d+ +@@$/},{b:/^\*\*\* +\d+,\d+ +\*\*\*\*$/},{b:/^\-\-\- +\d+,\d+ +\-\-\-\-$/}]},{cN:"header",v:[{b:/Index: /,e:/$/},{b:/=====/,e:/=====$/},{b:/^\-\-\-/,e:/$/},{b:/^\*{3} /,e:/$/},{b:/^\+\+\+/,e:/$/},{b:/\*{5}/,e:/\*{5}$/}]},{cN:"addition",b:"^\\+",e:"$"},{cN:"deletion",b:"^\\-",e:"$"},{cN:"change",b:"^\\!",e:"$"}]}});hljs.registerLanguage("parser3",function(r){var e=r.C("{","}",{c:["self"]});return{sL:"xml",r:0,c:[r.C("^#","$"),r.C("\\^rem{","}",{r:10,c:[e]}),{cN:"preprocessor",b:"^@(?:BASE|USE|CLASS|OPTIONS)$",r:10},{cN:"title",b:"@[\\w\\-]+\\[[\\w^;\\-]*\\](?:\\[[\\w^;\\-]*\\])?(?:.*)$"},{cN:"variable",b:"\\$\\{?[\\w\\-\\.\\:]+\\}?"},{cN:"keyword",b:"\\^[\\w\\-\\.\\:]+"},{cN:"number",b:"\\^#[0-9a-fA-F]+"},r.CNM]}});hljs.registerLanguage("django",function(e){var t={cN:"filter",b:/\|[A-Za-z]+:?/,k:"truncatewords removetags linebreaksbr yesno get_digit timesince random striptags filesizeformat escape linebreaks length_is ljust rjust cut urlize fix_ampersands title floatformat capfirst pprint divisibleby add make_list unordered_list urlencode timeuntil urlizetrunc wordcount stringformat linenumbers slice date dictsort dictsortreversed default_if_none pluralize lower join center default truncatewords_html upper length phone2numeric wordwrap time addslashes slugify first escapejs force_escape iriencode last safe safeseq truncatechars localize unlocalize localtime utc timezone",c:[{cN:"argument",b:/"/,e:/"/},{cN:"argument",b:/'/,e:/'/}]};return{aliases:["jinja"],cI:!0,sL:"xml",subLanguageMode:"continuous",c:[e.C(/\{%\s*comment\s*%}/,/\{%\s*endcomment\s*%}/),e.C(/\{#/,/#}/),{cN:"template_tag",b:/\{%/,e:/%}/,k:"comment endcomment load templatetag ifchanged endifchanged if endif firstof for endfor in ifnotequal endifnotequal widthratio extends include spaceless endspaceless regroup by as ifequal endifequal ssi now with cycle url filter endfilter debug block endblock else autoescape endautoescape csrf_token empty elif endwith static trans blocktrans endblocktrans get_static_prefix get_media_prefix plural get_current_language language get_available_languages get_current_language_bidi get_language_info get_language_info_list localize endlocalize localtime endlocaltime timezone endtimezone get_current_timezone verbatim",c:[t]},{cN:"variable",b:/\{\{/,e:/}}/,c:[t]}]}});hljs.registerLanguage("rust",function(e){var t=e.inherit(e.CBCM);return t.c.push("self"),{aliases:["rs"],k:{keyword:"alignof as be box break const continue crate do else enum extern false fn for if impl in let loop match mod mut offsetof once priv proc pub pure ref return self sizeof static struct super trait true type typeof unsafe unsized use virtual while yield int i8 i16 i32 i64 uint u8 u32 u64 float f32 f64 str char bool",built_in:"assert! assert_eq! bitflags! bytes! cfg! col! concat! concat_idents! debug_assert! debug_assert_eq! env! panic! file! format! format_args! include_bin! include_str! line! local_data_key! module_path! option_env! print! println! select! stringify! try! unimplemented! unreachable! vec! write! writeln!"},l:e.IR+"!?",i:"</",c:[e.CLCM,t,e.inherit(e.QSM,{i:null}),{cN:"string",b:/r(#*)".*?"\1(?!#)/},{cN:"string",b:/'\\?(x\w{2}|u\w{4}|U\w{8}|.)'/},{b:/'[a-zA-Z_][a-zA-Z0-9_]*/},{cN:"number",b:/\b(0[xbo][A-Fa-f0-9_]+|\d[\d_]*(\.[0-9_]+)?([eE][+-]?[0-9_]+)?)([uif](8|16|32|64|size))?/,r:0},{cN:"function",bK:"fn",e:"(\\(|<)",eE:!0,c:[e.UTM]},{cN:"preprocessor",b:"#\\!?\\[",e:"\\]"},{bK:"type",e:"(=|<)",c:[e.UTM],i:"\\S"},{bK:"trait enum",e:"({|<)",c:[e.UTM],i:"\\S"},{b:e.IR+"::"},{b:"->"}]}});hljs.registerLanguage("vhdl",function(e){var t="\\d(_|\\d)*",r="[eE][-+]?"+t,n=t+"(\\."+t+")?("+r+")?",o="\\w+",i=t+"#"+o+"(\\."+o+")?#("+r+")?",a="\\b("+i+"|"+n+")";return{cI:!0,k:{keyword:"abs access after alias all and architecture array assert attribute begin block body buffer bus case component configuration constant context cover disconnect downto default else elsif end entity exit fairness file for force function generate generic group guarded if impure in inertial inout is label library linkage literal loop map mod nand new next nor not null of on open or others out package port postponed procedure process property protected pure range record register reject release rem report restrict restrict_guarantee return rol ror select sequence severity shared signal sla sll sra srl strong subtype then to transport type unaffected units until use variable vmode vprop vunit wait when while with xnor xor",typename:"boolean bit character severity_level integer time delay_length natural positive string bit_vector file_open_kind file_open_status std_ulogic std_ulogic_vector std_logic std_logic_vector unsigned signed boolean_vector integer_vector real_vector time_vector"},i:"{",c:[e.CBCM,e.C("--","$"),e.QSM,{cN:"number",b:a,r:0},{cN:"literal",b:"'(U|X|0|1|Z|W|L|H|-)'",c:[e.BE]},{cN:"attribute",b:"'[A-Za-z](_?[A-Za-z0-9])*",c:[e.BE]}]}});hljs.registerLanguage("ocaml",function(e){return{aliases:["ml"],k:{keyword:"and as assert asr begin class constraint do done downto else end exception external for fun function functor if in include inherit! inherit initializer land lazy let lor lsl lsr lxor match method!|10 method mod module mutable new object of open! open or private rec sig struct then to try type val! val virtual when while with parser value",built_in:"array bool bytes char exn|5 float int int32 int64 list lazy_t|5 nativeint|5 string unit in_channel out_channel ref",literal:"true false"},i:/\/\/|>>/,l:"[a-z_]\\w*!?",c:[{cN:"literal",b:"\\[(\\|\\|)?\\]|\\(\\)"},e.C("\\(\\*","\\*\\)",{c:["self"]}),{cN:"symbol",b:"'[A-Za-z_](?!')[\\w']*"},{cN:"tag",b:"`[A-Z][\\w']*"},{cN:"type",b:"\\b[A-Z][\\w']*",r:0},{b:"[a-z_]\\w*'[\\w']*"},e.inherit(e.ASM,{cN:"char",r:0}),e.inherit(e.QSM,{i:null}),{cN:"number",b:"\\b(0[xX][a-fA-F0-9_]+[Lln]?|0[oO][0-7_]+[Lln]?|0[bB][01_]+[Lln]?|[0-9][0-9_]*([Lln]|(\\.[0-9_]*)?([eE][-+]?[0-9_]+)?)?)",r:0},{b:/[-=]>/}]}});hljs.registerLanguage("cmake",function(e){return{aliases:["cmake.in"],cI:!0,k:{keyword:"add_custom_command add_custom_target add_definitions add_dependencies add_executable add_library add_subdirectory add_test aux_source_directory break build_command cmake_minimum_required cmake_policy configure_file create_test_sourcelist define_property else elseif enable_language enable_testing endforeach endfunction endif endmacro endwhile execute_process export find_file find_library find_package find_path find_program fltk_wrap_ui foreach function get_cmake_property get_directory_property get_filename_component get_property get_source_file_property get_target_property get_test_property if include include_directories include_external_msproject include_regular_expression install link_directories load_cache load_command macro mark_as_advanced message option output_required_files project qt_wrap_cpp qt_wrap_ui remove_definitions return separate_arguments set set_directory_properties set_property set_source_files_properties set_target_properties set_tests_properties site_name source_group string target_link_libraries try_compile try_run unset variable_watch while build_name exec_program export_library_dependencies install_files install_programs install_targets link_libraries make_directory remove subdir_depends subdirs use_mangled_mesa utility_source variable_requires write_file qt5_use_modules qt5_use_package qt5_wrap_cpp on off true false and or",operator:"equal less greater strless strgreater strequal matches"},c:[{cN:"envvar",b:"\\${",e:"}"},e.HCM,e.QSM,e.NM]}});hljs.registerLanguage("1c",function(c){var e="[a-zA-Zа-яА-Я][a-zA-Z0-9_а-яА-Я]*",r="возврат дата для если и или иначе иначеесли исключение конецесли конецпопытки конецпроцедуры конецфункции конеццикла константа не перейти перем перечисление по пока попытка прервать продолжить процедура строка тогда фс функция цикл число экспорт",t="ansitooem oemtoansi ввестивидсубконто ввестидату ввестизначение ввестиперечисление ввестипериод ввестиплансчетов ввестистроку ввестичисло вопрос восстановитьзначение врег выбранныйплансчетов вызватьисключение датагод датамесяц датачисло добавитьмесяц завершитьработусистемы заголовоксистемы записьжурналарегистрации запуститьприложение зафиксироватьтранзакцию значениевстроку значениевстрокувнутр значениевфайл значениеизстроки значениеизстрокивнутр значениеизфайла имякомпьютера имяпользователя каталогвременныхфайлов каталогиб каталогпользователя каталогпрограммы кодсимв командасистемы конгода конецпериодаби конецрассчитанногопериодаби конецстандартногоинтервала конквартала конмесяца коннедели лев лог лог10 макс максимальноеколичествосубконто мин монопольныйрежим названиеинтерфейса названиенабораправ назначитьвид назначитьсчет найти найтипомеченныенаудаление найтиссылки началопериодаби началостандартногоинтервала начатьтранзакцию начгода начквартала начмесяца начнедели номерднягода номерднянедели номернеделигода нрег обработкаожидания окр описаниеошибки основнойжурналрасчетов основнойплансчетов основнойязык открытьформу открытьформумодально отменитьтранзакцию очиститьокносообщений периодстр полноеимяпользователя получитьвремята получитьдатута получитьдокументта получитьзначенияотбора получитьпозициюта получитьпустоезначение получитьта прав праводоступа предупреждение префиксавтонумерации пустаястрока пустоезначение рабочаядаттьпустоезначение рабочаядата разделительстраниц разделительстрок разм разобратьпозициюдокумента рассчитатьрегистрына рассчитатьрегистрыпо сигнал симв символтабуляции создатьобъект сокрл сокрлп сокрп сообщить состояние сохранитьзначение сред статусвозврата стрдлина стрзаменить стрколичествострок стрполучитьстроку  стрчисловхождений сформироватьпозициюдокумента счетпокоду текущаядата текущеевремя типзначения типзначениястр удалитьобъекты установитьтана установитьтапо фиксшаблон формат цел шаблон",i={cN:"dquote",b:'""'},n={cN:"string",b:'"',e:'"|$',c:[i]},a={cN:"string",b:"\\|",e:'"|$',c:[i]};return{cI:!0,l:e,k:{keyword:r,built_in:t},c:[c.CLCM,c.NM,n,a,{cN:"function",b:"(процедура|функция)",e:"$",l:e,k:"процедура функция",c:[c.inherit(c.TM,{b:e}),{cN:"tail",eW:!0,c:[{cN:"params",b:"\\(",e:"\\)",l:e,k:"знач",c:[n,a]},{cN:"export",b:"экспорт",eW:!0,l:e,k:"экспорт",c:[c.CLCM]}]},c.CLCM]},{cN:"preprocessor",b:"#",e:"$"},{cN:"date",b:"'\\d{2}\\.\\d{2}\\.(\\d{2}|\\d{4})'"}]}});hljs.registerLanguage("tcl",function(e){return{aliases:["tk"],k:"after append apply array auto_execok auto_import auto_load auto_mkindex auto_mkindex_old auto_qualify auto_reset bgerror binary break catch cd chan clock close concat continue dde dict encoding eof error eval exec exit expr fblocked fconfigure fcopy file fileevent filename flush for foreach format gets glob global history http if incr info interp join lappend|10 lassign|10 lindex|10 linsert|10 list llength|10 load lrange|10 lrepeat|10 lreplace|10 lreverse|10 lsearch|10 lset|10 lsort|10 mathfunc mathop memory msgcat namespace open package parray pid pkg::create pkg_mkIndex platform platform::shell proc puts pwd read refchan regexp registry regsub|10 rename return safe scan seek set socket source split string subst switch tcl_endOfWord tcl_findLibrary tcl_startOfNextWord tcl_startOfPreviousWord tcl_wordBreakAfter tcl_wordBreakBefore tcltest tclvars tell time tm trace unknown unload unset update uplevel upvar variable vwait while",c:[e.C(";[ \\t]*#","$"),e.C("^[ \\t]*#","$"),{bK:"proc",e:"[\\{]",eE:!0,c:[{cN:"symbol",b:"[ \\t\\n\\r]+(::)?[a-zA-Z_]((::)?[a-zA-Z0-9_])*",e:"[ \\t\\n\\r]",eW:!0,eE:!0}]},{cN:"variable",eE:!0,v:[{b:"\\$(\\{)?(::)?[a-zA-Z_]((::)?[a-zA-Z0-9_])*\\(([a-zA-Z0-9_])*\\)",e:"[^a-zA-Z0-9_\\}\\$]"},{b:"\\$(\\{)?(::)?[a-zA-Z_]((::)?[a-zA-Z0-9_])*",e:"(\\))?[^a-zA-Z0-9_\\}\\$]"}]},{cN:"string",c:[e.BE],v:[e.inherit(e.ASM,{i:null}),e.inherit(e.QSM,{i:null})]},{cN:"number",v:[e.BNM,e.CNM]}]}});hljs.registerLanguage("groovy",function(e){return{k:{typename:"byte short char int long boolean float double void",literal:"true false null",keyword:"def as in assert trait super this abstract static volatile transient public private protected synchronized final class interface enum if else for while switch case break default continue throw throws try catch finally implements extends new import package return instanceof"},c:[e.CLCM,{cN:"javadoc",b:"/\\*\\*",e:"\\*//*",r:0,c:[{cN:"javadoctag",b:"(^|\\s)@[A-Za-z]+"}]},e.CBCM,{cN:"string",b:'"""',e:'"""'},{cN:"string",b:"'''",e:"'''"},{cN:"string",b:"\\$/",e:"/\\$",r:10},e.ASM,{cN:"regexp",b:/~?\/[^\/\n]+\//,c:[e.BE]},e.QSM,{cN:"shebang",b:"^#!/usr/bin/env",e:"$",i:"\n"},e.BNM,{cN:"class",bK:"class interface trait enum",e:"{",i:":",c:[{bK:"extends implements"},e.UTM]},e.CNM,{cN:"annotation",b:"@[A-Za-z]+"},{cN:"string",b:/[^\?]{0}[A-Za-z0-9_$]+ *:/},{b:/\?/,e:/\:/},{cN:"label",b:"^\\s*[A-Za-z0-9_$]+:",r:0}]}});hljs.registerLanguage("erlang-repl",function(r){return{k:{special_functions:"spawn spawn_link self",reserved:"after and andalso|10 band begin bnot bor bsl bsr bxor case catch cond div end fun if let not of or orelse|10 query receive rem try when xor"},c:[{cN:"prompt",b:"^[0-9]+> ",r:10},r.C("%","$"),{cN:"number",b:"\\b(\\d+#[a-fA-F0-9]+|\\d+(\\.\\d+)?([eE][-+]?\\d+)?)",r:0},r.ASM,r.QSM,{cN:"constant",b:"\\?(::)?([A-Z]\\w*(::)?)+"},{cN:"arrow",b:"->"},{cN:"ok",b:"ok"},{cN:"exclamation_mark",b:"!"},{cN:"function_or_atom",b:"(\\b[a-z'][a-zA-Z0-9_']*:[a-z'][a-zA-Z0-9_']*)|(\\b[a-z'][a-zA-Z0-9_']*)",r:0},{cN:"variable",b:"[A-Z][a-zA-Z0-9_']*",r:0}]}});hljs.registerLanguage("nginx",function(e){var r={cN:"variable",v:[{b:/\$\d+/},{b:/\$\{/,e:/}/},{b:"[\\$\\@]"+e.UIR}]},b={eW:!0,l:"[a-z/_]+",k:{built_in:"on off yes no true false none blocked debug info notice warn error crit select break last permanent redirect kqueue rtsig epoll poll /dev/poll"},r:0,i:"=>",c:[e.HCM,{cN:"string",c:[e.BE,r],v:[{b:/"/,e:/"/},{b:/'/,e:/'/}]},{cN:"url",b:"([a-z]+):/",e:"\\s",eW:!0,eE:!0,c:[r]},{cN:"regexp",c:[e.BE,r],v:[{b:"\\s\\^",e:"\\s|{|;",rE:!0},{b:"~\\*?\\s+",e:"\\s|{|;",rE:!0},{b:"\\*(\\.[a-z\\-]+)+"},{b:"([a-z\\-]+\\.)+\\*"}]},{cN:"number",b:"\\b\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}(:\\d{1,5})?\\b"},{cN:"number",b:"\\b\\d+[kKmMgGdshdwy]*\\b",r:0},r]};return{aliases:["nginxconf"],c:[e.HCM,{b:e.UIR+"\\s",e:";|{",rB:!0,c:[{cN:"title",b:e.UIR,starts:b}],r:0}],i:"[^\\s\\}]"}});hljs.registerLanguage("mathematica",function(e){return{aliases:["mma"],l:"(\\$|\\b)"+e.IR+"\\b",k:"AbelianGroup Abort AbortKernels AbortProtect Above Abs Absolute AbsoluteCorrelation AbsoluteCorrelationFunction AbsoluteCurrentValue AbsoluteDashing AbsoluteFileName AbsoluteOptions AbsolutePointSize AbsoluteThickness AbsoluteTime AbsoluteTiming AccountingForm Accumulate Accuracy AccuracyGoal ActionDelay ActionMenu ActionMenuBox ActionMenuBoxOptions Active ActiveItem ActiveStyle AcyclicGraphQ AddOnHelpPath AddTo AdjacencyGraph AdjacencyList AdjacencyMatrix AdjustmentBox AdjustmentBoxOptions AdjustTimeSeriesForecast AffineTransform After AiryAi AiryAiPrime AiryAiZero AiryBi AiryBiPrime AiryBiZero AlgebraicIntegerQ AlgebraicNumber AlgebraicNumberDenominator AlgebraicNumberNorm AlgebraicNumberPolynomial AlgebraicNumberTrace AlgebraicRules AlgebraicRulesData Algebraics AlgebraicUnitQ Alignment AlignmentMarker AlignmentPoint All AllowedDimensions AllowGroupClose AllowInlineCells AllowKernelInitialization AllowReverseGroupClose AllowScriptLevelChange AlphaChannel AlternatingGroup AlternativeHypothesis Alternatives AmbientLight Analytic AnchoredSearch And AndersonDarlingTest AngerJ AngleBracket AngularGauge Animate AnimationCycleOffset AnimationCycleRepetitions AnimationDirection AnimationDisplayTime AnimationRate AnimationRepetitions AnimationRunning Animator AnimatorBox AnimatorBoxOptions AnimatorElements Annotation Annuity AnnuityDue Antialiasing Antisymmetric Apart ApartSquareFree Appearance AppearanceElements AppellF1 Append AppendTo Apply ArcCos ArcCosh ArcCot ArcCoth ArcCsc ArcCsch ArcSec ArcSech ArcSin ArcSinDistribution ArcSinh ArcTan ArcTanh Arg ArgMax ArgMin ArgumentCountQ ARIMAProcess ArithmeticGeometricMean ARMAProcess ARProcess Array ArrayComponents ArrayDepth ArrayFlatten ArrayPad ArrayPlot ArrayQ ArrayReshape ArrayRules Arrays Arrow Arrow3DBox ArrowBox Arrowheads AspectRatio AspectRatioFixed Assert Assuming Assumptions AstronomicalData Asynchronous AsynchronousTaskObject AsynchronousTasks AtomQ Attributes AugmentedSymmetricPolynomial AutoAction AutoDelete AutoEvaluateEvents AutoGeneratedPackage AutoIndent AutoIndentSpacings AutoItalicWords AutoloadPath AutoMatch Automatic AutomaticImageSize AutoMultiplicationSymbol AutoNumberFormatting AutoOpenNotebooks AutoOpenPalettes AutorunSequencing AutoScaling AutoScroll AutoSpacing AutoStyleOptions AutoStyleWords Axes AxesEdge AxesLabel AxesOrigin AxesStyle Axis BabyMonsterGroupB Back Background BackgroundTasksSettings Backslash Backsubstitution Backward Band BandpassFilter BandstopFilter BarabasiAlbertGraphDistribution BarChart BarChart3D BarLegend BarlowProschanImportance BarnesG BarOrigin BarSpacing BartlettHannWindow BartlettWindow BaseForm Baseline BaselinePosition BaseStyle BatesDistribution BattleLemarieWavelet Because BeckmannDistribution Beep Before Begin BeginDialogPacket BeginFrontEndInteractionPacket BeginPackage BellB BellY Below BenfordDistribution BeniniDistribution BenktanderGibratDistribution BenktanderWeibullDistribution BernoulliB BernoulliDistribution BernoulliGraphDistribution BernoulliProcess BernsteinBasis BesselFilterModel BesselI BesselJ BesselJZero BesselK BesselY BesselYZero Beta BetaBinomialDistribution BetaDistribution BetaNegativeBinomialDistribution BetaPrimeDistribution BetaRegularized BetweennessCentrality BezierCurve BezierCurve3DBox BezierCurve3DBoxOptions BezierCurveBox BezierCurveBoxOptions BezierFunction BilateralFilter Binarize BinaryFormat BinaryImageQ BinaryRead BinaryReadList BinaryWrite BinCounts BinLists Binomial BinomialDistribution BinomialProcess BinormalDistribution BiorthogonalSplineWavelet BipartiteGraphQ BirnbaumImportance BirnbaumSaundersDistribution BitAnd BitClear BitGet BitLength BitNot BitOr BitSet BitShiftLeft BitShiftRight BitXor Black BlackmanHarrisWindow BlackmanNuttallWindow BlackmanWindow Blank BlankForm BlankNullSequence BlankSequence Blend Block BlockRandom BlomqvistBeta BlomqvistBetaTest Blue Blur BodePlot BohmanWindow Bold Bookmarks Boole BooleanConsecutiveFunction BooleanConvert BooleanCountingFunction BooleanFunction BooleanGraph BooleanMaxterms BooleanMinimize BooleanMinterms Booleans BooleanTable BooleanVariables BorderDimensions BorelTannerDistribution Bottom BottomHatTransform BoundaryStyle Bounds Box BoxBaselineShift BoxData BoxDimensions Boxed Boxes BoxForm BoxFormFormatTypes BoxFrame BoxID BoxMargins BoxMatrix BoxRatios BoxRotation BoxRotationPoint BoxStyle BoxWhiskerChart Bra BracketingBar BraKet BrayCurtisDistance BreadthFirstScan Break Brown BrownForsytheTest BrownianBridgeProcess BrowserCategory BSplineBasis BSplineCurve BSplineCurve3DBox BSplineCurveBox BSplineCurveBoxOptions BSplineFunction BSplineSurface BSplineSurface3DBox BubbleChart BubbleChart3D BubbleScale BubbleSizes BulletGauge BusinessDayQ ButterflyGraph ButterworthFilterModel Button ButtonBar ButtonBox ButtonBoxOptions ButtonCell ButtonContents ButtonData ButtonEvaluator ButtonExpandable ButtonFrame ButtonFunction ButtonMargins ButtonMinHeight ButtonNote ButtonNotebook ButtonSource ButtonStyle ButtonStyleMenuListing Byte ByteCount ByteOrdering C CachedValue CacheGraphics CalendarData CalendarType CallPacket CanberraDistance Cancel CancelButton CandlestickChart Cap CapForm CapitalDifferentialD CardinalBSplineBasis CarmichaelLambda Cases Cashflow Casoratian Catalan CatalanNumber Catch CauchyDistribution CauchyWindow CayleyGraph CDF CDFDeploy CDFInformation CDFWavelet Ceiling Cell CellAutoOverwrite CellBaseline CellBoundingBox CellBracketOptions CellChangeTimes CellContents CellContext CellDingbat CellDynamicExpression CellEditDuplicate CellElementsBoundingBox CellElementSpacings CellEpilog CellEvaluationDuplicate CellEvaluationFunction CellEventActions CellFrame CellFrameColor CellFrameLabelMargins CellFrameLabels CellFrameMargins CellGroup CellGroupData CellGrouping CellGroupingRules CellHorizontalScrolling CellID CellLabel CellLabelAutoDelete CellLabelMargins CellLabelPositioning CellMargins CellObject CellOpen CellPrint CellProlog Cells CellSize CellStyle CellTags CellularAutomaton CensoredDistribution Censoring Center CenterDot CentralMoment CentralMomentGeneratingFunction CForm ChampernowneNumber ChanVeseBinarize Character CharacterEncoding CharacterEncodingsPath CharacteristicFunction CharacteristicPolynomial CharacterRange Characters ChartBaseStyle ChartElementData ChartElementDataFunction ChartElementFunction ChartElements ChartLabels ChartLayout ChartLegends ChartStyle Chebyshev1FilterModel Chebyshev2FilterModel ChebyshevDistance ChebyshevT ChebyshevU Check CheckAbort CheckAll Checkbox CheckboxBar CheckboxBox CheckboxBoxOptions ChemicalData ChessboardDistance ChiDistribution ChineseRemainder ChiSquareDistribution ChoiceButtons ChoiceDialog CholeskyDecomposition Chop Circle CircleBox CircleDot CircleMinus CirclePlus CircleTimes CirculantGraph CityData Clear ClearAll ClearAttributes ClearSystemCache ClebschGordan ClickPane Clip ClipboardNotebook ClipFill ClippingStyle ClipPlanes ClipRange Clock ClockGauge ClockwiseContourIntegral Close Closed CloseKernels ClosenessCentrality Closing ClosingAutoSave ClosingEvent ClusteringComponents CMYKColor Coarse Coefficient CoefficientArrays CoefficientDomain CoefficientList CoefficientRules CoifletWavelet Collect Colon ColonForm ColorCombine ColorConvert ColorData ColorDataFunction ColorFunction ColorFunctionScaling Colorize ColorNegate ColorOutput ColorProfileData ColorQuantize ColorReplace ColorRules ColorSelectorSettings ColorSeparate ColorSetter ColorSetterBox ColorSetterBoxOptions ColorSlider ColorSpace Column ColumnAlignments ColumnBackgrounds ColumnForm ColumnLines ColumnsEqual ColumnSpacings ColumnWidths CommonDefaultFormatTypes Commonest CommonestFilter CommonUnits CommunityBoundaryStyle CommunityGraphPlot CommunityLabels CommunityRegionStyle CompatibleUnitQ CompilationOptions CompilationTarget Compile Compiled CompiledFunction Complement CompleteGraph CompleteGraphQ CompleteKaryTree CompletionsListPacket Complex Complexes ComplexExpand ComplexInfinity ComplexityFunction ComponentMeasurements ComponentwiseContextMenu Compose ComposeList ComposeSeries Composition CompoundExpression CompoundPoissonDistribution CompoundPoissonProcess CompoundRenewalProcess Compress CompressedData Condition ConditionalExpression Conditioned Cone ConeBox ConfidenceLevel ConfidenceRange ConfidenceTransform ConfigurationPath Congruent Conjugate ConjugateTranspose Conjunction Connect ConnectedComponents ConnectedGraphQ ConnesWindow ConoverTest ConsoleMessage ConsoleMessagePacket ConsolePrint Constant ConstantArray Constants ConstrainedMax ConstrainedMin ContentPadding ContentsBoundingBox ContentSelectable ContentSize Context ContextMenu Contexts ContextToFilename ContextToFileName Continuation Continue ContinuedFraction ContinuedFractionK ContinuousAction ContinuousMarkovProcess ContinuousTimeModelQ ContinuousWaveletData ContinuousWaveletTransform ContourDetect ContourGraphics ContourIntegral ContourLabels ContourLines ContourPlot ContourPlot3D Contours ContourShading ContourSmoothing ContourStyle ContraharmonicMean Control ControlActive ControlAlignment ControllabilityGramian ControllabilityMatrix ControllableDecomposition ControllableModelQ ControllerDuration ControllerInformation ControllerInformationData ControllerLinking ControllerManipulate ControllerMethod ControllerPath ControllerState ControlPlacement ControlsRendering ControlType Convergents ConversionOptions ConversionRules ConvertToBitmapPacket ConvertToPostScript ConvertToPostScriptPacket Convolve ConwayGroupCo1 ConwayGroupCo2 ConwayGroupCo3 CoordinateChartData CoordinatesToolOptions CoordinateTransform CoordinateTransformData CoprimeQ Coproduct CopulaDistribution Copyable CopyDirectory CopyFile CopyTag CopyToClipboard CornerFilter CornerNeighbors Correlation CorrelationDistance CorrelationFunction CorrelationTest Cos Cosh CoshIntegral CosineDistance CosineWindow CosIntegral Cot Coth Count CounterAssignments CounterBox CounterBoxOptions CounterClockwiseContourIntegral CounterEvaluator CounterFunction CounterIncrements CounterStyle CounterStyleMenuListing CountRoots CountryData Covariance CovarianceEstimatorFunction CovarianceFunction CoxianDistribution CoxIngersollRossProcess CoxModel CoxModelFit CramerVonMisesTest CreateArchive CreateDialog CreateDirectory CreateDocument CreateIntermediateDirectories CreatePalette CreatePalettePacket CreateScheduledTask CreateTemporary CreateWindow CriticalityFailureImportance CriticalitySuccessImportance CriticalSection Cross CrossingDetect CrossMatrix Csc Csch CubeRoot Cubics Cuboid CuboidBox Cumulant CumulantGeneratingFunction Cup CupCap Curl CurlyDoubleQuote CurlyQuote CurrentImage CurrentlySpeakingPacket CurrentValue CurvatureFlowFilter CurveClosed Cyan CycleGraph CycleIndexPolynomial Cycles CyclicGroup Cyclotomic Cylinder CylinderBox CylindricalDecomposition D DagumDistribution DamerauLevenshteinDistance DampingFactor Darker Dashed Dashing DataCompression DataDistribution DataRange DataReversed Date DateDelimiters DateDifference DateFunction DateList DateListLogPlot DateListPlot DatePattern DatePlus DateRange DateString DateTicksFormat DaubechiesWavelet DavisDistribution DawsonF DayCount DayCountConvention DayMatchQ DayName DayPlus DayRange DayRound DeBruijnGraph Debug DebugTag Decimal DeclareKnownSymbols DeclarePackage Decompose Decrement DedekindEta Default DefaultAxesStyle DefaultBaseStyle DefaultBoxStyle DefaultButton DefaultColor DefaultControlPlacement DefaultDuplicateCellStyle DefaultDuration DefaultElement DefaultFaceGridsStyle DefaultFieldHintStyle DefaultFont DefaultFontProperties DefaultFormatType DefaultFormatTypeForStyle DefaultFrameStyle DefaultFrameTicksStyle DefaultGridLinesStyle DefaultInlineFormatType DefaultInputFormatType DefaultLabelStyle DefaultMenuStyle DefaultNaturalLanguage DefaultNewCellStyle DefaultNewInlineCellStyle DefaultNotebook DefaultOptions DefaultOutputFormatType DefaultStyle DefaultStyleDefinitions DefaultTextFormatType DefaultTextInlineFormatType DefaultTicksStyle DefaultTooltipStyle DefaultValues Defer DefineExternal DefineInputStreamMethod DefineOutputStreamMethod Definition Degree DegreeCentrality DegreeGraphDistribution DegreeLexicographic DegreeReverseLexicographic Deinitialization Del Deletable Delete DeleteBorderComponents DeleteCases DeleteContents DeleteDirectory DeleteDuplicates DeleteFile DeleteSmallComponents DeleteWithContents DeletionWarning Delimiter DelimiterFlashTime DelimiterMatching Delimiters Denominator DensityGraphics DensityHistogram DensityPlot DependentVariables Deploy Deployed Depth DepthFirstScan Derivative DerivativeFilter DescriptorStateSpace DesignMatrix Det DGaussianWavelet DiacriticalPositioning Diagonal DiagonalMatrix Dialog DialogIndent DialogInput DialogLevel DialogNotebook DialogProlog DialogReturn DialogSymbols Diamond DiamondMatrix DiceDissimilarity DictionaryLookup DifferenceDelta DifferenceOrder DifferenceRoot DifferenceRootReduce Differences DifferentialD DifferentialRoot DifferentialRootReduce DifferentiatorFilter DigitBlock DigitBlockMinimum DigitCharacter DigitCount DigitQ DihedralGroup Dilation Dimensions DiracComb DiracDelta DirectedEdge DirectedEdges DirectedGraph DirectedGraphQ DirectedInfinity Direction Directive Directory DirectoryName DirectoryQ DirectoryStack DirichletCharacter DirichletConvolve DirichletDistribution DirichletL DirichletTransform DirichletWindow DisableConsolePrintPacket DiscreteChirpZTransform DiscreteConvolve DiscreteDelta DiscreteHadamardTransform DiscreteIndicator DiscreteLQEstimatorGains DiscreteLQRegulatorGains DiscreteLyapunovSolve DiscreteMarkovProcess DiscretePlot DiscretePlot3D DiscreteRatio DiscreteRiccatiSolve DiscreteShift DiscreteTimeModelQ DiscreteUniformDistribution DiscreteVariables DiscreteWaveletData DiscreteWaveletPacketTransform DiscreteWaveletTransform Discriminant Disjunction Disk DiskBox DiskMatrix Dispatch DispersionEstimatorFunction Display DisplayAllSteps DisplayEndPacket DisplayFlushImagePacket DisplayForm DisplayFunction DisplayPacket DisplayRules DisplaySetSizePacket DisplayString DisplayTemporary DisplayWith DisplayWithRef DisplayWithVariable DistanceFunction DistanceTransform Distribute Distributed DistributedContexts DistributeDefinitions DistributionChart DistributionDomain DistributionFitTest DistributionParameterAssumptions DistributionParameterQ Dithering Div Divergence Divide DivideBy Dividers Divisible Divisors DivisorSigma DivisorSum DMSList DMSString Do DockedCells DocumentNotebook DominantColors DOSTextFormat Dot DotDashed DotEqual Dotted DoubleBracketingBar DoubleContourIntegral DoubleDownArrow DoubleLeftArrow DoubleLeftRightArrow DoubleLeftTee DoubleLongLeftArrow DoubleLongLeftRightArrow DoubleLongRightArrow DoubleRightArrow DoubleRightTee DoubleUpArrow DoubleUpDownArrow DoubleVerticalBar DoublyInfinite Down DownArrow DownArrowBar DownArrowUpArrow DownLeftRightVector DownLeftTeeVector DownLeftVector DownLeftVectorBar DownRightTeeVector DownRightVector DownRightVectorBar Downsample DownTee DownTeeArrow DownValues DragAndDrop DrawEdges DrawFrontFaces DrawHighlighted Drop DSolve Dt DualLinearProgramming DualSystemsModel DumpGet DumpSave DuplicateFreeQ Dynamic DynamicBox DynamicBoxOptions DynamicEvaluationTimeout DynamicLocation DynamicModule DynamicModuleBox DynamicModuleBoxOptions DynamicModuleParent DynamicModuleValues DynamicName DynamicNamespace DynamicReference DynamicSetting DynamicUpdating DynamicWrapper DynamicWrapperBox DynamicWrapperBoxOptions E EccentricityCentrality EdgeAdd EdgeBetweennessCentrality EdgeCapacity EdgeCapForm EdgeColor EdgeConnectivity EdgeCost EdgeCount EdgeCoverQ EdgeDashing EdgeDelete EdgeDetect EdgeForm EdgeIndex EdgeJoinForm EdgeLabeling EdgeLabels EdgeLabelStyle EdgeList EdgeOpacity EdgeQ EdgeRenderingFunction EdgeRules EdgeShapeFunction EdgeStyle EdgeThickness EdgeWeight Editable EditButtonSettings EditCellTagsSettings EditDistance EffectiveInterest Eigensystem Eigenvalues EigenvectorCentrality Eigenvectors Element ElementData Eliminate EliminationOrder EllipticE EllipticExp EllipticExpPrime EllipticF EllipticFilterModel EllipticK EllipticLog EllipticNomeQ EllipticPi EllipticReducedHalfPeriods EllipticTheta EllipticThetaPrime EmitSound EmphasizeSyntaxErrors EmpiricalDistribution Empty EmptyGraphQ EnableConsolePrintPacket Enabled Encode End EndAdd EndDialogPacket EndFrontEndInteractionPacket EndOfFile EndOfLine EndOfString EndPackage EngineeringForm Enter EnterExpressionPacket EnterTextPacket Entropy EntropyFilter Environment Epilog Equal EqualColumns EqualRows EqualTilde EquatedTo Equilibrium EquirippleFilterKernel Equivalent Erf Erfc Erfi ErlangB ErlangC ErlangDistribution Erosion ErrorBox ErrorBoxOptions ErrorNorm ErrorPacket ErrorsDialogSettings EstimatedDistribution EstimatedProcess EstimatorGains EstimatorRegulator EuclideanDistance EulerE EulerGamma EulerianGraphQ EulerPhi Evaluatable Evaluate Evaluated EvaluatePacket EvaluationCell EvaluationCompletionAction EvaluationElements EvaluationMode EvaluationMonitor EvaluationNotebook EvaluationObject EvaluationOrder Evaluator EvaluatorNames EvenQ EventData EventEvaluator EventHandler EventHandlerTag EventLabels ExactBlackmanWindow ExactNumberQ ExactRootIsolation ExampleData Except ExcludedForms ExcludePods Exclusions ExclusionsStyle Exists Exit ExitDialog Exp Expand ExpandAll ExpandDenominator ExpandFileName ExpandNumerator Expectation ExpectationE ExpectedValue ExpGammaDistribution ExpIntegralE ExpIntegralEi Exponent ExponentFunction ExponentialDistribution ExponentialFamily ExponentialGeneratingFunction ExponentialMovingAverage ExponentialPowerDistribution ExponentPosition ExponentStep Export ExportAutoReplacements ExportPacket ExportString Expression ExpressionCell ExpressionPacket ExpToTrig ExtendedGCD Extension ExtentElementFunction ExtentMarkers ExtentSize ExternalCall ExternalDataCharacterEncoding Extract ExtractArchive ExtremeValueDistribution FaceForm FaceGrids FaceGridsStyle Factor FactorComplete Factorial Factorial2 FactorialMoment FactorialMomentGeneratingFunction FactorialPower FactorInteger FactorList FactorSquareFree FactorSquareFreeList FactorTerms FactorTermsList Fail FailureDistribution False FARIMAProcess FEDisableConsolePrintPacket FeedbackSector FeedbackSectorStyle FeedbackType FEEnableConsolePrintPacket Fibonacci FieldHint FieldHintStyle FieldMasked FieldSize File FileBaseName FileByteCount FileDate FileExistsQ FileExtension FileFormat FileHash FileInformation FileName FileNameDepth FileNameDialogSettings FileNameDrop FileNameJoin FileNames FileNameSetter FileNameSplit FileNameTake FilePrint FileType FilledCurve FilledCurveBox Filling FillingStyle FillingTransform FilterRules FinancialBond FinancialData FinancialDerivative FinancialIndicator Find FindArgMax FindArgMin FindClique FindClusters FindCurvePath FindDistributionParameters FindDivisions FindEdgeCover FindEdgeCut FindEulerianCycle FindFaces FindFile FindFit FindGeneratingFunction FindGeoLocation FindGeometricTransform FindGraphCommunities FindGraphIsomorphism FindGraphPartition FindHamiltonianCycle FindIndependentEdgeSet FindIndependentVertexSet FindInstance FindIntegerNullVector FindKClan FindKClique FindKClub FindKPlex FindLibrary FindLinearRecurrence FindList FindMaximum FindMaximumFlow FindMaxValue FindMinimum FindMinimumCostFlow FindMinimumCut FindMinValue FindPermutation FindPostmanTour FindProcessParameters FindRoot FindSequenceFunction FindSettings FindShortestPath FindShortestTour FindThreshold FindVertexCover FindVertexCut Fine FinishDynamic FiniteAbelianGroupCount FiniteGroupCount FiniteGroupData First FirstPassageTimeDistribution FischerGroupFi22 FischerGroupFi23 FischerGroupFi24Prime FisherHypergeometricDistribution FisherRatioTest FisherZDistribution Fit FitAll FittedModel FixedPoint FixedPointList FlashSelection Flat Flatten FlattenAt FlatTopWindow FlipView Floor FlushPrintOutputPacket Fold FoldList Font FontColor FontFamily FontForm FontName FontOpacity FontPostScriptName FontProperties FontReencoding FontSize FontSlant FontSubstitutions FontTracking FontVariations FontWeight For ForAll Format FormatRules FormatType FormatTypeAutoConvert FormatValues FormBox FormBoxOptions FortranForm Forward ForwardBackward Fourier FourierCoefficient FourierCosCoefficient FourierCosSeries FourierCosTransform FourierDCT FourierDCTFilter FourierDCTMatrix FourierDST FourierDSTMatrix FourierMatrix FourierParameters FourierSequenceTransform FourierSeries FourierSinCoefficient FourierSinSeries FourierSinTransform FourierTransform FourierTrigSeries FractionalBrownianMotionProcess FractionalPart FractionBox FractionBoxOptions FractionLine Frame FrameBox FrameBoxOptions Framed FrameInset FrameLabel Frameless FrameMargins FrameStyle FrameTicks FrameTicksStyle FRatioDistribution FrechetDistribution FreeQ FrequencySamplingFilterKernel FresnelC FresnelS Friday FrobeniusNumber FrobeniusSolve FromCharacterCode FromCoefficientRules FromContinuedFraction FromDate FromDigits FromDMS Front FrontEndDynamicExpression FrontEndEventActions FrontEndExecute FrontEndObject FrontEndResource FrontEndResourceString FrontEndStackSize FrontEndToken FrontEndTokenExecute FrontEndValueCache FrontEndVersion FrontFaceColor FrontFaceOpacity Full FullAxes FullDefinition FullForm FullGraphics FullOptions FullSimplify Function FunctionExpand FunctionInterpolation FunctionSpace FussellVeselyImportance GaborFilter GaborMatrix GaborWavelet GainMargins GainPhaseMargins Gamma GammaDistribution GammaRegularized GapPenalty Gather GatherBy GaugeFaceElementFunction GaugeFaceStyle GaugeFrameElementFunction GaugeFrameSize GaugeFrameStyle GaugeLabels GaugeMarkers GaugeStyle GaussianFilter GaussianIntegers GaussianMatrix GaussianWindow GCD GegenbauerC General GeneralizedLinearModelFit GenerateConditions GeneratedCell GeneratedParameters GeneratingFunction Generic GenericCylindricalDecomposition GenomeData GenomeLookup GeodesicClosing GeodesicDilation GeodesicErosion GeodesicOpening GeoDestination GeodesyData GeoDirection GeoDistance GeoGridPosition GeometricBrownianMotionProcess GeometricDistribution GeometricMean GeometricMeanFilter GeometricTransformation GeometricTransformation3DBox GeometricTransformation3DBoxOptions GeometricTransformationBox GeometricTransformationBoxOptions GeoPosition GeoPositionENU GeoPositionXYZ GeoProjectionData GestureHandler GestureHandlerTag Get GetBoundingBoxSizePacket GetContext GetEnvironment GetFileName GetFrontEndOptionsDataPacket GetLinebreakInformationPacket GetMenusPacket GetPageBreakInformationPacket Glaisher GlobalClusteringCoefficient GlobalPreferences GlobalSession Glow GoldenRatio GompertzMakehamDistribution GoodmanKruskalGamma GoodmanKruskalGammaTest Goto Grad Gradient GradientFilter GradientOrientationFilter Graph GraphAssortativity GraphCenter GraphComplement GraphData GraphDensity GraphDiameter GraphDifference GraphDisjointUnion GraphDistance GraphDistanceMatrix GraphElementData GraphEmbedding GraphHighlight GraphHighlightStyle GraphHub Graphics Graphics3D Graphics3DBox Graphics3DBoxOptions GraphicsArray GraphicsBaseline GraphicsBox GraphicsBoxOptions GraphicsColor GraphicsColumn GraphicsComplex GraphicsComplex3DBox GraphicsComplex3DBoxOptions GraphicsComplexBox GraphicsComplexBoxOptions GraphicsContents GraphicsData GraphicsGrid GraphicsGridBox GraphicsGroup GraphicsGroup3DBox GraphicsGroup3DBoxOptions GraphicsGroupBox GraphicsGroupBoxOptions GraphicsGrouping GraphicsHighlightColor GraphicsRow GraphicsSpacing GraphicsStyle GraphIntersection GraphLayout GraphLinkEfficiency GraphPeriphery GraphPlot GraphPlot3D GraphPower GraphPropertyDistribution GraphQ GraphRadius GraphReciprocity GraphRoot GraphStyle GraphUnion Gray GrayLevel GreatCircleDistance Greater GreaterEqual GreaterEqualLess GreaterFullEqual GreaterGreater GreaterLess GreaterSlantEqual GreaterTilde Green Grid GridBaseline GridBox GridBoxAlignment GridBoxBackground GridBoxDividers GridBoxFrame GridBoxItemSize GridBoxItemStyle GridBoxOptions GridBoxSpacings GridCreationSettings GridDefaultElement GridElementStyleOptions GridFrame GridFrameMargins GridGraph GridLines GridLinesStyle GroebnerBasis GroupActionBase GroupCentralizer GroupElementFromWord GroupElementPosition GroupElementQ GroupElements GroupElementToWord GroupGenerators GroupMultiplicationTable GroupOrbits GroupOrder GroupPageBreakWithin GroupSetwiseStabilizer GroupStabilizer GroupStabilizerChain Gudermannian GumbelDistribution HaarWavelet HadamardMatrix HalfNormalDistribution HamiltonianGraphQ HammingDistance HammingWindow HankelH1 HankelH2 HankelMatrix HannPoissonWindow HannWindow HaradaNortonGroupHN HararyGraph HarmonicMean HarmonicMeanFilter HarmonicNumber Hash HashTable Haversine HazardFunction Head HeadCompose Heads HeavisideLambda HeavisidePi HeavisideTheta HeldGroupHe HeldPart HelpBrowserLookup HelpBrowserNotebook HelpBrowserSettings HermiteDecomposition HermiteH HermitianMatrixQ HessenbergDecomposition Hessian HexadecimalCharacter Hexahedron HexahedronBox HexahedronBoxOptions HiddenSurface HighlightGraph HighlightImage HighpassFilter HigmanSimsGroupHS HilbertFilter HilbertMatrix Histogram Histogram3D HistogramDistribution HistogramList HistogramTransform HistogramTransformInterpolation HitMissTransform HITSCentrality HodgeDual HoeffdingD HoeffdingDTest Hold HoldAll HoldAllComplete HoldComplete HoldFirst HoldForm HoldPattern HoldRest HolidayCalendar HomeDirectory HomePage Horizontal HorizontalForm HorizontalGauge HorizontalScrollPosition HornerForm HotellingTSquareDistribution HoytDistribution HTMLSave Hue HumpDownHump HumpEqual HurwitzLerchPhi HurwitzZeta HyperbolicDistribution HypercubeGraph HyperexponentialDistribution Hyperfactorial Hypergeometric0F1 Hypergeometric0F1Regularized Hypergeometric1F1 Hypergeometric1F1Regularized Hypergeometric2F1 Hypergeometric2F1Regularized HypergeometricDistribution HypergeometricPFQ HypergeometricPFQRegularized HypergeometricU Hyperlink HyperlinkCreationSettings Hyphenation HyphenationOptions HypoexponentialDistribution HypothesisTestData I Identity IdentityMatrix If IgnoreCase Im Image Image3D Image3DSlices ImageAccumulate ImageAdd ImageAdjust ImageAlign ImageApply ImageAspectRatio ImageAssemble ImageCache ImageCacheValid ImageCapture ImageChannels ImageClip ImageColorSpace ImageCompose ImageConvolve ImageCooccurrence ImageCorners ImageCorrelate ImageCorrespondingPoints ImageCrop ImageData ImageDataPacket ImageDeconvolve ImageDemosaic ImageDifference ImageDimensions ImageDistance ImageEffect ImageFeatureTrack ImageFileApply ImageFileFilter ImageFileScan ImageFilter ImageForestingComponents ImageForwardTransformation ImageHistogram ImageKeypoints ImageLevels ImageLines ImageMargins ImageMarkers ImageMeasurements ImageMultiply ImageOffset ImagePad ImagePadding ImagePartition ImagePeriodogram ImagePerspectiveTransformation ImageQ ImageRangeCache ImageReflect ImageRegion ImageResize ImageResolution ImageRotate ImageRotated ImageScaled ImageScan ImageSize ImageSizeAction ImageSizeCache ImageSizeMultipliers ImageSizeRaw ImageSubtract ImageTake ImageTransformation ImageTrim ImageType ImageValue ImageValuePositions Implies Import ImportAutoReplacements ImportString ImprovementImportance In IncidenceGraph IncidenceList IncidenceMatrix IncludeConstantBasis IncludeFileExtension IncludePods IncludeSingularTerm Increment Indent IndentingNewlineSpacings IndentMaxFraction IndependenceTest IndependentEdgeSetQ IndependentUnit IndependentVertexSetQ Indeterminate IndexCreationOptions Indexed IndexGraph IndexTag Inequality InexactNumberQ InexactNumbers Infinity Infix Information Inherited InheritScope Initialization InitializationCell InitializationCellEvaluation InitializationCellWarning InlineCounterAssignments InlineCounterIncrements InlineRules Inner Inpaint Input InputAliases InputAssumptions InputAutoReplacements InputField InputFieldBox InputFieldBoxOptions InputForm InputGrouping InputNamePacket InputNotebook InputPacket InputSettings InputStream InputString InputStringPacket InputToBoxFormPacket Insert InsertionPointObject InsertResults Inset Inset3DBox Inset3DBoxOptions InsetBox InsetBoxOptions Install InstallService InString Integer IntegerDigits IntegerExponent IntegerLength IntegerPart IntegerPartitions IntegerQ Integers IntegerString Integral Integrate Interactive InteractiveTradingChart Interlaced Interleaving InternallyBalancedDecomposition InterpolatingFunction InterpolatingPolynomial Interpolation InterpolationOrder InterpolationPoints InterpolationPrecision Interpretation InterpretationBox InterpretationBoxOptions InterpretationFunction InterpretTemplate InterquartileRange Interrupt InterruptSettings Intersection Interval IntervalIntersection IntervalMemberQ IntervalUnion Inverse InverseBetaRegularized InverseCDF InverseChiSquareDistribution InverseContinuousWaveletTransform InverseDistanceTransform InverseEllipticNomeQ InverseErf InverseErfc InverseFourier InverseFourierCosTransform InverseFourierSequenceTransform InverseFourierSinTransform InverseFourierTransform InverseFunction InverseFunctions InverseGammaDistribution InverseGammaRegularized InverseGaussianDistribution InverseGudermannian InverseHaversine InverseJacobiCD InverseJacobiCN InverseJacobiCS InverseJacobiDC InverseJacobiDN InverseJacobiDS InverseJacobiNC InverseJacobiND InverseJacobiNS InverseJacobiSC InverseJacobiSD InverseJacobiSN InverseLaplaceTransform InversePermutation InverseRadon InverseSeries InverseSurvivalFunction InverseWaveletTransform InverseWeierstrassP InverseZTransform Invisible InvisibleApplication InvisibleTimes IrreduciblePolynomialQ IsolatingInterval IsomorphicGraphQ IsotopeData Italic Item ItemBox ItemBoxOptions ItemSize ItemStyle ItoProcess JaccardDissimilarity JacobiAmplitude Jacobian JacobiCD JacobiCN JacobiCS JacobiDC JacobiDN JacobiDS JacobiNC JacobiND JacobiNS JacobiP JacobiSC JacobiSD JacobiSN JacobiSymbol JacobiZeta JankoGroupJ1 JankoGroupJ2 JankoGroupJ3 JankoGroupJ4 JarqueBeraALMTest JohnsonDistribution Join Joined JoinedCurve JoinedCurveBox JoinForm JordanDecomposition JordanModelDecomposition K KagiChart KaiserBesselWindow KaiserWindow KalmanEstimator KalmanFilter KarhunenLoeveDecomposition KaryTree KatzCentrality KCoreComponents KDistribution KelvinBei KelvinBer KelvinKei KelvinKer KendallTau KendallTauTest KernelExecute KernelMixtureDistribution KernelObject Kernels Ket Khinchin KirchhoffGraph KirchhoffMatrix KleinInvariantJ KnightTourGraph KnotData KnownUnitQ KolmogorovSmirnovTest KroneckerDelta KroneckerModelDecomposition KroneckerProduct KroneckerSymbol KuiperTest KumaraswamyDistribution Kurtosis KuwaharaFilter Label Labeled LabeledSlider LabelingFunction LabelStyle LaguerreL LambdaComponents LambertW LanczosWindow LandauDistribution Language LanguageCategory LaplaceDistribution LaplaceTransform Laplacian LaplacianFilter LaplacianGaussianFilter Large Larger Last Latitude LatitudeLongitude LatticeData LatticeReduce Launch LaunchKernels LayeredGraphPlot LayerSizeFunction LayoutInformation LCM LeafCount LeapYearQ LeastSquares LeastSquaresFilterKernel Left LeftArrow LeftArrowBar LeftArrowRightArrow LeftDownTeeVector LeftDownVector LeftDownVectorBar LeftRightArrow LeftRightVector LeftTee LeftTeeArrow LeftTeeVector LeftTriangle LeftTriangleBar LeftTriangleEqual LeftUpDownVector LeftUpTeeVector LeftUpVector LeftUpVectorBar LeftVector LeftVectorBar LegendAppearance Legended LegendFunction LegendLabel LegendLayout LegendMargins LegendMarkers LegendMarkerSize LegendreP LegendreQ LegendreType Length LengthWhile LerchPhi Less LessEqual LessEqualGreater LessFullEqual LessGreater LessLess LessSlantEqual LessTilde LetterCharacter LetterQ Level LeveneTest LeviCivitaTensor LevyDistribution Lexicographic LibraryFunction LibraryFunctionError LibraryFunctionInformation LibraryFunctionLoad LibraryFunctionUnload LibraryLoad LibraryUnload LicenseID LiftingFilterData LiftingWaveletTransform LightBlue LightBrown LightCyan Lighter LightGray LightGreen Lighting LightingAngle LightMagenta LightOrange LightPink LightPurple LightRed LightSources LightYellow Likelihood Limit LimitsPositioning LimitsPositioningTokens LindleyDistribution Line Line3DBox LinearFilter LinearFractionalTransform LinearModelFit LinearOffsetFunction LinearProgramming LinearRecurrence LinearSolve LinearSolveFunction LineBox LineBreak LinebreakAdjustments LineBreakChart LineBreakWithin LineColor LineForm LineGraph LineIndent LineIndentMaxFraction LineIntegralConvolutionPlot LineIntegralConvolutionScale LineLegend LineOpacity LineSpacing LineWrapParts LinkActivate LinkClose LinkConnect LinkConnectedQ LinkCreate LinkError LinkFlush LinkFunction LinkHost LinkInterrupt LinkLaunch LinkMode LinkObject LinkOpen LinkOptions LinkPatterns LinkProtocol LinkRead LinkReadHeld LinkReadyQ Links LinkWrite LinkWriteHeld LiouvilleLambda List Listable ListAnimate ListContourPlot ListContourPlot3D ListConvolve ListCorrelate ListCurvePathPlot ListDeconvolve ListDensityPlot Listen ListFourierSequenceTransform ListInterpolation ListLineIntegralConvolutionPlot ListLinePlot ListLogLinearPlot ListLogLogPlot ListLogPlot ListPicker ListPickerBox ListPickerBoxBackground ListPickerBoxOptions ListPlay ListPlot ListPlot3D ListPointPlot3D ListPolarPlot ListQ ListStreamDensityPlot ListStreamPlot ListSurfacePlot3D ListVectorDensityPlot ListVectorPlot ListVectorPlot3D ListZTransform Literal LiteralSearch LocalClusteringCoefficient LocalizeVariables LocationEquivalenceTest LocationTest Locator LocatorAutoCreate LocatorBox LocatorBoxOptions LocatorCentering LocatorPane LocatorPaneBox LocatorPaneBoxOptions LocatorRegion Locked Log Log10 Log2 LogBarnesG LogGamma LogGammaDistribution LogicalExpand LogIntegral LogisticDistribution LogitModelFit LogLikelihood LogLinearPlot LogLogisticDistribution LogLogPlot LogMultinormalDistribution LogNormalDistribution LogPlot LogRankTest LogSeriesDistribution LongEqual Longest LongestAscendingSequence LongestCommonSequence LongestCommonSequencePositions LongestCommonSubsequence LongestCommonSubsequencePositions LongestMatch LongForm Longitude LongLeftArrow LongLeftRightArrow LongRightArrow Loopback LoopFreeGraphQ LowerCaseQ LowerLeftArrow LowerRightArrow LowerTriangularize LowpassFilter LQEstimatorGains LQGRegulator LQOutputRegulatorGains LQRegulatorGains LUBackSubstitution LucasL LuccioSamiComponents LUDecomposition LyapunovSolve LyonsGroupLy MachineID MachineName MachineNumberQ MachinePrecision MacintoshSystemPageSetup Magenta Magnification Magnify MainSolve MaintainDynamicCaches Majority MakeBoxes MakeExpression MakeRules MangoldtLambda ManhattanDistance Manipulate Manipulator MannWhitneyTest MantissaExponent Manual Map MapAll MapAt MapIndexed MAProcess MapThread MarcumQ MardiaCombinedTest MardiaKurtosisTest MardiaSkewnessTest MarginalDistribution MarkovProcessProperties Masking MatchingDissimilarity MatchLocalNameQ MatchLocalNames MatchQ Material MathematicaNotation MathieuC MathieuCharacteristicA MathieuCharacteristicB MathieuCharacteristicExponent MathieuCPrime MathieuGroupM11 MathieuGroupM12 MathieuGroupM22 MathieuGroupM23 MathieuGroupM24 MathieuS MathieuSPrime MathMLForm MathMLText Matrices MatrixExp MatrixForm MatrixFunction MatrixLog MatrixPlot MatrixPower MatrixQ MatrixRank Max MaxBend MaxDetect MaxExtraBandwidths MaxExtraConditions MaxFeatures MaxFilter Maximize MaxIterations MaxMemoryUsed MaxMixtureKernels MaxPlotPoints MaxPoints MaxRecursion MaxStableDistribution MaxStepFraction MaxSteps MaxStepSize MaxValue MaxwellDistribution McLaughlinGroupMcL Mean MeanClusteringCoefficient MeanDegreeConnectivity MeanDeviation MeanFilter MeanGraphDistance MeanNeighborDegree MeanShift MeanShiftFilter Median MedianDeviation MedianFilter Medium MeijerG MeixnerDistribution MemberQ MemoryConstrained MemoryInUse Menu MenuAppearance MenuCommandKey MenuEvaluator MenuItem MenuPacket MenuSortingValue MenuStyle MenuView MergeDifferences Mesh MeshFunctions MeshRange MeshShading MeshStyle Message MessageDialog MessageList MessageName MessageOptions MessagePacket Messages MessagesNotebook MetaCharacters MetaInformation Method MethodOptions MexicanHatWavelet MeyerWavelet Min MinDetect MinFilter MinimalPolynomial MinimalStateSpaceModel Minimize Minors MinRecursion MinSize MinStableDistribution Minus MinusPlus MinValue Missing MissingDataMethod MittagLefflerE MixedRadix MixedRadixQuantity MixtureDistribution Mod Modal Mode Modular ModularLambda Module Modulus MoebiusMu Moment Momentary MomentConvert MomentEvaluate MomentGeneratingFunction Monday Monitor MonomialList MonomialOrder MonsterGroupM MorletWavelet MorphologicalBinarize MorphologicalBranchPoints MorphologicalComponents MorphologicalEulerNumber MorphologicalGraph MorphologicalPerimeter MorphologicalTransform Most MouseAnnotation MouseAppearance MouseAppearanceTag MouseButtons Mouseover MousePointerNote MousePosition MovingAverage MovingMedian MoyalDistribution MultiedgeStyle MultilaunchWarning MultiLetterItalics MultiLetterStyle MultilineFunction Multinomial MultinomialDistribution MultinormalDistribution MultiplicativeOrder Multiplicity Multiselection MultivariateHypergeometricDistribution MultivariatePoissonDistribution MultivariateTDistribution N NakagamiDistribution NameQ Names NamespaceBox Nand NArgMax NArgMin NBernoulliB NCache NDSolve NDSolveValue Nearest NearestFunction NeedCurrentFrontEndPackagePacket NeedCurrentFrontEndSymbolsPacket NeedlemanWunschSimilarity Needs Negative NegativeBinomialDistribution NegativeMultinomialDistribution NeighborhoodGraph Nest NestedGreaterGreater NestedLessLess NestedScriptRules NestList NestWhile NestWhileList NevilleThetaC NevilleThetaD NevilleThetaN NevilleThetaS NewPrimitiveStyle NExpectation Next NextPrime NHoldAll NHoldFirst NHoldRest NicholsGridLines NicholsPlot NIntegrate NMaximize NMaxValue NMinimize NMinValue NominalVariables NonAssociative NoncentralBetaDistribution NoncentralChiSquareDistribution NoncentralFRatioDistribution NoncentralStudentTDistribution NonCommutativeMultiply NonConstants None NonlinearModelFit NonlocalMeansFilter NonNegative NonPositive Nor NorlundB Norm Normal NormalDistribution NormalGrouping Normalize NormalizedSquaredEuclideanDistance NormalsFunction NormFunction Not NotCongruent NotCupCap NotDoubleVerticalBar Notebook NotebookApply NotebookAutoSave NotebookClose NotebookConvertSettings NotebookCreate NotebookCreateReturnObject NotebookDefault NotebookDelete NotebookDirectory NotebookDynamicExpression NotebookEvaluate NotebookEventActions NotebookFileName NotebookFind NotebookFindReturnObject NotebookGet NotebookGetLayoutInformationPacket NotebookGetMisspellingsPacket NotebookInformation NotebookInterfaceObject NotebookLocate NotebookObject NotebookOpen NotebookOpenReturnObject NotebookPath NotebookPrint NotebookPut NotebookPutReturnObject NotebookRead NotebookResetGeneratedCells Notebooks NotebookSave NotebookSaveAs NotebookSelection NotebookSetupLayoutInformationPacket NotebooksMenu NotebookWrite NotElement NotEqualTilde NotExists NotGreater NotGreaterEqual NotGreaterFullEqual NotGreaterGreater NotGreaterLess NotGreaterSlantEqual NotGreaterTilde NotHumpDownHump NotHumpEqual NotLeftTriangle NotLeftTriangleBar NotLeftTriangleEqual NotLess NotLessEqual NotLessFullEqual NotLessGreater NotLessLess NotLessSlantEqual NotLessTilde NotNestedGreaterGreater NotNestedLessLess NotPrecedes NotPrecedesEqual NotPrecedesSlantEqual NotPrecedesTilde NotReverseElement NotRightTriangle NotRightTriangleBar NotRightTriangleEqual NotSquareSubset NotSquareSubsetEqual NotSquareSuperset NotSquareSupersetEqual NotSubset NotSubsetEqual NotSucceeds NotSucceedsEqual NotSucceedsSlantEqual NotSucceedsTilde NotSuperset NotSupersetEqual NotTilde NotTildeEqual NotTildeFullEqual NotTildeTilde NotVerticalBar NProbability NProduct NProductFactors NRoots NSolve NSum NSumTerms Null NullRecords NullSpace NullWords Number NumberFieldClassNumber NumberFieldDiscriminant NumberFieldFundamentalUnits NumberFieldIntegralBasis NumberFieldNormRepresentatives NumberFieldRegulator NumberFieldRootsOfUnity NumberFieldSignature NumberForm NumberFormat NumberMarks NumberMultiplier NumberPadding NumberPoint NumberQ NumberSeparator NumberSigns NumberString Numerator NumericFunction NumericQ NuttallWindow NValues NyquistGridLines NyquistPlot O ObservabilityGramian ObservabilityMatrix ObservableDecomposition ObservableModelQ OddQ Off Offset OLEData On ONanGroupON OneIdentity Opacity Open OpenAppend Opener OpenerBox OpenerBoxOptions OpenerView OpenFunctionInspectorPacket Opening OpenRead OpenSpecialOptions OpenTemporary OpenWrite Operate OperatingSystem OptimumFlowData Optional OptionInspectorSettings OptionQ Options OptionsPacket OptionsPattern OptionValue OptionValueBox OptionValueBoxOptions Or Orange Order OrderDistribution OrderedQ Ordering Orderless OrnsteinUhlenbeckProcess Orthogonalize Out Outer OutputAutoOverwrite OutputControllabilityMatrix OutputControllableModelQ OutputForm OutputFormData OutputGrouping OutputMathEditExpression OutputNamePacket OutputResponse OutputSizeLimit OutputStream Over OverBar OverDot Overflow OverHat Overlaps Overlay OverlayBox OverlayBoxOptions Overscript OverscriptBox OverscriptBoxOptions OverTilde OverVector OwenT OwnValues PackingMethod PaddedForm Padding PadeApproximant PadLeft PadRight PageBreakAbove PageBreakBelow PageBreakWithin PageFooterLines PageFooters PageHeaderLines PageHeaders PageHeight PageRankCentrality PageWidth PairedBarChart PairedHistogram PairedSmoothHistogram PairedTTest PairedZTest PaletteNotebook PalettePath Pane PaneBox PaneBoxOptions Panel PanelBox PanelBoxOptions Paneled PaneSelector PaneSelectorBox PaneSelectorBoxOptions PaperWidth ParabolicCylinderD ParagraphIndent ParagraphSpacing ParallelArray ParallelCombine ParallelDo ParallelEvaluate Parallelization Parallelize ParallelMap ParallelNeeds ParallelProduct ParallelSubmit ParallelSum ParallelTable ParallelTry Parameter ParameterEstimator ParameterMixtureDistribution ParameterVariables ParametricFunction ParametricNDSolve ParametricNDSolveValue ParametricPlot ParametricPlot3D ParentConnect ParentDirectory ParentForm Parenthesize ParentList ParetoDistribution Part PartialCorrelationFunction PartialD ParticleData Partition PartitionsP PartitionsQ ParzenWindow PascalDistribution PassEventsDown PassEventsUp Paste PasteBoxFormInlineCells PasteButton Path PathGraph PathGraphQ Pattern PatternSequence PatternTest PauliMatrix PaulWavelet Pause PausedTime PDF PearsonChiSquareTest PearsonCorrelationTest PearsonDistribution PerformanceGoal PeriodicInterpolation Periodogram PeriodogramArray PermutationCycles PermutationCyclesQ PermutationGroup PermutationLength PermutationList PermutationListQ PermutationMax PermutationMin PermutationOrder PermutationPower PermutationProduct PermutationReplace Permutations PermutationSupport Permute PeronaMalikFilter Perpendicular PERTDistribution PetersenGraph PhaseMargins Pi Pick PIDData PIDDerivativeFilter PIDFeedforward PIDTune Piecewise PiecewiseExpand PieChart PieChart3D PillaiTrace PillaiTraceTest Pink Pivoting PixelConstrained PixelValue PixelValuePositions Placed Placeholder PlaceholderReplace Plain PlanarGraphQ Play PlayRange Plot Plot3D Plot3Matrix PlotDivision PlotJoined PlotLabel PlotLayout PlotLegends PlotMarkers PlotPoints PlotRange PlotRangeClipping PlotRangePadding PlotRegion PlotStyle Plus PlusMinus Pochhammer PodStates PodWidth Point Point3DBox PointBox PointFigureChart PointForm PointLegend PointSize PoissonConsulDistribution PoissonDistribution PoissonProcess PoissonWindow PolarAxes PolarAxesOrigin PolarGridLines PolarPlot PolarTicks PoleZeroMarkers PolyaAeppliDistribution PolyGamma Polygon Polygon3DBox Polygon3DBoxOptions PolygonBox PolygonBoxOptions PolygonHoleScale PolygonIntersections PolygonScale PolyhedronData PolyLog PolynomialExtendedGCD PolynomialForm PolynomialGCD PolynomialLCM PolynomialMod PolynomialQ PolynomialQuotient PolynomialQuotientRemainder PolynomialReduce PolynomialRemainder Polynomials PopupMenu PopupMenuBox PopupMenuBoxOptions PopupView PopupWindow Position Positive PositiveDefiniteMatrixQ PossibleZeroQ Postfix PostScript Power PowerDistribution PowerExpand PowerMod PowerModList PowerSpectralDensity PowersRepresentations PowerSymmetricPolynomial Precedence PrecedenceForm Precedes PrecedesEqual PrecedesSlantEqual PrecedesTilde Precision PrecisionGoal PreDecrement PredictionRoot PreemptProtect PreferencesPath Prefix PreIncrement Prepend PrependTo PreserveImageOptions Previous PriceGraphDistribution PrimaryPlaceholder Prime PrimeNu PrimeOmega PrimePi PrimePowerQ PrimeQ Primes PrimeZetaP PrimitiveRoot PrincipalComponents PrincipalValue Print PrintAction PrintForm PrintingCopies PrintingOptions PrintingPageRange PrintingStartingPageNumber PrintingStyleEnvironment PrintPrecision PrintTemporary Prism PrismBox PrismBoxOptions PrivateCellOptions PrivateEvaluationOptions PrivateFontOptions PrivateFrontEndOptions PrivateNotebookOptions PrivatePaths Probability ProbabilityDistribution ProbabilityPlot ProbabilityPr ProbabilityScalePlot ProbitModelFit ProcessEstimator ProcessParameterAssumptions ProcessParameterQ ProcessStateDomain ProcessTimeDomain Product ProductDistribution ProductLog ProgressIndicator ProgressIndicatorBox ProgressIndicatorBoxOptions Projection Prolog PromptForm Properties Property PropertyList PropertyValue Proportion Proportional Protect Protected ProteinData Pruning PseudoInverse Purple Put PutAppend Pyramid PyramidBox PyramidBoxOptions QBinomial QFactorial QGamma QHypergeometricPFQ QPochhammer QPolyGamma QRDecomposition QuadraticIrrationalQ Quantile QuantilePlot Quantity QuantityForm QuantityMagnitude QuantityQ QuantityUnit Quartics QuartileDeviation Quartiles QuartileSkewness QueueingNetworkProcess QueueingProcess QueueProperties Quiet Quit Quotient QuotientRemainder RadialityCentrality RadicalBox RadicalBoxOptions RadioButton RadioButtonBar RadioButtonBox RadioButtonBoxOptions Radon RamanujanTau RamanujanTauL RamanujanTauTheta RamanujanTauZ Random RandomChoice RandomComplex RandomFunction RandomGraph RandomImage RandomInteger RandomPermutation RandomPrime RandomReal RandomSample RandomSeed RandomVariate RandomWalkProcess Range RangeFilter RangeSpecification RankedMax RankedMin Raster Raster3D Raster3DBox Raster3DBoxOptions RasterArray RasterBox RasterBoxOptions Rasterize RasterSize Rational RationalFunctions Rationalize Rationals Ratios Raw RawArray RawBoxes RawData RawMedium RayleighDistribution Re Read ReadList ReadProtected Real RealBlockDiagonalForm RealDigits RealExponent Reals Reap Record RecordLists RecordSeparators Rectangle RectangleBox RectangleBoxOptions RectangleChart RectangleChart3D RecurrenceFilter RecurrenceTable RecurringDigitsForm Red Reduce RefBox ReferenceLineStyle ReferenceMarkers ReferenceMarkerStyle Refine ReflectionMatrix ReflectionTransform Refresh RefreshRate RegionBinarize RegionFunction RegionPlot RegionPlot3D RegularExpression Regularization Reinstall Release ReleaseHold ReliabilityDistribution ReliefImage ReliefPlot Remove RemoveAlphaChannel RemoveAsynchronousTask Removed RemoveInputStreamMethod RemoveOutputStreamMethod RemoveProperty RemoveScheduledTask RenameDirectory RenameFile RenderAll RenderingOptions RenewalProcess RenkoChart Repeated RepeatedNull RepeatedString Replace ReplaceAll ReplaceHeldPart ReplaceImageValue ReplaceList ReplacePart ReplacePixelValue ReplaceRepeated Resampling Rescale RescalingTransform ResetDirectory ResetMenusPacket ResetScheduledTask Residue Resolve Rest Resultant ResumePacket Return ReturnExpressionPacket ReturnInputFormPacket ReturnPacket ReturnTextPacket Reverse ReverseBiorthogonalSplineWavelet ReverseElement ReverseEquilibrium ReverseGraph ReverseUpEquilibrium RevolutionAxis RevolutionPlot3D RGBColor RiccatiSolve RiceDistribution RidgeFilter RiemannR RiemannSiegelTheta RiemannSiegelZ Riffle Right RightArrow RightArrowBar RightArrowLeftArrow RightCosetRepresentative RightDownTeeVector RightDownVector RightDownVectorBar RightTee RightTeeArrow RightTeeVector RightTriangle RightTriangleBar RightTriangleEqual RightUpDownVector RightUpTeeVector RightUpVector RightUpVectorBar RightVector RightVectorBar RiskAchievementImportance RiskReductionImportance RogersTanimotoDissimilarity Root RootApproximant RootIntervals RootLocusPlot RootMeanSquare RootOfUnityQ RootReduce Roots RootSum Rotate RotateLabel RotateLeft RotateRight RotationAction RotationBox RotationBoxOptions RotationMatrix RotationTransform Round RoundImplies RoundingRadius Row RowAlignments RowBackgrounds RowBox RowHeights RowLines RowMinHeight RowReduce RowsEqual RowSpacings RSolve RudvalisGroupRu Rule RuleCondition RuleDelayed RuleForm RulerUnits Run RunScheduledTask RunThrough RuntimeAttributes RuntimeOptions RussellRaoDissimilarity SameQ SameTest SampleDepth SampledSoundFunction SampledSoundList SampleRate SamplingPeriod SARIMAProcess SARMAProcess SatisfiabilityCount SatisfiabilityInstances SatisfiableQ Saturday Save Saveable SaveAutoDelete SaveDefinitions SawtoothWave Scale Scaled ScaleDivisions ScaledMousePosition ScaleOrigin ScalePadding ScaleRanges ScaleRangeStyle ScalingFunctions ScalingMatrix ScalingTransform Scan ScheduledTaskActiveQ ScheduledTaskData ScheduledTaskObject ScheduledTasks SchurDecomposition ScientificForm ScreenRectangle ScreenStyleEnvironment ScriptBaselineShifts ScriptLevel ScriptMinSize ScriptRules ScriptSizeMultipliers Scrollbars ScrollingOptions ScrollPosition Sec Sech SechDistribution SectionGrouping SectorChart SectorChart3D SectorOrigin SectorSpacing SeedRandom Select Selectable SelectComponents SelectedCells SelectedNotebook Selection SelectionAnimate SelectionCell SelectionCellCreateCell SelectionCellDefaultStyle SelectionCellParentStyle SelectionCreateCell SelectionDebuggerTag SelectionDuplicateCell SelectionEvaluate SelectionEvaluateCreateCell SelectionMove SelectionPlaceholder SelectionSetStyle SelectWithContents SelfLoops SelfLoopStyle SemialgebraicComponentInstances SendMail Sequence SequenceAlignment SequenceForm SequenceHold SequenceLimit Series SeriesCoefficient SeriesData SessionTime Set SetAccuracy SetAlphaChannel SetAttributes Setbacks SetBoxFormNamesPacket SetDelayed SetDirectory SetEnvironment SetEvaluationNotebook SetFileDate SetFileLoadingContext SetNotebookStatusLine SetOptions SetOptionsPacket SetPrecision SetProperty SetSelectedNotebook SetSharedFunction SetSharedVariable SetSpeechParametersPacket SetStreamPosition SetSystemOptions Setter SetterBar SetterBox SetterBoxOptions Setting SetValue Shading Shallow ShannonWavelet ShapiroWilkTest Share Sharpen ShearingMatrix ShearingTransform ShenCastanMatrix Short ShortDownArrow Shortest ShortestMatch ShortestPathFunction ShortLeftArrow ShortRightArrow ShortUpArrow Show ShowAutoStyles ShowCellBracket ShowCellLabel ShowCellTags ShowClosedCellArea ShowContents ShowControls ShowCursorTracker ShowGroupOpenCloseIcon ShowGroupOpener ShowInvisibleCharacters ShowPageBreaks ShowPredictiveInterface ShowSelection ShowShortBoxForm ShowSpecialCharacters ShowStringCharacters ShowSyntaxStyles ShrinkingDelay ShrinkWrapBoundingBox SiegelTheta SiegelTukeyTest Sign Signature SignedRankTest SignificanceLevel SignPadding SignTest SimilarityRules SimpleGraph SimpleGraphQ Simplify Sin Sinc SinghMaddalaDistribution SingleEvaluation SingleLetterItalics SingleLetterStyle SingularValueDecomposition SingularValueList SingularValuePlot SingularValues Sinh SinhIntegral SinIntegral SixJSymbol Skeleton SkeletonTransform SkellamDistribution Skewness SkewNormalDistribution Skip SliceDistribution Slider Slider2D Slider2DBox Slider2DBoxOptions SliderBox SliderBoxOptions SlideView Slot SlotSequence Small SmallCircle Smaller SmithDelayCompensator SmithWatermanSimilarity SmoothDensityHistogram SmoothHistogram SmoothHistogram3D SmoothKernelDistribution SocialMediaData Socket SokalSneathDissimilarity Solve SolveAlways SolveDelayed Sort SortBy Sound SoundAndGraphics SoundNote SoundVolume Sow Space SpaceForm Spacer Spacings Span SpanAdjustments SpanCharacterRounding SpanFromAbove SpanFromBoth SpanFromLeft SpanLineThickness SpanMaxSize SpanMinSize SpanningCharacters SpanSymmetric SparseArray SpatialGraphDistribution Speak SpeakTextPacket SpearmanRankTest SpearmanRho Spectrogram SpectrogramArray Specularity SpellingCorrection SpellingDictionaries SpellingDictionariesPath SpellingOptions SpellingSuggestionsPacket Sphere SphereBox SphericalBesselJ SphericalBesselY SphericalHankelH1 SphericalHankelH2 SphericalHarmonicY SphericalPlot3D SphericalRegion SpheroidalEigenvalue SpheroidalJoiningFactor SpheroidalPS SpheroidalPSPrime SpheroidalQS SpheroidalQSPrime SpheroidalRadialFactor SpheroidalS1 SpheroidalS1Prime SpheroidalS2 SpheroidalS2Prime Splice SplicedDistribution SplineClosed SplineDegree SplineKnots SplineWeights Split SplitBy SpokenString Sqrt SqrtBox SqrtBoxOptions Square SquaredEuclideanDistance SquareFreeQ SquareIntersection SquaresR SquareSubset SquareSubsetEqual SquareSuperset SquareSupersetEqual SquareUnion SquareWave StabilityMargins StabilityMarginsStyle StableDistribution Stack StackBegin StackComplete StackInhibit StandardDeviation StandardDeviationFilter StandardForm Standardize StandbyDistribution Star StarGraph StartAsynchronousTask StartingStepSize StartOfLine StartOfString StartScheduledTask StartupSound StateDimensions StateFeedbackGains StateOutputEstimator StateResponse StateSpaceModel StateSpaceRealization StateSpaceTransform StationaryDistribution StationaryWaveletPacketTransform StationaryWaveletTransform StatusArea StatusCentrality StepMonitor StieltjesGamma StirlingS1 StirlingS2 StopAsynchronousTask StopScheduledTask StrataVariables StratonovichProcess StreamColorFunction StreamColorFunctionScaling StreamDensityPlot StreamPlot StreamPoints StreamPosition Streams StreamScale StreamStyle String StringBreak StringByteCount StringCases StringCount StringDrop StringExpression StringForm StringFormat StringFreeQ StringInsert StringJoin StringLength StringMatchQ StringPosition StringQ StringReplace StringReplaceList StringReplacePart StringReverse StringRotateLeft StringRotateRight StringSkeleton StringSplit StringTake StringToStream StringTrim StripBoxes StripOnInput StripWrapperBoxes StrokeForm StructuralImportance StructuredArray StructuredSelection StruveH StruveL Stub StudentTDistribution Style StyleBox StyleBoxAutoDelete StyleBoxOptions StyleData StyleDefinitions StyleForm StyleKeyMapping StyleMenuListing StyleNameDialogSettings StyleNames StylePrint StyleSheetPath Subfactorial Subgraph SubMinus SubPlus SubresultantPolynomialRemainders SubresultantPolynomials Subresultants Subscript SubscriptBox SubscriptBoxOptions Subscripted Subset SubsetEqual Subsets SubStar Subsuperscript SubsuperscriptBox SubsuperscriptBoxOptions Subtract SubtractFrom SubValues Succeeds SucceedsEqual SucceedsSlantEqual SucceedsTilde SuchThat Sum SumConvergence Sunday SuperDagger SuperMinus SuperPlus Superscript SuperscriptBox SuperscriptBoxOptions Superset SupersetEqual SuperStar Surd SurdForm SurfaceColor SurfaceGraphics SurvivalDistribution SurvivalFunction SurvivalModel SurvivalModelFit SuspendPacket SuzukiDistribution SuzukiGroupSuz SwatchLegend Switch Symbol SymbolName SymletWavelet Symmetric SymmetricGroup SymmetricMatrixQ SymmetricPolynomial SymmetricReduction Symmetrize SymmetrizedArray SymmetrizedArrayRules SymmetrizedDependentComponents SymmetrizedIndependentComponents SymmetrizedReplacePart SynchronousInitialization SynchronousUpdating Syntax SyntaxForm SyntaxInformation SyntaxLength SyntaxPacket SyntaxQ SystemDialogInput SystemException SystemHelpPath SystemInformation SystemInformationData SystemOpen SystemOptions SystemsModelDelay SystemsModelDelayApproximate SystemsModelDelete SystemsModelDimensions SystemsModelExtract SystemsModelFeedbackConnect SystemsModelLabels SystemsModelOrder SystemsModelParallelConnect SystemsModelSeriesConnect SystemsModelStateFeedbackConnect SystemStub Tab TabFilling Table TableAlignments TableDepth TableDirections TableForm TableHeadings TableSpacing TableView TableViewBox TabSpacings TabView TabViewBox TabViewBoxOptions TagBox TagBoxNote TagBoxOptions TaggingRules TagSet TagSetDelayed TagStyle TagUnset Take TakeWhile Tally Tan Tanh TargetFunctions TargetUnits TautologyQ TelegraphProcess TemplateBox TemplateBoxOptions TemplateSlotSequence TemporalData Temporary TemporaryVariable TensorContract TensorDimensions TensorExpand TensorProduct TensorQ TensorRank TensorReduce TensorSymmetry TensorTranspose TensorWedge Tetrahedron TetrahedronBox TetrahedronBoxOptions TeXForm TeXSave Text Text3DBox Text3DBoxOptions TextAlignment TextBand TextBoundingBox TextBox TextCell TextClipboardType TextData TextForm TextJustification TextLine TextPacket TextParagraph TextRecognize TextRendering TextStyle Texture TextureCoordinateFunction TextureCoordinateScaling Therefore ThermometerGauge Thick Thickness Thin Thinning ThisLink ThompsonGroupTh Thread ThreeJSymbol Threshold Through Throw Thumbnail Thursday Ticks TicksStyle Tilde TildeEqual TildeFullEqual TildeTilde TimeConstrained TimeConstraint Times TimesBy TimeSeriesForecast TimeSeriesInvertibility TimeUsed TimeValue TimeZone Timing Tiny TitleGrouping TitsGroupT ToBoxes ToCharacterCode ToColor ToContinuousTimeModel ToDate ToDiscreteTimeModel ToeplitzMatrix ToExpression ToFileName Together Toggle ToggleFalse Toggler TogglerBar TogglerBox TogglerBoxOptions ToHeldExpression ToInvertibleTimeSeries TokenWords Tolerance ToLowerCase ToNumberField TooBig Tooltip TooltipBox TooltipBoxOptions TooltipDelay TooltipStyle Top TopHatTransform TopologicalSort ToRadicals ToRules ToString Total TotalHeight TotalVariationFilter TotalWidth TouchscreenAutoZoom TouchscreenControlPlacement ToUpperCase Tr Trace TraceAbove TraceAction TraceBackward TraceDepth TraceDialog TraceForward TraceInternal TraceLevel TraceOff TraceOn TraceOriginal TracePrint TraceScan TrackedSymbols TradingChart TraditionalForm TraditionalFunctionNotation TraditionalNotation TraditionalOrder TransferFunctionCancel TransferFunctionExpand TransferFunctionFactor TransferFunctionModel TransferFunctionPoles TransferFunctionTransform TransferFunctionZeros TransformationFunction TransformationFunctions TransformationMatrix TransformedDistribution TransformedField Translate TranslationTransform TransparentColor Transpose TreeForm TreeGraph TreeGraphQ TreePlot TrendStyle TriangleWave TriangularDistribution Trig TrigExpand TrigFactor TrigFactorList Trigger TrigReduce TrigToExp TrimmedMean True TrueQ TruncatedDistribution TsallisQExponentialDistribution TsallisQGaussianDistribution TTest Tube TubeBezierCurveBox TubeBezierCurveBoxOptions TubeBox TubeBSplineCurveBox TubeBSplineCurveBoxOptions Tuesday TukeyLambdaDistribution TukeyWindow Tuples TuranGraph TuringMachine Transparent UnateQ Uncompress Undefined UnderBar Underflow Underlined Underoverscript UnderoverscriptBox UnderoverscriptBoxOptions Underscript UnderscriptBox UnderscriptBoxOptions UndirectedEdge UndirectedGraph UndirectedGraphQ UndocumentedTestFEParserPacket UndocumentedTestGetSelectionPacket Unequal Unevaluated UniformDistribution UniformGraphDistribution UniformSumDistribution Uninstall Union UnionPlus Unique UnitBox UnitConvert UnitDimensions Unitize UnitRootTest UnitSimplify UnitStep UnitTriangle UnitVector Unprotect UnsameQ UnsavedVariables Unset UnsetShared UntrackedVariables Up UpArrow UpArrowBar UpArrowDownArrow Update UpdateDynamicObjects UpdateDynamicObjectsSynchronous UpdateInterval UpDownArrow UpEquilibrium UpperCaseQ UpperLeftArrow UpperRightArrow UpperTriangularize Upsample UpSet UpSetDelayed UpTee UpTeeArrow UpValues URL URLFetch URLFetchAsynchronous URLSave URLSaveAsynchronous UseGraphicsRange Using UsingFrontEnd V2Get ValidationLength Value ValueBox ValueBoxOptions ValueForm ValueQ ValuesData Variables Variance VarianceEquivalenceTest VarianceEstimatorFunction VarianceGammaDistribution VarianceTest VectorAngle VectorColorFunction VectorColorFunctionScaling VectorDensityPlot VectorGlyphData VectorPlot VectorPlot3D VectorPoints VectorQ Vectors VectorScale VectorStyle Vee Verbatim Verbose VerboseConvertToPostScriptPacket VerifyConvergence VerifySolutions VerifyTestAssumptions Version VersionNumber VertexAdd VertexCapacity VertexColors VertexComponent VertexConnectivity VertexCoordinateRules VertexCoordinates VertexCorrelationSimilarity VertexCosineSimilarity VertexCount VertexCoverQ VertexDataCoordinates VertexDegree VertexDelete VertexDiceSimilarity VertexEccentricity VertexInComponent VertexInDegree VertexIndex VertexJaccardSimilarity VertexLabeling VertexLabels VertexLabelStyle VertexList VertexNormals VertexOutComponent VertexOutDegree VertexQ VertexRenderingFunction VertexReplace VertexShape VertexShapeFunction VertexSize VertexStyle VertexTextureCoordinates VertexWeight Vertical VerticalBar VerticalForm VerticalGauge VerticalSeparator VerticalSlider VerticalTilde ViewAngle ViewCenter ViewMatrix ViewPoint ViewPointSelectorSettings ViewPort ViewRange ViewVector ViewVertical VirtualGroupData Visible VisibleCell VoigtDistribution VonMisesDistribution WaitAll WaitAsynchronousTask WaitNext WaitUntil WakebyDistribution WalleniusHypergeometricDistribution WaringYuleDistribution WatershedComponents WatsonUSquareTest WattsStrogatzGraphDistribution WaveletBestBasis WaveletFilterCoefficients WaveletImagePlot WaveletListPlot WaveletMapIndexed WaveletMatrixPlot WaveletPhi WaveletPsi WaveletScale WaveletScalogram WaveletThreshold WeaklyConnectedComponents WeaklyConnectedGraphQ WeakStationarity WeatherData WeberE Wedge Wednesday WeibullDistribution WeierstrassHalfPeriods WeierstrassInvariants WeierstrassP WeierstrassPPrime WeierstrassSigma WeierstrassZeta WeightedAdjacencyGraph WeightedAdjacencyMatrix WeightedData WeightedGraphQ Weights WelchWindow WheelGraph WhenEvent Which While White Whitespace WhitespaceCharacter WhittakerM WhittakerW WienerFilter WienerProcess WignerD WignerSemicircleDistribution WilksW WilksWTest WindowClickSelect WindowElements WindowFloating WindowFrame WindowFrameElements WindowMargins WindowMovable WindowOpacity WindowSelected WindowSize WindowStatusArea WindowTitle WindowToolbars WindowWidth With WolframAlpha WolframAlphaDate WolframAlphaQuantity WolframAlphaResult Word WordBoundary WordCharacter WordData WordSearch WordSeparators WorkingPrecision Write WriteString Wronskian XMLElement XMLObject Xnor Xor Yellow YuleDissimilarity ZernikeR ZeroSymmetric ZeroTest ZeroWidthTimes Zeta ZetaZero ZipfDistribution ZTest ZTransform $Aborted $ActivationGroupID $ActivationKey $ActivationUserRegistered $AddOnsDirectory $AssertFunction $Assumptions $AsynchronousTask $BaseDirectory $BatchInput $BatchOutput $BoxForms $ByteOrdering $Canceled $CharacterEncoding $CharacterEncodings $CommandLine $CompilationTarget $ConditionHold $ConfiguredKernels $Context $ContextPath $ControlActiveSetting $CreationDate $CurrentLink $DateStringFormat $DefaultFont $DefaultFrontEnd $DefaultImagingDevice $DefaultPath $Display $DisplayFunction $DistributedContexts $DynamicEvaluation $Echo $Epilog $ExportFormats $Failed $FinancialDataSource $FormatType $FrontEnd $FrontEndSession $GeoLocation $HistoryLength $HomeDirectory $HTTPCookies $IgnoreEOF $ImagingDevices $ImportFormats $InitialDirectory $Input $InputFileName $InputStreamMethods $Inspector $InstallationDate $InstallationDirectory $InterfaceEnvironment $IterationLimit $KernelCount $KernelID $Language $LaunchDirectory $LibraryPath $LicenseExpirationDate $LicenseID $LicenseProcesses $LicenseServer $LicenseSubprocesses $LicenseType $Line $Linked $LinkSupported $LoadedFiles $MachineAddresses $MachineDomain $MachineDomains $MachineEpsilon $MachineID $MachineName $MachinePrecision $MachineType $MaxExtraPrecision $MaxLicenseProcesses $MaxLicenseSubprocesses $MaxMachineNumber $MaxNumber $MaxPiecewiseCases $MaxPrecision $MaxRootDegree $MessageGroups $MessageList $MessagePrePrint $Messages $MinMachineNumber $MinNumber $MinorReleaseNumber $MinPrecision $ModuleNumber $NetworkLicense $NewMessage $NewSymbol $Notebooks $NumberMarks $Off $OperatingSystem $Output $OutputForms $OutputSizeLimit $OutputStreamMethods $Packages $ParentLink $ParentProcessID $PasswordFile $PatchLevelID $Path $PathnameSeparator $PerformanceGoal $PipeSupported $Post $Pre $PreferencesDirectory $PrePrint $PreRead $PrintForms $PrintLiteral $ProcessID $ProcessorCount $ProcessorType $ProductInformation $ProgramName $RandomState $RecursionLimit $ReleaseNumber $RootDirectory $ScheduledTask $ScriptCommandLine $SessionID $SetParentLink $SharedFunctions $SharedVariables $SoundDisplay $SoundDisplayFunction $SuppressInputFormHeads $SynchronousEvaluation $SyntaxHandler $System $SystemCharacterEncoding $SystemID $SystemWordLength $TemporaryDirectory $TemporaryPrefix $TextStyle $TimedOut $TimeUnit $TimeZone $TopDirectory $TraceOff $TraceOn $TracePattern $TracePostAction $TracePreAction $Urgent $UserAddOnsDirectory $UserBaseDirectory $UserDocumentsDirectory $UserName $Version $VersionNumber",
-c:[{cN:"comment",b:/\(\*/,e:/\*\)/},e.ASM,e.QSM,e.CNM,{cN:"list",b:/\{/,e:/\}/,i:/:/}]}});hljs.registerLanguage("fsharp",function(e){var t={b:"<",e:">",c:[e.inherit(e.TM,{b:/'[a-zA-Z0-9_]+/})]};return{aliases:["fs"],k:"yield! return! let! do!abstract and as assert base begin class default delegate do done downcast downto elif else end exception extern false finally for fun function global if in inherit inline interface internal lazy let match member module mutable namespace new null of open or override private public rec return sig static struct then to true try type upcast use val void when while with yield",c:[{cN:"string",b:'@"',e:'"',c:[{b:'""'}]},{cN:"string",b:'"""',e:'"""'},e.C("\\(\\*","\\*\\)"),{cN:"class",bK:"type",e:"\\(|=|$",eE:!0,c:[e.UTM,t]},{cN:"annotation",b:"\\[<",e:">\\]",r:10},{cN:"attribute",b:"\\B('[A-Za-z])\\b",c:[e.BE]},e.CLCM,e.inherit(e.QSM,{i:null}),e.CNM]}});hljs.registerLanguage("verilog",function(e){return{aliases:["v"],cI:!0,k:{keyword:"always and assign begin buf bufif0 bufif1 case casex casez cmos deassign default defparam disable edge else end endcase endfunction endmodule endprimitive endspecify endtable endtask event for force forever fork function if ifnone initial inout input join macromodule module nand negedge nmos nor not notif0 notif1 or output parameter pmos posedge primitive pulldown pullup rcmos release repeat rnmos rpmos rtran rtranif0 rtranif1 specify specparam table task timescale tran tranif0 tranif1 wait while xnor xor",typename:"highz0 highz1 integer large medium pull0 pull1 real realtime reg scalared signed small strong0 strong1 supply0 supply0 supply1 supply1 time tri tri0 tri1 triand trior trireg vectored wand weak0 weak1 wire wor"},c:[e.CBCM,e.CLCM,e.QSM,{cN:"number",b:"\\b(\\d+'(b|h|o|d|B|H|O|D))?[0-9xzXZ]+",c:[e.BE],r:0},{cN:"typename",b:"\\.\\w+",r:0},{cN:"value",b:"#\\((?!parameter).+\\)"},{cN:"keyword",b:"\\+|-|\\*|/|%|<|>|=|#|`|\\!|&|\\||@|:|\\^|~|\\{|\\}",r:0}]}});hljs.registerLanguage("dos",function(e){var r=e.C(/@?rem\b/,/$/,{r:10}),t={cN:"label",b:"^\\s*[A-Za-z._?][A-Za-z0-9_$#@~.?]*(:|\\s+label)",r:0};return{aliases:["bat","cmd"],cI:!0,k:{flow:"if else goto for in do call exit not exist errorlevel defined",operator:"equ neq lss leq gtr geq",keyword:"shift cd dir echo setlocal endlocal set pause copy",stream:"prn nul lpt3 lpt2 lpt1 con com4 com3 com2 com1 aux",winutils:"ping net ipconfig taskkill xcopy ren del",built_in:"append assoc at attrib break cacls cd chcp chdir chkdsk chkntfs cls cmd color comp compact convert date dir diskcomp diskcopy doskey erase fs find findstr format ftype graftabl help keyb label md mkdir mode more move path pause print popd pushd promt rd recover rem rename replace restore rmdir shiftsort start subst time title tree type ver verify vol"},c:[{cN:"envvar",b:/%%[^ ]|%[^ ]+?%|![^ ]+?!/},{cN:"function",b:t.b,e:"goto:eof",c:[e.inherit(e.TM,{b:"([_a-zA-Z]\\w*\\.)*([_a-zA-Z]\\w*:)?[_a-zA-Z]\\w*"}),r]},{cN:"number",b:"\\b\\d+",r:0},r]}});hljs.registerLanguage("gherkin",function(e){return{aliases:["feature"],k:"Feature Background Ability Business Need Scenario Scenarios Scenario Outline Scenario Template Examples Given And Then But When",c:[{cN:"keyword",b:"\\*"},e.C("@[^@\r\n	 ]+","$"),{cN:"string",b:"\\|",e:"\\$"},{cN:"variable",b:"<",e:">"},e.HCM,{cN:"string",b:'"""',e:'"""'},e.QSM]}});hljs.registerLanguage("xml",function(t){var e="[A-Za-z0-9\\._:-]+",s={b:/<\?(php)?(?!\w)/,e:/\?>/,sL:"php",subLanguageMode:"continuous"},c={eW:!0,i:/</,r:0,c:[s,{cN:"attribute",b:e,r:0},{b:"=",r:0,c:[{cN:"value",c:[s],v:[{b:/"/,e:/"/},{b:/'/,e:/'/},{b:/[^\s\/>]+/}]}]}]};return{aliases:["html","xhtml","rss","atom","xsl","plist"],cI:!0,c:[{cN:"doctype",b:"<!DOCTYPE",e:">",r:10,c:[{b:"\\[",e:"\\]"}]},t.C("<!--","-->",{r:10}),{cN:"cdata",b:"<\\!\\[CDATA\\[",e:"\\]\\]>",r:10},{cN:"tag",b:"<style(?=\\s|>|$)",e:">",k:{title:"style"},c:[c],starts:{e:"</style>",rE:!0,sL:"css"}},{cN:"tag",b:"<script(?=\\s|>|$)",e:">",k:{title:"script"},c:[c],starts:{e:"</script>",rE:!0,sL:""}},s,{cN:"pi",b:/<\?\w+/,e:/\?>/,r:10},{cN:"tag",b:"</?",e:"/?>",c:[{cN:"title",b:/[^ \/><\n\t]+/,r:0},c]}]}});hljs.registerLanguage("autohotkey",function(e){var r={cN:"escape",b:"`[\\s\\S]"},c=e.C(";","$",{r:0}),n=[{cN:"built_in",b:"A_[a-zA-Z0-9]+"},{cN:"built_in",bK:"ComSpec Clipboard ClipboardAll ErrorLevel"}];return{cI:!0,k:{keyword:"Break Continue Else Gosub If Loop Return While",literal:"A true false NOT AND OR"},c:n.concat([r,e.inherit(e.QSM,{c:[r]}),c,{cN:"number",b:e.NR,r:0},{cN:"var_expand",b:"%",e:"%",i:"\\n",c:[r]},{cN:"label",c:[r],v:[{b:'^[^\\n";]+::(?!=)'},{b:'^[^\\n";]+:(?!=)',r:0}]},{b:",\\s*,",r:10}])}});hljs.registerLanguage("r",function(e){var r="([a-zA-Z]|\\.[a-zA-Z.])[a-zA-Z0-9._]*";return{c:[e.HCM,{b:r,l:r,k:{keyword:"function if in break next repeat else for return switch while try tryCatch stop warning require library attach detach source setMethod setGeneric setGroupGeneric setClass ...",literal:"NULL NA TRUE FALSE T F Inf NaN NA_integer_|10 NA_real_|10 NA_character_|10 NA_complex_|10"},r:0},{cN:"number",b:"0[xX][0-9a-fA-F]+[Li]?\\b",r:0},{cN:"number",b:"\\d+(?:[eE][+\\-]?\\d*)?L\\b",r:0},{cN:"number",b:"\\d+\\.(?!\\d)(?:i\\b)?",r:0},{cN:"number",b:"\\d+(?:\\.\\d*)?(?:[eE][+\\-]?\\d*)?i?\\b",r:0},{cN:"number",b:"\\.\\d+(?:[eE][+\\-]?\\d*)?i?\\b",r:0},{b:"`",e:"`",r:0},{cN:"string",c:[e.BE],v:[{b:'"',e:'"'},{b:"'",e:"'"}]}]}});hljs.registerLanguage("cs",function(e){var r="abstract as base bool break byte case catch char checked const continue decimal dynamic default delegate do double else enum event explicit extern false finally fixed float for foreach goto if implicit in int interface internal is lock long null when object operator out override params private protected public readonly ref sbyte sealed short sizeof stackalloc static string struct switch this true try typeof uint ulong unchecked unsafe ushort using virtual volatile void while async protected public private internal ascending descending from get group into join let orderby partial select set value var where yield",t=e.IR+"(<"+e.IR+">)?";return{aliases:["csharp"],k:r,i:/::/,c:[e.C("///","$",{rB:!0,c:[{cN:"xmlDocTag",v:[{b:"///",r:0},{b:"<!--|-->"},{b:"</?",e:">"}]}]}),e.CLCM,e.CBCM,{cN:"preprocessor",b:"#",e:"$",k:"if else elif endif define undef warning error line region endregion pragma checksum"},{cN:"string",b:'@"',e:'"',c:[{b:'""'}]},e.ASM,e.QSM,e.CNM,{bK:"class namespace interface",e:/[{;=]/,i:/[^\s:]/,c:[e.TM,e.CLCM,e.CBCM]},{bK:"new return throw await",r:0},{cN:"function",b:"("+t+"\\s+)+"+e.IR+"\\s*\\(",rB:!0,e:/[{;=]/,eE:!0,k:r,c:[{b:e.IR+"\\s*\\(",rB:!0,c:[e.TM],r:0},{cN:"params",b:/\(/,e:/\)/,k:r,r:0,c:[e.ASM,e.QSM,e.CNM,e.CBCM]},e.CLCM,e.CBCM]}]}});hljs.registerLanguage("nsis",function(e){var t={cN:"symbol",b:"\\$(ADMINTOOLS|APPDATA|CDBURN_AREA|CMDLINE|COMMONFILES32|COMMONFILES64|COMMONFILES|COOKIES|DESKTOP|DOCUMENTS|EXEDIR|EXEFILE|EXEPATH|FAVORITES|FONTS|HISTORY|HWNDPARENT|INSTDIR|INTERNET_CACHE|LANGUAGE|LOCALAPPDATA|MUSIC|NETHOOD|OUTDIR|PICTURES|PLUGINSDIR|PRINTHOOD|PROFILE|PROGRAMFILES32|PROGRAMFILES64|PROGRAMFILES|QUICKLAUNCH|RECENT|RESOURCES_LOCALIZED|RESOURCES|SENDTO|SMPROGRAMS|SMSTARTUP|STARTMENU|SYSDIR|TEMP|TEMPLATES|VIDEOS|WINDIR)"},n={cN:"constant",b:"\\$+{[a-zA-Z0-9_]+}"},i={cN:"variable",b:"\\$+[a-zA-Z0-9_]+",i:"\\(\\){}"},r={cN:"constant",b:"\\$+\\([a-zA-Z0-9_]+\\)"},o={cN:"params",b:"(ARCHIVE|FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_NORMAL|FILE_ATTRIBUTE_OFFLINE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_TEMPORARY|HKCR|HKCU|HKDD|HKEY_CLASSES_ROOT|HKEY_CURRENT_CONFIG|HKEY_CURRENT_USER|HKEY_DYN_DATA|HKEY_LOCAL_MACHINE|HKEY_PERFORMANCE_DATA|HKEY_USERS|HKLM|HKPD|HKU|IDABORT|IDCANCEL|IDIGNORE|IDNO|IDOK|IDRETRY|IDYES|MB_ABORTRETRYIGNORE|MB_DEFBUTTON1|MB_DEFBUTTON2|MB_DEFBUTTON3|MB_DEFBUTTON4|MB_ICONEXCLAMATION|MB_ICONINFORMATION|MB_ICONQUESTION|MB_ICONSTOP|MB_OK|MB_OKCANCEL|MB_RETRYCANCEL|MB_RIGHT|MB_RTLREADING|MB_SETFOREGROUND|MB_TOPMOST|MB_USERICON|MB_YESNO|NORMAL|OFFLINE|READONLY|SHCTX|SHELL_CONTEXT|SYSTEM|TEMPORARY)"},l={cN:"constant",b:"\\!(addincludedir|addplugindir|appendfile|cd|define|delfile|echo|else|endif|error|execute|finalize|getdllversionsystem|ifdef|ifmacrodef|ifmacrondef|ifndef|if|include|insertmacro|macroend|macro|makensis|packhdr|searchparse|searchreplace|tempfile|undef|verbose|warning)"};return{cI:!1,k:{keyword:"Abort AddBrandingImage AddSize AllowRootDirInstall AllowSkipFiles AutoCloseWindow BGFont BGGradient BrandingText BringToFront Call CallInstDLL Caption ChangeUI CheckBitmap ClearErrors CompletedText ComponentText CopyFiles CRCCheck CreateDirectory CreateFont CreateShortCut Delete DeleteINISec DeleteINIStr DeleteRegKey DeleteRegValue DetailPrint DetailsButtonText DirText DirVar DirVerify EnableWindow EnumRegKey EnumRegValue Exch Exec ExecShell ExecWait ExpandEnvStrings File FileBufSize FileClose FileErrorText FileOpen FileRead FileReadByte FileReadUTF16LE FileReadWord FileSeek FileWrite FileWriteByte FileWriteUTF16LE FileWriteWord FindClose FindFirst FindNext FindWindow FlushINI FunctionEnd GetCurInstType GetCurrentAddress GetDlgItem GetDLLVersion GetDLLVersionLocal GetErrorLevel GetFileTime GetFileTimeLocal GetFullPathName GetFunctionAddress GetInstDirError GetLabelAddress GetTempFileName Goto HideWindow Icon IfAbort IfErrors IfFileExists IfRebootFlag IfSilent InitPluginsDir InstallButtonText InstallColors InstallDir InstallDirRegKey InstProgressFlags InstType InstTypeGetText InstTypeSetText IntCmp IntCmpU IntFmt IntOp IsWindow LangString LicenseBkColor LicenseData LicenseForceSelection LicenseLangString LicenseText LoadLanguageFile LockWindow LogSet LogText ManifestDPIAware ManifestSupportedOS MessageBox MiscButtonText Name Nop OutFile Page PageCallbacks PageExEnd Pop Push Quit ReadEnvStr ReadINIStr ReadRegDWORD ReadRegStr Reboot RegDLL Rename RequestExecutionLevel ReserveFile Return RMDir SearchPath SectionEnd SectionGetFlags SectionGetInstTypes SectionGetSize SectionGetText SectionGroupEnd SectionIn SectionSetFlags SectionSetInstTypes SectionSetSize SectionSetText SendMessage SetAutoClose SetBrandingImage SetCompress SetCompressor SetCompressorDictSize SetCtlColors SetCurInstType SetDatablockOptimize SetDateSave SetDetailsPrint SetDetailsView SetErrorLevel SetErrors SetFileAttributes SetFont SetOutPath SetOverwrite SetPluginUnload SetRebootFlag SetRegView SetShellVarContext SetSilent ShowInstDetails ShowUninstDetails ShowWindow SilentInstall SilentUnInstall Sleep SpaceTexts StrCmp StrCmpS StrCpy StrLen SubCaption SubSectionEnd Unicode UninstallButtonText UninstallCaption UninstallIcon UninstallSubCaption UninstallText UninstPage UnRegDLL Var VIAddVersionKey VIFileVersion VIProductVersion WindowIcon WriteINIStr WriteRegBin WriteRegDWORD WriteRegExpandStr WriteRegStr WriteUninstaller XPStyle",literal:"admin all auto both colored current false force hide highest lastused leave listonly none normal notset off on open print show silent silentlog smooth textonly true user "},c:[e.HCM,e.CBCM,{cN:"string",b:'"',e:'"',i:"\\n",c:[{cN:"symbol",b:"\\$(\\\\(n|r|t)|\\$)"},t,n,i,r]},e.C(";","$",{r:0}),{cN:"function",bK:"Function PageEx Section SectionGroup SubSection",e:"$"},l,n,i,r,o,e.NM,{cN:"literal",b:e.IR+"::"+e.IR}]}});hljs.registerLanguage("less",function(e){var r="[\\w-]+",t="("+r+"|@{"+r+"})",a=[],c=[],n=function(e){return{cN:"string",b:"~?"+e+".*?"+e}},i=function(e,r,t){return{cN:e,b:r,r:t}},s=function(r,t,a){return e.inherit({cN:r,b:t+"\\(",e:"\\(",rB:!0,eE:!0,r:0},a)},b={b:"\\(",e:"\\)",c:c,r:0};c.push(e.CLCM,e.CBCM,n("'"),n('"'),e.CSSNM,i("hexcolor","#[0-9A-Fa-f]+\\b"),s("function","(url|data-uri)",{starts:{cN:"string",e:"[\\)\\n]",eE:!0}}),s("function",r),b,i("variable","@@?"+r,10),i("variable","@{"+r+"}"),i("built_in","~?`[^`]*?`"),{cN:"attribute",b:r+"\\s*:",e:":",rB:!0,eE:!0});var o=c.concat({b:"{",e:"}",c:a}),u={bK:"when",eW:!0,c:[{bK:"and not"}].concat(c)},C={cN:"attribute",b:t,e:":",eE:!0,c:[e.CLCM,e.CBCM],i:/\S/,starts:{e:"[;}]",rE:!0,c:c,i:"[<=$]"}},l={cN:"at_rule",b:"@(import|media|charset|font-face|(-[a-z]+-)?keyframes|supports|document|namespace|page|viewport|host)\\b",starts:{e:"[;{}]",rE:!0,c:c,r:0}},d={cN:"variable",v:[{b:"@"+r+"\\s*:",r:15},{b:"@"+r}],starts:{e:"[;}]",rE:!0,c:o}},p={v:[{b:"[\\.#:&\\[]",e:"[;{}]"},{b:t+"[^;]*{",e:"{"}],rB:!0,rE:!0,i:"[<='$\"]",c:[e.CLCM,e.CBCM,u,i("keyword","all\\b"),i("variable","@{"+r+"}"),i("tag",t+"%?",0),i("id","#"+t),i("class","\\."+t,0),i("keyword","&",0),s("pseudo",":not"),s("keyword",":extend"),i("pseudo","::?"+t),{cN:"attr_selector",b:"\\[",e:"\\]"},{b:"\\(",e:"\\)",c:o},{b:"!important"}]};return a.push(e.CLCM,e.CBCM,l,d,p,C),{cI:!0,i:"[=>'/<($\"]",c:a}});hljs.registerLanguage("pf",function(t){var o={cN:"variable",b:/\$[\w\d#@][\w\d_]*/},e={cN:"variable",b:/</,e:/>/};return{aliases:["pf.conf"],l:/[a-z0-9_<>-]+/,k:{built_in:"block match pass load anchor|5 antispoof|10 set table",keyword:"in out log quick on rdomain inet inet6 proto from port os to routeallow-opts divert-packet divert-reply divert-to flags group icmp-typeicmp6-type label once probability recieved-on rtable prio queuetos tag tagged user keep fragment for os dropaf-to|10 binat-to|10 nat-to|10 rdr-to|10 bitmask least-stats random round-robinsource-hash static-portdup-to reply-to route-toparent bandwidth default min max qlimitblock-policy debug fingerprints hostid limit loginterface optimizationreassemble ruleset-optimization basic none profile skip state-defaultsstate-policy timeoutconst counters persistno modulate synproxy state|5 floating if-bound no-sync pflow|10 sloppysource-track global rule max-src-nodes max-src-states max-src-connmax-src-conn-rate overload flushscrub|5 max-mss min-ttl no-df|10 random-id",literal:"all any no-route self urpf-failed egress|5 unknown"},c:[t.HCM,t.NM,t.QSM,o,e]}});hljs.registerLanguage("lasso",function(e){var r="[a-zA-Z_][a-zA-Z0-9_.]*",a="<\\?(lasso(script)?|=)",t="\\]|\\?>",s={literal:"true false none minimal full all void and or not bw nbw ew new cn ncn lt lte gt gte eq neq rx nrx ft",built_in:"array date decimal duration integer map pair string tag xml null boolean bytes keyword list locale queue set stack staticarray local var variable global data self inherited",keyword:"error_code error_msg error_pop error_push error_reset cache database_names database_schemanames database_tablenames define_tag define_type email_batch encode_set html_comment handle handle_error header if inline iterate ljax_target link link_currentaction link_currentgroup link_currentrecord link_detail link_firstgroup link_firstrecord link_lastgroup link_lastrecord link_nextgroup link_nextrecord link_prevgroup link_prevrecord log loop namespace_using output_none portal private protect records referer referrer repeating resultset rows search_args search_arguments select sort_args sort_arguments thread_atomic value_list while abort case else if_empty if_false if_null if_true loop_abort loop_continue loop_count params params_up return return_value run_children soap_definetag soap_lastrequest soap_lastresponse tag_name ascending average by define descending do equals frozen group handle_failure import in into join let match max min on order parent protected provide public require returnhome skip split_thread sum take thread to trait type where with yield yieldhome"},n=e.C("<!--","-->",{r:0}),o={cN:"preprocessor",b:"\\[noprocess\\]",starts:{cN:"markup",e:"\\[/noprocess\\]",rE:!0,c:[n]}},i={cN:"preprocessor",b:"\\[/noprocess|"+a},l={cN:"variable",b:"'"+r+"'"},c=[e.CLCM,{cN:"javadoc",b:"/\\*\\*!",e:"\\*/",c:[e.PWM]},e.CBCM,e.inherit(e.CNM,{b:e.CNR+"|(-?infinity|nan)\\b"}),e.inherit(e.ASM,{i:null}),e.inherit(e.QSM,{i:null}),{cN:"string",b:"`",e:"`"},{cN:"variable",v:[{b:"[#$]"+r},{b:"#",e:"\\d+",i:"\\W"}]},{cN:"tag",b:"::\\s*",e:r,i:"\\W"},{cN:"attribute",v:[{b:"-"+e.UIR,r:0},{b:"(\\.\\.\\.)"}]},{cN:"subst",v:[{b:"->\\s*",c:[l]},{b:":=|/(?!\\w)=?|[-+*%=<>&|!?\\\\]+",r:0}]},{cN:"built_in",b:"\\.\\.?\\s*",r:0,c:[l]},{cN:"class",bK:"define",rE:!0,e:"\\(|=>",c:[e.inherit(e.TM,{b:e.UIR+"(=(?!>))?"})]}];return{aliases:["ls","lassoscript"],cI:!0,l:r+"|&[lg]t;",k:s,c:[{cN:"preprocessor",b:t,r:0,starts:{cN:"markup",e:"\\[|"+a,rE:!0,r:0,c:[n]}},o,i,{cN:"preprocessor",b:"\\[no_square_brackets",starts:{e:"\\[/no_square_brackets\\]",l:r+"|&[lg]t;",k:s,c:[{cN:"preprocessor",b:t,r:0,starts:{cN:"markup",e:"\\[noprocess\\]|"+a,rE:!0,c:[n]}},o,i].concat(c)}},{cN:"preprocessor",b:"\\[",r:0},{cN:"shebang",b:"^#!.+lasso9\\b",r:10}].concat(c)}});hljs.registerLanguage("prolog",function(c){var r={cN:"atom",b:/[a-z][A-Za-z0-9_]*/,r:0},b={cN:"name",v:[{b:/[A-Z][a-zA-Z0-9_]*/},{b:/_[A-Za-z0-9_]*/}],r:0},a={b:/\(/,e:/\)/,r:0},e={b:/\[/,e:/\]/},n={cN:"comment",b:/%/,e:/$/,c:[c.PWM]},t={cN:"string",b:/`/,e:/`/,c:[c.BE]},g={cN:"string",b:/0\'(\\\'|.)/},N={cN:"string",b:/0\'\\s/},o={b:/:-/},s=[r,b,a,o,e,n,c.CBCM,c.QSM,c.ASM,t,g,N,c.CNM];return a.c=s,e.c=s,{c:s.concat([{b:/\.$/}])}});hljs.registerLanguage("oxygene",function(e){var r="abstract add and array as asc aspect assembly async begin break block by case class concat const copy constructor continue create default delegate desc distinct div do downto dynamic each else empty end ensure enum equals event except exit extension external false final finalize finalizer finally flags for forward from function future global group has if implementation implements implies in index inherited inline interface into invariants is iterator join locked locking loop matching method mod module namespace nested new nil not notify nullable of old on operator or order out override parallel params partial pinned private procedure property protected public queryable raise read readonly record reintroduce remove repeat require result reverse sealed select self sequence set shl shr skip static step soft take then to true try tuple type union unit unsafe until uses using var virtual raises volatile where while with write xor yield await mapped deprecated stdcall cdecl pascal register safecall overload library platform reference packed strict published autoreleasepool selector strong weak unretained",t=e.C("{","}",{r:0}),a=e.C("\\(\\*","\\*\\)",{r:10}),n={cN:"string",b:"'",e:"'",c:[{b:"''"}]},o={cN:"string",b:"(#\\d+)+"},i={cN:"function",bK:"function constructor destructor procedure method",e:"[:;]",k:"function constructor|10 destructor|10 procedure|10 method|10",c:[e.TM,{cN:"params",b:"\\(",e:"\\)",k:r,c:[n,o]},t,a]};return{cI:!0,k:r,i:'("|\\$[G-Zg-z]|\\/\\*|</|=>|->)',c:[t,a,e.CLCM,n,o,e.NM,i,{cN:"class",b:"=\\bclass\\b",e:"end;",k:r,c:[n,o,t,a,e.CLCM,i]}]}});hljs.registerLanguage("applescript",function(e){var t=e.inherit(e.QSM,{i:""}),r={cN:"params",b:"\\(",e:"\\)",c:["self",e.CNM,t]},o=e.C("--","$"),n=e.C("\\(\\*","\\*\\)",{c:["self",o]}),a=[o,n,e.HCM];return{aliases:["osascript"],k:{keyword:"about above after against and around as at back before beginning behind below beneath beside between but by considering contain contains continue copy div does eighth else end equal equals error every exit fifth first for fourth from front get given global if ignoring in into is it its last local me middle mod my ninth not of on onto or over prop property put ref reference repeat returning script second set seventh since sixth some tell tenth that the|0 then third through thru timeout times to transaction try until where while whose with without",constant:"AppleScript false linefeed return pi quote result space tab true",type:"alias application boolean class constant date file integer list number real record string text",command:"activate beep count delay launch log offset read round run say summarize write",property:"character characters contents day frontmost id item length month name paragraph paragraphs rest reverse running time version weekday word words year"},c:[t,e.CNM,{cN:"type",b:"\\bPOSIX file\\b"},{cN:"command",b:"\\b(clipboard info|the clipboard|info for|list (disks|folder)|mount volume|path to|(close|open for) access|(get|set) eof|current date|do shell script|get volume settings|random number|set volume|system attribute|system info|time to GMT|(load|run|store) script|scripting components|ASCII (character|number)|localized string|choose (application|color|file|file name|folder|from list|remote application|URL)|display (alert|dialog))\\b|^\\s*return\\b"},{cN:"constant",b:"\\b(text item delimiters|current application|missing value)\\b"},{cN:"keyword",b:"\\b(apart from|aside from|instead of|out of|greater than|isn't|(doesn't|does not) (equal|come before|come after|contain)|(greater|less) than( or equal)?|(starts?|ends|begins?) with|contained by|comes (before|after)|a (ref|reference))\\b"},{cN:"property",b:"\\b(POSIX path|(date|time) string|quoted form)\\b"},{cN:"function_start",bK:"on",i:"[${=;\\n]",c:[e.UTM,r]}].concat(a),i:"//|->|=>"}});hljs.registerLanguage("makefile",function(e){var a={cN:"variable",b:/\$\(/,e:/\)/,c:[e.BE]};return{aliases:["mk","mak"],c:[e.HCM,{b:/^\w+\s*\W*=/,rB:!0,r:0,starts:{cN:"constant",e:/\s*\W*=/,eE:!0,starts:{e:/$/,r:0,c:[a]}}},{cN:"title",b:/^[\w]+:\s*$/},{cN:"phony",b:/^\.PHONY:/,e:/$/,k:".PHONY",l:/[\.\w]+/},{b:/^\t+/,e:/$/,r:0,c:[e.QSM,a]}]}});hljs.registerLanguage("dust",function(e){var a="if eq ne lt lte gt gte select default math sep";return{aliases:["dst"],cI:!0,sL:"xml",subLanguageMode:"continuous",c:[{cN:"expression",b:"{",e:"}",r:0,c:[{cN:"begin-block",b:"#[a-zA-Z- .]+",k:a},{cN:"string",b:'"',e:'"'},{cN:"end-block",b:"\\/[a-zA-Z- .]+",k:a},{cN:"variable",b:"[a-zA-Z-.]+",k:a,r:0}]}]}});hljs.registerLanguage("clojure-repl",function(e){return{c:[{cN:"prompt",b:/^([\w.-]+|\s*#_)=>/,starts:{e:/$/,sL:"clojure",subLanguageMode:"continuous"}}]}});hljs.registerLanguage("dart",function(e){var t={cN:"subst",b:"\\$\\{",e:"}",k:"true false null this is new super"},r={cN:"string",v:[{b:"r'''",e:"'''"},{b:'r"""',e:'"""'},{b:"r'",e:"'",i:"\\n"},{b:'r"',e:'"',i:"\\n"},{b:"'''",e:"'''",c:[e.BE,t]},{b:'"""',e:'"""',c:[e.BE,t]},{b:"'",e:"'",i:"\\n",c:[e.BE,t]},{b:'"',e:'"',i:"\\n",c:[e.BE,t]}]};t.c=[e.CNM,r];var n={keyword:"assert break case catch class const continue default do else enum extends false final finally for if in is new null rethrow return super switch this throw true try var void while with",literal:"abstract as dynamic export external factory get implements import library operator part set static typedef",built_in:"print Comparable DateTime Duration Function Iterable Iterator List Map Match Null Object Pattern RegExp Set Stopwatch String StringBuffer StringSink Symbol Type Uri bool double int num document window querySelector querySelectorAll Element ElementList"};return{k:n,c:[r,{cN:"dartdoc",b:"/\\*\\*",e:"\\*/",sL:"markdown",subLanguageMode:"continuous"},{cN:"dartdoc",b:"///",e:"$",sL:"markdown",subLanguageMode:"continuous"},e.CLCM,e.CBCM,{cN:"class",bK:"class interface",e:"{",eE:!0,c:[{bK:"extends implements"},e.UTM]},e.CNM,{cN:"annotation",b:"@[A-Za-z]+"},{b:"=>"}]}});
\ No newline at end of file
diff --git a/js/jquery-2.1.1.min.js b/js/jquery-2.1.1.min.js
deleted file mode 100644
index e5ace116..00000000
--- a/js/jquery-2.1.1.min.js
+++ /dev/null
@@ -1,4 +0,0 @@
-/*! jQuery v2.1.1 | (c) 2005, 2014 jQuery Foundation, Inc. | jquery.org/license */
-!function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){var c=[],d=c.slice,e=c.concat,f=c.push,g=c.indexOf,h={},i=h.toString,j=h.hasOwnProperty,k={},l=a.document,m="2.1.1",n=function(a,b){return new n.fn.init(a,b)},o=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,p=/^-ms-/,q=/-([\da-z])/gi,r=function(a,b){return b.toUpperCase()};n.fn=n.prototype={jquery:m,constructor:n,selector:"",length:0,toArray:function(){return d.call(this)},get:function(a){return null!=a?0>a?this[a+this.length]:this[a]:d.call(this)},pushStack:function(a){var b=n.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a,b){return n.each(this,a,b)},map:function(a){return this.pushStack(n.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(d.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor(null)},push:f,sort:c.sort,splice:c.splice},n.extend=n.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||n.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(a=arguments[h]))for(b in a)c=g[b],d=a[b],g!==d&&(j&&d&&(n.isPlainObject(d)||(e=n.isArray(d)))?(e?(e=!1,f=c&&n.isArray(c)?c:[]):f=c&&n.isPlainObject(c)?c:{},g[b]=n.extend(j,f,d)):void 0!==d&&(g[b]=d));return g},n.extend({expando:"jQuery"+(m+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===n.type(a)},isArray:Array.isArray,isWindow:function(a){return null!=a&&a===a.window},isNumeric:function(a){return!n.isArray(a)&&a-parseFloat(a)>=0},isPlainObject:function(a){return"object"!==n.type(a)||a.nodeType||n.isWindow(a)?!1:a.constructor&&!j.call(a.constructor.prototype,"isPrototypeOf")?!1:!0},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?h[i.call(a)]||"object":typeof a},globalEval:function(a){var b,c=eval;a=n.trim(a),a&&(1===a.indexOf("use strict")?(b=l.createElement("script"),b.text=a,l.head.appendChild(b).parentNode.removeChild(b)):c(a))},camelCase:function(a){return a.replace(p,"ms-").replace(q,r)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b,c){var d,e=0,f=a.length,g=s(a);if(c){if(g){for(;f>e;e++)if(d=b.apply(a[e],c),d===!1)break}else for(e in a)if(d=b.apply(a[e],c),d===!1)break}else if(g){for(;f>e;e++)if(d=b.call(a[e],e,a[e]),d===!1)break}else for(e in a)if(d=b.call(a[e],e,a[e]),d===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(o,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(s(Object(a))?n.merge(c,"string"==typeof a?[a]:a):f.call(c,a)),c},inArray:function(a,b,c){return null==b?-1:g.call(b,a,c)},merge:function(a,b){for(var c=+b.length,d=0,e=a.length;c>d;d++)a[e++]=b[d];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,f=0,g=a.length,h=s(a),i=[];if(h)for(;g>f;f++)d=b(a[f],f,c),null!=d&&i.push(d);else for(f in a)d=b(a[f],f,c),null!=d&&i.push(d);return e.apply([],i)},guid:1,proxy:function(a,b){var c,e,f;return"string"==typeof b&&(c=a[b],b=a,a=c),n.isFunction(a)?(e=d.call(arguments,2),f=function(){return a.apply(b||this,e.concat(d.call(arguments)))},f.guid=a.guid=a.guid||n.guid++,f):void 0},now:Date.now,support:k}),n.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(a,b){h["[object "+b+"]"]=b.toLowerCase()});function s(a){var b=a.length,c=n.type(a);return"function"===c||n.isWindow(a)?!1:1===a.nodeType&&b?!0:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var t=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+-new Date,v=a.document,w=0,x=0,y=gb(),z=gb(),A=gb(),B=function(a,b){return a===b&&(l=!0),0},C="undefined",D=1<<31,E={}.hasOwnProperty,F=[],G=F.pop,H=F.push,I=F.push,J=F.slice,K=F.indexOf||function(a){for(var b=0,c=this.length;c>b;b++)if(this[b]===a)return b;return-1},L="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",M="[\\x20\\t\\r\\n\\f]",N="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",O=N.replace("w","w#"),P="\\["+M+"*("+N+")(?:"+M+"*([*^$|!~]?=)"+M+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+O+"))|)"+M+"*\\]",Q=":("+N+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+P+")*)|.*)\\)|)",R=new RegExp("^"+M+"+|((?:^|[^\\\\])(?:\\\\.)*)"+M+"+$","g"),S=new RegExp("^"+M+"*,"+M+"*"),T=new RegExp("^"+M+"*([>+~]|"+M+")"+M+"*"),U=new RegExp("="+M+"*([^\\]'\"]*?)"+M+"*\\]","g"),V=new RegExp(Q),W=new RegExp("^"+O+"$"),X={ID:new RegExp("^#("+N+")"),CLASS:new RegExp("^\\.("+N+")"),TAG:new RegExp("^("+N.replace("w","w*")+")"),ATTR:new RegExp("^"+P),PSEUDO:new RegExp("^"+Q),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+L+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/^(?:input|select|textarea|button)$/i,Z=/^h\d$/i,$=/^[^{]+\{\s*\[native \w/,_=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ab=/[+~]/,bb=/'|\\/g,cb=new RegExp("\\\\([\\da-f]{1,6}"+M+"?|("+M+")|.)","ig"),db=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:0>d?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)};try{I.apply(F=J.call(v.childNodes),v.childNodes),F[v.childNodes.length].nodeType}catch(eb){I={apply:F.length?function(a,b){H.apply(a,J.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function fb(a,b,d,e){var f,h,j,k,l,o,r,s,w,x;if((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,d=d||[],!a||"string"!=typeof a)return d;if(1!==(k=b.nodeType)&&9!==k)return[];if(p&&!e){if(f=_.exec(a))if(j=f[1]){if(9===k){if(h=b.getElementById(j),!h||!h.parentNode)return d;if(h.id===j)return d.push(h),d}else if(b.ownerDocument&&(h=b.ownerDocument.getElementById(j))&&t(b,h)&&h.id===j)return d.push(h),d}else{if(f[2])return I.apply(d,b.getElementsByTagName(a)),d;if((j=f[3])&&c.getElementsByClassName&&b.getElementsByClassName)return I.apply(d,b.getElementsByClassName(j)),d}if(c.qsa&&(!q||!q.test(a))){if(s=r=u,w=b,x=9===k&&a,1===k&&"object"!==b.nodeName.toLowerCase()){o=g(a),(r=b.getAttribute("id"))?s=r.replace(bb,"\\$&"):b.setAttribute("id",s),s="[id='"+s+"'] ",l=o.length;while(l--)o[l]=s+qb(o[l]);w=ab.test(a)&&ob(b.parentNode)||b,x=o.join(",")}if(x)try{return I.apply(d,w.querySelectorAll(x)),d}catch(y){}finally{r||b.removeAttribute("id")}}}return i(a.replace(R,"$1"),b,d,e)}function gb(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function hb(a){return a[u]=!0,a}function ib(a){var b=n.createElement("div");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function jb(a,b){var c=a.split("|"),e=a.length;while(e--)d.attrHandle[c[e]]=b}function kb(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&(~b.sourceIndex||D)-(~a.sourceIndex||D);if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function lb(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function mb(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function nb(a){return hb(function(b){return b=+b,hb(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function ob(a){return a&&typeof a.getElementsByTagName!==C&&a}c=fb.support={},f=fb.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?"HTML"!==b.nodeName:!1},m=fb.setDocument=function(a){var b,e=a?a.ownerDocument||a:v,g=e.defaultView;return e!==n&&9===e.nodeType&&e.documentElement?(n=e,o=e.documentElement,p=!f(e),g&&g!==g.top&&(g.addEventListener?g.addEventListener("unload",function(){m()},!1):g.attachEvent&&g.attachEvent("onunload",function(){m()})),c.attributes=ib(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ib(function(a){return a.appendChild(e.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=$.test(e.getElementsByClassName)&&ib(function(a){return a.innerHTML="<div class='a'></div><div class='a i'></div>",a.firstChild.className="i",2===a.getElementsByClassName("i").length}),c.getById=ib(function(a){return o.appendChild(a).id=u,!e.getElementsByName||!e.getElementsByName(u).length}),c.getById?(d.find.ID=function(a,b){if(typeof b.getElementById!==C&&p){var c=b.getElementById(a);return c&&c.parentNode?[c]:[]}},d.filter.ID=function(a){var b=a.replace(cb,db);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(cb,db);return function(a){var c=typeof a.getAttributeNode!==C&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return typeof b.getElementsByTagName!==C?b.getElementsByTagName(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){return typeof b.getElementsByClassName!==C&&p?b.getElementsByClassName(a):void 0},r=[],q=[],(c.qsa=$.test(e.querySelectorAll))&&(ib(function(a){a.innerHTML="<select msallowclip=''><option selected=''></option></select>",a.querySelectorAll("[msallowclip^='']").length&&q.push("[*^$]="+M+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+M+"*(?:value|"+L+")"),a.querySelectorAll(":checked").length||q.push(":checked")}),ib(function(a){var b=e.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+M+"*[*^$|!~]?="),a.querySelectorAll(":enabled").length||q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=$.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ib(function(a){c.disconnectedMatch=s.call(a,"div"),s.call(a,"[s!='']:x"),r.push("!=",Q)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=$.test(o.compareDocumentPosition),t=b||$.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===e||a.ownerDocument===v&&t(v,a)?-1:b===e||b.ownerDocument===v&&t(v,b)?1:k?K.call(k,a)-K.call(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,f=a.parentNode,g=b.parentNode,h=[a],i=[b];if(!f||!g)return a===e?-1:b===e?1:f?-1:g?1:k?K.call(k,a)-K.call(k,b):0;if(f===g)return kb(a,b);c=a;while(c=c.parentNode)h.unshift(c);c=b;while(c=c.parentNode)i.unshift(c);while(h[d]===i[d])d++;return d?kb(h[d],i[d]):h[d]===v?-1:i[d]===v?1:0},e):n},fb.matches=function(a,b){return fb(a,null,null,b)},fb.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(U,"='$1']"),!(!c.matchesSelector||!p||r&&r.test(b)||q&&q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return fb(b,n,null,[a]).length>0},fb.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},fb.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&E.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},fb.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},fb.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=fb.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=fb.selectors={cacheLength:50,createPseudo:hb,match:X,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(cb,db),a[3]=(a[3]||a[4]||a[5]||"").replace(cb,db),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||fb.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&fb.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return X.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&V.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(cb,db).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+M+")"+a+"("+M+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||typeof a.getAttribute!==C&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=fb.attr(d,a);return null==e?"!="===b:b?(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e+" ").indexOf(c)>-1:"|="===b?e===c||e.slice(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h;if(q){if(f){while(p){l=b;while(l=l[p])if(h?l.nodeName.toLowerCase()===r:1===l.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){k=q[u]||(q[u]={}),j=k[a]||[],n=j[0]===w&&j[1],m=j[0]===w&&j[2],l=n&&q.childNodes[n];while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if(1===l.nodeType&&++m&&l===b){k[a]=[w,n,m];break}}else if(s&&(j=(b[u]||(b[u]={}))[a])&&j[0]===w)m=j[1];else while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if((h?l.nodeName.toLowerCase()===r:1===l.nodeType)&&++m&&(s&&((l[u]||(l[u]={}))[a]=[w,m]),l===b))break;return m-=e,m===d||m%d===0&&m/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||fb.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?hb(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=K.call(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:hb(function(a){var b=[],c=[],d=h(a.replace(R,"$1"));return d[u]?hb(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),!c.pop()}}),has:hb(function(a){return function(b){return fb(a,b).length>0}}),contains:hb(function(a){return function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:hb(function(a){return W.test(a||"")||fb.error("unsupported lang: "+a),a=a.replace(cb,db).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return Z.test(a.nodeName)},input:function(a){return Y.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:nb(function(){return[0]}),last:nb(function(a,b){return[b-1]}),eq:nb(function(a,b,c){return[0>c?c+b:c]}),even:nb(function(a,b){for(var c=0;b>c;c+=2)a.push(c);return a}),odd:nb(function(a,b){for(var c=1;b>c;c+=2)a.push(c);return a}),lt:nb(function(a,b,c){for(var d=0>c?c+b:c;--d>=0;)a.push(d);return a}),gt:nb(function(a,b,c){for(var d=0>c?c+b:c;++d<b;)a.push(d);return a})}},d.pseudos.nth=d.pseudos.eq;for(b in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})d.pseudos[b]=lb(b);for(b in{submit:!0,reset:!0})d.pseudos[b]=mb(b);function pb(){}pb.prototype=d.filters=d.pseudos,d.setFilters=new pb,g=fb.tokenize=function(a,b){var c,e,f,g,h,i,j,k=z[a+" "];if(k)return b?0:k.slice(0);h=a,i=[],j=d.preFilter;while(h){(!c||(e=S.exec(h)))&&(e&&(h=h.slice(e[0].length)||h),i.push(f=[])),c=!1,(e=T.exec(h))&&(c=e.shift(),f.push({value:c,type:e[0].replace(R," ")}),h=h.slice(c.length));for(g in d.filter)!(e=X[g].exec(h))||j[g]&&!(e=j[g](e))||(c=e.shift(),f.push({value:c,type:g,matches:e}),h=h.slice(c.length));if(!c)break}return b?h.length:h?fb.error(a):z(a,i).slice(0)};function qb(a){for(var b=0,c=a.length,d="";c>b;b++)d+=a[b].value;return d}function rb(a,b,c){var d=b.dir,e=c&&"parentNode"===d,f=x++;return b.first?function(b,c,f){while(b=b[d])if(1===b.nodeType||e)return a(b,c,f)}:function(b,c,g){var h,i,j=[w,f];if(g){while(b=b[d])if((1===b.nodeType||e)&&a(b,c,g))return!0}else while(b=b[d])if(1===b.nodeType||e){if(i=b[u]||(b[u]={}),(h=i[d])&&h[0]===w&&h[1]===f)return j[2]=h[2];if(i[d]=j,j[2]=a(b,c,g))return!0}}}function sb(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function tb(a,b,c){for(var d=0,e=b.length;e>d;d++)fb(a,b[d],c);return c}function ub(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;i>h;h++)(f=a[h])&&(!c||c(f,d,e))&&(g.push(f),j&&b.push(h));return g}function vb(a,b,c,d,e,f){return d&&!d[u]&&(d=vb(d)),e&&!e[u]&&(e=vb(e,f)),hb(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||tb(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:ub(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=ub(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?K.call(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=ub(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):I.apply(g,r)})}function wb(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=rb(function(a){return a===b},h,!0),l=rb(function(a){return K.call(b,a)>-1},h,!0),m=[function(a,c,d){return!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d))}];f>i;i++)if(c=d.relative[a[i].type])m=[rb(sb(m),c)];else{if(c=d.filter[a[i].type].apply(null,a[i].matches),c[u]){for(e=++i;f>e;e++)if(d.relative[a[e].type])break;return vb(i>1&&sb(m),i>1&&qb(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(R,"$1"),c,e>i&&wb(a.slice(i,e)),f>e&&wb(a=a.slice(e)),f>e&&qb(a))}m.push(c)}return sb(m)}function xb(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,h,i,k){var l,m,o,p=0,q="0",r=f&&[],s=[],t=j,u=f||e&&d.find.TAG("*",k),v=w+=null==t?1:Math.random()||.1,x=u.length;for(k&&(j=g!==n&&g);q!==x&&null!=(l=u[q]);q++){if(e&&l){m=0;while(o=a[m++])if(o(l,g,h)){i.push(l);break}k&&(w=v)}c&&((l=!o&&l)&&p--,f&&r.push(l))}if(p+=q,c&&q!==p){m=0;while(o=b[m++])o(r,s,g,h);if(f){if(p>0)while(q--)r[q]||s[q]||(s[q]=G.call(i));s=ub(s)}I.apply(i,s),k&&!f&&s.length>0&&p+b.length>1&&fb.uniqueSort(i)}return k&&(w=v,j=t),r};return c?hb(f):f}return h=fb.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=wb(b[c]),f[u]?d.push(f):e.push(f);f=A(a,xb(e,d)),f.selector=a}return f},i=fb.select=function(a,b,e,f){var i,j,k,l,m,n="function"==typeof a&&a,o=!f&&g(a=n.selector||a);if(e=e||[],1===o.length){if(j=o[0]=o[0].slice(0),j.length>2&&"ID"===(k=j[0]).type&&c.getById&&9===b.nodeType&&p&&d.relative[j[1].type]){if(b=(d.find.ID(k.matches[0].replace(cb,db),b)||[])[0],!b)return e;n&&(b=b.parentNode),a=a.slice(j.shift().value.length)}i=X.needsContext.test(a)?0:j.length;while(i--){if(k=j[i],d.relative[l=k.type])break;if((m=d.find[l])&&(f=m(k.matches[0].replace(cb,db),ab.test(j[0].type)&&ob(b.parentNode)||b))){if(j.splice(i,1),a=f.length&&qb(j),!a)return I.apply(e,f),e;break}}}return(n||h(a,o))(f,b,!p,e,ab.test(a)&&ob(b.parentNode)||b),e},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ib(function(a){return 1&a.compareDocumentPosition(n.createElement("div"))}),ib(function(a){return a.innerHTML="<a href='#'></a>","#"===a.firstChild.getAttribute("href")})||jb("type|href|height|width",function(a,b,c){return c?void 0:a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ib(function(a){return a.innerHTML="<input/>",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||jb("value",function(a,b,c){return c||"input"!==a.nodeName.toLowerCase()?void 0:a.defaultValue}),ib(function(a){return null==a.getAttribute("disabled")})||jb(L,function(a,b,c){var d;return c?void 0:a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),fb}(a);n.find=t,n.expr=t.selectors,n.expr[":"]=n.expr.pseudos,n.unique=t.uniqueSort,n.text=t.getText,n.isXMLDoc=t.isXML,n.contains=t.contains;var u=n.expr.match.needsContext,v=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,w=/^.[^:#\[\.,]*$/;function x(a,b,c){if(n.isFunction(b))return n.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return n.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(w.test(b))return n.filter(b,a,c);b=n.filter(b,a)}return n.grep(a,function(a){return g.call(b,a)>=0!==c})}n.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?n.find.matchesSelector(d,a)?[d]:[]:n.find.matches(a,n.grep(b,function(a){return 1===a.nodeType}))},n.fn.extend({find:function(a){var b,c=this.length,d=[],e=this;if("string"!=typeof a)return this.pushStack(n(a).filter(function(){for(b=0;c>b;b++)if(n.contains(e[b],this))return!0}));for(b=0;c>b;b++)n.find(a,e[b],d);return d=this.pushStack(c>1?n.unique(d):d),d.selector=this.selector?this.selector+" "+a:a,d},filter:function(a){return this.pushStack(x(this,a||[],!1))},not:function(a){return this.pushStack(x(this,a||[],!0))},is:function(a){return!!x(this,"string"==typeof a&&u.test(a)?n(a):a||[],!1).length}});var y,z=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,A=n.fn.init=function(a,b){var c,d;if(!a)return this;if("string"==typeof a){if(c="<"===a[0]&&">"===a[a.length-1]&&a.length>=3?[null,a,null]:z.exec(a),!c||!c[1]&&b)return!b||b.jquery?(b||y).find(a):this.constructor(b).find(a);if(c[1]){if(b=b instanceof n?b[0]:b,n.merge(this,n.parseHTML(c[1],b&&b.nodeType?b.ownerDocument||b:l,!0)),v.test(c[1])&&n.isPlainObject(b))for(c in b)n.isFunction(this[c])?this[c](b[c]):this.attr(c,b[c]);return this}return d=l.getElementById(c[2]),d&&d.parentNode&&(this.length=1,this[0]=d),this.context=l,this.selector=a,this}return a.nodeType?(this.context=this[0]=a,this.length=1,this):n.isFunction(a)?"undefined"!=typeof y.ready?y.ready(a):a(n):(void 0!==a.selector&&(this.selector=a.selector,this.context=a.context),n.makeArray(a,this))};A.prototype=n.fn,y=n(l);var B=/^(?:parents|prev(?:Until|All))/,C={children:!0,contents:!0,next:!0,prev:!0};n.extend({dir:function(a,b,c){var d=[],e=void 0!==c;while((a=a[b])&&9!==a.nodeType)if(1===a.nodeType){if(e&&n(a).is(c))break;d.push(a)}return d},sibling:function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c}}),n.fn.extend({has:function(a){var b=n(a,this),c=b.length;return this.filter(function(){for(var a=0;c>a;a++)if(n.contains(this,b[a]))return!0})},closest:function(a,b){for(var c,d=0,e=this.length,f=[],g=u.test(a)||"string"!=typeof a?n(a,b||this.context):0;e>d;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&n.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?n.unique(f):f)},index:function(a){return a?"string"==typeof a?g.call(n(a),this[0]):g.call(this,a.jquery?a[0]:a):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(n.unique(n.merge(this.get(),n(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function D(a,b){while((a=a[b])&&1!==a.nodeType);return a}n.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return n.dir(a,"parentNode")},parentsUntil:function(a,b,c){return n.dir(a,"parentNode",c)},next:function(a){return D(a,"nextSibling")},prev:function(a){return D(a,"previousSibling")},nextAll:function(a){return n.dir(a,"nextSibling")},prevAll:function(a){return n.dir(a,"previousSibling")},nextUntil:function(a,b,c){return n.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return n.dir(a,"previousSibling",c)},siblings:function(a){return n.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return n.sibling(a.firstChild)},contents:function(a){return a.contentDocument||n.merge([],a.childNodes)}},function(a,b){n.fn[a]=function(c,d){var e=n.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=n.filter(d,e)),this.length>1&&(C[a]||n.unique(e),B.test(a)&&e.reverse()),this.pushStack(e)}});var E=/\S+/g,F={};function G(a){var b=F[a]={};return n.each(a.match(E)||[],function(a,c){b[c]=!0}),b}n.Callbacks=function(a){a="string"==typeof a?F[a]||G(a):n.extend({},a);var b,c,d,e,f,g,h=[],i=!a.once&&[],j=function(l){for(b=a.memory&&l,c=!0,g=e||0,e=0,f=h.length,d=!0;h&&f>g;g++)if(h[g].apply(l[0],l[1])===!1&&a.stopOnFalse){b=!1;break}d=!1,h&&(i?i.length&&j(i.shift()):b?h=[]:k.disable())},k={add:function(){if(h){var c=h.length;!function g(b){n.each(b,function(b,c){var d=n.type(c);"function"===d?a.unique&&k.has(c)||h.push(c):c&&c.length&&"string"!==d&&g(c)})}(arguments),d?f=h.length:b&&(e=c,j(b))}return this},remove:function(){return h&&n.each(arguments,function(a,b){var c;while((c=n.inArray(b,h,c))>-1)h.splice(c,1),d&&(f>=c&&f--,g>=c&&g--)}),this},has:function(a){return a?n.inArray(a,h)>-1:!(!h||!h.length)},empty:function(){return h=[],f=0,this},disable:function(){return h=i=b=void 0,this},disabled:function(){return!h},lock:function(){return i=void 0,b||k.disable(),this},locked:function(){return!i},fireWith:function(a,b){return!h||c&&!i||(b=b||[],b=[a,b.slice?b.slice():b],d?i.push(b):j(b)),this},fire:function(){return k.fireWith(this,arguments),this},fired:function(){return!!c}};return k},n.extend({Deferred:function(a){var b=[["resolve","done",n.Callbacks("once memory"),"resolved"],["reject","fail",n.Callbacks("once memory"),"rejected"],["notify","progress",n.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return n.Deferred(function(c){n.each(b,function(b,f){var g=n.isFunction(a[b])&&a[b];e[f[1]](function(){var a=g&&g.apply(this,arguments);a&&n.isFunction(a.promise)?a.promise().done(c.resolve).fail(c.reject).progress(c.notify):c[f[0]+"With"](this===d?c.promise():this,g?[a]:arguments)})}),a=null}).promise()},promise:function(a){return null!=a?n.extend(a,d):d}},e={};return d.pipe=d.then,n.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[1^a][2].disable,b[2][2].lock),e[f[0]]=function(){return e[f[0]+"With"](this===e?d:this,arguments),this},e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=d.call(arguments),e=c.length,f=1!==e||a&&n.isFunction(a.promise)?e:0,g=1===f?a:n.Deferred(),h=function(a,b,c){return function(e){b[a]=this,c[a]=arguments.length>1?d.call(arguments):e,c===i?g.notifyWith(b,c):--f||g.resolveWith(b,c)}},i,j,k;if(e>1)for(i=new Array(e),j=new Array(e),k=new Array(e);e>b;b++)c[b]&&n.isFunction(c[b].promise)?c[b].promise().done(h(b,k,c)).fail(g.reject).progress(h(b,j,i)):--f;return f||g.resolveWith(k,c),g.promise()}});var H;n.fn.ready=function(a){return n.ready.promise().done(a),this},n.extend({isReady:!1,readyWait:1,holdReady:function(a){a?n.readyWait++:n.ready(!0)},ready:function(a){(a===!0?--n.readyWait:n.isReady)||(n.isReady=!0,a!==!0&&--n.readyWait>0||(H.resolveWith(l,[n]),n.fn.triggerHandler&&(n(l).triggerHandler("ready"),n(l).off("ready"))))}});function I(){l.removeEventListener("DOMContentLoaded",I,!1),a.removeEventListener("load",I,!1),n.ready()}n.ready.promise=function(b){return H||(H=n.Deferred(),"complete"===l.readyState?setTimeout(n.ready):(l.addEventListener("DOMContentLoaded",I,!1),a.addEventListener("load",I,!1))),H.promise(b)},n.ready.promise();var J=n.access=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===n.type(c)){e=!0;for(h in c)n.access(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,n.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(n(a),c)})),b))for(;i>h;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f};n.acceptData=function(a){return 1===a.nodeType||9===a.nodeType||!+a.nodeType};function K(){Object.defineProperty(this.cache={},0,{get:function(){return{}}}),this.expando=n.expando+Math.random()}K.uid=1,K.accepts=n.acceptData,K.prototype={key:function(a){if(!K.accepts(a))return 0;var b={},c=a[this.expando];if(!c){c=K.uid++;try{b[this.expando]={value:c},Object.defineProperties(a,b)}catch(d){b[this.expando]=c,n.extend(a,b)}}return this.cache[c]||(this.cache[c]={}),c},set:function(a,b,c){var d,e=this.key(a),f=this.cache[e];if("string"==typeof b)f[b]=c;else if(n.isEmptyObject(f))n.extend(this.cache[e],b);else for(d in b)f[d]=b[d];return f},get:function(a,b){var c=this.cache[this.key(a)];return void 0===b?c:c[b]},access:function(a,b,c){var d;return void 0===b||b&&"string"==typeof b&&void 0===c?(d=this.get(a,b),void 0!==d?d:this.get(a,n.camelCase(b))):(this.set(a,b,c),void 0!==c?c:b)},remove:function(a,b){var c,d,e,f=this.key(a),g=this.cache[f];if(void 0===b)this.cache[f]={};else{n.isArray(b)?d=b.concat(b.map(n.camelCase)):(e=n.camelCase(b),b in g?d=[b,e]:(d=e,d=d in g?[d]:d.match(E)||[])),c=d.length;while(c--)delete g[d[c]]}},hasData:function(a){return!n.isEmptyObject(this.cache[a[this.expando]]||{})},discard:function(a){a[this.expando]&&delete this.cache[a[this.expando]]}};var L=new K,M=new K,N=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,O=/([A-Z])/g;function P(a,b,c){var d;if(void 0===c&&1===a.nodeType)if(d="data-"+b.replace(O,"-$1").toLowerCase(),c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:N.test(c)?n.parseJSON(c):c}catch(e){}M.set(a,b,c)}else c=void 0;return c}n.extend({hasData:function(a){return M.hasData(a)||L.hasData(a)},data:function(a,b,c){return M.access(a,b,c)},removeData:function(a,b){M.remove(a,b)
-},_data:function(a,b,c){return L.access(a,b,c)},_removeData:function(a,b){L.remove(a,b)}}),n.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=M.get(f),1===f.nodeType&&!L.get(f,"hasDataAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=n.camelCase(d.slice(5)),P(f,d,e[d])));L.set(f,"hasDataAttrs",!0)}return e}return"object"==typeof a?this.each(function(){M.set(this,a)}):J(this,function(b){var c,d=n.camelCase(a);if(f&&void 0===b){if(c=M.get(f,a),void 0!==c)return c;if(c=M.get(f,d),void 0!==c)return c;if(c=P(f,d,void 0),void 0!==c)return c}else this.each(function(){var c=M.get(this,d);M.set(this,d,b),-1!==a.indexOf("-")&&void 0!==c&&M.set(this,a,b)})},null,b,arguments.length>1,null,!0)},removeData:function(a){return this.each(function(){M.remove(this,a)})}}),n.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=L.get(a,b),c&&(!d||n.isArray(c)?d=L.access(a,b,n.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=n.queue(a,b),d=c.length,e=c.shift(),f=n._queueHooks(a,b),g=function(){n.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return L.get(a,c)||L.access(a,c,{empty:n.Callbacks("once memory").add(function(){L.remove(a,[b+"queue",c])})})}}),n.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.length<c?n.queue(this[0],a):void 0===b?this:this.each(function(){var c=n.queue(this,a,b);n._queueHooks(this,a),"fx"===a&&"inprogress"!==c[0]&&n.dequeue(this,a)})},dequeue:function(a){return this.each(function(){n.dequeue(this,a)})},clearQueue:function(a){return this.queue(a||"fx",[])},promise:function(a,b){var c,d=1,e=n.Deferred(),f=this,g=this.length,h=function(){--d||e.resolveWith(f,[f])};"string"!=typeof a&&(b=a,a=void 0),a=a||"fx";while(g--)c=L.get(f[g],a+"queueHooks"),c&&c.empty&&(d++,c.empty.add(h));return h(),e.promise(b)}});var Q=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,R=["Top","Right","Bottom","Left"],S=function(a,b){return a=b||a,"none"===n.css(a,"display")||!n.contains(a.ownerDocument,a)},T=/^(?:checkbox|radio)$/i;!function(){var a=l.createDocumentFragment(),b=a.appendChild(l.createElement("div")),c=l.createElement("input");c.setAttribute("type","radio"),c.setAttribute("checked","checked"),c.setAttribute("name","t"),b.appendChild(c),k.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,b.innerHTML="<textarea>x</textarea>",k.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue}();var U="undefined";k.focusinBubbles="onfocusin"in a;var V=/^key/,W=/^(?:mouse|pointer|contextmenu)|click/,X=/^(?:focusinfocus|focusoutblur)$/,Y=/^([^.]*)(?:\.(.+)|)$/;function Z(){return!0}function $(){return!1}function _(){try{return l.activeElement}catch(a){}}n.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=L.get(a);if(r){c.handler&&(f=c,c=f.handler,e=f.selector),c.guid||(c.guid=n.guid++),(i=r.events)||(i=r.events={}),(g=r.handle)||(g=r.handle=function(b){return typeof n!==U&&n.event.triggered!==b.type?n.event.dispatch.apply(a,arguments):void 0}),b=(b||"").match(E)||[""],j=b.length;while(j--)h=Y.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o&&(l=n.event.special[o]||{},o=(e?l.delegateType:l.bindType)||o,l=n.event.special[o]||{},k=n.extend({type:o,origType:q,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&n.expr.match.needsContext.test(e),namespace:p.join(".")},f),(m=i[o])||(m=i[o]=[],m.delegateCount=0,l.setup&&l.setup.call(a,d,p,g)!==!1||a.addEventListener&&a.addEventListener(o,g,!1)),l.add&&(l.add.call(a,k),k.handler.guid||(k.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,k):m.push(k),n.event.global[o]=!0)}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=L.hasData(a)&&L.get(a);if(r&&(i=r.events)){b=(b||"").match(E)||[""],j=b.length;while(j--)if(h=Y.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o){l=n.event.special[o]||{},o=(d?l.delegateType:l.bindType)||o,m=i[o]||[],h=h[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),g=f=m.length;while(f--)k=m[f],!e&&q!==k.origType||c&&c.guid!==k.guid||h&&!h.test(k.namespace)||d&&d!==k.selector&&("**"!==d||!k.selector)||(m.splice(f,1),k.selector&&m.delegateCount--,l.remove&&l.remove.call(a,k));g&&!m.length&&(l.teardown&&l.teardown.call(a,p,r.handle)!==!1||n.removeEvent(a,o,r.handle),delete i[o])}else for(o in i)n.event.remove(a,o+b[j],c,d,!0);n.isEmptyObject(i)&&(delete r.handle,L.remove(a,"events"))}},trigger:function(b,c,d,e){var f,g,h,i,k,m,o,p=[d||l],q=j.call(b,"type")?b.type:b,r=j.call(b,"namespace")?b.namespace.split("."):[];if(g=h=d=d||l,3!==d.nodeType&&8!==d.nodeType&&!X.test(q+n.event.triggered)&&(q.indexOf(".")>=0&&(r=q.split("."),q=r.shift(),r.sort()),k=q.indexOf(":")<0&&"on"+q,b=b[n.expando]?b:new n.Event(q,"object"==typeof b&&b),b.isTrigger=e?2:3,b.namespace=r.join("."),b.namespace_re=b.namespace?new RegExp("(^|\\.)"+r.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=d),c=null==c?[b]:n.makeArray(c,[b]),o=n.event.special[q]||{},e||!o.trigger||o.trigger.apply(d,c)!==!1)){if(!e&&!o.noBubble&&!n.isWindow(d)){for(i=o.delegateType||q,X.test(i+q)||(g=g.parentNode);g;g=g.parentNode)p.push(g),h=g;h===(d.ownerDocument||l)&&p.push(h.defaultView||h.parentWindow||a)}f=0;while((g=p[f++])&&!b.isPropagationStopped())b.type=f>1?i:o.bindType||q,m=(L.get(g,"events")||{})[b.type]&&L.get(g,"handle"),m&&m.apply(g,c),m=k&&g[k],m&&m.apply&&n.acceptData(g)&&(b.result=m.apply(g,c),b.result===!1&&b.preventDefault());return b.type=q,e||b.isDefaultPrevented()||o._default&&o._default.apply(p.pop(),c)!==!1||!n.acceptData(d)||k&&n.isFunction(d[q])&&!n.isWindow(d)&&(h=d[k],h&&(d[k]=null),n.event.triggered=q,d[q](),n.event.triggered=void 0,h&&(d[k]=h)),b.result}},dispatch:function(a){a=n.event.fix(a);var b,c,e,f,g,h=[],i=d.call(arguments),j=(L.get(this,"events")||{})[a.type]||[],k=n.event.special[a.type]||{};if(i[0]=a,a.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,a)!==!1){h=n.event.handlers.call(this,a,j),b=0;while((f=h[b++])&&!a.isPropagationStopped()){a.currentTarget=f.elem,c=0;while((g=f.handlers[c++])&&!a.isImmediatePropagationStopped())(!a.namespace_re||a.namespace_re.test(g.namespace))&&(a.handleObj=g,a.data=g.data,e=((n.event.special[g.origType]||{}).handle||g.handler).apply(f.elem,i),void 0!==e&&(a.result=e)===!1&&(a.preventDefault(),a.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,a),a.result}},handlers:function(a,b){var c,d,e,f,g=[],h=b.delegateCount,i=a.target;if(h&&i.nodeType&&(!a.button||"click"!==a.type))for(;i!==this;i=i.parentNode||this)if(i.disabled!==!0||"click"!==a.type){for(d=[],c=0;h>c;c++)f=b[c],e=f.selector+" ",void 0===d[e]&&(d[e]=f.needsContext?n(e,this).index(i)>=0:n.find(e,this,null,[i]).length),d[e]&&d.push(f);d.length&&g.push({elem:i,handlers:d})}return h<b.length&&g.push({elem:this,handlers:b.slice(h)}),g},props:"altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(a,b){return null==a.which&&(a.which=null!=b.charCode?b.charCode:b.keyCode),a}},mouseHooks:{props:"button buttons clientX clientY offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(a,b){var c,d,e,f=b.button;return null==a.pageX&&null!=b.clientX&&(c=a.target.ownerDocument||l,d=c.documentElement,e=c.body,a.pageX=b.clientX+(d&&d.scrollLeft||e&&e.scrollLeft||0)-(d&&d.clientLeft||e&&e.clientLeft||0),a.pageY=b.clientY+(d&&d.scrollTop||e&&e.scrollTop||0)-(d&&d.clientTop||e&&e.clientTop||0)),a.which||void 0===f||(a.which=1&f?1:2&f?3:4&f?2:0),a}},fix:function(a){if(a[n.expando])return a;var b,c,d,e=a.type,f=a,g=this.fixHooks[e];g||(this.fixHooks[e]=g=W.test(e)?this.mouseHooks:V.test(e)?this.keyHooks:{}),d=g.props?this.props.concat(g.props):this.props,a=new n.Event(f),b=d.length;while(b--)c=d[b],a[c]=f[c];return a.target||(a.target=l),3===a.target.nodeType&&(a.target=a.target.parentNode),g.filter?g.filter(a,f):a},special:{load:{noBubble:!0},focus:{trigger:function(){return this!==_()&&this.focus?(this.focus(),!1):void 0},delegateType:"focusin"},blur:{trigger:function(){return this===_()&&this.blur?(this.blur(),!1):void 0},delegateType:"focusout"},click:{trigger:function(){return"checkbox"===this.type&&this.click&&n.nodeName(this,"input")?(this.click(),!1):void 0},_default:function(a){return n.nodeName(a.target,"a")}},beforeunload:{postDispatch:function(a){void 0!==a.result&&a.originalEvent&&(a.originalEvent.returnValue=a.result)}}},simulate:function(a,b,c,d){var e=n.extend(new n.Event,c,{type:a,isSimulated:!0,originalEvent:{}});d?n.event.trigger(e,null,b):n.event.dispatch.call(b,e),e.isDefaultPrevented()&&c.preventDefault()}},n.removeEvent=function(a,b,c){a.removeEventListener&&a.removeEventListener(b,c,!1)},n.Event=function(a,b){return this instanceof n.Event?(a&&a.type?(this.originalEvent=a,this.type=a.type,this.isDefaultPrevented=a.defaultPrevented||void 0===a.defaultPrevented&&a.returnValue===!1?Z:$):this.type=a,b&&n.extend(this,b),this.timeStamp=a&&a.timeStamp||n.now(),void(this[n.expando]=!0)):new n.Event(a,b)},n.Event.prototype={isDefaultPrevented:$,isPropagationStopped:$,isImmediatePropagationStopped:$,preventDefault:function(){var a=this.originalEvent;this.isDefaultPrevented=Z,a&&a.preventDefault&&a.preventDefault()},stopPropagation:function(){var a=this.originalEvent;this.isPropagationStopped=Z,a&&a.stopPropagation&&a.stopPropagation()},stopImmediatePropagation:function(){var a=this.originalEvent;this.isImmediatePropagationStopped=Z,a&&a.stopImmediatePropagation&&a.stopImmediatePropagation(),this.stopPropagation()}},n.each({mouseenter:"mouseover",mouseleave:"mouseout",pointerenter:"pointerover",pointerleave:"pointerout"},function(a,b){n.event.special[a]={delegateType:b,bindType:b,handle:function(a){var c,d=this,e=a.relatedTarget,f=a.handleObj;return(!e||e!==d&&!n.contains(d,e))&&(a.type=f.origType,c=f.handler.apply(this,arguments),a.type=b),c}}}),k.focusinBubbles||n.each({focus:"focusin",blur:"focusout"},function(a,b){var c=function(a){n.event.simulate(b,a.target,n.event.fix(a),!0)};n.event.special[b]={setup:function(){var d=this.ownerDocument||this,e=L.access(d,b);e||d.addEventListener(a,c,!0),L.access(d,b,(e||0)+1)},teardown:function(){var d=this.ownerDocument||this,e=L.access(d,b)-1;e?L.access(d,b,e):(d.removeEventListener(a,c,!0),L.remove(d,b))}}}),n.fn.extend({on:function(a,b,c,d,e){var f,g;if("object"==typeof a){"string"!=typeof b&&(c=c||b,b=void 0);for(g in a)this.on(g,b,c,a[g],e);return this}if(null==c&&null==d?(d=b,c=b=void 0):null==d&&("string"==typeof b?(d=c,c=void 0):(d=c,c=b,b=void 0)),d===!1)d=$;else if(!d)return this;return 1===e&&(f=d,d=function(a){return n().off(a),f.apply(this,arguments)},d.guid=f.guid||(f.guid=n.guid++)),this.each(function(){n.event.add(this,a,d,c,b)})},one:function(a,b,c,d){return this.on(a,b,c,d,1)},off:function(a,b,c){var d,e;if(a&&a.preventDefault&&a.handleObj)return d=a.handleObj,n(a.delegateTarget).off(d.namespace?d.origType+"."+d.namespace:d.origType,d.selector,d.handler),this;if("object"==typeof a){for(e in a)this.off(e,b,a[e]);return this}return(b===!1||"function"==typeof b)&&(c=b,b=void 0),c===!1&&(c=$),this.each(function(){n.event.remove(this,a,c,b)})},trigger:function(a,b){return this.each(function(){n.event.trigger(a,b,this)})},triggerHandler:function(a,b){var c=this[0];return c?n.event.trigger(a,b,c,!0):void 0}});var ab=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,bb=/<([\w:]+)/,cb=/<|&#?\w+;/,db=/<(?:script|style|link)/i,eb=/checked\s*(?:[^=]|=\s*.checked.)/i,fb=/^$|\/(?:java|ecma)script/i,gb=/^true\/(.*)/,hb=/^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g,ib={option:[1,"<select multiple='multiple'>","</select>"],thead:[1,"<table>","</table>"],col:[2,"<table><colgroup>","</colgroup></table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:[0,"",""]};ib.optgroup=ib.option,ib.tbody=ib.tfoot=ib.colgroup=ib.caption=ib.thead,ib.th=ib.td;function jb(a,b){return n.nodeName(a,"table")&&n.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function kb(a){return a.type=(null!==a.getAttribute("type"))+"/"+a.type,a}function lb(a){var b=gb.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function mb(a,b){for(var c=0,d=a.length;d>c;c++)L.set(a[c],"globalEval",!b||L.get(b[c],"globalEval"))}function nb(a,b){var c,d,e,f,g,h,i,j;if(1===b.nodeType){if(L.hasData(a)&&(f=L.access(a),g=L.set(b,f),j=f.events)){delete g.handle,g.events={};for(e in j)for(c=0,d=j[e].length;d>c;c++)n.event.add(b,e,j[e][c])}M.hasData(a)&&(h=M.access(a),i=n.extend({},h),M.set(b,i))}}function ob(a,b){var c=a.getElementsByTagName?a.getElementsByTagName(b||"*"):a.querySelectorAll?a.querySelectorAll(b||"*"):[];return void 0===b||b&&n.nodeName(a,b)?n.merge([a],c):c}function pb(a,b){var c=b.nodeName.toLowerCase();"input"===c&&T.test(a.type)?b.checked=a.checked:("input"===c||"textarea"===c)&&(b.defaultValue=a.defaultValue)}n.extend({clone:function(a,b,c){var d,e,f,g,h=a.cloneNode(!0),i=n.contains(a.ownerDocument,a);if(!(k.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||n.isXMLDoc(a)))for(g=ob(h),f=ob(a),d=0,e=f.length;e>d;d++)pb(f[d],g[d]);if(b)if(c)for(f=f||ob(a),g=g||ob(h),d=0,e=f.length;e>d;d++)nb(f[d],g[d]);else nb(a,h);return g=ob(h,"script"),g.length>0&&mb(g,!i&&ob(a,"script")),h},buildFragment:function(a,b,c,d){for(var e,f,g,h,i,j,k=b.createDocumentFragment(),l=[],m=0,o=a.length;o>m;m++)if(e=a[m],e||0===e)if("object"===n.type(e))n.merge(l,e.nodeType?[e]:e);else if(cb.test(e)){f=f||k.appendChild(b.createElement("div")),g=(bb.exec(e)||["",""])[1].toLowerCase(),h=ib[g]||ib._default,f.innerHTML=h[1]+e.replace(ab,"<$1></$2>")+h[2],j=h[0];while(j--)f=f.lastChild;n.merge(l,f.childNodes),f=k.firstChild,f.textContent=""}else l.push(b.createTextNode(e));k.textContent="",m=0;while(e=l[m++])if((!d||-1===n.inArray(e,d))&&(i=n.contains(e.ownerDocument,e),f=ob(k.appendChild(e),"script"),i&&mb(f),c)){j=0;while(e=f[j++])fb.test(e.type||"")&&c.push(e)}return k},cleanData:function(a){for(var b,c,d,e,f=n.event.special,g=0;void 0!==(c=a[g]);g++){if(n.acceptData(c)&&(e=c[L.expando],e&&(b=L.cache[e]))){if(b.events)for(d in b.events)f[d]?n.event.remove(c,d):n.removeEvent(c,d,b.handle);L.cache[e]&&delete L.cache[e]}delete M.cache[c[M.expando]]}}}),n.fn.extend({text:function(a){return J(this,function(a){return void 0===a?n.text(this):this.empty().each(function(){(1===this.nodeType||11===this.nodeType||9===this.nodeType)&&(this.textContent=a)})},null,a,arguments.length)},append:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=jb(this,a);b.appendChild(a)}})},prepend:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=jb(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},remove:function(a,b){for(var c,d=a?n.filter(a,this):this,e=0;null!=(c=d[e]);e++)b||1!==c.nodeType||n.cleanData(ob(c)),c.parentNode&&(b&&n.contains(c.ownerDocument,c)&&mb(ob(c,"script")),c.parentNode.removeChild(c));return this},empty:function(){for(var a,b=0;null!=(a=this[b]);b++)1===a.nodeType&&(n.cleanData(ob(a,!1)),a.textContent="");return this},clone:function(a,b){return a=null==a?!1:a,b=null==b?a:b,this.map(function(){return n.clone(this,a,b)})},html:function(a){return J(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a&&1===b.nodeType)return b.innerHTML;if("string"==typeof a&&!db.test(a)&&!ib[(bb.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(ab,"<$1></$2>");try{for(;d>c;c++)b=this[c]||{},1===b.nodeType&&(n.cleanData(ob(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=arguments[0];return this.domManip(arguments,function(b){a=this.parentNode,n.cleanData(ob(this)),a&&a.replaceChild(b,this)}),a&&(a.length||a.nodeType)?this:this.remove()},detach:function(a){return this.remove(a,!0)},domManip:function(a,b){a=e.apply([],a);var c,d,f,g,h,i,j=0,l=this.length,m=this,o=l-1,p=a[0],q=n.isFunction(p);if(q||l>1&&"string"==typeof p&&!k.checkClone&&eb.test(p))return this.each(function(c){var d=m.eq(c);q&&(a[0]=p.call(this,c,d.html())),d.domManip(a,b)});if(l&&(c=n.buildFragment(a,this[0].ownerDocument,!1,this),d=c.firstChild,1===c.childNodes.length&&(c=d),d)){for(f=n.map(ob(c,"script"),kb),g=f.length;l>j;j++)h=c,j!==o&&(h=n.clone(h,!0,!0),g&&n.merge(f,ob(h,"script"))),b.call(this[j],h,j);if(g)for(i=f[f.length-1].ownerDocument,n.map(f,lb),j=0;g>j;j++)h=f[j],fb.test(h.type||"")&&!L.access(h,"globalEval")&&n.contains(i,h)&&(h.src?n._evalUrl&&n._evalUrl(h.src):n.globalEval(h.textContent.replace(hb,"")))}return this}}),n.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){n.fn[a]=function(a){for(var c,d=[],e=n(a),g=e.length-1,h=0;g>=h;h++)c=h===g?this:this.clone(!0),n(e[h])[b](c),f.apply(d,c.get());return this.pushStack(d)}});var qb,rb={};function sb(b,c){var d,e=n(c.createElement(b)).appendTo(c.body),f=a.getDefaultComputedStyle&&(d=a.getDefaultComputedStyle(e[0]))?d.display:n.css(e[0],"display");return e.detach(),f}function tb(a){var b=l,c=rb[a];return c||(c=sb(a,b),"none"!==c&&c||(qb=(qb||n("<iframe frameborder='0' width='0' height='0'/>")).appendTo(b.documentElement),b=qb[0].contentDocument,b.write(),b.close(),c=sb(a,b),qb.detach()),rb[a]=c),c}var ub=/^margin/,vb=new RegExp("^("+Q+")(?!px)[a-z%]+$","i"),wb=function(a){return a.ownerDocument.defaultView.getComputedStyle(a,null)};function xb(a,b,c){var d,e,f,g,h=a.style;return c=c||wb(a),c&&(g=c.getPropertyValue(b)||c[b]),c&&(""!==g||n.contains(a.ownerDocument,a)||(g=n.style(a,b)),vb.test(g)&&ub.test(b)&&(d=h.width,e=h.minWidth,f=h.maxWidth,h.minWidth=h.maxWidth=h.width=g,g=c.width,h.width=d,h.minWidth=e,h.maxWidth=f)),void 0!==g?g+"":g}function yb(a,b){return{get:function(){return a()?void delete this.get:(this.get=b).apply(this,arguments)}}}!function(){var b,c,d=l.documentElement,e=l.createElement("div"),f=l.createElement("div");if(f.style){f.style.backgroundClip="content-box",f.cloneNode(!0).style.backgroundClip="",k.clearCloneStyle="content-box"===f.style.backgroundClip,e.style.cssText="border:0;width:0;height:0;top:0;left:-9999px;margin-top:1px;position:absolute",e.appendChild(f);function g(){f.style.cssText="-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;display:block;margin-top:1%;top:1%;border:1px;padding:1px;width:4px;position:absolute",f.innerHTML="",d.appendChild(e);var g=a.getComputedStyle(f,null);b="1%"!==g.top,c="4px"===g.width,d.removeChild(e)}a.getComputedStyle&&n.extend(k,{pixelPosition:function(){return g(),b},boxSizingReliable:function(){return null==c&&g(),c},reliableMarginRight:function(){var b,c=f.appendChild(l.createElement("div"));return c.style.cssText=f.style.cssText="-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;display:block;margin:0;border:0;padding:0",c.style.marginRight=c.style.width="0",f.style.width="1px",d.appendChild(e),b=!parseFloat(a.getComputedStyle(c,null).marginRight),d.removeChild(e),b}})}}(),n.swap=function(a,b,c,d){var e,f,g={};for(f in b)g[f]=a.style[f],a.style[f]=b[f];e=c.apply(a,d||[]);for(f in b)a.style[f]=g[f];return e};var zb=/^(none|table(?!-c[ea]).+)/,Ab=new RegExp("^("+Q+")(.*)$","i"),Bb=new RegExp("^([+-])=("+Q+")","i"),Cb={position:"absolute",visibility:"hidden",display:"block"},Db={letterSpacing:"0",fontWeight:"400"},Eb=["Webkit","O","Moz","ms"];function Fb(a,b){if(b in a)return b;var c=b[0].toUpperCase()+b.slice(1),d=b,e=Eb.length;while(e--)if(b=Eb[e]+c,b in a)return b;return d}function Gb(a,b,c){var d=Ab.exec(b);return d?Math.max(0,d[1]-(c||0))+(d[2]||"px"):b}function Hb(a,b,c,d,e){for(var f=c===(d?"border":"content")?4:"width"===b?1:0,g=0;4>f;f+=2)"margin"===c&&(g+=n.css(a,c+R[f],!0,e)),d?("content"===c&&(g-=n.css(a,"padding"+R[f],!0,e)),"margin"!==c&&(g-=n.css(a,"border"+R[f]+"Width",!0,e))):(g+=n.css(a,"padding"+R[f],!0,e),"padding"!==c&&(g+=n.css(a,"border"+R[f]+"Width",!0,e)));return g}function Ib(a,b,c){var d=!0,e="width"===b?a.offsetWidth:a.offsetHeight,f=wb(a),g="border-box"===n.css(a,"boxSizing",!1,f);if(0>=e||null==e){if(e=xb(a,b,f),(0>e||null==e)&&(e=a.style[b]),vb.test(e))return e;d=g&&(k.boxSizingReliable()||e===a.style[b]),e=parseFloat(e)||0}return e+Hb(a,b,c||(g?"border":"content"),d,f)+"px"}function Jb(a,b){for(var c,d,e,f=[],g=0,h=a.length;h>g;g++)d=a[g],d.style&&(f[g]=L.get(d,"olddisplay"),c=d.style.display,b?(f[g]||"none"!==c||(d.style.display=""),""===d.style.display&&S(d)&&(f[g]=L.access(d,"olddisplay",tb(d.nodeName)))):(e=S(d),"none"===c&&e||L.set(d,"olddisplay",e?c:n.css(d,"display"))));for(g=0;h>g;g++)d=a[g],d.style&&(b&&"none"!==d.style.display&&""!==d.style.display||(d.style.display=b?f[g]||"":"none"));return a}n.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=xb(a,"opacity");return""===c?"1":c}}}},cssNumber:{columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":"cssFloat"},style:function(a,b,c,d){if(a&&3!==a.nodeType&&8!==a.nodeType&&a.style){var e,f,g,h=n.camelCase(b),i=a.style;return b=n.cssProps[h]||(n.cssProps[h]=Fb(i,h)),g=n.cssHooks[b]||n.cssHooks[h],void 0===c?g&&"get"in g&&void 0!==(e=g.get(a,!1,d))?e:i[b]:(f=typeof c,"string"===f&&(e=Bb.exec(c))&&(c=(e[1]+1)*e[2]+parseFloat(n.css(a,b)),f="number"),null!=c&&c===c&&("number"!==f||n.cssNumber[h]||(c+="px"),k.clearCloneStyle||""!==c||0!==b.indexOf("background")||(i[b]="inherit"),g&&"set"in g&&void 0===(c=g.set(a,c,d))||(i[b]=c)),void 0)}},css:function(a,b,c,d){var e,f,g,h=n.camelCase(b);return b=n.cssProps[h]||(n.cssProps[h]=Fb(a.style,h)),g=n.cssHooks[b]||n.cssHooks[h],g&&"get"in g&&(e=g.get(a,!0,c)),void 0===e&&(e=xb(a,b,d)),"normal"===e&&b in Db&&(e=Db[b]),""===c||c?(f=parseFloat(e),c===!0||n.isNumeric(f)?f||0:e):e}}),n.each(["height","width"],function(a,b){n.cssHooks[b]={get:function(a,c,d){return c?zb.test(n.css(a,"display"))&&0===a.offsetWidth?n.swap(a,Cb,function(){return Ib(a,b,d)}):Ib(a,b,d):void 0},set:function(a,c,d){var e=d&&wb(a);return Gb(a,c,d?Hb(a,b,d,"border-box"===n.css(a,"boxSizing",!1,e),e):0)}}}),n.cssHooks.marginRight=yb(k.reliableMarginRight,function(a,b){return b?n.swap(a,{display:"inline-block"},xb,[a,"marginRight"]):void 0}),n.each({margin:"",padding:"",border:"Width"},function(a,b){n.cssHooks[a+b]={expand:function(c){for(var d=0,e={},f="string"==typeof c?c.split(" "):[c];4>d;d++)e[a+R[d]+b]=f[d]||f[d-2]||f[0];return e}},ub.test(a)||(n.cssHooks[a+b].set=Gb)}),n.fn.extend({css:function(a,b){return J(this,function(a,b,c){var d,e,f={},g=0;if(n.isArray(b)){for(d=wb(a),e=b.length;e>g;g++)f[b[g]]=n.css(a,b[g],!1,d);return f}return void 0!==c?n.style(a,b,c):n.css(a,b)},a,b,arguments.length>1)},show:function(){return Jb(this,!0)},hide:function(){return Jb(this)},toggle:function(a){return"boolean"==typeof a?a?this.show():this.hide():this.each(function(){S(this)?n(this).show():n(this).hide()})}});function Kb(a,b,c,d,e){return new Kb.prototype.init(a,b,c,d,e)}n.Tween=Kb,Kb.prototype={constructor:Kb,init:function(a,b,c,d,e,f){this.elem=a,this.prop=c,this.easing=e||"swing",this.options=b,this.start=this.now=this.cur(),this.end=d,this.unit=f||(n.cssNumber[c]?"":"px")},cur:function(){var a=Kb.propHooks[this.prop];return a&&a.get?a.get(this):Kb.propHooks._default.get(this)},run:function(a){var b,c=Kb.propHooks[this.prop];return this.pos=b=this.options.duration?n.easing[this.easing](a,this.options.duration*a,0,1,this.options.duration):a,this.now=(this.end-this.start)*b+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),c&&c.set?c.set(this):Kb.propHooks._default.set(this),this}},Kb.prototype.init.prototype=Kb.prototype,Kb.propHooks={_default:{get:function(a){var b;return null==a.elem[a.prop]||a.elem.style&&null!=a.elem.style[a.prop]?(b=n.css(a.elem,a.prop,""),b&&"auto"!==b?b:0):a.elem[a.prop]},set:function(a){n.fx.step[a.prop]?n.fx.step[a.prop](a):a.elem.style&&(null!=a.elem.style[n.cssProps[a.prop]]||n.cssHooks[a.prop])?n.style(a.elem,a.prop,a.now+a.unit):a.elem[a.prop]=a.now}}},Kb.propHooks.scrollTop=Kb.propHooks.scrollLeft={set:function(a){a.elem.nodeType&&a.elem.parentNode&&(a.elem[a.prop]=a.now)}},n.easing={linear:function(a){return a},swing:function(a){return.5-Math.cos(a*Math.PI)/2}},n.fx=Kb.prototype.init,n.fx.step={};var Lb,Mb,Nb=/^(?:toggle|show|hide)$/,Ob=new RegExp("^(?:([+-])=|)("+Q+")([a-z%]*)$","i"),Pb=/queueHooks$/,Qb=[Vb],Rb={"*":[function(a,b){var c=this.createTween(a,b),d=c.cur(),e=Ob.exec(b),f=e&&e[3]||(n.cssNumber[a]?"":"px"),g=(n.cssNumber[a]||"px"!==f&&+d)&&Ob.exec(n.css(c.elem,a)),h=1,i=20;if(g&&g[3]!==f){f=f||g[3],e=e||[],g=+d||1;do h=h||".5",g/=h,n.style(c.elem,a,g+f);while(h!==(h=c.cur()/d)&&1!==h&&--i)}return e&&(g=c.start=+g||+d||0,c.unit=f,c.end=e[1]?g+(e[1]+1)*e[2]:+e[2]),c}]};function Sb(){return setTimeout(function(){Lb=void 0}),Lb=n.now()}function Tb(a,b){var c,d=0,e={height:a};for(b=b?1:0;4>d;d+=2-b)c=R[d],e["margin"+c]=e["padding"+c]=a;return b&&(e.opacity=e.width=a),e}function Ub(a,b,c){for(var d,e=(Rb[b]||[]).concat(Rb["*"]),f=0,g=e.length;g>f;f++)if(d=e[f].call(c,b,a))return d}function Vb(a,b,c){var d,e,f,g,h,i,j,k,l=this,m={},o=a.style,p=a.nodeType&&S(a),q=L.get(a,"fxshow");c.queue||(h=n._queueHooks(a,"fx"),null==h.unqueued&&(h.unqueued=0,i=h.empty.fire,h.empty.fire=function(){h.unqueued||i()}),h.unqueued++,l.always(function(){l.always(function(){h.unqueued--,n.queue(a,"fx").length||h.empty.fire()})})),1===a.nodeType&&("height"in b||"width"in b)&&(c.overflow=[o.overflow,o.overflowX,o.overflowY],j=n.css(a,"display"),k="none"===j?L.get(a,"olddisplay")||tb(a.nodeName):j,"inline"===k&&"none"===n.css(a,"float")&&(o.display="inline-block")),c.overflow&&(o.overflow="hidden",l.always(function(){o.overflow=c.overflow[0],o.overflowX=c.overflow[1],o.overflowY=c.overflow[2]}));for(d in b)if(e=b[d],Nb.exec(e)){if(delete b[d],f=f||"toggle"===e,e===(p?"hide":"show")){if("show"!==e||!q||void 0===q[d])continue;p=!0}m[d]=q&&q[d]||n.style(a,d)}else j=void 0;if(n.isEmptyObject(m))"inline"===("none"===j?tb(a.nodeName):j)&&(o.display=j);else{q?"hidden"in q&&(p=q.hidden):q=L.access(a,"fxshow",{}),f&&(q.hidden=!p),p?n(a).show():l.done(function(){n(a).hide()}),l.done(function(){var b;L.remove(a,"fxshow");for(b in m)n.style(a,b,m[b])});for(d in m)g=Ub(p?q[d]:0,d,l),d in q||(q[d]=g.start,p&&(g.end=g.start,g.start="width"===d||"height"===d?1:0))}}function Wb(a,b){var c,d,e,f,g;for(c in a)if(d=n.camelCase(c),e=b[d],f=a[c],n.isArray(f)&&(e=f[1],f=a[c]=f[0]),c!==d&&(a[d]=f,delete a[c]),g=n.cssHooks[d],g&&"expand"in g){f=g.expand(f),delete a[d];for(c in f)c in a||(a[c]=f[c],b[c]=e)}else b[d]=e}function Xb(a,b,c){var d,e,f=0,g=Qb.length,h=n.Deferred().always(function(){delete i.elem}),i=function(){if(e)return!1;for(var b=Lb||Sb(),c=Math.max(0,j.startTime+j.duration-b),d=c/j.duration||0,f=1-d,g=0,i=j.tweens.length;i>g;g++)j.tweens[g].run(f);return h.notifyWith(a,[j,f,c]),1>f&&i?c:(h.resolveWith(a,[j]),!1)},j=h.promise({elem:a,props:n.extend({},b),opts:n.extend(!0,{specialEasing:{}},c),originalProperties:b,originalOptions:c,startTime:Lb||Sb(),duration:c.duration,tweens:[],createTween:function(b,c){var d=n.Tween(a,j.opts,b,c,j.opts.specialEasing[b]||j.opts.easing);return j.tweens.push(d),d},stop:function(b){var c=0,d=b?j.tweens.length:0;if(e)return this;for(e=!0;d>c;c++)j.tweens[c].run(1);return b?h.resolveWith(a,[j,b]):h.rejectWith(a,[j,b]),this}}),k=j.props;for(Wb(k,j.opts.specialEasing);g>f;f++)if(d=Qb[f].call(j,a,k,j.opts))return d;return n.map(k,Ub,j),n.isFunction(j.opts.start)&&j.opts.start.call(a,j),n.fx.timer(n.extend(i,{elem:a,anim:j,queue:j.opts.queue})),j.progress(j.opts.progress).done(j.opts.done,j.opts.complete).fail(j.opts.fail).always(j.opts.always)}n.Animation=n.extend(Xb,{tweener:function(a,b){n.isFunction(a)?(b=a,a=["*"]):a=a.split(" ");for(var c,d=0,e=a.length;e>d;d++)c=a[d],Rb[c]=Rb[c]||[],Rb[c].unshift(b)},prefilter:function(a,b){b?Qb.unshift(a):Qb.push(a)}}),n.speed=function(a,b,c){var d=a&&"object"==typeof a?n.extend({},a):{complete:c||!c&&b||n.isFunction(a)&&a,duration:a,easing:c&&b||b&&!n.isFunction(b)&&b};return d.duration=n.fx.off?0:"number"==typeof d.duration?d.duration:d.duration in n.fx.speeds?n.fx.speeds[d.duration]:n.fx.speeds._default,(null==d.queue||d.queue===!0)&&(d.queue="fx"),d.old=d.complete,d.complete=function(){n.isFunction(d.old)&&d.old.call(this),d.queue&&n.dequeue(this,d.queue)},d},n.fn.extend({fadeTo:function(a,b,c,d){return this.filter(S).css("opacity",0).show().end().animate({opacity:b},a,c,d)},animate:function(a,b,c,d){var e=n.isEmptyObject(a),f=n.speed(b,c,d),g=function(){var b=Xb(this,n.extend({},a),f);(e||L.get(this,"finish"))&&b.stop(!0)};return g.finish=g,e||f.queue===!1?this.each(g):this.queue(f.queue,g)},stop:function(a,b,c){var d=function(a){var b=a.stop;delete a.stop,b(c)};return"string"!=typeof a&&(c=b,b=a,a=void 0),b&&a!==!1&&this.queue(a||"fx",[]),this.each(function(){var b=!0,e=null!=a&&a+"queueHooks",f=n.timers,g=L.get(this);if(e)g[e]&&g[e].stop&&d(g[e]);else for(e in g)g[e]&&g[e].stop&&Pb.test(e)&&d(g[e]);for(e=f.length;e--;)f[e].elem!==this||null!=a&&f[e].queue!==a||(f[e].anim.stop(c),b=!1,f.splice(e,1));(b||!c)&&n.dequeue(this,a)})},finish:function(a){return a!==!1&&(a=a||"fx"),this.each(function(){var b,c=L.get(this),d=c[a+"queue"],e=c[a+"queueHooks"],f=n.timers,g=d?d.length:0;for(c.finish=!0,n.queue(this,a,[]),e&&e.stop&&e.stop.call(this,!0),b=f.length;b--;)f[b].elem===this&&f[b].queue===a&&(f[b].anim.stop(!0),f.splice(b,1));for(b=0;g>b;b++)d[b]&&d[b].finish&&d[b].finish.call(this);delete c.finish})}}),n.each(["toggle","show","hide"],function(a,b){var c=n.fn[b];n.fn[b]=function(a,d,e){return null==a||"boolean"==typeof a?c.apply(this,arguments):this.animate(Tb(b,!0),a,d,e)}}),n.each({slideDown:Tb("show"),slideUp:Tb("hide"),slideToggle:Tb("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){n.fn[a]=function(a,c,d){return this.animate(b,a,c,d)}}),n.timers=[],n.fx.tick=function(){var a,b=0,c=n.timers;for(Lb=n.now();b<c.length;b++)a=c[b],a()||c[b]!==a||c.splice(b--,1);c.length||n.fx.stop(),Lb=void 0},n.fx.timer=function(a){n.timers.push(a),a()?n.fx.start():n.timers.pop()},n.fx.interval=13,n.fx.start=function(){Mb||(Mb=setInterval(n.fx.tick,n.fx.interval))},n.fx.stop=function(){clearInterval(Mb),Mb=null},n.fx.speeds={slow:600,fast:200,_default:400},n.fn.delay=function(a,b){return a=n.fx?n.fx.speeds[a]||a:a,b=b||"fx",this.queue(b,function(b,c){var d=setTimeout(b,a);c.stop=function(){clearTimeout(d)}})},function(){var a=l.createElement("input"),b=l.createElement("select"),c=b.appendChild(l.createElement("option"));a.type="checkbox",k.checkOn=""!==a.value,k.optSelected=c.selected,b.disabled=!0,k.optDisabled=!c.disabled,a=l.createElement("input"),a.value="t",a.type="radio",k.radioValue="t"===a.value}();var Yb,Zb,$b=n.expr.attrHandle;n.fn.extend({attr:function(a,b){return J(this,n.attr,a,b,arguments.length>1)},removeAttr:function(a){return this.each(function(){n.removeAttr(this,a)})}}),n.extend({attr:function(a,b,c){var d,e,f=a.nodeType;if(a&&3!==f&&8!==f&&2!==f)return typeof a.getAttribute===U?n.prop(a,b,c):(1===f&&n.isXMLDoc(a)||(b=b.toLowerCase(),d=n.attrHooks[b]||(n.expr.match.bool.test(b)?Zb:Yb)),void 0===c?d&&"get"in d&&null!==(e=d.get(a,b))?e:(e=n.find.attr(a,b),null==e?void 0:e):null!==c?d&&"set"in d&&void 0!==(e=d.set(a,c,b))?e:(a.setAttribute(b,c+""),c):void n.removeAttr(a,b))
-},removeAttr:function(a,b){var c,d,e=0,f=b&&b.match(E);if(f&&1===a.nodeType)while(c=f[e++])d=n.propFix[c]||c,n.expr.match.bool.test(c)&&(a[d]=!1),a.removeAttribute(c)},attrHooks:{type:{set:function(a,b){if(!k.radioValue&&"radio"===b&&n.nodeName(a,"input")){var c=a.value;return a.setAttribute("type",b),c&&(a.value=c),b}}}}}),Zb={set:function(a,b,c){return b===!1?n.removeAttr(a,c):a.setAttribute(c,c),c}},n.each(n.expr.match.bool.source.match(/\w+/g),function(a,b){var c=$b[b]||n.find.attr;$b[b]=function(a,b,d){var e,f;return d||(f=$b[b],$b[b]=e,e=null!=c(a,b,d)?b.toLowerCase():null,$b[b]=f),e}});var _b=/^(?:input|select|textarea|button)$/i;n.fn.extend({prop:function(a,b){return J(this,n.prop,a,b,arguments.length>1)},removeProp:function(a){return this.each(function(){delete this[n.propFix[a]||a]})}}),n.extend({propFix:{"for":"htmlFor","class":"className"},prop:function(a,b,c){var d,e,f,g=a.nodeType;if(a&&3!==g&&8!==g&&2!==g)return f=1!==g||!n.isXMLDoc(a),f&&(b=n.propFix[b]||b,e=n.propHooks[b]),void 0!==c?e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:a[b]=c:e&&"get"in e&&null!==(d=e.get(a,b))?d:a[b]},propHooks:{tabIndex:{get:function(a){return a.hasAttribute("tabindex")||_b.test(a.nodeName)||a.href?a.tabIndex:-1}}}}),k.optSelected||(n.propHooks.selected={get:function(a){var b=a.parentNode;return b&&b.parentNode&&b.parentNode.selectedIndex,null}}),n.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){n.propFix[this.toLowerCase()]=this});var ac=/[\t\r\n\f]/g;n.fn.extend({addClass:function(a){var b,c,d,e,f,g,h="string"==typeof a&&a,i=0,j=this.length;if(n.isFunction(a))return this.each(function(b){n(this).addClass(a.call(this,b,this.className))});if(h)for(b=(a||"").match(E)||[];j>i;i++)if(c=this[i],d=1===c.nodeType&&(c.className?(" "+c.className+" ").replace(ac," "):" ")){f=0;while(e=b[f++])d.indexOf(" "+e+" ")<0&&(d+=e+" ");g=n.trim(d),c.className!==g&&(c.className=g)}return this},removeClass:function(a){var b,c,d,e,f,g,h=0===arguments.length||"string"==typeof a&&a,i=0,j=this.length;if(n.isFunction(a))return this.each(function(b){n(this).removeClass(a.call(this,b,this.className))});if(h)for(b=(a||"").match(E)||[];j>i;i++)if(c=this[i],d=1===c.nodeType&&(c.className?(" "+c.className+" ").replace(ac," "):"")){f=0;while(e=b[f++])while(d.indexOf(" "+e+" ")>=0)d=d.replace(" "+e+" "," ");g=a?n.trim(d):"",c.className!==g&&(c.className=g)}return this},toggleClass:function(a,b){var c=typeof a;return"boolean"==typeof b&&"string"===c?b?this.addClass(a):this.removeClass(a):this.each(n.isFunction(a)?function(c){n(this).toggleClass(a.call(this,c,this.className,b),b)}:function(){if("string"===c){var b,d=0,e=n(this),f=a.match(E)||[];while(b=f[d++])e.hasClass(b)?e.removeClass(b):e.addClass(b)}else(c===U||"boolean"===c)&&(this.className&&L.set(this,"__className__",this.className),this.className=this.className||a===!1?"":L.get(this,"__className__")||"")})},hasClass:function(a){for(var b=" "+a+" ",c=0,d=this.length;d>c;c++)if(1===this[c].nodeType&&(" "+this[c].className+" ").replace(ac," ").indexOf(b)>=0)return!0;return!1}});var bc=/\r/g;n.fn.extend({val:function(a){var b,c,d,e=this[0];{if(arguments.length)return d=n.isFunction(a),this.each(function(c){var e;1===this.nodeType&&(e=d?a.call(this,c,n(this).val()):a,null==e?e="":"number"==typeof e?e+="":n.isArray(e)&&(e=n.map(e,function(a){return null==a?"":a+""})),b=n.valHooks[this.type]||n.valHooks[this.nodeName.toLowerCase()],b&&"set"in b&&void 0!==b.set(this,e,"value")||(this.value=e))});if(e)return b=n.valHooks[e.type]||n.valHooks[e.nodeName.toLowerCase()],b&&"get"in b&&void 0!==(c=b.get(e,"value"))?c:(c=e.value,"string"==typeof c?c.replace(bc,""):null==c?"":c)}}}),n.extend({valHooks:{option:{get:function(a){var b=n.find.attr(a,"value");return null!=b?b:n.trim(n.text(a))}},select:{get:function(a){for(var b,c,d=a.options,e=a.selectedIndex,f="select-one"===a.type||0>e,g=f?null:[],h=f?e+1:d.length,i=0>e?h:f?e:0;h>i;i++)if(c=d[i],!(!c.selected&&i!==e||(k.optDisabled?c.disabled:null!==c.getAttribute("disabled"))||c.parentNode.disabled&&n.nodeName(c.parentNode,"optgroup"))){if(b=n(c).val(),f)return b;g.push(b)}return g},set:function(a,b){var c,d,e=a.options,f=n.makeArray(b),g=e.length;while(g--)d=e[g],(d.selected=n.inArray(d.value,f)>=0)&&(c=!0);return c||(a.selectedIndex=-1),f}}}}),n.each(["radio","checkbox"],function(){n.valHooks[this]={set:function(a,b){return n.isArray(b)?a.checked=n.inArray(n(a).val(),b)>=0:void 0}},k.checkOn||(n.valHooks[this].get=function(a){return null===a.getAttribute("value")?"on":a.value})}),n.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu".split(" "),function(a,b){n.fn[b]=function(a,c){return arguments.length>0?this.on(b,null,a,c):this.trigger(b)}}),n.fn.extend({hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)},bind:function(a,b,c){return this.on(a,null,b,c)},unbind:function(a,b){return this.off(a,null,b)},delegate:function(a,b,c,d){return this.on(b,a,c,d)},undelegate:function(a,b,c){return 1===arguments.length?this.off(a,"**"):this.off(b,a||"**",c)}});var cc=n.now(),dc=/\?/;n.parseJSON=function(a){return JSON.parse(a+"")},n.parseXML=function(a){var b,c;if(!a||"string"!=typeof a)return null;try{c=new DOMParser,b=c.parseFromString(a,"text/xml")}catch(d){b=void 0}return(!b||b.getElementsByTagName("parsererror").length)&&n.error("Invalid XML: "+a),b};var ec,fc,gc=/#.*$/,hc=/([?&])_=[^&]*/,ic=/^(.*?):[ \t]*([^\r\n]*)$/gm,jc=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,kc=/^(?:GET|HEAD)$/,lc=/^\/\//,mc=/^([\w.+-]+:)(?:\/\/(?:[^\/?#]*@|)([^\/?#:]*)(?::(\d+)|)|)/,nc={},oc={},pc="*/".concat("*");try{fc=location.href}catch(qc){fc=l.createElement("a"),fc.href="",fc=fc.href}ec=mc.exec(fc.toLowerCase())||[];function rc(a){return function(b,c){"string"!=typeof b&&(c=b,b="*");var d,e=0,f=b.toLowerCase().match(E)||[];if(n.isFunction(c))while(d=f[e++])"+"===d[0]?(d=d.slice(1)||"*",(a[d]=a[d]||[]).unshift(c)):(a[d]=a[d]||[]).push(c)}}function sc(a,b,c,d){var e={},f=a===oc;function g(h){var i;return e[h]=!0,n.each(a[h]||[],function(a,h){var j=h(b,c,d);return"string"!=typeof j||f||e[j]?f?!(i=j):void 0:(b.dataTypes.unshift(j),g(j),!1)}),i}return g(b.dataTypes[0])||!e["*"]&&g("*")}function tc(a,b){var c,d,e=n.ajaxSettings.flatOptions||{};for(c in b)void 0!==b[c]&&((e[c]?a:d||(d={}))[c]=b[c]);return d&&n.extend(!0,a,d),a}function uc(a,b,c){var d,e,f,g,h=a.contents,i=a.dataTypes;while("*"===i[0])i.shift(),void 0===d&&(d=a.mimeType||b.getResponseHeader("Content-Type"));if(d)for(e in h)if(h[e]&&h[e].test(d)){i.unshift(e);break}if(i[0]in c)f=i[0];else{for(e in c){if(!i[0]||a.converters[e+" "+i[0]]){f=e;break}g||(g=e)}f=f||g}return f?(f!==i[0]&&i.unshift(f),c[f]):void 0}function vc(a,b,c,d){var e,f,g,h,i,j={},k=a.dataTypes.slice();if(k[1])for(g in a.converters)j[g.toLowerCase()]=a.converters[g];f=k.shift();while(f)if(a.responseFields[f]&&(c[a.responseFields[f]]=b),!i&&d&&a.dataFilter&&(b=a.dataFilter(b,a.dataType)),i=f,f=k.shift())if("*"===f)f=i;else if("*"!==i&&i!==f){if(g=j[i+" "+f]||j["* "+f],!g)for(e in j)if(h=e.split(" "),h[1]===f&&(g=j[i+" "+h[0]]||j["* "+h[0]])){g===!0?g=j[e]:j[e]!==!0&&(f=h[0],k.unshift(h[1]));break}if(g!==!0)if(g&&a["throws"])b=g(b);else try{b=g(b)}catch(l){return{state:"parsererror",error:g?l:"No conversion from "+i+" to "+f}}}return{state:"success",data:b}}n.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:fc,type:"GET",isLocal:jc.test(ec[1]),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":pc,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":n.parseJSON,"text xml":n.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(a,b){return b?tc(tc(a,n.ajaxSettings),b):tc(n.ajaxSettings,a)},ajaxPrefilter:rc(nc),ajaxTransport:rc(oc),ajax:function(a,b){"object"==typeof a&&(b=a,a=void 0),b=b||{};var c,d,e,f,g,h,i,j,k=n.ajaxSetup({},b),l=k.context||k,m=k.context&&(l.nodeType||l.jquery)?n(l):n.event,o=n.Deferred(),p=n.Callbacks("once memory"),q=k.statusCode||{},r={},s={},t=0,u="canceled",v={readyState:0,getResponseHeader:function(a){var b;if(2===t){if(!f){f={};while(b=ic.exec(e))f[b[1].toLowerCase()]=b[2]}b=f[a.toLowerCase()]}return null==b?null:b},getAllResponseHeaders:function(){return 2===t?e:null},setRequestHeader:function(a,b){var c=a.toLowerCase();return t||(a=s[c]=s[c]||a,r[a]=b),this},overrideMimeType:function(a){return t||(k.mimeType=a),this},statusCode:function(a){var b;if(a)if(2>t)for(b in a)q[b]=[q[b],a[b]];else v.always(a[v.status]);return this},abort:function(a){var b=a||u;return c&&c.abort(b),x(0,b),this}};if(o.promise(v).complete=p.add,v.success=v.done,v.error=v.fail,k.url=((a||k.url||fc)+"").replace(gc,"").replace(lc,ec[1]+"//"),k.type=b.method||b.type||k.method||k.type,k.dataTypes=n.trim(k.dataType||"*").toLowerCase().match(E)||[""],null==k.crossDomain&&(h=mc.exec(k.url.toLowerCase()),k.crossDomain=!(!h||h[1]===ec[1]&&h[2]===ec[2]&&(h[3]||("http:"===h[1]?"80":"443"))===(ec[3]||("http:"===ec[1]?"80":"443")))),k.data&&k.processData&&"string"!=typeof k.data&&(k.data=n.param(k.data,k.traditional)),sc(nc,k,b,v),2===t)return v;i=k.global,i&&0===n.active++&&n.event.trigger("ajaxStart"),k.type=k.type.toUpperCase(),k.hasContent=!kc.test(k.type),d=k.url,k.hasContent||(k.data&&(d=k.url+=(dc.test(d)?"&":"?")+k.data,delete k.data),k.cache===!1&&(k.url=hc.test(d)?d.replace(hc,"$1_="+cc++):d+(dc.test(d)?"&":"?")+"_="+cc++)),k.ifModified&&(n.lastModified[d]&&v.setRequestHeader("If-Modified-Since",n.lastModified[d]),n.etag[d]&&v.setRequestHeader("If-None-Match",n.etag[d])),(k.data&&k.hasContent&&k.contentType!==!1||b.contentType)&&v.setRequestHeader("Content-Type",k.contentType),v.setRequestHeader("Accept",k.dataTypes[0]&&k.accepts[k.dataTypes[0]]?k.accepts[k.dataTypes[0]]+("*"!==k.dataTypes[0]?", "+pc+"; q=0.01":""):k.accepts["*"]);for(j in k.headers)v.setRequestHeader(j,k.headers[j]);if(k.beforeSend&&(k.beforeSend.call(l,v,k)===!1||2===t))return v.abort();u="abort";for(j in{success:1,error:1,complete:1})v[j](k[j]);if(c=sc(oc,k,b,v)){v.readyState=1,i&&m.trigger("ajaxSend",[v,k]),k.async&&k.timeout>0&&(g=setTimeout(function(){v.abort("timeout")},k.timeout));try{t=1,c.send(r,x)}catch(w){if(!(2>t))throw w;x(-1,w)}}else x(-1,"No Transport");function x(a,b,f,h){var j,r,s,u,w,x=b;2!==t&&(t=2,g&&clearTimeout(g),c=void 0,e=h||"",v.readyState=a>0?4:0,j=a>=200&&300>a||304===a,f&&(u=uc(k,v,f)),u=vc(k,u,v,j),j?(k.ifModified&&(w=v.getResponseHeader("Last-Modified"),w&&(n.lastModified[d]=w),w=v.getResponseHeader("etag"),w&&(n.etag[d]=w)),204===a||"HEAD"===k.type?x="nocontent":304===a?x="notmodified":(x=u.state,r=u.data,s=u.error,j=!s)):(s=x,(a||!x)&&(x="error",0>a&&(a=0))),v.status=a,v.statusText=(b||x)+"",j?o.resolveWith(l,[r,x,v]):o.rejectWith(l,[v,x,s]),v.statusCode(q),q=void 0,i&&m.trigger(j?"ajaxSuccess":"ajaxError",[v,k,j?r:s]),p.fireWith(l,[v,x]),i&&(m.trigger("ajaxComplete",[v,k]),--n.active||n.event.trigger("ajaxStop")))}return v},getJSON:function(a,b,c){return n.get(a,b,c,"json")},getScript:function(a,b){return n.get(a,void 0,b,"script")}}),n.each(["get","post"],function(a,b){n[b]=function(a,c,d,e){return n.isFunction(c)&&(e=e||d,d=c,c=void 0),n.ajax({url:a,type:b,dataType:e,data:c,success:d})}}),n.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(a,b){n.fn[b]=function(a){return this.on(b,a)}}),n._evalUrl=function(a){return n.ajax({url:a,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0})},n.fn.extend({wrapAll:function(a){var b;return n.isFunction(a)?this.each(function(b){n(this).wrapAll(a.call(this,b))}):(this[0]&&(b=n(a,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstElementChild)a=a.firstElementChild;return a}).append(this)),this)},wrapInner:function(a){return this.each(n.isFunction(a)?function(b){n(this).wrapInner(a.call(this,b))}:function(){var b=n(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=n.isFunction(a);return this.each(function(c){n(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){n.nodeName(this,"body")||n(this).replaceWith(this.childNodes)}).end()}}),n.expr.filters.hidden=function(a){return a.offsetWidth<=0&&a.offsetHeight<=0},n.expr.filters.visible=function(a){return!n.expr.filters.hidden(a)};var wc=/%20/g,xc=/\[\]$/,yc=/\r?\n/g,zc=/^(?:submit|button|image|reset|file)$/i,Ac=/^(?:input|select|textarea|keygen)/i;function Bc(a,b,c,d){var e;if(n.isArray(b))n.each(b,function(b,e){c||xc.test(a)?d(a,e):Bc(a+"["+("object"==typeof e?b:"")+"]",e,c,d)});else if(c||"object"!==n.type(b))d(a,b);else for(e in b)Bc(a+"["+e+"]",b[e],c,d)}n.param=function(a,b){var c,d=[],e=function(a,b){b=n.isFunction(b)?b():null==b?"":b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};if(void 0===b&&(b=n.ajaxSettings&&n.ajaxSettings.traditional),n.isArray(a)||a.jquery&&!n.isPlainObject(a))n.each(a,function(){e(this.name,this.value)});else for(c in a)Bc(c,a[c],b,e);return d.join("&").replace(wc,"+")},n.fn.extend({serialize:function(){return n.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var a=n.prop(this,"elements");return a?n.makeArray(a):this}).filter(function(){var a=this.type;return this.name&&!n(this).is(":disabled")&&Ac.test(this.nodeName)&&!zc.test(a)&&(this.checked||!T.test(a))}).map(function(a,b){var c=n(this).val();return null==c?null:n.isArray(c)?n.map(c,function(a){return{name:b.name,value:a.replace(yc,"\r\n")}}):{name:b.name,value:c.replace(yc,"\r\n")}}).get()}}),n.ajaxSettings.xhr=function(){try{return new XMLHttpRequest}catch(a){}};var Cc=0,Dc={},Ec={0:200,1223:204},Fc=n.ajaxSettings.xhr();a.ActiveXObject&&n(a).on("unload",function(){for(var a in Dc)Dc[a]()}),k.cors=!!Fc&&"withCredentials"in Fc,k.ajax=Fc=!!Fc,n.ajaxTransport(function(a){var b;return k.cors||Fc&&!a.crossDomain?{send:function(c,d){var e,f=a.xhr(),g=++Cc;if(f.open(a.type,a.url,a.async,a.username,a.password),a.xhrFields)for(e in a.xhrFields)f[e]=a.xhrFields[e];a.mimeType&&f.overrideMimeType&&f.overrideMimeType(a.mimeType),a.crossDomain||c["X-Requested-With"]||(c["X-Requested-With"]="XMLHttpRequest");for(e in c)f.setRequestHeader(e,c[e]);b=function(a){return function(){b&&(delete Dc[g],b=f.onload=f.onerror=null,"abort"===a?f.abort():"error"===a?d(f.status,f.statusText):d(Ec[f.status]||f.status,f.statusText,"string"==typeof f.responseText?{text:f.responseText}:void 0,f.getAllResponseHeaders()))}},f.onload=b(),f.onerror=b("error"),b=Dc[g]=b("abort");try{f.send(a.hasContent&&a.data||null)}catch(h){if(b)throw h}},abort:function(){b&&b()}}:void 0}),n.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/(?:java|ecma)script/},converters:{"text script":function(a){return n.globalEval(a),a}}}),n.ajaxPrefilter("script",function(a){void 0===a.cache&&(a.cache=!1),a.crossDomain&&(a.type="GET")}),n.ajaxTransport("script",function(a){if(a.crossDomain){var b,c;return{send:function(d,e){b=n("<script>").prop({async:!0,charset:a.scriptCharset,src:a.url}).on("load error",c=function(a){b.remove(),c=null,a&&e("error"===a.type?404:200,a.type)}),l.head.appendChild(b[0])},abort:function(){c&&c()}}}});var Gc=[],Hc=/(=)\?(?=&|$)|\?\?/;n.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var a=Gc.pop()||n.expando+"_"+cc++;return this[a]=!0,a}}),n.ajaxPrefilter("json jsonp",function(b,c,d){var e,f,g,h=b.jsonp!==!1&&(Hc.test(b.url)?"url":"string"==typeof b.data&&!(b.contentType||"").indexOf("application/x-www-form-urlencoded")&&Hc.test(b.data)&&"data");return h||"jsonp"===b.dataTypes[0]?(e=b.jsonpCallback=n.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,h?b[h]=b[h].replace(Hc,"$1"+e):b.jsonp!==!1&&(b.url+=(dc.test(b.url)?"&":"?")+b.jsonp+"="+e),b.converters["script json"]=function(){return g||n.error(e+" was not called"),g[0]},b.dataTypes[0]="json",f=a[e],a[e]=function(){g=arguments},d.always(function(){a[e]=f,b[e]&&(b.jsonpCallback=c.jsonpCallback,Gc.push(e)),g&&n.isFunction(f)&&f(g[0]),g=f=void 0}),"script"):void 0}),n.parseHTML=function(a,b,c){if(!a||"string"!=typeof a)return null;"boolean"==typeof b&&(c=b,b=!1),b=b||l;var d=v.exec(a),e=!c&&[];return d?[b.createElement(d[1])]:(d=n.buildFragment([a],b,e),e&&e.length&&n(e).remove(),n.merge([],d.childNodes))};var Ic=n.fn.load;n.fn.load=function(a,b,c){if("string"!=typeof a&&Ic)return Ic.apply(this,arguments);var d,e,f,g=this,h=a.indexOf(" ");return h>=0&&(d=n.trim(a.slice(h)),a=a.slice(0,h)),n.isFunction(b)?(c=b,b=void 0):b&&"object"==typeof b&&(e="POST"),g.length>0&&n.ajax({url:a,type:e,dataType:"html",data:b}).done(function(a){f=arguments,g.html(d?n("<div>").append(n.parseHTML(a)).find(d):a)}).complete(c&&function(a,b){g.each(c,f||[a.responseText,b,a])}),this},n.expr.filters.animated=function(a){return n.grep(n.timers,function(b){return a===b.elem}).length};var Jc=a.document.documentElement;function Kc(a){return n.isWindow(a)?a:9===a.nodeType&&a.defaultView}n.offset={setOffset:function(a,b,c){var d,e,f,g,h,i,j,k=n.css(a,"position"),l=n(a),m={};"static"===k&&(a.style.position="relative"),h=l.offset(),f=n.css(a,"top"),i=n.css(a,"left"),j=("absolute"===k||"fixed"===k)&&(f+i).indexOf("auto")>-1,j?(d=l.position(),g=d.top,e=d.left):(g=parseFloat(f)||0,e=parseFloat(i)||0),n.isFunction(b)&&(b=b.call(a,c,h)),null!=b.top&&(m.top=b.top-h.top+g),null!=b.left&&(m.left=b.left-h.left+e),"using"in b?b.using.call(a,m):l.css(m)}},n.fn.extend({offset:function(a){if(arguments.length)return void 0===a?this:this.each(function(b){n.offset.setOffset(this,a,b)});var b,c,d=this[0],e={top:0,left:0},f=d&&d.ownerDocument;if(f)return b=f.documentElement,n.contains(b,d)?(typeof d.getBoundingClientRect!==U&&(e=d.getBoundingClientRect()),c=Kc(f),{top:e.top+c.pageYOffset-b.clientTop,left:e.left+c.pageXOffset-b.clientLeft}):e},position:function(){if(this[0]){var a,b,c=this[0],d={top:0,left:0};return"fixed"===n.css(c,"position")?b=c.getBoundingClientRect():(a=this.offsetParent(),b=this.offset(),n.nodeName(a[0],"html")||(d=a.offset()),d.top+=n.css(a[0],"borderTopWidth",!0),d.left+=n.css(a[0],"borderLeftWidth",!0)),{top:b.top-d.top-n.css(c,"marginTop",!0),left:b.left-d.left-n.css(c,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||Jc;while(a&&!n.nodeName(a,"html")&&"static"===n.css(a,"position"))a=a.offsetParent;return a||Jc})}}),n.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(b,c){var d="pageYOffset"===c;n.fn[b]=function(e){return J(this,function(b,e,f){var g=Kc(b);return void 0===f?g?g[c]:b[e]:void(g?g.scrollTo(d?a.pageXOffset:f,d?f:a.pageYOffset):b[e]=f)},b,e,arguments.length,null)}}),n.each(["top","left"],function(a,b){n.cssHooks[b]=yb(k.pixelPosition,function(a,c){return c?(c=xb(a,b),vb.test(c)?n(a).position()[b]+"px":c):void 0})}),n.each({Height:"height",Width:"width"},function(a,b){n.each({padding:"inner"+a,content:b,"":"outer"+a},function(c,d){n.fn[d]=function(d,e){var f=arguments.length&&(c||"boolean"!=typeof d),g=c||(d===!0||e===!0?"margin":"border");return J(this,function(b,c,d){var e;return n.isWindow(b)?b.document.documentElement["client"+a]:9===b.nodeType?(e=b.documentElement,Math.max(b.body["scroll"+a],e["scroll"+a],b.body["offset"+a],e["offset"+a],e["client"+a])):void 0===d?n.css(b,c,g):n.style(b,c,d,g)},b,f?d:void 0,f,null)}})}),n.fn.size=function(){return this.length},n.fn.andSelf=n.fn.addBack,"function"==typeof define&&define.amd&&define("jquery",[],function(){return n});var Lc=a.jQuery,Mc=a.$;return n.noConflict=function(b){return a.$===n&&(a.$=Mc),b&&a.jQuery===n&&(a.jQuery=Lc),n},typeof b===U&&(a.jQuery=a.$=n),n});
diff --git a/js/modernizr-2.8.3.min.js b/js/modernizr-2.8.3.min.js
deleted file mode 100644
index 40dd2a9f..00000000
--- a/js/modernizr-2.8.3.min.js
+++ /dev/null
@@ -1 +0,0 @@
-window.Modernizr=function(e,t,n){function r(e){b.cssText=e}function o(e,t){return r(S.join(e+";")+(t||""))}function a(e,t){return typeof e===t}function i(e,t){return!!~(""+e).indexOf(t)}function c(e,t){for(var r in e){var o=e[r];if(!i(o,"-")&&b[o]!==n)return"pfx"==t?o:!0}return!1}function s(e,t,r){for(var o in e){var i=t[e[o]];if(i!==n)return r===!1?e[o]:a(i,"function")?i.bind(r||t):i}return!1}function u(e,t,n){var r=e.charAt(0).toUpperCase()+e.slice(1),o=(e+" "+k.join(r+" ")+r).split(" ");return a(t,"string")||a(t,"undefined")?c(o,t):(o=(e+" "+T.join(r+" ")+r).split(" "),s(o,t,n))}function l(){p.input=function(n){for(var r=0,o=n.length;o>r;r++)j[n[r]]=!!(n[r]in E);return j.list&&(j.list=!(!t.createElement("datalist")||!e.HTMLDataListElement)),j}("autocomplete autofocus list placeholder max min multiple pattern required step".split(" ")),p.inputtypes=function(e){for(var r,o,a,i=0,c=e.length;c>i;i++)E.setAttribute("type",o=e[i]),r="text"!==E.type,r&&(E.value=x,E.style.cssText="position:absolute;visibility:hidden;",/^range$/.test(o)&&E.style.WebkitAppearance!==n?(g.appendChild(E),a=t.defaultView,r=a.getComputedStyle&&"textfield"!==a.getComputedStyle(E,null).WebkitAppearance&&0!==E.offsetHeight,g.removeChild(E)):/^(search|tel)$/.test(o)||(r=/^(url|email)$/.test(o)?E.checkValidity&&E.checkValidity()===!1:E.value!=x)),P[e[i]]=!!r;return P}("search tel url email datetime date month week time datetime-local number range color".split(" "))}var d,f,m="2.8.3",p={},h=!0,g=t.documentElement,v="modernizr",y=t.createElement(v),b=y.style,E=t.createElement("input"),x=":)",w={}.toString,S=" -webkit- -moz- -o- -ms- ".split(" "),C="Webkit Moz O ms",k=C.split(" "),T=C.toLowerCase().split(" "),N={svg:"http://www.w3.org/2000/svg"},M={},P={},j={},$=[],D=$.slice,F=function(e,n,r,o){var a,i,c,s,u=t.createElement("div"),l=t.body,d=l||t.createElement("body");if(parseInt(r,10))for(;r--;)c=t.createElement("div"),c.id=o?o[r]:v+(r+1),u.appendChild(c);return a=["&#173;",'<style id="s',v,'">',e,"</style>"].join(""),u.id=v,(l?u:d).innerHTML+=a,d.appendChild(u),l||(d.style.background="",d.style.overflow="hidden",s=g.style.overflow,g.style.overflow="hidden",g.appendChild(d)),i=n(u,e),l?u.parentNode.removeChild(u):(d.parentNode.removeChild(d),g.style.overflow=s),!!i},z=function(t){var n=e.matchMedia||e.msMatchMedia;if(n)return n(t)&&n(t).matches||!1;var r;return F("@media "+t+" { #"+v+" { position: absolute; } }",function(t){r="absolute"==(e.getComputedStyle?getComputedStyle(t,null):t.currentStyle).position}),r},A=function(){function e(e,o){o=o||t.createElement(r[e]||"div"),e="on"+e;var i=e in o;return i||(o.setAttribute||(o=t.createElement("div")),o.setAttribute&&o.removeAttribute&&(o.setAttribute(e,""),i=a(o[e],"function"),a(o[e],"undefined")||(o[e]=n),o.removeAttribute(e))),o=null,i}var r={select:"input",change:"input",submit:"form",reset:"form",error:"img",load:"img",abort:"img"};return e}(),L={}.hasOwnProperty;f=a(L,"undefined")||a(L.call,"undefined")?function(e,t){return t in e&&a(e.constructor.prototype[t],"undefined")}:function(e,t){return L.call(e,t)},Function.prototype.bind||(Function.prototype.bind=function(e){var t=this;if("function"!=typeof t)throw new TypeError;var n=D.call(arguments,1),r=function(){if(this instanceof r){var o=function(){};o.prototype=t.prototype;var a=new o,i=t.apply(a,n.concat(D.call(arguments)));return Object(i)===i?i:a}return t.apply(e,n.concat(D.call(arguments)))};return r}),M.flexbox=function(){return u("flexWrap")},M.flexboxlegacy=function(){return u("boxDirection")},M.canvas=function(){var e=t.createElement("canvas");return!(!e.getContext||!e.getContext("2d"))},M.canvastext=function(){return!(!p.canvas||!a(t.createElement("canvas").getContext("2d").fillText,"function"))},M.webgl=function(){return!!e.WebGLRenderingContext},M.touch=function(){var n;return"ontouchstart"in e||e.DocumentTouch&&t instanceof DocumentTouch?n=!0:F(["@media (",S.join("touch-enabled),("),v,")","{#modernizr{top:9px;position:absolute}}"].join(""),function(e){n=9===e.offsetTop}),n},M.geolocation=function(){return"geolocation"in navigator},M.postmessage=function(){return!!e.postMessage},M.websqldatabase=function(){return!!e.openDatabase},M.indexedDB=function(){return!!u("indexedDB",e)},M.hashchange=function(){return A("hashchange",e)&&(t.documentMode===n||t.documentMode>7)},M.history=function(){return!(!e.history||!history.pushState)},M.draganddrop=function(){var e=t.createElement("div");return"draggable"in e||"ondragstart"in e&&"ondrop"in e},M.websockets=function(){return"WebSocket"in e||"MozWebSocket"in e},M.rgba=function(){return r("background-color:rgba(150,255,150,.5)"),i(b.backgroundColor,"rgba")},M.hsla=function(){return r("background-color:hsla(120,40%,100%,.5)"),i(b.backgroundColor,"rgba")||i(b.backgroundColor,"hsla")},M.multiplebgs=function(){return r("background:url(https://),url(https://),red url(https://)"),/(url\s*\(.*?){3}/.test(b.background)},M.backgroundsize=function(){return u("backgroundSize")},M.borderimage=function(){return u("borderImage")},M.borderradius=function(){return u("borderRadius")},M.boxshadow=function(){return u("boxShadow")},M.textshadow=function(){return""===t.createElement("div").style.textShadow},M.opacity=function(){return o("opacity:.55"),/^0.55$/.test(b.opacity)},M.cssanimations=function(){return u("animationName")},M.csscolumns=function(){return u("columnCount")},M.cssgradients=function(){var e="background-image:",t="gradient(linear,left top,right bottom,from(#9f9),to(white));",n="linear-gradient(left top,#9f9, white);";return r((e+"-webkit- ".split(" ").join(t+e)+S.join(n+e)).slice(0,-e.length)),i(b.backgroundImage,"gradient")},M.cssreflections=function(){return u("boxReflect")},M.csstransforms=function(){return!!u("transform")},M.csstransforms3d=function(){var e=!!u("perspective");return e&&"webkitPerspective"in g.style&&F("@media (transform-3d),(-webkit-transform-3d){#modernizr{left:9px;position:absolute;height:3px;}}",function(t){e=9===t.offsetLeft&&3===t.offsetHeight}),e},M.csstransitions=function(){return u("transition")},M.fontface=function(){var e;return F('@font-face {font-family:"font";src:url("https://")}',function(n,r){var o=t.getElementById("smodernizr"),a=o.sheet||o.styleSheet,i=a?a.cssRules&&a.cssRules[0]?a.cssRules[0].cssText:a.cssText||"":"";e=/src/i.test(i)&&0===i.indexOf(r.split(" ")[0])}),e},M.generatedcontent=function(){var e;return F(["#",v,"{font:0/0 a}#",v,':after{content:"',x,'";visibility:hidden;font:3px/1 a}'].join(""),function(t){e=t.offsetHeight>=3}),e},M.video=function(){var e=t.createElement("video"),n=!1;try{(n=!!e.canPlayType)&&(n=new Boolean(n),n.ogg=e.canPlayType('video/ogg; codecs="theora"').replace(/^no$/,""),n.h264=e.canPlayType('video/mp4; codecs="avc1.42E01E"').replace(/^no$/,""),n.webm=e.canPlayType('video/webm; codecs="vp8, vorbis"').replace(/^no$/,""))}catch(r){}return n},M.audio=function(){var e=t.createElement("audio"),n=!1;try{(n=!!e.canPlayType)&&(n=new Boolean(n),n.ogg=e.canPlayType('audio/ogg; codecs="vorbis"').replace(/^no$/,""),n.mp3=e.canPlayType("audio/mpeg;").replace(/^no$/,""),n.wav=e.canPlayType('audio/wav; codecs="1"').replace(/^no$/,""),n.m4a=(e.canPlayType("audio/x-m4a;")||e.canPlayType("audio/aac;")).replace(/^no$/,""))}catch(r){}return n},M.localstorage=function(){try{return localStorage.setItem(v,v),localStorage.removeItem(v),!0}catch(e){return!1}},M.sessionstorage=function(){try{return sessionStorage.setItem(v,v),sessionStorage.removeItem(v),!0}catch(e){return!1}},M.webworkers=function(){return!!e.Worker},M.applicationcache=function(){return!!e.applicationCache},M.svg=function(){return!!t.createElementNS&&!!t.createElementNS(N.svg,"svg").createSVGRect},M.inlinesvg=function(){var e=t.createElement("div");return e.innerHTML="<svg/>",(e.firstChild&&e.firstChild.namespaceURI)==N.svg},M.smil=function(){return!!t.createElementNS&&/SVGAnimate/.test(w.call(t.createElementNS(N.svg,"animate")))},M.svgclippaths=function(){return!!t.createElementNS&&/SVGClipPath/.test(w.call(t.createElementNS(N.svg,"clipPath")))};for(var H in M)f(M,H)&&(d=H.toLowerCase(),p[d]=M[H](),$.push((p[d]?"":"no-")+d));return p.input||l(),p.addTest=function(e,t){if("object"==typeof e)for(var r in e)f(e,r)&&p.addTest(r,e[r]);else{if(e=e.toLowerCase(),p[e]!==n)return p;t="function"==typeof t?t():t,"undefined"!=typeof h&&h&&(g.className+=" "+(t?"":"no-")+e),p[e]=t}return p},r(""),y=E=null,function(e,t){function n(e,t){var n=e.createElement("p"),r=e.getElementsByTagName("head")[0]||e.documentElement;return n.innerHTML="x<style>"+t+"</style>",r.insertBefore(n.lastChild,r.firstChild)}function r(){var e=y.elements;return"string"==typeof e?e.split(" "):e}function o(e){var t=v[e[h]];return t||(t={},g++,e[h]=g,v[g]=t),t}function a(e,n,r){if(n||(n=t),l)return n.createElement(e);r||(r=o(n));var a;return a=r.cache[e]?r.cache[e].cloneNode():p.test(e)?(r.cache[e]=r.createElem(e)).cloneNode():r.createElem(e),!a.canHaveChildren||m.test(e)||a.tagUrn?a:r.frag.appendChild(a)}function i(e,n){if(e||(e=t),l)return e.createDocumentFragment();n=n||o(e);for(var a=n.frag.cloneNode(),i=0,c=r(),s=c.length;s>i;i++)a.createElement(c[i]);return a}function c(e,t){t.cache||(t.cache={},t.createElem=e.createElement,t.createFrag=e.createDocumentFragment,t.frag=t.createFrag()),e.createElement=function(n){return y.shivMethods?a(n,e,t):t.createElem(n)},e.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+r().join().replace(/[\w\-]+/g,function(e){return t.createElem(e),t.frag.createElement(e),'c("'+e+'")'})+");return n}")(y,t.frag)}function s(e){e||(e=t);var r=o(e);return!y.shivCSS||u||r.hasCSS||(r.hasCSS=!!n(e,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),l||c(e,r),e}var u,l,d="3.7.0",f=e.html5||{},m=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,p=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,h="_html5shiv",g=0,v={};!function(){try{var e=t.createElement("a");e.innerHTML="<xyz></xyz>",u="hidden"in e,l=1==e.childNodes.length||function(){t.createElement("a");var e=t.createDocumentFragment();return"undefined"==typeof e.cloneNode||"undefined"==typeof e.createDocumentFragment||"undefined"==typeof e.createElement}()}catch(n){u=!0,l=!0}}();var y={elements:f.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output progress section summary template time video",version:d,shivCSS:f.shivCSS!==!1,supportsUnknownElements:l,shivMethods:f.shivMethods!==!1,type:"default",shivDocument:s,createElement:a,createDocumentFragment:i};e.html5=y,s(t)}(this,t),p._version=m,p._prefixes=S,p._domPrefixes=T,p._cssomPrefixes=k,p.mq=z,p.hasEvent=A,p.testProp=function(e){return c([e])},p.testAllProps=u,p.testStyles=F,p.prefixed=function(e,t,n){return t?u(e,t,n):u(e,"pfx")},g.className=g.className.replace(/(^|\s)no-js(\s|$)/,"$1$2")+(h?" js "+$.join(" "):""),p}(this,this.document);
\ No newline at end of file
diff --git a/js/theme.js b/js/theme.js
deleted file mode 100644
index 6396162c..00000000
--- a/js/theme.js
+++ /dev/null
@@ -1,55 +0,0 @@
-$( document ).ready(function() {
-
-    // Shift nav in mobile when clicking the menu.
-    $(document).on('click', "[data-toggle='wy-nav-top']", function() {
-      $("[data-toggle='wy-nav-shift']").toggleClass("shift");
-      $("[data-toggle='rst-versions']").toggleClass("shift");
-    });
-
-    // Close menu when you click a link.
-    $(document).on('click', ".wy-menu-vertical .current ul li a", function() {
-      $("[data-toggle='wy-nav-shift']").removeClass("shift");
-      $("[data-toggle='rst-versions']").toggleClass("shift");
-    });
-
-    $(document).on('click', "[data-toggle='rst-current-version']", function() {
-      $("[data-toggle='rst-versions']").toggleClass("shift-up");
-    });
-
-    // Make tables responsive
-    $("table.docutils:not(.field-list)").wrap("<div class='wy-table-responsive'></div>");
-
-    hljs.initHighlightingOnLoad();
-
-    $('table').addClass('docutils');
-});
-
-window.SphinxRtdTheme = (function (jquery) {
-    var stickyNav = (function () {
-        var navBar,
-            win,
-            stickyNavCssClass = 'stickynav',
-            applyStickNav = function () {
-                if (navBar.height() <= win.height()) {
-                    navBar.addClass(stickyNavCssClass);
-                } else {
-                    navBar.removeClass(stickyNavCssClass);
-                }
-            },
-            enable = function () {
-                applyStickNav();
-                win.on('resize', applyStickNav);
-            },
-            init = function () {
-                navBar = jquery('nav.wy-nav-side:first');
-                win    = jquery(window);
-            };
-        jquery(init);
-        return {
-            enable : enable
-        };
-    }());
-    return {
-        StickyNav : stickyNav
-    };
-}($));
diff --git a/mkdocs.yml b/mkdocs.yml
new file mode 100644
index 00000000..c0b3d9a0
--- /dev/null
+++ b/mkdocs.yml
@@ -0,0 +1,9 @@
+site_name: "mailcow: dockerized"
+repo_url: https://github.com/andryyy/mailcow-dockerized/
+remote_branch: gh-pages
+theme: readthedocs
+pages:
+    - 'Overview': 'index.md'
+    - 'Installation': 'install.md'
+    - 'First Steps': 'first_steps.md'
+    - 'Usage & Examples': 'u_and_e.md'
diff --git a/mkdocs/js/lunr.min.js b/mkdocs/js/lunr.min.js
deleted file mode 100644
index b0198dff..00000000
--- a/mkdocs/js/lunr.min.js
+++ /dev/null
@@ -1,7 +0,0 @@
-/**
- * lunr - http://lunrjs.com - A bit like Solr, but much smaller and not as bright - 0.7.0
- * Copyright (C) 2016 Oliver Nightingale
- * MIT Licensed
- * @license
- */
-!function(){var t=function(e){var n=new t.Index;return n.pipeline.add(t.trimmer,t.stopWordFilter,t.stemmer),e&&e.call(n,n),n};t.version="0.7.0",t.utils={},t.utils.warn=function(t){return function(e){t.console&&console.warn&&console.warn(e)}}(this),t.utils.asString=function(t){return void 0===t||null===t?"":t.toString()},t.EventEmitter=function(){this.events={}},t.EventEmitter.prototype.addListener=function(){var t=Array.prototype.slice.call(arguments),e=t.pop(),n=t;if("function"!=typeof e)throw new TypeError("last argument must be a function");n.forEach(function(t){this.hasHandler(t)||(this.events[t]=[]),this.events[t].push(e)},this)},t.EventEmitter.prototype.removeListener=function(t,e){if(this.hasHandler(t)){var n=this.events[t].indexOf(e);this.events[t].splice(n,1),this.events[t].length||delete this.events[t]}},t.EventEmitter.prototype.emit=function(t){if(this.hasHandler(t)){var e=Array.prototype.slice.call(arguments,1);this.events[t].forEach(function(t){t.apply(void 0,e)})}},t.EventEmitter.prototype.hasHandler=function(t){return t in this.events},t.tokenizer=function(e){return arguments.length&&null!=e&&void 0!=e?Array.isArray(e)?e.map(function(e){return t.utils.asString(e).toLowerCase()}):e.toString().trim().toLowerCase().split(t.tokenizer.seperator):[]},t.tokenizer.seperator=/[\s\-]+/,t.tokenizer.load=function(t){var e=this.registeredFunctions[t];if(!e)throw new Error("Cannot load un-registered function: "+t);return e},t.tokenizer.label="default",t.tokenizer.registeredFunctions={"default":t.tokenizer},t.tokenizer.registerFunction=function(e,n){n in this.registeredFunctions&&t.utils.warn("Overwriting existing tokenizer: "+n),e.label=n,this.registeredFunctions[n]=e},t.Pipeline=function(){this._stack=[]},t.Pipeline.registeredFunctions={},t.Pipeline.registerFunction=function(e,n){n in this.registeredFunctions&&t.utils.warn("Overwriting existing registered function: "+n),e.label=n,t.Pipeline.registeredFunctions[e.label]=e},t.Pipeline.warnIfFunctionNotRegistered=function(e){var n=e.label&&e.label in this.registeredFunctions;n||t.utils.warn("Function is not registered with pipeline. This may cause problems when serialising the index.\n",e)},t.Pipeline.load=function(e){var n=new t.Pipeline;return e.forEach(function(e){var i=t.Pipeline.registeredFunctions[e];if(!i)throw new Error("Cannot load un-registered function: "+e);n.add(i)}),n},t.Pipeline.prototype.add=function(){var e=Array.prototype.slice.call(arguments);e.forEach(function(e){t.Pipeline.warnIfFunctionNotRegistered(e),this._stack.push(e)},this)},t.Pipeline.prototype.after=function(e,n){t.Pipeline.warnIfFunctionNotRegistered(n);var i=this._stack.indexOf(e);if(-1==i)throw new Error("Cannot find existingFn");i+=1,this._stack.splice(i,0,n)},t.Pipeline.prototype.before=function(e,n){t.Pipeline.warnIfFunctionNotRegistered(n);var i=this._stack.indexOf(e);if(-1==i)throw new Error("Cannot find existingFn");this._stack.splice(i,0,n)},t.Pipeline.prototype.remove=function(t){var e=this._stack.indexOf(t);-1!=e&&this._stack.splice(e,1)},t.Pipeline.prototype.run=function(t){for(var e=[],n=t.length,i=this._stack.length,r=0;n>r;r++){for(var o=t[r],s=0;i>s&&(o=this._stack[s](o,r,t),void 0!==o&&""!==o);s++);void 0!==o&&""!==o&&e.push(o)}return e},t.Pipeline.prototype.reset=function(){this._stack=[]},t.Pipeline.prototype.toJSON=function(){return this._stack.map(function(e){return t.Pipeline.warnIfFunctionNotRegistered(e),e.label})},t.Vector=function(){this._magnitude=null,this.list=void 0,this.length=0},t.Vector.Node=function(t,e,n){this.idx=t,this.val=e,this.next=n},t.Vector.prototype.insert=function(e,n){this._magnitude=void 0;var i=this.list;if(!i)return this.list=new t.Vector.Node(e,n,i),this.length++;if(e<i.idx)return this.list=new t.Vector.Node(e,n,i),this.length++;for(var r=i,o=i.next;void 0!=o;){if(e<o.idx)return r.next=new t.Vector.Node(e,n,o),this.length++;r=o,o=o.next}return r.next=new t.Vector.Node(e,n,o),this.length++},t.Vector.prototype.magnitude=function(){if(this._magnitude)return this._magnitude;for(var t,e=this.list,n=0;e;)t=e.val,n+=t*t,e=e.next;return this._magnitude=Math.sqrt(n)},t.Vector.prototype.dot=function(t){for(var e=this.list,n=t.list,i=0;e&&n;)e.idx<n.idx?e=e.next:e.idx>n.idx?n=n.next:(i+=e.val*n.val,e=e.next,n=n.next);return i},t.Vector.prototype.similarity=function(t){return this.dot(t)/(this.magnitude()*t.magnitude())},t.SortedSet=function(){this.length=0,this.elements=[]},t.SortedSet.load=function(t){var e=new this;return e.elements=t,e.length=t.length,e},t.SortedSet.prototype.add=function(){var t,e;for(t=0;t<arguments.length;t++)e=arguments[t],~this.indexOf(e)||this.elements.splice(this.locationFor(e),0,e);this.length=this.elements.length},t.SortedSet.prototype.toArray=function(){return this.elements.slice()},t.SortedSet.prototype.map=function(t,e){return this.elements.map(t,e)},t.SortedSet.prototype.forEach=function(t,e){return this.elements.forEach(t,e)},t.SortedSet.prototype.indexOf=function(t){for(var e=0,n=this.elements.length,i=n-e,r=e+Math.floor(i/2),o=this.elements[r];i>1;){if(o===t)return r;t>o&&(e=r),o>t&&(n=r),i=n-e,r=e+Math.floor(i/2),o=this.elements[r]}return o===t?r:-1},t.SortedSet.prototype.locationFor=function(t){for(var e=0,n=this.elements.length,i=n-e,r=e+Math.floor(i/2),o=this.elements[r];i>1;)t>o&&(e=r),o>t&&(n=r),i=n-e,r=e+Math.floor(i/2),o=this.elements[r];return o>t?r:t>o?r+1:void 0},t.SortedSet.prototype.intersect=function(e){for(var n=new t.SortedSet,i=0,r=0,o=this.length,s=e.length,a=this.elements,h=e.elements;;){if(i>o-1||r>s-1)break;a[i]!==h[r]?a[i]<h[r]?i++:a[i]>h[r]&&r++:(n.add(a[i]),i++,r++)}return n},t.SortedSet.prototype.clone=function(){var e=new t.SortedSet;return e.elements=this.toArray(),e.length=e.elements.length,e},t.SortedSet.prototype.union=function(t){var e,n,i;this.length>=t.length?(e=this,n=t):(e=t,n=this),i=e.clone();for(var r=0,o=n.toArray();r<o.length;r++)i.add(o[r]);return i},t.SortedSet.prototype.toJSON=function(){return this.toArray()},t.Index=function(){this._fields=[],this._ref="id",this.pipeline=new t.Pipeline,this.documentStore=new t.Store,this.tokenStore=new t.TokenStore,this.corpusTokens=new t.SortedSet,this.eventEmitter=new t.EventEmitter,this.tokenizerFn=t.tokenizer,this._idfCache={},this.on("add","remove","update",function(){this._idfCache={}}.bind(this))},t.Index.prototype.on=function(){var t=Array.prototype.slice.call(arguments);return this.eventEmitter.addListener.apply(this.eventEmitter,t)},t.Index.prototype.off=function(t,e){return this.eventEmitter.removeListener(t,e)},t.Index.load=function(e){e.version!==t.version&&t.utils.warn("version mismatch: current "+t.version+" importing "+e.version);var n=new this;return n._fields=e.fields,n._ref=e.ref,n.tokenizer=t.tokenizer.load(e.tokenizer),n.documentStore=t.Store.load(e.documentStore),n.tokenStore=t.TokenStore.load(e.tokenStore),n.corpusTokens=t.SortedSet.load(e.corpusTokens),n.pipeline=t.Pipeline.load(e.pipeline),n},t.Index.prototype.field=function(t,e){var e=e||{},n={name:t,boost:e.boost||1};return this._fields.push(n),this},t.Index.prototype.ref=function(t){return this._ref=t,this},t.Index.prototype.tokenizer=function(e){var n=e.label&&e.label in t.tokenizer.registeredFunctions;return n||t.utils.warn("Function is not a registered tokenizer. This may cause problems when serialising the index"),this.tokenizerFn=e,this},t.Index.prototype.add=function(e,n){var i={},r=new t.SortedSet,o=e[this._ref],n=void 0===n?!0:n;this._fields.forEach(function(t){var n=this.pipeline.run(this.tokenizerFn(e[t.name]));i[t.name]=n;for(var o=0;o<n.length;o++){var s=n[o];r.add(s),this.corpusTokens.add(s)}},this),this.documentStore.set(o,r);for(var s=0;s<r.length;s++){for(var a=r.elements[s],h=0,u=0;u<this._fields.length;u++){var l=this._fields[u],c=i[l.name],f=c.length;if(f){for(var d=0,p=0;f>p;p++)c[p]===a&&d++;h+=d/f*l.boost}}this.tokenStore.add(a,{ref:o,tf:h})}n&&this.eventEmitter.emit("add",e,this)},t.Index.prototype.remove=function(t,e){var n=t[this._ref],e=void 0===e?!0:e;if(this.documentStore.has(n)){var i=this.documentStore.get(n);this.documentStore.remove(n),i.forEach(function(t){this.tokenStore.remove(t,n)},this),e&&this.eventEmitter.emit("remove",t,this)}},t.Index.prototype.update=function(t,e){var e=void 0===e?!0:e;this.remove(t,!1),this.add(t,!1),e&&this.eventEmitter.emit("update",t,this)},t.Index.prototype.idf=function(t){var e="@"+t;if(Object.prototype.hasOwnProperty.call(this._idfCache,e))return this._idfCache[e];var n=this.tokenStore.count(t),i=1;return n>0&&(i=1+Math.log(this.documentStore.length/n)),this._idfCache[e]=i},t.Index.prototype.search=function(e){var n=this.pipeline.run(this.tokenizerFn(e)),i=new t.Vector,r=[],o=this._fields.reduce(function(t,e){return t+e.boost},0),s=n.some(function(t){return this.tokenStore.has(t)},this);if(!s)return[];n.forEach(function(e,n,s){var a=1/s.length*this._fields.length*o,h=this,u=this.tokenStore.expand(e).reduce(function(n,r){var o=h.corpusTokens.indexOf(r),s=h.idf(r),u=1,l=new t.SortedSet;if(r!==e){var c=Math.max(3,r.length-e.length);u=1/Math.log(c)}o>-1&&i.insert(o,a*s*u);for(var f=h.tokenStore.get(r),d=Object.keys(f),p=d.length,v=0;p>v;v++)l.add(f[d[v]].ref);return n.union(l)},new t.SortedSet);r.push(u)},this);var a=r.reduce(function(t,e){return t.intersect(e)});return a.map(function(t){return{ref:t,score:i.similarity(this.documentVector(t))}},this).sort(function(t,e){return e.score-t.score})},t.Index.prototype.documentVector=function(e){for(var n=this.documentStore.get(e),i=n.length,r=new t.Vector,o=0;i>o;o++){var s=n.elements[o],a=this.tokenStore.get(s)[e].tf,h=this.idf(s);r.insert(this.corpusTokens.indexOf(s),a*h)}return r},t.Index.prototype.toJSON=function(){return{version:t.version,fields:this._fields,ref:this._ref,tokenizer:this.tokenizerFn.label,documentStore:this.documentStore.toJSON(),tokenStore:this.tokenStore.toJSON(),corpusTokens:this.corpusTokens.toJSON(),pipeline:this.pipeline.toJSON()}},t.Index.prototype.use=function(t){var e=Array.prototype.slice.call(arguments,1);e.unshift(this),t.apply(this,e)},t.Store=function(){this.store={},this.length=0},t.Store.load=function(e){var n=new this;return n.length=e.length,n.store=Object.keys(e.store).reduce(function(n,i){return n[i]=t.SortedSet.load(e.store[i]),n},{}),n},t.Store.prototype.set=function(t,e){this.has(t)||this.length++,this.store[t]=e},t.Store.prototype.get=function(t){return this.store[t]},t.Store.prototype.has=function(t){return t in this.store},t.Store.prototype.remove=function(t){this.has(t)&&(delete this.store[t],this.length--)},t.Store.prototype.toJSON=function(){return{store:this.store,length:this.length}},t.stemmer=function(){var t={ational:"ate",tional:"tion",enci:"ence",anci:"ance",izer:"ize",bli:"ble",alli:"al",entli:"ent",eli:"e",ousli:"ous",ization:"ize",ation:"ate",ator:"ate",alism:"al",iveness:"ive",fulness:"ful",ousness:"ous",aliti:"al",iviti:"ive",biliti:"ble",logi:"log"},e={icate:"ic",ative:"",alize:"al",iciti:"ic",ical:"ic",ful:"",ness:""},n="[^aeiou]",i="[aeiouy]",r=n+"[^aeiouy]*",o=i+"[aeiou]*",s="^("+r+")?"+o+r,a="^("+r+")?"+o+r+"("+o+")?$",h="^("+r+")?"+o+r+o+r,u="^("+r+")?"+i,l=new RegExp(s),c=new RegExp(h),f=new RegExp(a),d=new RegExp(u),p=/^(.+?)(ss|i)es$/,v=/^(.+?)([^s])s$/,g=/^(.+?)eed$/,m=/^(.+?)(ed|ing)$/,y=/.$/,S=/(at|bl|iz)$/,w=new RegExp("([^aeiouylsz])\\1$"),k=new RegExp("^"+r+i+"[^aeiouwxy]$"),x=/^(.+?[^aeiou])y$/,b=/^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/,E=/^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/,F=/^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/,_=/^(.+?)(s|t)(ion)$/,z=/^(.+?)e$/,O=/ll$/,P=new RegExp("^"+r+i+"[^aeiouwxy]$"),T=function(n){var i,r,o,s,a,h,u;if(n.length<3)return n;if(o=n.substr(0,1),"y"==o&&(n=o.toUpperCase()+n.substr(1)),s=p,a=v,s.test(n)?n=n.replace(s,"$1$2"):a.test(n)&&(n=n.replace(a,"$1$2")),s=g,a=m,s.test(n)){var T=s.exec(n);s=l,s.test(T[1])&&(s=y,n=n.replace(s,""))}else if(a.test(n)){var T=a.exec(n);i=T[1],a=d,a.test(i)&&(n=i,a=S,h=w,u=k,a.test(n)?n+="e":h.test(n)?(s=y,n=n.replace(s,"")):u.test(n)&&(n+="e"))}if(s=x,s.test(n)){var T=s.exec(n);i=T[1],n=i+"i"}if(s=b,s.test(n)){var T=s.exec(n);i=T[1],r=T[2],s=l,s.test(i)&&(n=i+t[r])}if(s=E,s.test(n)){var T=s.exec(n);i=T[1],r=T[2],s=l,s.test(i)&&(n=i+e[r])}if(s=F,a=_,s.test(n)){var T=s.exec(n);i=T[1],s=c,s.test(i)&&(n=i)}else if(a.test(n)){var T=a.exec(n);i=T[1]+T[2],a=c,a.test(i)&&(n=i)}if(s=z,s.test(n)){var T=s.exec(n);i=T[1],s=c,a=f,h=P,(s.test(i)||a.test(i)&&!h.test(i))&&(n=i)}return s=O,a=c,s.test(n)&&a.test(n)&&(s=y,n=n.replace(s,"")),"y"==o&&(n=o.toLowerCase()+n.substr(1)),n};return T}(),t.Pipeline.registerFunction(t.stemmer,"stemmer"),t.generateStopWordFilter=function(t){var e=t.reduce(function(t,e){return t[e]=e,t},{});return function(t){return t&&e[t]!==t?t:void 0}},t.stopWordFilter=t.generateStopWordFilter(["a","able","about","across","after","all","almost","also","am","among","an","and","any","are","as","at","be","because","been","but","by","can","cannot","could","dear","did","do","does","either","else","ever","every","for","from","get","got","had","has","have","he","her","hers","him","his","how","however","i","if","in","into","is","it","its","just","least","let","like","likely","may","me","might","most","must","my","neither","no","nor","not","of","off","often","on","only","or","other","our","own","rather","said","say","says","she","should","since","so","some","than","that","the","their","them","then","there","these","they","this","tis","to","too","twas","us","wants","was","we","were","what","when","where","which","while","who","whom","why","will","with","would","yet","you","your"]),t.Pipeline.registerFunction(t.stopWordFilter,"stopWordFilter"),t.trimmer=function(t){return t.replace(/^\W+/,"").replace(/\W+$/,"")},t.Pipeline.registerFunction(t.trimmer,"trimmer"),t.TokenStore=function(){this.root={docs:{}},this.length=0},t.TokenStore.load=function(t){var e=new this;return e.root=t.root,e.length=t.length,e},t.TokenStore.prototype.add=function(t,e,n){var n=n||this.root,i=t.charAt(0),r=t.slice(1);return i in n||(n[i]={docs:{}}),0===r.length?(n[i].docs[e.ref]=e,void(this.length+=1)):this.add(r,e,n[i])},t.TokenStore.prototype.has=function(t){if(!t)return!1;for(var e=this.root,n=0;n<t.length;n++){if(!e[t.charAt(n)])return!1;e=e[t.charAt(n)]}return!0},t.TokenStore.prototype.getNode=function(t){if(!t)return{};for(var e=this.root,n=0;n<t.length;n++){if(!e[t.charAt(n)])return{};e=e[t.charAt(n)]}return e},t.TokenStore.prototype.get=function(t,e){return this.getNode(t,e).docs||{}},t.TokenStore.prototype.count=function(t,e){return Object.keys(this.get(t,e)).length},t.TokenStore.prototype.remove=function(t,e){if(t){for(var n=this.root,i=0;i<t.length;i++){if(!(t.charAt(i)in n))return;n=n[t.charAt(i)]}delete n.docs[e]}},t.TokenStore.prototype.expand=function(t,e){var n=this.getNode(t),i=n.docs||{},e=e||[];return Object.keys(i).length&&e.push(t),Object.keys(n).forEach(function(n){"docs"!==n&&e.concat(this.expand(t+n,e))},this),e},t.TokenStore.prototype.toJSON=function(){return{root:this.root,length:this.length}},function(t,e){"function"==typeof define&&define.amd?define(e):"object"==typeof exports?module.exports=e():t.lunr=e()}(this,function(){return t})}();
diff --git a/mkdocs/js/mustache.min.js b/mkdocs/js/mustache.min.js
deleted file mode 100644
index 7fc6da86..00000000
--- a/mkdocs/js/mustache.min.js
+++ /dev/null
@@ -1 +0,0 @@
-(function(global,factory){if(typeof exports==="object"&&exports){factory(exports)}else if(typeof define==="function"&&define.amd){define(["exports"],factory)}else{factory(global.Mustache={})}})(this,function(mustache){var Object_toString=Object.prototype.toString;var isArray=Array.isArray||function(object){return Object_toString.call(object)==="[object Array]"};function isFunction(object){return typeof object==="function"}function escapeRegExp(string){return string.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g,"\\$&")}var RegExp_test=RegExp.prototype.test;function testRegExp(re,string){return RegExp_test.call(re,string)}var nonSpaceRe=/\S/;function isWhitespace(string){return!testRegExp(nonSpaceRe,string)}var entityMap={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;","/":"&#x2F;"};function escapeHtml(string){return String(string).replace(/[&<>"'\/]/g,function(s){return entityMap[s]})}var whiteRe=/\s*/;var spaceRe=/\s+/;var equalsRe=/\s*=/;var curlyRe=/\s*\}/;var tagRe=/#|\^|\/|>|\{|&|=|!/;function parseTemplate(template,tags){if(!template)return[];var sections=[];var tokens=[];var spaces=[];var hasTag=false;var nonSpace=false;function stripSpace(){if(hasTag&&!nonSpace){while(spaces.length)delete tokens[spaces.pop()]}else{spaces=[]}hasTag=false;nonSpace=false}var openingTagRe,closingTagRe,closingCurlyRe;function compileTags(tags){if(typeof tags==="string")tags=tags.split(spaceRe,2);if(!isArray(tags)||tags.length!==2)throw new Error("Invalid tags: "+tags);openingTagRe=new RegExp(escapeRegExp(tags[0])+"\\s*");closingTagRe=new RegExp("\\s*"+escapeRegExp(tags[1]));closingCurlyRe=new RegExp("\\s*"+escapeRegExp("}"+tags[1]))}compileTags(tags||mustache.tags);var scanner=new Scanner(template);var start,type,value,chr,token,openSection;while(!scanner.eos()){start=scanner.pos;value=scanner.scanUntil(openingTagRe);if(value){for(var i=0,valueLength=value.length;i<valueLength;++i){chr=value.charAt(i);if(isWhitespace(chr)){spaces.push(tokens.length)}else{nonSpace=true}tokens.push(["text",chr,start,start+1]);start+=1;if(chr==="\n")stripSpace()}}if(!scanner.scan(openingTagRe))break;hasTag=true;type=scanner.scan(tagRe)||"name";scanner.scan(whiteRe);if(type==="="){value=scanner.scanUntil(equalsRe);scanner.scan(equalsRe);scanner.scanUntil(closingTagRe)}else if(type==="{"){value=scanner.scanUntil(closingCurlyRe);scanner.scan(curlyRe);scanner.scanUntil(closingTagRe);type="&"}else{value=scanner.scanUntil(closingTagRe)}if(!scanner.scan(closingTagRe))throw new Error("Unclosed tag at "+scanner.pos);token=[type,value,start,scanner.pos];tokens.push(token);if(type==="#"||type==="^"){sections.push(token)}else if(type==="/"){openSection=sections.pop();if(!openSection)throw new Error('Unopened section "'+value+'" at '+start);if(openSection[1]!==value)throw new Error('Unclosed section "'+openSection[1]+'" at '+start)}else if(type==="name"||type==="{"||type==="&"){nonSpace=true}else if(type==="="){compileTags(value)}}openSection=sections.pop();if(openSection)throw new Error('Unclosed section "'+openSection[1]+'" at '+scanner.pos);return nestTokens(squashTokens(tokens))}function squashTokens(tokens){var squashedTokens=[];var token,lastToken;for(var i=0,numTokens=tokens.length;i<numTokens;++i){token=tokens[i];if(token){if(token[0]==="text"&&lastToken&&lastToken[0]==="text"){lastToken[1]+=token[1];lastToken[3]=token[3]}else{squashedTokens.push(token);lastToken=token}}}return squashedTokens}function nestTokens(tokens){var nestedTokens=[];var collector=nestedTokens;var sections=[];var token,section;for(var i=0,numTokens=tokens.length;i<numTokens;++i){token=tokens[i];switch(token[0]){case"#":case"^":collector.push(token);sections.push(token);collector=token[4]=[];break;case"/":section=sections.pop();section[5]=token[2];collector=sections.length>0?sections[sections.length-1][4]:nestedTokens;break;default:collector.push(token)}}return nestedTokens}function Scanner(string){this.string=string;this.tail=string;this.pos=0}Scanner.prototype.eos=function(){return this.tail===""};Scanner.prototype.scan=function(re){var match=this.tail.match(re);if(!match||match.index!==0)return"";var string=match[0];this.tail=this.tail.substring(string.length);this.pos+=string.length;return string};Scanner.prototype.scanUntil=function(re){var index=this.tail.search(re),match;switch(index){case-1:match=this.tail;this.tail="";break;case 0:match="";break;default:match=this.tail.substring(0,index);this.tail=this.tail.substring(index)}this.pos+=match.length;return match};function Context(view,parentContext){this.view=view;this.cache={".":this.view};this.parent=parentContext}Context.prototype.push=function(view){return new Context(view,this)};Context.prototype.lookup=function(name){var cache=this.cache;var value;if(name in cache){value=cache[name]}else{var context=this,names,index,lookupHit=false;while(context){if(name.indexOf(".")>0){value=context.view;names=name.split(".");index=0;while(value!=null&&index<names.length){if(index===names.length-1&&value!=null)lookupHit=typeof value==="object"&&value.hasOwnProperty(names[index]);value=value[names[index++]]}}else if(context.view!=null&&typeof context.view==="object"){value=context.view[name];lookupHit=context.view.hasOwnProperty(name)}if(lookupHit)break;context=context.parent}cache[name]=value}if(isFunction(value))value=value.call(this.view);return value};function Writer(){this.cache={}}Writer.prototype.clearCache=function(){this.cache={}};Writer.prototype.parse=function(template,tags){var cache=this.cache;var tokens=cache[template];if(tokens==null)tokens=cache[template]=parseTemplate(template,tags);return tokens};Writer.prototype.render=function(template,view,partials){var tokens=this.parse(template);var context=view instanceof Context?view:new Context(view);return this.renderTokens(tokens,context,partials,template)};Writer.prototype.renderTokens=function(tokens,context,partials,originalTemplate){var buffer="";var token,symbol,value;for(var i=0,numTokens=tokens.length;i<numTokens;++i){value=undefined;token=tokens[i];symbol=token[0];if(symbol==="#")value=this._renderSection(token,context,partials,originalTemplate);else if(symbol==="^")value=this._renderInverted(token,context,partials,originalTemplate);else if(symbol===">")value=this._renderPartial(token,context,partials,originalTemplate);else if(symbol==="&")value=this._unescapedValue(token,context);else if(symbol==="name")value=this._escapedValue(token,context);else if(symbol==="text")value=this._rawValue(token);if(value!==undefined)buffer+=value}return buffer};Writer.prototype._renderSection=function(token,context,partials,originalTemplate){var self=this;var buffer="";var value=context.lookup(token[1]);function subRender(template){return self.render(template,context,partials)}if(!value)return;if(isArray(value)){for(var j=0,valueLength=value.length;j<valueLength;++j){buffer+=this.renderTokens(token[4],context.push(value[j]),partials,originalTemplate)}}else if(typeof value==="object"||typeof value==="string"||typeof value==="number"){buffer+=this.renderTokens(token[4],context.push(value),partials,originalTemplate)}else if(isFunction(value)){if(typeof originalTemplate!=="string")throw new Error("Cannot use higher-order sections without the original template");value=value.call(context.view,originalTemplate.slice(token[3],token[5]),subRender);if(value!=null)buffer+=value}else{buffer+=this.renderTokens(token[4],context,partials,originalTemplate)}return buffer};Writer.prototype._renderInverted=function(token,context,partials,originalTemplate){var value=context.lookup(token[1]);if(!value||isArray(value)&&value.length===0)return this.renderTokens(token[4],context,partials,originalTemplate)};Writer.prototype._renderPartial=function(token,context,partials){if(!partials)return;var value=isFunction(partials)?partials(token[1]):partials[token[1]];if(value!=null)return this.renderTokens(this.parse(value),context,partials,value)};Writer.prototype._unescapedValue=function(token,context){var value=context.lookup(token[1]);if(value!=null)return value};Writer.prototype._escapedValue=function(token,context){var value=context.lookup(token[1]);if(value!=null)return mustache.escape(value)};Writer.prototype._rawValue=function(token){return token[1]};mustache.name="mustache.js";mustache.version="2.0.0";mustache.tags=["{{","}}"];var defaultWriter=new Writer;mustache.clearCache=function(){return defaultWriter.clearCache()};mustache.parse=function(template,tags){return defaultWriter.parse(template,tags)};mustache.render=function(template,view,partials){return defaultWriter.render(template,view,partials)};mustache.to_html=function(template,view,partials,send){var result=mustache.render(template,view,partials);if(isFunction(send)){send(result)}else{return result}};mustache.escape=escapeHtml;mustache.Scanner=Scanner;mustache.Context=Context;mustache.Writer=Writer});
\ No newline at end of file
diff --git a/mkdocs/js/require.js b/mkdocs/js/require.js
deleted file mode 100644
index 8638a310..00000000
--- a/mkdocs/js/require.js
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- RequireJS 2.1.16 Copyright (c) 2010-2015, The Dojo Foundation All Rights Reserved.
- Available via the MIT or new BSD license.
- see: http://github.com/jrburke/requirejs for details
-*/
-var requirejs,require,define;
-(function(ba){function G(b){return"[object Function]"===K.call(b)}function H(b){return"[object Array]"===K.call(b)}function v(b,c){if(b){var d;for(d=0;d<b.length&&(!b[d]||!c(b[d],d,b));d+=1);}}function T(b,c){if(b){var d;for(d=b.length-1;-1<d&&(!b[d]||!c(b[d],d,b));d-=1);}}function t(b,c){return fa.call(b,c)}function m(b,c){return t(b,c)&&b[c]}function B(b,c){for(var d in b)if(t(b,d)&&c(b[d],d))break}function U(b,c,d,e){c&&B(c,function(c,g){if(d||!t(b,g))e&&"object"===typeof c&&c&&!H(c)&&!G(c)&&!(c instanceof
-RegExp)?(b[g]||(b[g]={}),U(b[g],c,d,e)):b[g]=c});return b}function u(b,c){return function(){return c.apply(b,arguments)}}function ca(b){throw b;}function da(b){if(!b)return b;var c=ba;v(b.split("."),function(b){c=c[b]});return c}function C(b,c,d,e){c=Error(c+"\nhttp://requirejs.org/docs/errors.html#"+b);c.requireType=b;c.requireModules=e;d&&(c.originalError=d);return c}function ga(b){function c(a,k,b){var f,l,c,d,e,g,i,p,k=k&&k.split("/"),h=j.map,n=h&&h["*"];if(a){a=a.split("/");l=a.length-1;j.nodeIdCompat&&
-Q.test(a[l])&&(a[l]=a[l].replace(Q,""));"."===a[0].charAt(0)&&k&&(l=k.slice(0,k.length-1),a=l.concat(a));l=a;for(c=0;c<l.length;c++)if(d=l[c],"."===d)l.splice(c,1),c-=1;else if(".."===d&&!(0===c||1==c&&".."===l[2]||".."===l[c-1])&&0<c)l.splice(c-1,2),c-=2;a=a.join("/")}if(b&&h&&(k||n)){l=a.split("/");c=l.length;a:for(;0<c;c-=1){e=l.slice(0,c).join("/");if(k)for(d=k.length;0<d;d-=1)if(b=m(h,k.slice(0,d).join("/")))if(b=m(b,e)){f=b;g=c;break a}!i&&(n&&m(n,e))&&(i=m(n,e),p=c)}!f&&i&&(f=i,g=p);f&&(l.splice(0,
-g,f),a=l.join("/"))}return(f=m(j.pkgs,a))?f:a}function d(a){z&&v(document.getElementsByTagName("script"),function(k){if(k.getAttribute("data-requiremodule")===a&&k.getAttribute("data-requirecontext")===i.contextName)return k.parentNode.removeChild(k),!0})}function e(a){var k=m(j.paths,a);if(k&&H(k)&&1<k.length)return k.shift(),i.require.undef(a),i.makeRequire(null,{skipMap:!0})([a]),!0}function n(a){var k,c=a?a.indexOf("!"):-1;-1<c&&(k=a.substring(0,c),a=a.substring(c+1,a.length));return[k,a]}function p(a,
-k,b,f){var l,d,e=null,g=k?k.name:null,j=a,p=!0,h="";a||(p=!1,a="_@r"+(K+=1));a=n(a);e=a[0];a=a[1];e&&(e=c(e,g,f),d=m(r,e));a&&(e?h=d&&d.normalize?d.normalize(a,function(a){return c(a,g,f)}):-1===a.indexOf("!")?c(a,g,f):a:(h=c(a,g,f),a=n(h),e=a[0],h=a[1],b=!0,l=i.nameToUrl(h)));b=e&&!d&&!b?"_unnormalized"+(O+=1):"";return{prefix:e,name:h,parentMap:k,unnormalized:!!b,url:l,originalName:j,isDefine:p,id:(e?e+"!"+h:h)+b}}function s(a){var k=a.id,b=m(h,k);b||(b=h[k]=new i.Module(a));return b}function q(a,
-k,b){var f=a.id,c=m(h,f);if(t(r,f)&&(!c||c.defineEmitComplete))"defined"===k&&b(r[f]);else if(c=s(a),c.error&&"error"===k)b(c.error);else c.on(k,b)}function w(a,b){var c=a.requireModules,f=!1;if(b)b(a);else if(v(c,function(b){if(b=m(h,b))b.error=a,b.events.error&&(f=!0,b.emit("error",a))}),!f)g.onError(a)}function x(){R.length&&(ha.apply(A,[A.length,0].concat(R)),R=[])}function y(a){delete h[a];delete V[a]}function F(a,b,c){var f=a.map.id;a.error?a.emit("error",a.error):(b[f]=!0,v(a.depMaps,function(f,
-d){var e=f.id,g=m(h,e);g&&(!a.depMatched[d]&&!c[e])&&(m(b,e)?(a.defineDep(d,r[e]),a.check()):F(g,b,c))}),c[f]=!0)}function D(){var a,b,c=(a=1E3*j.waitSeconds)&&i.startTime+a<(new Date).getTime(),f=[],l=[],g=!1,h=!0;if(!W){W=!0;B(V,function(a){var i=a.map,j=i.id;if(a.enabled&&(i.isDefine||l.push(a),!a.error))if(!a.inited&&c)e(j)?g=b=!0:(f.push(j),d(j));else if(!a.inited&&(a.fetched&&i.isDefine)&&(g=!0,!i.prefix))return h=!1});if(c&&f.length)return a=C("timeout","Load timeout for modules: "+f,null,
-f),a.contextName=i.contextName,w(a);h&&v(l,function(a){F(a,{},{})});if((!c||b)&&g)if((z||ea)&&!X)X=setTimeout(function(){X=0;D()},50);W=!1}}function E(a){t(r,a[0])||s(p(a[0],null,!0)).init(a[1],a[2])}function I(a){var a=a.currentTarget||a.srcElement,b=i.onScriptLoad;a.detachEvent&&!Y?a.detachEvent("onreadystatechange",b):a.removeEventListener("load",b,!1);b=i.onScriptError;(!a.detachEvent||Y)&&a.removeEventListener("error",b,!1);return{node:a,id:a&&a.getAttribute("data-requiremodule")}}function J(){var a;
-for(x();A.length;){a=A.shift();if(null===a[0])return w(C("mismatch","Mismatched anonymous define() module: "+a[a.length-1]));E(a)}}var W,Z,i,L,X,j={waitSeconds:7,baseUrl:"./",paths:{},bundles:{},pkgs:{},shim:{},config:{}},h={},V={},$={},A=[],r={},S={},aa={},K=1,O=1;L={require:function(a){return a.require?a.require:a.require=i.makeRequire(a.map)},exports:function(a){a.usingExports=!0;if(a.map.isDefine)return a.exports?r[a.map.id]=a.exports:a.exports=r[a.map.id]={}},module:function(a){return a.module?
-a.module:a.module={id:a.map.id,uri:a.map.url,config:function(){return m(j.config,a.map.id)||{}},exports:a.exports||(a.exports={})}}};Z=function(a){this.events=m($,a.id)||{};this.map=a;this.shim=m(j.shim,a.id);this.depExports=[];this.depMaps=[];this.depMatched=[];this.pluginMaps={};this.depCount=0};Z.prototype={init:function(a,b,c,f){f=f||{};if(!this.inited){this.factory=b;if(c)this.on("error",c);else this.events.error&&(c=u(this,function(a){this.emit("error",a)}));this.depMaps=a&&a.slice(0);this.errback=
-c;this.inited=!0;this.ignore=f.ignore;f.enabled||this.enabled?this.enable():this.check()}},defineDep:function(a,b){this.depMatched[a]||(this.depMatched[a]=!0,this.depCount-=1,this.depExports[a]=b)},fetch:function(){if(!this.fetched){this.fetched=!0;i.startTime=(new Date).getTime();var a=this.map;if(this.shim)i.makeRequire(this.map,{enableBuildCallback:!0})(this.shim.deps||[],u(this,function(){return a.prefix?this.callPlugin():this.load()}));else return a.prefix?this.callPlugin():this.load()}},load:function(){var a=
-this.map.url;S[a]||(S[a]=!0,i.load(this.map.id,a))},check:function(){if(this.enabled&&!this.enabling){var a,b,c=this.map.id;b=this.depExports;var f=this.exports,l=this.factory;if(this.inited)if(this.error)this.emit("error",this.error);else{if(!this.defining){this.defining=!0;if(1>this.depCount&&!this.defined){if(G(l)){if(this.events.error&&this.map.isDefine||g.onError!==ca)try{f=i.execCb(c,l,b,f)}catch(d){a=d}else f=i.execCb(c,l,b,f);this.map.isDefine&&void 0===f&&((b=this.module)?f=b.exports:this.usingExports&&
-(f=this.exports));if(a)return a.requireMap=this.map,a.requireModules=this.map.isDefine?[this.map.id]:null,a.requireType=this.map.isDefine?"define":"require",w(this.error=a)}else f=l;this.exports=f;if(this.map.isDefine&&!this.ignore&&(r[c]=f,g.onResourceLoad))g.onResourceLoad(i,this.map,this.depMaps);y(c);this.defined=!0}this.defining=!1;this.defined&&!this.defineEmitted&&(this.defineEmitted=!0,this.emit("defined",this.exports),this.defineEmitComplete=!0)}}else this.fetch()}},callPlugin:function(){var a=
-this.map,b=a.id,d=p(a.prefix);this.depMaps.push(d);q(d,"defined",u(this,function(f){var l,d;d=m(aa,this.map.id);var e=this.map.name,P=this.map.parentMap?this.map.parentMap.name:null,n=i.makeRequire(a.parentMap,{enableBuildCallback:!0});if(this.map.unnormalized){if(f.normalize&&(e=f.normalize(e,function(a){return c(a,P,!0)})||""),f=p(a.prefix+"!"+e,this.map.parentMap),q(f,"defined",u(this,function(a){this.init([],function(){return a},null,{enabled:!0,ignore:!0})})),d=m(h,f.id)){this.depMaps.push(f);
-if(this.events.error)d.on("error",u(this,function(a){this.emit("error",a)}));d.enable()}}else d?(this.map.url=i.nameToUrl(d),this.load()):(l=u(this,function(a){this.init([],function(){return a},null,{enabled:!0})}),l.error=u(this,function(a){this.inited=!0;this.error=a;a.requireModules=[b];B(h,function(a){0===a.map.id.indexOf(b+"_unnormalized")&&y(a.map.id)});w(a)}),l.fromText=u(this,function(f,c){var d=a.name,e=p(d),P=M;c&&(f=c);P&&(M=!1);s(e);t(j.config,b)&&(j.config[d]=j.config[b]);try{g.exec(f)}catch(h){return w(C("fromtexteval",
-"fromText eval for "+b+" failed: "+h,h,[b]))}P&&(M=!0);this.depMaps.push(e);i.completeLoad(d);n([d],l)}),f.load(a.name,n,l,j))}));i.enable(d,this);this.pluginMaps[d.id]=d},enable:function(){V[this.map.id]=this;this.enabling=this.enabled=!0;v(this.depMaps,u(this,function(a,b){var c,f;if("string"===typeof a){a=p(a,this.map.isDefine?this.map:this.map.parentMap,!1,!this.skipMap);this.depMaps[b]=a;if(c=m(L,a.id)){this.depExports[b]=c(this);return}this.depCount+=1;q(a,"defined",u(this,function(a){this.defineDep(b,
-a);this.check()}));this.errback?q(a,"error",u(this,this.errback)):this.events.error&&q(a,"error",u(this,function(a){this.emit("error",a)}))}c=a.id;f=h[c];!t(L,c)&&(f&&!f.enabled)&&i.enable(a,this)}));B(this.pluginMaps,u(this,function(a){var b=m(h,a.id);b&&!b.enabled&&i.enable(a,this)}));this.enabling=!1;this.check()},on:function(a,b){var c=this.events[a];c||(c=this.events[a]=[]);c.push(b)},emit:function(a,b){v(this.events[a],function(a){a(b)});"error"===a&&delete this.events[a]}};i={config:j,contextName:b,
-registry:h,defined:r,urlFetched:S,defQueue:A,Module:Z,makeModuleMap:p,nextTick:g.nextTick,onError:w,configure:function(a){a.baseUrl&&"/"!==a.baseUrl.charAt(a.baseUrl.length-1)&&(a.baseUrl+="/");var b=j.shim,c={paths:!0,bundles:!0,config:!0,map:!0};B(a,function(a,b){c[b]?(j[b]||(j[b]={}),U(j[b],a,!0,!0)):j[b]=a});a.bundles&&B(a.bundles,function(a,b){v(a,function(a){a!==b&&(aa[a]=b)})});a.shim&&(B(a.shim,function(a,c){H(a)&&(a={deps:a});if((a.exports||a.init)&&!a.exportsFn)a.exportsFn=i.makeShimExports(a);
-b[c]=a}),j.shim=b);a.packages&&v(a.packages,function(a){var b,a="string"===typeof a?{name:a}:a;b=a.name;a.location&&(j.paths[b]=a.location);j.pkgs[b]=a.name+"/"+(a.main||"main").replace(ia,"").replace(Q,"")});B(h,function(a,b){!a.inited&&!a.map.unnormalized&&(a.map=p(b))});if(a.deps||a.callback)i.require(a.deps||[],a.callback)},makeShimExports:function(a){return function(){var b;a.init&&(b=a.init.apply(ba,arguments));return b||a.exports&&da(a.exports)}},makeRequire:function(a,e){function j(c,d,m){var n,
-q;e.enableBuildCallback&&(d&&G(d))&&(d.__requireJsBuild=!0);if("string"===typeof c){if(G(d))return w(C("requireargs","Invalid require call"),m);if(a&&t(L,c))return L[c](h[a.id]);if(g.get)return g.get(i,c,a,j);n=p(c,a,!1,!0);n=n.id;return!t(r,n)?w(C("notloaded",'Module name "'+n+'" has not been loaded yet for context: '+b+(a?"":". Use require([])"))):r[n]}J();i.nextTick(function(){J();q=s(p(null,a));q.skipMap=e.skipMap;q.init(c,d,m,{enabled:!0});D()});return j}e=e||{};U(j,{isBrowser:z,toUrl:function(b){var d,
-e=b.lastIndexOf("."),k=b.split("/")[0];if(-1!==e&&(!("."===k||".."===k)||1<e))d=b.substring(e,b.length),b=b.substring(0,e);return i.nameToUrl(c(b,a&&a.id,!0),d,!0)},defined:function(b){return t(r,p(b,a,!1,!0).id)},specified:function(b){b=p(b,a,!1,!0).id;return t(r,b)||t(h,b)}});a||(j.undef=function(b){x();var c=p(b,a,!0),e=m(h,b);d(b);delete r[b];delete S[c.url];delete $[b];T(A,function(a,c){a[0]===b&&A.splice(c,1)});e&&(e.events.defined&&($[b]=e.events),y(b))});return j},enable:function(a){m(h,a.id)&&
-s(a).enable()},completeLoad:function(a){var b,c,d=m(j.shim,a)||{},g=d.exports;for(x();A.length;){c=A.shift();if(null===c[0]){c[0]=a;if(b)break;b=!0}else c[0]===a&&(b=!0);E(c)}c=m(h,a);if(!b&&!t(r,a)&&c&&!c.inited){if(j.enforceDefine&&(!g||!da(g)))return e(a)?void 0:w(C("nodefine","No define call for "+a,null,[a]));E([a,d.deps||[],d.exportsFn])}D()},nameToUrl:function(a,b,c){var d,e,h;(d=m(j.pkgs,a))&&(a=d);if(d=m(aa,a))return i.nameToUrl(d,b,c);if(g.jsExtRegExp.test(a))d=a+(b||"");else{d=j.paths;
-a=a.split("/");for(e=a.length;0<e;e-=1)if(h=a.slice(0,e).join("/"),h=m(d,h)){H(h)&&(h=h[0]);a.splice(0,e,h);break}d=a.join("/");d+=b||(/^data\:|\?/.test(d)||c?"":".js");d=("/"===d.charAt(0)||d.match(/^[\w\+\.\-]+:/)?"":j.baseUrl)+d}return j.urlArgs?d+((-1===d.indexOf("?")?"?":"&")+j.urlArgs):d},load:function(a,b){g.load(i,a,b)},execCb:function(a,b,c,d){return b.apply(d,c)},onScriptLoad:function(a){if("load"===a.type||ja.test((a.currentTarget||a.srcElement).readyState))N=null,a=I(a),i.completeLoad(a.id)},
-onScriptError:function(a){var b=I(a);if(!e(b.id))return w(C("scripterror","Script error for: "+b.id,a,[b.id]))}};i.require=i.makeRequire();return i}var g,x,y,D,I,E,N,J,s,O,ka=/(\/\*([\s\S]*?)\*\/|([^:]|^)\/\/(.*)$)/mg,la=/[^.]\s*require\s*\(\s*["']([^'"\s]+)["']\s*\)/g,Q=/\.js$/,ia=/^\.\//;x=Object.prototype;var K=x.toString,fa=x.hasOwnProperty,ha=Array.prototype.splice,z=!!("undefined"!==typeof window&&"undefined"!==typeof navigator&&window.document),ea=!z&&"undefined"!==typeof importScripts,ja=
-z&&"PLAYSTATION 3"===navigator.platform?/^complete$/:/^(complete|loaded)$/,Y="undefined"!==typeof opera&&"[object Opera]"===opera.toString(),F={},q={},R=[],M=!1;if("undefined"===typeof define){if("undefined"!==typeof requirejs){if(G(requirejs))return;q=requirejs;requirejs=void 0}"undefined"!==typeof require&&!G(require)&&(q=require,require=void 0);g=requirejs=function(b,c,d,e){var n,p="_";!H(b)&&"string"!==typeof b&&(n=b,H(c)?(b=c,c=d,d=e):b=[]);n&&n.context&&(p=n.context);(e=m(F,p))||(e=F[p]=g.s.newContext(p));
-n&&e.configure(n);return e.require(b,c,d)};g.config=function(b){return g(b)};g.nextTick="undefined"!==typeof setTimeout?function(b){setTimeout(b,4)}:function(b){b()};require||(require=g);g.version="2.1.16";g.jsExtRegExp=/^\/|:|\?|\.js$/;g.isBrowser=z;x=g.s={contexts:F,newContext:ga};g({});v(["toUrl","undef","defined","specified"],function(b){g[b]=function(){var c=F._;return c.require[b].apply(c,arguments)}});if(z&&(y=x.head=document.getElementsByTagName("head")[0],D=document.getElementsByTagName("base")[0]))y=
-x.head=D.parentNode;g.onError=ca;g.createNode=function(b){var c=b.xhtml?document.createElementNS("http://www.w3.org/1999/xhtml","html:script"):document.createElement("script");c.type=b.scriptType||"text/javascript";c.charset="utf-8";c.async=!0;return c};g.load=function(b,c,d){var e=b&&b.config||{};if(z)return e=g.createNode(e,c,d),e.setAttribute("data-requirecontext",b.contextName),e.setAttribute("data-requiremodule",c),e.attachEvent&&!(e.attachEvent.toString&&0>e.attachEvent.toString().indexOf("[native code"))&&
-!Y?(M=!0,e.attachEvent("onreadystatechange",b.onScriptLoad)):(e.addEventListener("load",b.onScriptLoad,!1),e.addEventListener("error",b.onScriptError,!1)),e.src=d,J=e,D?y.insertBefore(e,D):y.appendChild(e),J=null,e;if(ea)try{importScripts(d),b.completeLoad(c)}catch(m){b.onError(C("importscripts","importScripts failed for "+c+" at "+d,m,[c]))}};z&&!q.skipDataMain&&T(document.getElementsByTagName("script"),function(b){y||(y=b.parentNode);if(I=b.getAttribute("data-main"))return s=I,q.baseUrl||(E=s.split("/"),
-s=E.pop(),O=E.length?E.join("/")+"/":"./",q.baseUrl=O),s=s.replace(Q,""),g.jsExtRegExp.test(s)&&(s=I),q.deps=q.deps?q.deps.concat(s):[s],!0});define=function(b,c,d){var e,g;"string"!==typeof b&&(d=c,c=b,b=null);H(c)||(d=c,c=null);!c&&G(d)&&(c=[],d.length&&(d.toString().replace(ka,"").replace(la,function(b,d){c.push(d)}),c=(1===d.length?["require"]:["require","exports","module"]).concat(c)));if(M){if(!(e=J))N&&"interactive"===N.readyState||T(document.getElementsByTagName("script"),function(b){if("interactive"===
-b.readyState)return N=b}),e=N;e&&(b||(b=e.getAttribute("data-requiremodule")),g=F[e.getAttribute("data-requirecontext")])}(g?g.defQueue:R).push([b,c,d])};define.amd={jQuery:!0};g.exec=function(b){return eval(b)};g(q)}})(this);
diff --git a/mkdocs/js/search-results-template.mustache b/mkdocs/js/search-results-template.mustache
deleted file mode 100644
index a8b3862f..00000000
--- a/mkdocs/js/search-results-template.mustache
+++ /dev/null
@@ -1,4 +0,0 @@
-<article>
-  <h3><a href="{{location}}">{{title}}</a></h3>
-  <p>{{summary}}</p>
-</article>
diff --git a/mkdocs/js/search.js b/mkdocs/js/search.js
deleted file mode 100644
index d5c86616..00000000
--- a/mkdocs/js/search.js
+++ /dev/null
@@ -1,88 +0,0 @@
-require([
-    base_url + '/mkdocs/js/mustache.min.js',
-    base_url + '/mkdocs/js/lunr.min.js',
-    'text!search-results-template.mustache',
-    'text!../search_index.json',
-], function (Mustache, lunr, results_template, data) {
-   "use strict";
-
-    function getSearchTerm()
-    {
-        var sPageURL = window.location.search.substring(1);
-        var sURLVariables = sPageURL.split('&');
-        for (var i = 0; i < sURLVariables.length; i++)
-        {
-            var sParameterName = sURLVariables[i].split('=');
-            if (sParameterName[0] == 'q')
-            {
-                return decodeURIComponent(sParameterName[1].replace(/\+/g, '%20'));
-            }
-        }
-    }
-
-    var index = lunr(function () {
-        this.field('title', {boost: 10});
-        this.field('text');
-        this.ref('location');
-    });
-
-    data = JSON.parse(data);
-    var documents = {};
-
-    for (var i=0; i < data.docs.length; i++){
-        var doc = data.docs[i];
-        doc.location = base_url + doc.location;
-        index.add(doc);
-        documents[doc.location] = doc;
-    }
-
-    var search = function(){
-
-        var query = document.getElementById('mkdocs-search-query').value;
-        var search_results = document.getElementById("mkdocs-search-results");
-        while (search_results.firstChild) {
-            search_results.removeChild(search_results.firstChild);
-        }
-
-        if(query === ''){
-            return;
-        }
-
-        var results = index.search(query);
-
-        if (results.length > 0){
-            for (var i=0; i < results.length; i++){
-                var result = results[i];
-                doc = documents[result.ref];
-                doc.base_url = base_url;
-                doc.summary = doc.text.substring(0, 200);
-                var html = Mustache.to_html(results_template, doc);
-                search_results.insertAdjacentHTML('beforeend', html);
-            }
-        } else {
-            search_results.insertAdjacentHTML('beforeend', "<p>No results found</p>");
-        }
-
-        if(jQuery){
-            /*
-             * We currently only automatically hide bootstrap models. This
-             * requires jQuery to work.
-             */
-            jQuery('#mkdocs_search_modal a').click(function(){
-                jQuery('#mkdocs_search_modal').modal('hide');
-            });
-        }
-
-    };
-
-    var search_input = document.getElementById('mkdocs-search-query');
-
-    var term = getSearchTerm();
-    if (term){
-        search_input.value = term;
-        search();
-    }
-
-    search_input.addEventListener("keyup", search);
-
-});
diff --git a/mkdocs/js/text.js b/mkdocs/js/text.js
deleted file mode 100644
index 17921b6e..00000000
--- a/mkdocs/js/text.js
+++ /dev/null
@@ -1,390 +0,0 @@
-/**
- * @license RequireJS text 2.0.12 Copyright (c) 2010-2014, The Dojo Foundation All Rights Reserved.
- * Available via the MIT or new BSD license.
- * see: http://github.com/requirejs/text for details
- */
-/*jslint regexp: true */
-/*global require, XMLHttpRequest, ActiveXObject,
-  define, window, process, Packages,
-  java, location, Components, FileUtils */
-
-define(['module'], function (module) {
-    'use strict';
-
-    var text, fs, Cc, Ci, xpcIsWindows,
-        progIds = ['Msxml2.XMLHTTP', 'Microsoft.XMLHTTP', 'Msxml2.XMLHTTP.4.0'],
-        xmlRegExp = /^\s*<\?xml(\s)+version=[\'\"](\d)*.(\d)*[\'\"](\s)*\?>/im,
-        bodyRegExp = /<body[^>]*>\s*([\s\S]+)\s*<\/body>/im,
-        hasLocation = typeof location !== 'undefined' && location.href,
-        defaultProtocol = hasLocation && location.protocol && location.protocol.replace(/\:/, ''),
-        defaultHostName = hasLocation && location.hostname,
-        defaultPort = hasLocation && (location.port || undefined),
-        buildMap = {},
-        masterConfig = (module.config && module.config()) || {};
-
-    text = {
-        version: '2.0.12',
-
-        strip: function (content) {
-            //Strips <?xml ...?> declarations so that external SVG and XML
-            //documents can be added to a document without worry. Also, if the string
-            //is an HTML document, only the part inside the body tag is returned.
-            if (content) {
-                content = content.replace(xmlRegExp, "");
-                var matches = content.match(bodyRegExp);
-                if (matches) {
-                    content = matches[1];
-                }
-            } else {
-                content = "";
-            }
-            return content;
-        },
-
-        jsEscape: function (content) {
-            return content.replace(/(['\\])/g, '\\$1')
-                .replace(/[\f]/g, "\\f")
-                .replace(/[\b]/g, "\\b")
-                .replace(/[\n]/g, "\\n")
-                .replace(/[\t]/g, "\\t")
-                .replace(/[\r]/g, "\\r")
-                .replace(/[\u2028]/g, "\\u2028")
-                .replace(/[\u2029]/g, "\\u2029");
-        },
-
-        createXhr: masterConfig.createXhr || function () {
-            //Would love to dump the ActiveX crap in here. Need IE 6 to die first.
-            var xhr, i, progId;
-            if (typeof XMLHttpRequest !== "undefined") {
-                return new XMLHttpRequest();
-            } else if (typeof ActiveXObject !== "undefined") {
-                for (i = 0; i < 3; i += 1) {
-                    progId = progIds[i];
-                    try {
-                        xhr = new ActiveXObject(progId);
-                    } catch (e) {}
-
-                    if (xhr) {
-                        progIds = [progId];  // so faster next time
-                        break;
-                    }
-                }
-            }
-
-            return xhr;
-        },
-
-        /**
-         * Parses a resource name into its component parts. Resource names
-         * look like: module/name.ext!strip, where the !strip part is
-         * optional.
-         * @param {String} name the resource name
-         * @returns {Object} with properties "moduleName", "ext" and "strip"
-         * where strip is a boolean.
-         */
-        parseName: function (name) {
-            var modName, ext, temp,
-                strip = false,
-                index = name.indexOf("."),
-                isRelative = name.indexOf('./') === 0 ||
-                             name.indexOf('../') === 0;
-
-            if (index !== -1 && (!isRelative || index > 1)) {
-                modName = name.substring(0, index);
-                ext = name.substring(index + 1, name.length);
-            } else {
-                modName = name;
-            }
-
-            temp = ext || modName;
-            index = temp.indexOf("!");
-            if (index !== -1) {
-                //Pull off the strip arg.
-                strip = temp.substring(index + 1) === "strip";
-                temp = temp.substring(0, index);
-                if (ext) {
-                    ext = temp;
-                } else {
-                    modName = temp;
-                }
-            }
-
-            return {
-                moduleName: modName,
-                ext: ext,
-                strip: strip
-            };
-        },
-
-        xdRegExp: /^((\w+)\:)?\/\/([^\/\\]+)/,
-
-        /**
-         * Is an URL on another domain. Only works for browser use, returns
-         * false in non-browser environments. Only used to know if an
-         * optimized .js version of a text resource should be loaded
-         * instead.
-         * @param {String} url
-         * @returns Boolean
-         */
-        useXhr: function (url, protocol, hostname, port) {
-            var uProtocol, uHostName, uPort,
-                match = text.xdRegExp.exec(url);
-            if (!match) {
-                return true;
-            }
-            uProtocol = match[2];
-            uHostName = match[3];
-
-            uHostName = uHostName.split(':');
-            uPort = uHostName[1];
-            uHostName = uHostName[0];
-
-            return (!uProtocol || uProtocol === protocol) &&
-                   (!uHostName || uHostName.toLowerCase() === hostname.toLowerCase()) &&
-                   ((!uPort && !uHostName) || uPort === port);
-        },
-
-        finishLoad: function (name, strip, content, onLoad) {
-            content = strip ? text.strip(content) : content;
-            if (masterConfig.isBuild) {
-                buildMap[name] = content;
-            }
-            onLoad(content);
-        },
-
-        load: function (name, req, onLoad, config) {
-            //Name has format: some.module.filext!strip
-            //The strip part is optional.
-            //if strip is present, then that means only get the string contents
-            //inside a body tag in an HTML string. For XML/SVG content it means
-            //removing the <?xml ...?> declarations so the content can be inserted
-            //into the current doc without problems.
-
-            // Do not bother with the work if a build and text will
-            // not be inlined.
-            if (config && config.isBuild && !config.inlineText) {
-                onLoad();
-                return;
-            }
-
-            masterConfig.isBuild = config && config.isBuild;
-
-            var parsed = text.parseName(name),
-                nonStripName = parsed.moduleName +
-                    (parsed.ext ? '.' + parsed.ext : ''),
-                url = req.toUrl(nonStripName),
-                useXhr = (masterConfig.useXhr) ||
-                         text.useXhr;
-
-            // Do not load if it is an empty: url
-            if (url.indexOf('empty:') === 0) {
-                onLoad();
-                return;
-            }
-
-            //Load the text. Use XHR if possible and in a browser.
-            if (!hasLocation || useXhr(url, defaultProtocol, defaultHostName, defaultPort)) {
-                text.get(url, function (content) {
-                    text.finishLoad(name, parsed.strip, content, onLoad);
-                }, function (err) {
-                    if (onLoad.error) {
-                        onLoad.error(err);
-                    }
-                });
-            } else {
-                //Need to fetch the resource across domains. Assume
-                //the resource has been optimized into a JS module. Fetch
-                //by the module name + extension, but do not include the
-                //!strip part to avoid file system issues.
-                req([nonStripName], function (content) {
-                    text.finishLoad(parsed.moduleName + '.' + parsed.ext,
-                                    parsed.strip, content, onLoad);
-                });
-            }
-        },
-
-        write: function (pluginName, moduleName, write, config) {
-            if (buildMap.hasOwnProperty(moduleName)) {
-                var content = text.jsEscape(buildMap[moduleName]);
-                write.asModule(pluginName + "!" + moduleName,
-                               "define(function () { return '" +
-                                   content +
-                               "';});\n");
-            }
-        },
-
-        writeFile: function (pluginName, moduleName, req, write, config) {
-            var parsed = text.parseName(moduleName),
-                extPart = parsed.ext ? '.' + parsed.ext : '',
-                nonStripName = parsed.moduleName + extPart,
-                //Use a '.js' file name so that it indicates it is a
-                //script that can be loaded across domains.
-                fileName = req.toUrl(parsed.moduleName + extPart) + '.js';
-
-            //Leverage own load() method to load plugin value, but only
-            //write out values that do not have the strip argument,
-            //to avoid any potential issues with ! in file names.
-            text.load(nonStripName, req, function (value) {
-                //Use own write() method to construct full module value.
-                //But need to create shell that translates writeFile's
-                //write() to the right interface.
-                var textWrite = function (contents) {
-                    return write(fileName, contents);
-                };
-                textWrite.asModule = function (moduleName, contents) {
-                    return write.asModule(moduleName, fileName, contents);
-                };
-
-                text.write(pluginName, nonStripName, textWrite, config);
-            }, config);
-        }
-    };
-
-    if (masterConfig.env === 'node' || (!masterConfig.env &&
-            typeof process !== "undefined" &&
-            process.versions &&
-            !!process.versions.node &&
-            !process.versions['node-webkit'])) {
-        //Using special require.nodeRequire, something added by r.js.
-        fs = require.nodeRequire('fs');
-
-        text.get = function (url, callback, errback) {
-            try {
-                var file = fs.readFileSync(url, 'utf8');
-                //Remove BOM (Byte Mark Order) from utf8 files if it is there.
-                if (file.indexOf('\uFEFF') === 0) {
-                    file = file.substring(1);
-                }
-                callback(file);
-            } catch (e) {
-                if (errback) {
-                    errback(e);
-                }
-            }
-        };
-    } else if (masterConfig.env === 'xhr' || (!masterConfig.env &&
-            text.createXhr())) {
-        text.get = function (url, callback, errback, headers) {
-            var xhr = text.createXhr(), header;
-            xhr.open('GET', url, true);
-
-            //Allow plugins direct access to xhr headers
-            if (headers) {
-                for (header in headers) {
-                    if (headers.hasOwnProperty(header)) {
-                        xhr.setRequestHeader(header.toLowerCase(), headers[header]);
-                    }
-                }
-            }
-
-            //Allow overrides specified in config
-            if (masterConfig.onXhr) {
-                masterConfig.onXhr(xhr, url);
-            }
-
-            xhr.onreadystatechange = function (evt) {
-                var status, err;
-                //Do not explicitly handle errors, those should be
-                //visible via console output in the browser.
-                if (xhr.readyState === 4) {
-                    status = xhr.status || 0;
-                    if (status > 399 && status < 600) {
-                        //An http 4xx or 5xx error. Signal an error.
-                        err = new Error(url + ' HTTP status: ' + status);
-                        err.xhr = xhr;
-                        if (errback) {
-                            errback(err);
-                        }
-                    } else {
-                        callback(xhr.responseText);
-                    }
-
-                    if (masterConfig.onXhrComplete) {
-                        masterConfig.onXhrComplete(xhr, url);
-                    }
-                }
-            };
-            xhr.send(null);
-        };
-    } else if (masterConfig.env === 'rhino' || (!masterConfig.env &&
-            typeof Packages !== 'undefined' && typeof java !== 'undefined')) {
-        //Why Java, why is this so awkward?
-        text.get = function (url, callback) {
-            var stringBuffer, line,
-                encoding = "utf-8",
-                file = new java.io.File(url),
-                lineSeparator = java.lang.System.getProperty("line.separator"),
-                input = new java.io.BufferedReader(new java.io.InputStreamReader(new java.io.FileInputStream(file), encoding)),
-                content = '';
-            try {
-                stringBuffer = new java.lang.StringBuffer();
-                line = input.readLine();
-
-                // Byte Order Mark (BOM) - The Unicode Standard, version 3.0, page 324
-                // http://www.unicode.org/faq/utf_bom.html
-
-                // Note that when we use utf-8, the BOM should appear as "EF BB BF", but it doesn't due to this bug in the JDK:
-                // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4508058
-                if (line && line.length() && line.charAt(0) === 0xfeff) {
-                    // Eat the BOM, since we've already found the encoding on this file,
-                    // and we plan to concatenating this buffer with others; the BOM should
-                    // only appear at the top of a file.
-                    line = line.substring(1);
-                }
-
-                if (line !== null) {
-                    stringBuffer.append(line);
-                }
-
-                while ((line = input.readLine()) !== null) {
-                    stringBuffer.append(lineSeparator);
-                    stringBuffer.append(line);
-                }
-                //Make sure we return a JavaScript string and not a Java string.
-                content = String(stringBuffer.toString()); //String
-            } finally {
-                input.close();
-            }
-            callback(content);
-        };
-    } else if (masterConfig.env === 'xpconnect' || (!masterConfig.env &&
-            typeof Components !== 'undefined' && Components.classes &&
-            Components.interfaces)) {
-        //Avert your gaze!
-        Cc = Components.classes;
-        Ci = Components.interfaces;
-        Components.utils['import']('resource://gre/modules/FileUtils.jsm');
-        xpcIsWindows = ('@mozilla.org/windows-registry-key;1' in Cc);
-
-        text.get = function (url, callback) {
-            var inStream, convertStream, fileObj,
-                readData = {};
-
-            if (xpcIsWindows) {
-                url = url.replace(/\//g, '\\');
-            }
-
-            fileObj = new FileUtils.File(url);
-
-            //XPCOM, you so crazy
-            try {
-                inStream = Cc['@mozilla.org/network/file-input-stream;1']
-                           .createInstance(Ci.nsIFileInputStream);
-                inStream.init(fileObj, 1, 0, false);
-
-                convertStream = Cc['@mozilla.org/intl/converter-input-stream;1']
-                                .createInstance(Ci.nsIConverterInputStream);
-                convertStream.init(inStream, "utf-8", inStream.available(),
-                Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER);
-
-                convertStream.readString(inStream.available(), readData);
-                convertStream.close();
-                inStream.close();
-                callback(readData.value);
-            } catch (e) {
-                throw new Error((fileObj && fileObj.path || '') + ': ' + e);
-            }
-        };
-    }
-    return text;
-});
diff --git a/mkdocs/search_index.json b/mkdocs/search_index.json
deleted file mode 100644
index 131eef40..00000000
--- a/mkdocs/search_index.json
+++ /dev/null
@@ -1,199 +0,0 @@
-{
-    "docs": [
-        {
-            "location": "/", 
-            "text": "mailcow: dockerized - \ud83d\udc2e + \ud83d\udc0b = \ud83d\udc95\n\n\n\n\nmailcow dockerized comes with \n11 containers\n linked in \none bridged network\n.\n\n\n\n\nDovecot\n\n\nMemcached\n\n\nRedis\n\n\nMySQL\n\n\nBind9 (Resolver) (formerly PDNS Recursor)\n\n\nPHP-FPM\n\n\nPostfix\n\n\nNginx\n\n\nRmilter\n\n\nRspamd\n\n\nSOGo\n\n\n\n\n6 volumes\n to keep dynamic data - take care of them!\n\n\n\n\nvmail-vol-1\n\n\ndkim-vol-1\n\n\nredis-vol-1\n\n\nmysql-vol-1\n\n\nrspamd-vol-1\n\n\npostfix-vol-1\n\n\n\n\nThe integrated \nmailcow UI\n allows administrative work on your mail server instance as well as separated domain administrator and mailbox user access:\n\n\n\n\nDKIM key management\n\n\nBlack- and whitelists per domain and per user\n\n\nSpam score managment per-user (reject spam, mark spam, greylist)\n\n\nAllow mailbox users to create temporary spam aliases\n\n\nPrepend mail tags to subject or move mail to subfolder (per-user)\n\n\nAllow mailbox users to toggle incoming and outgoing TLS enforcement\n\n\nAllow users to reset SOGo ActiveSync device caches\n\n\nimapsync to migrate or pull remote mailboxes regularly\n\n\nTFA: Yubi OTP and U2F USB (Google Chrome and derivates only)\n\n\nAdd domains, mailboxes, aliases, domain aliases and SOGo resources\n\n\n\n\nLooking for a farm to host your cow?", 
-            "title": "Overview"
-        }, 
-        {
-            "location": "/#mailcow-dockerized-", 
-            "text": "mailcow dockerized comes with  11 containers  linked in  one bridged network .   Dovecot  Memcached  Redis  MySQL  Bind9 (Resolver) (formerly PDNS Recursor)  PHP-FPM  Postfix  Nginx  Rmilter  Rspamd  SOGo   6 volumes  to keep dynamic data - take care of them!   vmail-vol-1  dkim-vol-1  redis-vol-1  mysql-vol-1  rspamd-vol-1  postfix-vol-1   The integrated  mailcow UI  allows administrative work on your mail server instance as well as separated domain administrator and mailbox user access:   DKIM key management  Black- and whitelists per domain and per user  Spam score managment per-user (reject spam, mark spam, greylist)  Allow mailbox users to create temporary spam aliases  Prepend mail tags to subject or move mail to subfolder (per-user)  Allow mailbox users to toggle incoming and outgoing TLS enforcement  Allow users to reset SOGo ActiveSync device caches  imapsync to migrate or pull remote mailboxes regularly  TFA: Yubi OTP and U2F USB (Google Chrome and derivates only)  Add domains, mailboxes, aliases, domain aliases and SOGo resources   Looking for a farm to host your cow?", 
-            "title": "mailcow: dockerized - \ud83d\udc2e + \ud83d\udc0b = \ud83d\udc95"
-        }, 
-        {
-            "location": "/install/", 
-            "text": "Install mailcow\n\n\n\n\n\n\nYou need Docker.\n\n\n\n\nMost systems can install Docker by running \nwget -qO- https://get.docker.com/ | sh\n\n\n\n\n\n\n\n\nYou need Docker Compose.\n\n\n\n\nLearn \nhow to install Docker Compose\n - or:\n\n\ncurl -L \"https://github.com/docker/compose/releases/download/composer_version/docker-compose-$(uname -s)-$(uname -m)\" -o /usr/local/bin/docker-compose \n chmod +x /usr/local/bin/docker-compose\n\n\n\n\n\n\n\n\nClone the master branch of the repository and run \n./generate_config.sh\n to generate a file \"mailcow.conf\". You will be asked for a hostname and a timezone:\n\n\n\n\ngit clone https://github.com/andryyy/mailcow-dockerized \n cd mailcow-dockerized\n\n\n./generate_config.sh\n\n\nOpen and check \"mailcow.conf\" if you need or want to make changes to ports (for example changing the default HTTPS port)\n\n\n\n\n\n\n\n\nRun the composer file.\n\n\n\n\ndocker-compose up -d\n\n\n\n\n\n\n\n\nDone.\n\n\nYou can now access \nhttps://${MAILCOW_HOSTNAME}\n with the default credentials \nadmin\n + password \nmoohoo\n.\n\n\nIt may take a while for MySQL to warm up, so please wait half a minute.\n\n\nThe database will be initialized right after a connection to MySQL can be established.\n\n\nUpdate mailcow\n\n\nThere is no update routine.\n\n\nYou need to refresh your pulled repository clone by running \ngit pull\n - this will likely fail due to changes to your local configuration. But that's why we use git! :-)\n\n\nWhatever file has local changes, add and commit it to your repository clone. For example:\n\n\ngit add data/conf/postfix/main.cf data/conf/dovecot/dovecot.conf\ngit commit -m \nMy changes to main.cf and dovecot.conf\n\n\n\n\nTry running \ngit pull\n again and resolve conflicts, if any.\n\n\nNow update all images, apply changes to containers and restart all services:\n\n\ndocker-compose pull\ndocker-compose up -d --remove-orphans\ndocker-compose restart\n\n\n\n\nUse dev branch (not recommended)\n\n\nWhen you checkout the dev branch, you will most likely end up using the \"master\" images with code base of \"dev\".\nIf there were critical changes to the images in dev, mailcow will not work.\n\n\nBut you can still build the images by yourself:\n\n\ndocker-compose up -d --build", 
-            "title": "Installation"
-        }, 
-        {
-            "location": "/install/#install-mailcow", 
-            "text": "You need Docker.   Most systems can install Docker by running  wget -qO- https://get.docker.com/ | sh     You need Docker Compose.   Learn  how to install Docker Compose  - or:  curl -L \"https://github.com/docker/compose/releases/download/composer_version/docker-compose-$(uname -s)-$(uname -m)\" -o /usr/local/bin/docker-compose   chmod +x /usr/local/bin/docker-compose     Clone the master branch of the repository and run  ./generate_config.sh  to generate a file \"mailcow.conf\". You will be asked for a hostname and a timezone:   git clone https://github.com/andryyy/mailcow-dockerized   cd mailcow-dockerized  ./generate_config.sh  Open and check \"mailcow.conf\" if you need or want to make changes to ports (for example changing the default HTTPS port)     Run the composer file.   docker-compose up -d     Done.  You can now access  https://${MAILCOW_HOSTNAME}  with the default credentials  admin  + password  moohoo .  It may take a while for MySQL to warm up, so please wait half a minute.  The database will be initialized right after a connection to MySQL can be established.", 
-            "title": "Install mailcow"
-        }, 
-        {
-            "location": "/install/#update-mailcow", 
-            "text": "There is no update routine.  You need to refresh your pulled repository clone by running  git pull  - this will likely fail due to changes to your local configuration. But that's why we use git! :-)  Whatever file has local changes, add and commit it to your repository clone. For example:  git add data/conf/postfix/main.cf data/conf/dovecot/dovecot.conf\ngit commit -m  My changes to main.cf and dovecot.conf  Try running  git pull  again and resolve conflicts, if any.  Now update all images, apply changes to containers and restart all services:  docker-compose pull\ndocker-compose up -d --remove-orphans\ndocker-compose restart", 
-            "title": "Update mailcow"
-        }, 
-        {
-            "location": "/install/#use-dev-branch-not-recommended", 
-            "text": "When you checkout the dev branch, you will most likely end up using the \"master\" images with code base of \"dev\".\nIf there were critical changes to the images in dev, mailcow will not work.  But you can still build the images by yourself:  docker-compose up -d --build", 
-            "title": "Use dev branch (not recommended)"
-        }, 
-        {
-            "location": "/first_steps/", 
-            "text": "Change default language\n\n\nChange \ndata/conf/sogo/sogo.conf\n and replace English by your language.\n\n\nCreate a file \ndata/web/inc/vars.local.inc.php\n and add \"DEFAULT_LANG\" with either \"en\", \"pt\", \"de\" or \"nl\":\n\n\n?php\n$DEFAULT_LANG = \nde\n;\n\n\n\n\nSSL (and: How to use Let's Encrypt)\n\n\nmailcow dockerized comes with a snakeoil CA \"mailcow\" and a server certificate in \ndata/assets/ssl\n. Please use your own trusted certificates.\n\n\nmailcow uses 3 domain names that should be covered by your new certificate:\n\n\n\n\n${MAILCOW_HOSTNAME}\n\n\nautodiscover.\nexample.org\n\n\nautoconfig.\nexample.org\n\n\n\n\nObtain multi-SAN certificate by Let's Encrypt\n\n\nThis is just an example of how to obtain certificates with certbot. There are several methods!\n\n\n\n\nGet the certbot client:\n\n\n\n\nwget https://dl.eff.org/certbot-auto -O /usr/local/sbin/certbot \n chmod +x /usr/local/sbin/certbot\n\n\n\n\n\n\n\n\nMake sure you set \nHTTP_BIND=0.0.0.0\n in \nmailcow.conf\n or setup a reverse proxy to enable connections to port 80. If you changed HTTP_BIND, then restart Nginx: \ndocker-compose restart nginx-mailcow\n.\n\n\n\n\n\n\nRequest the certificate with the webroot method:\n\n\n\n\n\n\ncd /path/to/git/clone/mailcow-dockerized\nsource mailcow.conf\ncertbot certonly \\\n        --webroot \\\n        -w ${PWD}/data/web \\\n        -d ${MAILCOW_HOSTNAME} \\\n        -d autodiscover.example.org \\\n        -d autoconfig.example.org \\\n        --email [email protected] \\\n        --agree-tos\n\n\n\n\n\n\nCreate hard links to the full path of the new certificates. Assuming you are still in the mailcow root folder:\n\n\n\n\nmv data/assets/ssl/cert.{pem,pem.backup}\nmv data/assets/ssl/key.{pem,pem.backup}\nln $(readlink -f /etc/letsencrypt/live/${MAILCOW_HOSTNAME}/fullchain.pem) data/assets/ssl/cert.pem\nln $(readlink -f /etc/letsencrypt/live/${MAILCOW_HOSTNAME}/privkey.pem) data/assets/ssl/key.pem\n\n\n\n\n\n\nRestart containers which use the certificate:\n\n\n\n\ndocker-compose restart postfix-mailcow dovecot-mailcow nginx-mailcow\n\n\n\n\nWhen renewing certificates, run the last two steps (link + restart) as post-hook in a script.\n\n\nRspamd UI access\n\n\nAt first you may want to setup Rspamds web interface which provides some useful features and information.\n\n\n\n\nGenerate a Rspamd controller password hash:\n\n\n\n\ndocker-compose exec rspamd-mailcow rspamadm pw\n\n\n\n\n\n\nReplace the default hash in \ndata/conf/rspamd/override.d/worker-controller.inc\n by your newly generated:\n\n\n\n\nenable_password = \nmyhash\n;\n\n\n\n\n\n\nRestart rspamd:\n\n\n\n\ndocker-compose restart rspamd-mailcow\n\n\n\n\nOpen https://${MAILCOW_HOSTNAME}/rspamd in a browser and login!", 
-            "title": "First Steps"
-        }, 
-        {
-            "location": "/first_steps/#change-default-language", 
-            "text": "Change  data/conf/sogo/sogo.conf  and replace English by your language.  Create a file  data/web/inc/vars.local.inc.php  and add \"DEFAULT_LANG\" with either \"en\", \"pt\", \"de\" or \"nl\":  ?php\n$DEFAULT_LANG =  de ;", 
-            "title": "Change default language"
-        }, 
-        {
-            "location": "/first_steps/#ssl-and-how-to-use-lets-encrypt", 
-            "text": "mailcow dockerized comes with a snakeoil CA \"mailcow\" and a server certificate in  data/assets/ssl . Please use your own trusted certificates.  mailcow uses 3 domain names that should be covered by your new certificate:   ${MAILCOW_HOSTNAME}  autodiscover. example.org  autoconfig. example.org", 
-            "title": "SSL (and: How to use Let's Encrypt)"
-        }, 
-        {
-            "location": "/first_steps/#obtain-multi-san-certificate-by-lets-encrypt", 
-            "text": "This is just an example of how to obtain certificates with certbot. There are several methods!   Get the certbot client:   wget https://dl.eff.org/certbot-auto -O /usr/local/sbin/certbot   chmod +x /usr/local/sbin/certbot    Make sure you set  HTTP_BIND=0.0.0.0  in  mailcow.conf  or setup a reverse proxy to enable connections to port 80. If you changed HTTP_BIND, then restart Nginx:  docker-compose restart nginx-mailcow .    Request the certificate with the webroot method:    cd /path/to/git/clone/mailcow-dockerized\nsource mailcow.conf\ncertbot certonly \\\n        --webroot \\\n        -w ${PWD}/data/web \\\n        -d ${MAILCOW_HOSTNAME} \\\n        -d autodiscover.example.org \\\n        -d autoconfig.example.org \\\n        --email [email protected] \\\n        --agree-tos   Create hard links to the full path of the new certificates. Assuming you are still in the mailcow root folder:   mv data/assets/ssl/cert.{pem,pem.backup}\nmv data/assets/ssl/key.{pem,pem.backup}\nln $(readlink -f /etc/letsencrypt/live/${MAILCOW_HOSTNAME}/fullchain.pem) data/assets/ssl/cert.pem\nln $(readlink -f /etc/letsencrypt/live/${MAILCOW_HOSTNAME}/privkey.pem) data/assets/ssl/key.pem   Restart containers which use the certificate:   docker-compose restart postfix-mailcow dovecot-mailcow nginx-mailcow  When renewing certificates, run the last two steps (link + restart) as post-hook in a script.", 
-            "title": "Obtain multi-SAN certificate by Let's Encrypt"
-        }, 
-        {
-            "location": "/first_steps/#rspamd-ui-access", 
-            "text": "At first you may want to setup Rspamds web interface which provides some useful features and information.   Generate a Rspamd controller password hash:   docker-compose exec rspamd-mailcow rspamadm pw   Replace the default hash in  data/conf/rspamd/override.d/worker-controller.inc  by your newly generated:   enable_password =  myhash ;   Restart rspamd:   docker-compose restart rspamd-mailcow  Open https://${MAILCOW_HOSTNAME}/rspamd in a browser and login!", 
-            "title": "Rspamd UI access"
-        }, 
-        {
-            "location": "/u_and_e/", 
-            "text": "Anonymize headers for smtp relayed\n\n\nSave as \ndata/conf/postfix/mailcow_anonymize_headers.pcre\n:\n\n\n/^\\s*Received:[^\\)]+\\)\\s+\\(Authenticated sender:(.+)/\n    REPLACE Received: from localhost (localhost [127.0.0.1]) (Authenticated sender:$1\n/^\\s*User-Agent/        IGNORE\n/^\\s*X-Enigmail/        IGNORE\n/^\\s*X-Mailer/          IGNORE\n/^\\s*X-Originating-IP/  IGNORE\n/^\\s*X-Forward/         IGNORE\n/^\\s*Mime-Version:/     IGNORE\n\n\n\n\nAdd this to \ndata/conf/postfix/main.cf\n:\n\n\nsmtp_header_checks = pcre:/opt/postfix/conf/mailcow_anonymize_headers.pcre\n\n\n\n\nBackup and restore maildir (simple tar file)\n\n\nBackup\n\n\nThis line backups the vmail directory to a file backup_vmail.tar.gz in the mailcow root directory:\n\n\ncd /path/to/mailcow-dockerized\nsource mailcow.conf\nDATE=$(date +\n%Y%m%d_%H%M%S\n)\ndocker run --rm -it -v $(docker inspect --format '{{ range .Mounts }}{{ if eq .Destination \n/var/vmail\n }}{{ .Name }}{{ end }}{{ end }}' $(docker-compose ps -q dovecot-mailcow)):/vmail -v ${PWD}:/backup debian:jessie tar cvfz /backup/backup_vmail.tar.gz /vmail\n\n\n\n\nYou can change the path by adjusting ${PWD} (which equals to the current directory) to any path you have write-access to.\nSet the filename \nbackup_vmail.tar.gz\n to any custom name, but leave the path as it is. Example: \n[...] tar cvfz /backup/my_own_filename_.tar.gz\n\n\nRestore\n\n\ncd /path/to/mailcow-dockerized\nsource mailcow.conf\nDATE=$(date +\n%Y%m%d_%H%M%S\n)\ndocker run --rm -it -v $(docker inspect --format '{{ range .Mounts }}{{ if eq .Destination \n/var/vmail\n }}{{ .Name }}{{ end }}{{ end }}' $(docker-compose ps -q dovecot-mailcow)):/vmail -v ${PWD}:/backup debian:jessie tar xvfz /backup/backup_vmail.tar.gz\n\n\n\n\nFor the tab-tab... :-)\n\n\ncurl -L https://raw.githubusercontent.com/docker/compose/$(docker-compose version --short)/contrib/completion/bash/docker-compose -o /etc/bash_completion.d/docker-compose\n\n\n\n\nBlack- and Whitelist\n\n\nEdit a domain as (domain) administrator to add an item to the filter table.\n\n\nBeware that a mailbox user can login to mailcow and override a domain policy filter item. \n\n\nChange default theme\n\n\nmailcow uses \nBootstrap\n, a HTML, CSS, and JS framework.\n\n\nOpen or create the file \ndata/web/inc/vars.local.inc.php\n and change \nDEFAULT_THEME\n to either cerulean, cosmo, custom, cyborg, darkly, flatly, journal, paper, readable, sandstone, simplex, slate, spacelab, superhero, united or yeti (see https://bootswatch.com/):\n\n\n?php\n$DEFAULT_THEME = \npaper\n;\n\n\n\n\nCustomize Dockerfiles\n\n\nMake your changes in \ndata/Dockerfiles/$service\n and build the image locally:\n\n\ndocker build data/Dockerfiles/service -t andryyy/mailcow-dockerized:$service\n\n\n\n\nNow auto-recreate modified containers:\n\n\ndocker-compose up -d\n\n\n\n\nDo not check sender addresses for any domain\n\n\nThis option is not best-practice and should only be implemented when there is no other option available to archive whatever you are trying to do.\n\n\nSimply create a file \ndata/conf/postfix/check_sender_access\n and enter the following content:\n\n\[email protected] OK\n\n\n\n\nOpen \ndata/conf/postfix/main.cf\n and find \nsmtpd_sender_restrictions\n. Prepend \ncheck_sasl_access hash:/opt/postfix/conf/check_sender_access\n like this:\n\n\nsmtpd_sender_restrictions = check_sasl_access hash:/opt/postfix/conf/check_sender_access reject_authenticated_sender [...]\n\n\n\n\nRun postmap on check_sasl_access:\n\n\ndocker-compose exec postfix-mailcow postmap /opt/postfix/conf/check_sasl_access\n\n\n\n\nRestart the Postfix container.\n\n\nInstall Roundcube\n\n\nDownload Roundcube 1.3.x (beta at the time of Feb 2017) to the web htdocs directory and extract it (here \nrc/\n):\n\n\ncd data/web/rc\nwget -O - https://github.com/roundcube/roundcubemail/releases/download/1.3-beta/roundcubemail-1.3-beta-complete.tar.gz | tar xfvz -\n# Change folder name\nmv roundcubemail-1.3* rc\n# Change permissions\nchown -R root: rc/\n\n\n\n\nCreate a file \ndata/web/rc/config/config.inc.php\n with the following content.\n\n\nChange the \ndes_key\n parameter to a random value.\n It is used to temporarily store your IMAP password.\n\n\n?php\nerror_reporting(0);\nif (!file_exists('/tmp/mime.types')) {\nfile_put_contents(\n/tmp/mime.types\n, fopen(\nhttp://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types\n, 'r'));\n}\n$config = array();\n$config['db_dsnw'] = 'mysql://' . getenv('DBUSER') . ':' . getenv('DBPASS') . '@mysql/' . getenv('DBNAME');\n$config['default_host'] = 'tls://dovecot';\n$config['default_port'] = '143';\n$config['smtp_server'] = 'tls://postfix';\n$config['smtp_port'] = 587;\n$config['smtp_user'] = '%u';\n$config['smtp_pass'] = '%p';\n$config['support_url'] = '';\n$config['product_name'] = 'Roundcube Webmail';\n$config['des_key'] = 'rcmail-!24ByteDESkey*Str';\n$config['log_dir'] = '/dev/null';\n$config['temp_dir'] = '/tmp';\n$config['plugins'] = array(\n    'archive',\n);\n$config['skin'] = 'larry';\n$config['mime_types'] = '/tmp/mime.types';\n$config['imap_conn_options'] = array(\n'ssl' =\n array('verify_peer' =\n false, 'verify_peer_name' =\n false, 'allow_self_signed' =\n true)\n);\n$config['enable_installer'] = false;\n$config['smtp_conn_options'] = array(\n'ssl' =\n array('verify_peer' =\n false, 'verify_peer_name' =\n false, 'allow_self_signed' =\n true)\n);\n\n\n\n\nPoint your browser to \nhttps://myserver/rc/installer\n and follow the instructions.\nInitialize the database and leave the installer.\n\nDelete the directory \ndata/web/rc/installer\n after a successful installation!\n\n\nEnable password changing\n\n\nOpen \ndata/web/rc/config.inc.php\n and enable the password plugin:\n\n\n...\n$config['plugins'] = array(\n    'archive',\n    'password',\n);\n...\n\n\n\n\nOpen \ndata/web/rc/plugins/password/password.php\n, search for \ncase 'ssha':\n and add above:\n\n\n        case 'ssha256':\n            $salt = rcube_utils::random_bytes(8);\n            $crypted = base64_encode( hash('sha256', $password . $salt, TRUE ) . $salt );\n            $prefix  = '{SSHA256}';\n            break;\n\n\n\n\nOpen \ndata/web/rc/plugins/password/config.inc.php\n and change the following parameters (or add them at the bottom of that file):\n\n\n$config['password_driver'] = 'sql';\n$config['password_algorithm'] = 'ssha256';\n$config['password_algorithm_prefix'] = '{SSHA256}';\n$config['password_query'] = \nUPDATE mailbox SET password = %P WHERE username = %u\n;\n\n\n\n\nLearn spam and ham\n\n\nRspamd learns mail as spam or ham when you move a message in or out of the junk folder to any mailbox besides trash.\nThis is archived by using the Dovecot plugin \"antispam\" and a simple parser script.\n\n\nRspamd also auto-learns mail when a high or low score is detected (see https://rspamd.com/doc/configuration/statistic.html#autolearning)\n\n\nThe bayes statistics are written to Redis as keys \nBAYES_HAM\n and \nBAYES_SPAM\n.\n\n\nYou can also use Rspamds web ui to learn ham and/or spam.\n\n\nMySQL\n\n\nConnect to the MySQL database:\n\n\nsource mailcow.conf\ndocker-compose exec mysql-mailcow mysql -u${DBUSER} -p${DBPASS} ${DBNAME}\n\n\n\n\nBackup the database:\n\n\ncd /path/to/mailcow-dockerized\nsource mailcow.conf\nDATE=$(date +\n%Y%m%d_%H%M%S\n)\ndocker-compose exec mysql-mailcow mysqldump --default-character-set=utf8mb4 -u${DBUSER} -p${DBPASS} ${DBNAME} \n backup_${DBNAME}_${DATE}.sql\n\n\n\n\nRestore the database:\n\n\ncd /path/to/mailcow-dockerized\nsource mailcow.conf\ndocker-compose exec mysql-mailcow mysql -u${DBUSER} -p${DBPASS} ${DBNAME} \n backup_file.sql\n\n\n\n\nRead logs\n\n\nYou can use \ndocker-compose logs $service-name\n for all containers.\n\n\nRun \ndocker-compose logs\n for all logs at once.\n\n\nFollow the log output by running docker-compose with \nlogs -f\n.\n\n\nRedirect port 80 to 443\n\n\nSince February the 28th 2017 mailcow does come with port 80 and 443 enabled.\n\n\nOpen \nmailcow.conf\n and set \nHTTP_BIND=0.0.0.0\n.\n\n\nOpen \ndata/conf/nginx/site.conf\n and add a new \"catch-all\" site at the top of that file:\n\n\nserver {\n    listen 80 default_server;\n    server_name _;\n    return 301 https://$host$request_uri;\n}\n\n\n\n\nRestart the stack, changed containers will be updated:\n\n\ndocker-compose up -d\n\n\nRedis\n\n\nConnect to redis key store:\n\n\ndocker-compose exec redis-mailcow redis-cli\n\n\n\n\nRemove persistent data\n\n\n\n\nRemove volume \nmysql-vol-1\n to remove all MySQL data.\n\n\nRemove volume \nredis-vol-1\n to remove all Redis data.\n\n\nRemove volume \nvmail-vol-1\n to remove all contents of \n/var/vmail\n mounted to \ndovecot-mailcow\n.\n\n\nRemove volume \ndkim-vol-1\n to remove all DKIM keys.\n\n\nRemove volume \nrspamd-vol-1\n to remove all Rspamd data.\n\n\n\n\nRunning \ndocker-compose down -v\n will \ndestroy all mailcow: dockerized volumes\n and delete any related containers.Reset mailcow admin to \nadmin:moohoo\n:\n\n\n\n\nDrop admin table\n\n\n\n\nsource mailcow.conf\ndocker-compose exec mysql-mailcow mysql -u${DBUSER} -p${DBPASS} ${DBNAME} -e \nDROP TABLE admin;\n\n\n\n\n\n\n\nOpen mailcow UI to auto-init the db\n\n\n\n\nRspamd\n\n\nRspamd CLI tools\n\n\ndocker-compose exec rspamd-mailcow rspamc --help\ndocker-compose exec rspamd-mailcow rspamadm --help\n\n\n\n\nSee \nRspamd documentation\n\n\nAdjust service configurations\n\n\nThe most important configuration files are mounted from the host into the related containers:\n\n\ndata/conf\n\u251c\u2500\u2500 bind9\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 named.conf\n\u251c\u2500\u2500 dovecot\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 dovecot.conf\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 dovecot-master.passwd\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 sieve_after\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 sql\n\u2502\u00a0\u00a0     \u251c\u2500\u2500 dovecot-dict-sql.conf\n\u2502\u00a0\u00a0     \u2514\u2500\u2500 dovecot-mysql.conf\n\u251c\u2500\u2500 mysql\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 my.cnf\n\u251c\u2500\u2500 nginx\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 dynmaps.conf\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 site.conf\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 templates\n\u2502\u00a0\u00a0     \u251c\u2500\u2500 listen_plain.template\n\u2502\u00a0\u00a0     \u251c\u2500\u2500 listen_ssl.template\n\u2502\u00a0\u00a0     \u2514\u2500\u2500 server_name.template\n\u251c\u2500\u2500 pdns\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 pdns_custom.lua\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 recursor.conf\n\u251c\u2500\u2500 postfix\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 main.cf\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 master.cf\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 postscreen_access.cidr\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 smtp_dsn_filter\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 sql\n\u2502\u00a0\u00a0     \u251c\u2500\u2500 mysql_relay_recipient_maps.cf\n\u2502\u00a0\u00a0     \u251c\u2500\u2500 mysql_tls_enforce_in_policy.cf\n\u2502\u00a0\u00a0     \u251c\u2500\u2500 mysql_tls_enforce_out_policy.cf\n\u2502\u00a0\u00a0     \u251c\u2500\u2500 mysql_virtual_alias_domain_catchall_maps.cf\n\u2502\u00a0\u00a0     \u251c\u2500\u2500 mysql_virtual_alias_domain_maps.cf\n\u2502\u00a0\u00a0     \u251c\u2500\u2500 mysql_virtual_alias_maps.cf\n\u2502\u00a0\u00a0     \u251c\u2500\u2500 mysql_virtual_domains_maps.cf\n\u2502\u00a0\u00a0     \u251c\u2500\u2500 mysql_virtual_mailbox_maps.cf\n\u2502\u00a0\u00a0     \u251c\u2500\u2500 mysql_virtual_relay_domain_maps.cf\n\u2502\u00a0\u00a0     \u251c\u2500\u2500 mysql_virtual_sender_acl.cf\n\u2502\u00a0\u00a0     \u2514\u2500\u2500 mysql_virtual_spamalias_maps.cf\n\u251c\u2500\u2500 rmilter\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 rmilter.conf\n\u251c\u2500\u2500 rspamd\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 dynmaps\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u251c\u2500\u2500 authoritative.php\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u251c\u2500\u2500 settings.php\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u251c\u2500\u2500 tags.php\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u2514\u2500\u2500 vars.inc.php -\n ../../../web/inc/vars.inc.php\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 local.d\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u251c\u2500\u2500 dkim.conf\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u251c\u2500\u2500 metrics.conf\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u251c\u2500\u2500 options.inc\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u251c\u2500\u2500 redis.conf\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u251c\u2500\u2500 rspamd.conf.local\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u2514\u2500\u2500 statistic.conf\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 lua\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u2514\u2500\u2500 rspamd.local.lua\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 override.d\n\u2502\u00a0\u00a0     \u251c\u2500\u2500 logging.inc\n\u2502\u00a0\u00a0     \u251c\u2500\u2500 worker-controller.inc\n\u2502\u00a0\u00a0     \u2514\u2500\u2500 worker-normal.inc\n\u2514\u2500\u2500 sogo\n    \u251c\u2500\u2500 sieve.creds\n    \u2514\u2500\u2500 sogo.conf\n\n\n\n\n\nJust change the according configuration file on the host and restart the related service: \ndocker-compose restart service-mailcow\n\n\nTagging\n\n\nMailbox users can tag their mail address like in \[email protected]\n and choose between to setups to handle this tag:\n\n\n\n\nMove this message to a subfolder \"facebook\" (will be created lower case if not existing)\n\n\nPrepend the tag to the subject: \"[facebook] Subject\"\n\n\n\n\nTwo-factor authentication\n\n\nSo far two methods for TFA are impelemented. Both work with the fantastic \nYubikey\n. \n\n\nWhile Yubi OTP needs an active internet connection and an API ID/key, U2F will work with any FIDO U2F USB key out of the box.\n\n\nBoth methods support mulitple YubiKeys.\n\n\nAs administrator you are able to temporary disable a domain adminsitrators TFA login until they successfully logged in.\n\n\nThe key used to login will be displayed in green, while other keys remain grey.\n\n\nYubi OTP\n\n\nThe Yubi API ID and Key will be checked against the Yubico Cloud API. When setting up TFA you will be asked for your personal API account for this key.\nThe API ID, API key and the first 12 characters (your YubiKeys ID in modhex) are stored in the MySQL table as secret.\n\n\nU2F\n\n\nOnly Google Chrome (+derivates) and Opera support U2F authentication to this day natively.\nFor Firefox you will need to install the \"U2F Support Add-on\" as provided on \nmozilla.org\n.\nU2F works without an internet connection.# Why does mailcow come with a DNS resolver?\n\n\nFor DNS blacklist lookups and DNSSEC.\n\n\nMost systems use either a public or a local caching DNS resolver.\nThat's a very bad idea when it comes to filter spam using DNS-based blackhole lists (DNSBL) or similar technics.\nMost if not all providers apply a rate limit based on the DNS resolver that is used to query their service.\nUsing a public resolver like Googles 4x8, OpenDNS or any other shared DNS resolver like your ISPs will hit that limit very soon.", 
-            "title": "Usage & Examples"
-        }, 
-        {
-            "location": "/u_and_e/#anonymize-headers-for-smtp-relayed", 
-            "text": "Save as  data/conf/postfix/mailcow_anonymize_headers.pcre :  /^\\s*Received:[^\\)]+\\)\\s+\\(Authenticated sender:(.+)/\n    REPLACE Received: from localhost (localhost [127.0.0.1]) (Authenticated sender:$1\n/^\\s*User-Agent/        IGNORE\n/^\\s*X-Enigmail/        IGNORE\n/^\\s*X-Mailer/          IGNORE\n/^\\s*X-Originating-IP/  IGNORE\n/^\\s*X-Forward/         IGNORE\n/^\\s*Mime-Version:/     IGNORE  Add this to  data/conf/postfix/main.cf :  smtp_header_checks = pcre:/opt/postfix/conf/mailcow_anonymize_headers.pcre", 
-            "title": "Anonymize headers for smtp relayed"
-        }, 
-        {
-            "location": "/u_and_e/#backup-and-restore-maildir-simple-tar-file", 
-            "text": "", 
-            "title": "Backup and restore maildir (simple tar file)"
-        }, 
-        {
-            "location": "/u_and_e/#backup", 
-            "text": "This line backups the vmail directory to a file backup_vmail.tar.gz in the mailcow root directory:  cd /path/to/mailcow-dockerized\nsource mailcow.conf\nDATE=$(date + %Y%m%d_%H%M%S )\ndocker run --rm -it -v $(docker inspect --format '{{ range .Mounts }}{{ if eq .Destination  /var/vmail  }}{{ .Name }}{{ end }}{{ end }}' $(docker-compose ps -q dovecot-mailcow)):/vmail -v ${PWD}:/backup debian:jessie tar cvfz /backup/backup_vmail.tar.gz /vmail  You can change the path by adjusting ${PWD} (which equals to the current directory) to any path you have write-access to.\nSet the filename  backup_vmail.tar.gz  to any custom name, but leave the path as it is. Example:  [...] tar cvfz /backup/my_own_filename_.tar.gz", 
-            "title": "Backup"
-        }, 
-        {
-            "location": "/u_and_e/#restore", 
-            "text": "cd /path/to/mailcow-dockerized\nsource mailcow.conf\nDATE=$(date + %Y%m%d_%H%M%S )\ndocker run --rm -it -v $(docker inspect --format '{{ range .Mounts }}{{ if eq .Destination  /var/vmail  }}{{ .Name }}{{ end }}{{ end }}' $(docker-compose ps -q dovecot-mailcow)):/vmail -v ${PWD}:/backup debian:jessie tar xvfz /backup/backup_vmail.tar.gz  For the tab-tab... :-)  curl -L https://raw.githubusercontent.com/docker/compose/$(docker-compose version --short)/contrib/completion/bash/docker-compose -o /etc/bash_completion.d/docker-compose", 
-            "title": "Restore"
-        }, 
-        {
-            "location": "/u_and_e/#black-and-whitelist", 
-            "text": "Edit a domain as (domain) administrator to add an item to the filter table.  Beware that a mailbox user can login to mailcow and override a domain policy filter item.", 
-            "title": "Black- and Whitelist"
-        }, 
-        {
-            "location": "/u_and_e/#change-default-theme", 
-            "text": "mailcow uses  Bootstrap , a HTML, CSS, and JS framework.  Open or create the file  data/web/inc/vars.local.inc.php  and change  DEFAULT_THEME  to either cerulean, cosmo, custom, cyborg, darkly, flatly, journal, paper, readable, sandstone, simplex, slate, spacelab, superhero, united or yeti (see https://bootswatch.com/):  ?php\n$DEFAULT_THEME =  paper ;", 
-            "title": "Change default theme"
-        }, 
-        {
-            "location": "/u_and_e/#customize-dockerfiles", 
-            "text": "Make your changes in  data/Dockerfiles/$service  and build the image locally:  docker build data/Dockerfiles/service -t andryyy/mailcow-dockerized:$service  Now auto-recreate modified containers:  docker-compose up -d", 
-            "title": "Customize Dockerfiles"
-        }, 
-        {
-            "location": "/u_and_e/#do-not-check-sender-addresses-for-any-domain", 
-            "text": "This option is not best-practice and should only be implemented when there is no other option available to archive whatever you are trying to do.  Simply create a file  data/conf/postfix/check_sender_access  and enter the following content:  [email protected] OK  Open  data/conf/postfix/main.cf  and find  smtpd_sender_restrictions . Prepend  check_sasl_access hash:/opt/postfix/conf/check_sender_access  like this:  smtpd_sender_restrictions = check_sasl_access hash:/opt/postfix/conf/check_sender_access reject_authenticated_sender [...]  Run postmap on check_sasl_access:  docker-compose exec postfix-mailcow postmap /opt/postfix/conf/check_sasl_access  Restart the Postfix container.", 
-            "title": "Do not check sender addresses for any domain"
-        }, 
-        {
-            "location": "/u_and_e/#install-roundcube", 
-            "text": "Download Roundcube 1.3.x (beta at the time of Feb 2017) to the web htdocs directory and extract it (here  rc/ ):  cd data/web/rc\nwget -O - https://github.com/roundcube/roundcubemail/releases/download/1.3-beta/roundcubemail-1.3-beta-complete.tar.gz | tar xfvz -\n# Change folder name\nmv roundcubemail-1.3* rc\n# Change permissions\nchown -R root: rc/  Create a file  data/web/rc/config/config.inc.php  with the following content.  Change the  des_key  parameter to a random value.  It is used to temporarily store your IMAP password.  ?php\nerror_reporting(0);\nif (!file_exists('/tmp/mime.types')) {\nfile_put_contents( /tmp/mime.types , fopen( http://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types , 'r'));\n}\n$config = array();\n$config['db_dsnw'] = 'mysql://' . getenv('DBUSER') . ':' . getenv('DBPASS') . '@mysql/' . getenv('DBNAME');\n$config['default_host'] = 'tls://dovecot';\n$config['default_port'] = '143';\n$config['smtp_server'] = 'tls://postfix';\n$config['smtp_port'] = 587;\n$config['smtp_user'] = '%u';\n$config['smtp_pass'] = '%p';\n$config['support_url'] = '';\n$config['product_name'] = 'Roundcube Webmail';\n$config['des_key'] = 'rcmail-!24ByteDESkey*Str';\n$config['log_dir'] = '/dev/null';\n$config['temp_dir'] = '/tmp';\n$config['plugins'] = array(\n    'archive',\n);\n$config['skin'] = 'larry';\n$config['mime_types'] = '/tmp/mime.types';\n$config['imap_conn_options'] = array(\n'ssl' =  array('verify_peer' =  false, 'verify_peer_name' =  false, 'allow_self_signed' =  true)\n);\n$config['enable_installer'] = false;\n$config['smtp_conn_options'] = array(\n'ssl' =  array('verify_peer' =  false, 'verify_peer_name' =  false, 'allow_self_signed' =  true)\n);  Point your browser to  https://myserver/rc/installer  and follow the instructions.\nInitialize the database and leave the installer. Delete the directory  data/web/rc/installer  after a successful installation!", 
-            "title": "Install Roundcube"
-        }, 
-        {
-            "location": "/u_and_e/#enable-password-changing", 
-            "text": "Open  data/web/rc/config.inc.php  and enable the password plugin:  ...\n$config['plugins'] = array(\n    'archive',\n    'password',\n);\n...  Open  data/web/rc/plugins/password/password.php , search for  case 'ssha':  and add above:          case 'ssha256':\n            $salt = rcube_utils::random_bytes(8);\n            $crypted = base64_encode( hash('sha256', $password . $salt, TRUE ) . $salt );\n            $prefix  = '{SSHA256}';\n            break;  Open  data/web/rc/plugins/password/config.inc.php  and change the following parameters (or add them at the bottom of that file):  $config['password_driver'] = 'sql';\n$config['password_algorithm'] = 'ssha256';\n$config['password_algorithm_prefix'] = '{SSHA256}';\n$config['password_query'] =  UPDATE mailbox SET password = %P WHERE username = %u ;", 
-            "title": "Enable password changing"
-        }, 
-        {
-            "location": "/u_and_e/#learn-spam-and-ham", 
-            "text": "Rspamd learns mail as spam or ham when you move a message in or out of the junk folder to any mailbox besides trash.\nThis is archived by using the Dovecot plugin \"antispam\" and a simple parser script.  Rspamd also auto-learns mail when a high or low score is detected (see https://rspamd.com/doc/configuration/statistic.html#autolearning)  The bayes statistics are written to Redis as keys  BAYES_HAM  and  BAYES_SPAM .  You can also use Rspamds web ui to learn ham and/or spam.", 
-            "title": "Learn spam and ham"
-        }, 
-        {
-            "location": "/u_and_e/#mysql", 
-            "text": "", 
-            "title": "MySQL"
-        }, 
-        {
-            "location": "/u_and_e/#connect-to-the-mysql-database", 
-            "text": "source mailcow.conf\ndocker-compose exec mysql-mailcow mysql -u${DBUSER} -p${DBPASS} ${DBNAME}", 
-            "title": "Connect to the MySQL database:"
-        }, 
-        {
-            "location": "/u_and_e/#backup-the-database", 
-            "text": "cd /path/to/mailcow-dockerized\nsource mailcow.conf\nDATE=$(date + %Y%m%d_%H%M%S )\ndocker-compose exec mysql-mailcow mysqldump --default-character-set=utf8mb4 -u${DBUSER} -p${DBPASS} ${DBNAME}   backup_${DBNAME}_${DATE}.sql", 
-            "title": "Backup the database:"
-        }, 
-        {
-            "location": "/u_and_e/#restore-the-database", 
-            "text": "cd /path/to/mailcow-dockerized\nsource mailcow.conf\ndocker-compose exec mysql-mailcow mysql -u${DBUSER} -p${DBPASS} ${DBNAME}   backup_file.sql", 
-            "title": "Restore the database:"
-        }, 
-        {
-            "location": "/u_and_e/#read-logs", 
-            "text": "You can use  docker-compose logs $service-name  for all containers.  Run  docker-compose logs  for all logs at once.  Follow the log output by running docker-compose with  logs -f .", 
-            "title": "Read logs"
-        }, 
-        {
-            "location": "/u_and_e/#redirect-port-80-to-443", 
-            "text": "Since February the 28th 2017 mailcow does come with port 80 and 443 enabled.  Open  mailcow.conf  and set  HTTP_BIND=0.0.0.0 .  Open  data/conf/nginx/site.conf  and add a new \"catch-all\" site at the top of that file:  server {\n    listen 80 default_server;\n    server_name _;\n    return 301 https://$host$request_uri;\n}  Restart the stack, changed containers will be updated:  docker-compose up -d", 
-            "title": "Redirect port 80 to 443"
-        }, 
-        {
-            "location": "/u_and_e/#redis", 
-            "text": "", 
-            "title": "Redis"
-        }, 
-        {
-            "location": "/u_and_e/#connect-to-redis-key-store", 
-            "text": "docker-compose exec redis-mailcow redis-cli", 
-            "title": "Connect to redis key store:"
-        }, 
-        {
-            "location": "/u_and_e/#remove-persistent-data", 
-            "text": "Remove volume  mysql-vol-1  to remove all MySQL data.  Remove volume  redis-vol-1  to remove all Redis data.  Remove volume  vmail-vol-1  to remove all contents of  /var/vmail  mounted to  dovecot-mailcow .  Remove volume  dkim-vol-1  to remove all DKIM keys.  Remove volume  rspamd-vol-1  to remove all Rspamd data.   Running  docker-compose down -v  will  destroy all mailcow: dockerized volumes  and delete any related containers.Reset mailcow admin to  admin:moohoo :   Drop admin table   source mailcow.conf\ndocker-compose exec mysql-mailcow mysql -u${DBUSER} -p${DBPASS} ${DBNAME} -e  DROP TABLE admin;    Open mailcow UI to auto-init the db", 
-            "title": "Remove persistent data"
-        }, 
-        {
-            "location": "/u_and_e/#rspamd", 
-            "text": "", 
-            "title": "Rspamd"
-        }, 
-        {
-            "location": "/u_and_e/#rspamd-cli-tools", 
-            "text": "docker-compose exec rspamd-mailcow rspamc --help\ndocker-compose exec rspamd-mailcow rspamadm --help  See  Rspamd documentation", 
-            "title": "Rspamd CLI tools"
-        }, 
-        {
-            "location": "/u_and_e/#adjust-service-configurations", 
-            "text": "The most important configuration files are mounted from the host into the related containers:  data/conf\n\u251c\u2500\u2500 bind9\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 named.conf\n\u251c\u2500\u2500 dovecot\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 dovecot.conf\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 dovecot-master.passwd\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 sieve_after\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 sql\n\u2502\u00a0\u00a0     \u251c\u2500\u2500 dovecot-dict-sql.conf\n\u2502\u00a0\u00a0     \u2514\u2500\u2500 dovecot-mysql.conf\n\u251c\u2500\u2500 mysql\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 my.cnf\n\u251c\u2500\u2500 nginx\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 dynmaps.conf\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 site.conf\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 templates\n\u2502\u00a0\u00a0     \u251c\u2500\u2500 listen_plain.template\n\u2502\u00a0\u00a0     \u251c\u2500\u2500 listen_ssl.template\n\u2502\u00a0\u00a0     \u2514\u2500\u2500 server_name.template\n\u251c\u2500\u2500 pdns\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 pdns_custom.lua\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 recursor.conf\n\u251c\u2500\u2500 postfix\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 main.cf\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 master.cf\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 postscreen_access.cidr\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 smtp_dsn_filter\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 sql\n\u2502\u00a0\u00a0     \u251c\u2500\u2500 mysql_relay_recipient_maps.cf\n\u2502\u00a0\u00a0     \u251c\u2500\u2500 mysql_tls_enforce_in_policy.cf\n\u2502\u00a0\u00a0     \u251c\u2500\u2500 mysql_tls_enforce_out_policy.cf\n\u2502\u00a0\u00a0     \u251c\u2500\u2500 mysql_virtual_alias_domain_catchall_maps.cf\n\u2502\u00a0\u00a0     \u251c\u2500\u2500 mysql_virtual_alias_domain_maps.cf\n\u2502\u00a0\u00a0     \u251c\u2500\u2500 mysql_virtual_alias_maps.cf\n\u2502\u00a0\u00a0     \u251c\u2500\u2500 mysql_virtual_domains_maps.cf\n\u2502\u00a0\u00a0     \u251c\u2500\u2500 mysql_virtual_mailbox_maps.cf\n\u2502\u00a0\u00a0     \u251c\u2500\u2500 mysql_virtual_relay_domain_maps.cf\n\u2502\u00a0\u00a0     \u251c\u2500\u2500 mysql_virtual_sender_acl.cf\n\u2502\u00a0\u00a0     \u2514\u2500\u2500 mysql_virtual_spamalias_maps.cf\n\u251c\u2500\u2500 rmilter\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 rmilter.conf\n\u251c\u2500\u2500 rspamd\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 dynmaps\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u251c\u2500\u2500 authoritative.php\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u251c\u2500\u2500 settings.php\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u251c\u2500\u2500 tags.php\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u2514\u2500\u2500 vars.inc.php -  ../../../web/inc/vars.inc.php\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 local.d\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u251c\u2500\u2500 dkim.conf\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u251c\u2500\u2500 metrics.conf\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u251c\u2500\u2500 options.inc\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u251c\u2500\u2500 redis.conf\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u251c\u2500\u2500 rspamd.conf.local\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u2514\u2500\u2500 statistic.conf\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 lua\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u2514\u2500\u2500 rspamd.local.lua\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 override.d\n\u2502\u00a0\u00a0     \u251c\u2500\u2500 logging.inc\n\u2502\u00a0\u00a0     \u251c\u2500\u2500 worker-controller.inc\n\u2502\u00a0\u00a0     \u2514\u2500\u2500 worker-normal.inc\n\u2514\u2500\u2500 sogo\n    \u251c\u2500\u2500 sieve.creds\n    \u2514\u2500\u2500 sogo.conf  Just change the according configuration file on the host and restart the related service:  docker-compose restart service-mailcow", 
-            "title": "Adjust service configurations"
-        }, 
-        {
-            "location": "/u_and_e/#tagging", 
-            "text": "Mailbox users can tag their mail address like in  [email protected]  and choose between to setups to handle this tag:   Move this message to a subfolder \"facebook\" (will be created lower case if not existing)  Prepend the tag to the subject: \"[facebook] Subject\"", 
-            "title": "Tagging"
-        }, 
-        {
-            "location": "/u_and_e/#two-factor-authentication", 
-            "text": "So far two methods for TFA are impelemented. Both work with the fantastic  Yubikey .   While Yubi OTP needs an active internet connection and an API ID/key, U2F will work with any FIDO U2F USB key out of the box.  Both methods support mulitple YubiKeys.  As administrator you are able to temporary disable a domain adminsitrators TFA login until they successfully logged in.  The key used to login will be displayed in green, while other keys remain grey.", 
-            "title": "Two-factor authentication"
-        }, 
-        {
-            "location": "/u_and_e/#yubi-otp", 
-            "text": "The Yubi API ID and Key will be checked against the Yubico Cloud API. When setting up TFA you will be asked for your personal API account for this key.\nThe API ID, API key and the first 12 characters (your YubiKeys ID in modhex) are stored in the MySQL table as secret.", 
-            "title": "Yubi OTP"
-        }, 
-        {
-            "location": "/u_and_e/#u2f", 
-            "text": "Only Google Chrome (+derivates) and Opera support U2F authentication to this day natively.\nFor Firefox you will need to install the \"U2F Support Add-on\" as provided on  mozilla.org .\nU2F works without an internet connection.# Why does mailcow come with a DNS resolver?  For DNS blacklist lookups and DNSSEC.  Most systems use either a public or a local caching DNS resolver.\nThat's a very bad idea when it comes to filter spam using DNS-based blackhole lists (DNSBL) or similar technics.\nMost if not all providers apply a rate limit based on the DNS resolver that is used to query their service.\nUsing a public resolver like Googles 4x8, OpenDNS or any other shared DNS resolver like your ISPs will hit that limit very soon.", 
-            "title": "U2F"
-        }
-    ]
-}
\ No newline at end of file
diff --git a/search.html b/search.html
deleted file mode 100644
index c410bf00..00000000
--- a/search.html
+++ /dev/null
@@ -1,150 +0,0 @@
-<!DOCTYPE html>
-<!--[if IE 8]><html class="no-js lt-ie9" lang="en" > <![endif]-->
-<!--[if gt IE 8]><!--> <html class="no-js" lang="en" > <!--<![endif]-->
-<head>
-  <meta charset="utf-8">
-  <meta http-equiv="X-UA-Compatible" content="IE=edge">
-  <meta name="viewport" content="width=device-width, initial-scale=1.0">
-  
-  
-  <link rel="shortcut icon" href="./img/favicon.ico">
-  <title>mailcow: dockerized</title>
-  <link href='https://fonts.googleapis.com/css?family=Lato:400,700|Roboto+Slab:400,700|Inconsolata:400,700' rel='stylesheet' type='text/css'>
-
-  <link rel="stylesheet" href="./css/theme.css" type="text/css" />
-  <link rel="stylesheet" href="./css/theme_extra.css" type="text/css" />
-  <link rel="stylesheet" href="./css/highlight.css">
-  
-  <script src="./js/jquery-2.1.1.min.js"></script>
-  <script src="./js/modernizr-2.8.3.min.js"></script>
-  <script type="text/javascript" src="./js/highlight.pack.js"></script>
-  <script>var base_url = '.';</script>
-  <script data-main="./mkdocs/js/search.js" src="./mkdocs/js/require.js"></script>
-
-  
-</head>
-
-<body class="wy-body-for-nav" role="document">
-
-  <div class="wy-grid-for-nav">
-
-    
-    <nav data-toggle="wy-nav-shift" class="wy-nav-side stickynav">
-      <div class="wy-side-nav-search">
-        <a href="." class="icon icon-home"> mailcow: dockerized</a>
-        <div role="search">
-  <form id ="rtd-search-form" class="wy-form" action="./search.html" method="get">
-    <input type="text" name="q" placeholder="Search docs" />
-  </form>
-</div>
-      </div>
-
-      <div class="wy-menu wy-menu-vertical" data-spy="affix" role="navigation" aria-label="main navigation">
-	<ul class="current">
-          
-            <li>
-    <li class="toctree-l1 ">
-        <a class="" href=".">Overview</a>
-        
-    </li>
-<li>
-          
-            <li>
-    <li class="toctree-l1 ">
-        <a class="" href="install/">Installation</a>
-        
-    </li>
-<li>
-          
-            <li>
-    <li class="toctree-l1 ">
-        <a class="" href="first_steps/">First Steps</a>
-        
-    </li>
-<li>
-          
-            <li>
-    <li class="toctree-l1 ">
-        <a class="" href="u_and_e/">Usage & Examples</a>
-        
-    </li>
-<li>
-          
-        </ul>
-      </div>
-      &nbsp;
-    </nav>
-
-    <section data-toggle="wy-nav-shift" class="wy-nav-content-wrap">
-
-      
-      <nav class="wy-nav-top" role="navigation" aria-label="top navigation">
-        <i data-toggle="wy-nav-top" class="fa fa-bars"></i>
-        <a href=".">mailcow: dockerized</a>
-      </nav>
-
-      
-      <div class="wy-nav-content">
-        <div class="rst-content">
-          <div role="navigation" aria-label="breadcrumbs navigation">
-  <ul class="wy-breadcrumbs">
-    <li><a href=".">Docs</a> &raquo;</li>
-    
-    
-    <li class="wy-breadcrumbs-aside">
-      
-    </li>
-  </ul>
-  <hr/>
-</div>
-          <div role="main">
-            <div class="section">
-              
-
-  <h1 id="search">Search Results</h1>
-
-  <form id="content_search" action="search.html">
-    <span role="status" aria-live="polite" class="ui-helper-hidden-accessible"></span>
-    <input name="q" id="mkdocs-search-query" type="text" class="search_input search-query ui-autocomplete-input" placeholder="Search the Docs" autocomplete="off" autofocus>
-  </form>
-
-  <div id="mkdocs-search-results" class="search-results">
-    Searching...
-  </div>
-
-
-            </div>
-          </div>
-          <footer>
-  
-
-  <hr/>
-
-  <div role="contentinfo">
-    <!-- Copyright etc -->
-    
-  </div>
-
-  Built with <a href="http://www.mkdocs.org">MkDocs</a> using a <a href="https://github.com/snide/sphinx_rtd_theme">theme</a> provided by <a href="https://readthedocs.org">Read the Docs</a>.
-</footer>
-	  
-        </div>
-      </div>
-
-    </section>
-    
-  </div>
-
-  <div class="rst-versions" role="note" style="cursor: pointer">
-    <span class="rst-current-version" data-toggle="rst-current-version">
-      
-          <a href="https://github.com/andryyy/mailcow-dockerized/" class="icon icon-github" style="float: left; color: #fcfcfc"> GitHub</a>
-      
-      
-      
-    </span>
-</div>
-    <script src="./js/theme.js"></script>
-
-</body>
-</html>
diff --git a/sitemap.xml b/sitemap.xml
deleted file mode 100644
index 83b1c57a..00000000
--- a/sitemap.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
-
-    
-    <url>
-     <loc>/</loc>
-     <lastmod>2017-03-02</lastmod>
-     <changefreq>daily</changefreq>
-    </url>
-    
-
-    
-    <url>
-     <loc>/install/</loc>
-     <lastmod>2017-03-02</lastmod>
-     <changefreq>daily</changefreq>
-    </url>
-    
-
-    
-    <url>
-     <loc>/first_steps/</loc>
-     <lastmod>2017-03-02</lastmod>
-     <changefreq>daily</changefreq>
-    </url>
-    
-
-    
-    <url>
-     <loc>/u_and_e/</loc>
-     <lastmod>2017-03-02</lastmod>
-     <changefreq>daily</changefreq>
-    </url>
-    
-
-</urlset>
\ No newline at end of file
diff --git a/u_and_e/index.html b/u_and_e/index.html
deleted file mode 100644
index 74559af4..00000000
--- a/u_and_e/index.html
+++ /dev/null
@@ -1,541 +0,0 @@
-<!DOCTYPE html>
-<!--[if IE 8]><html class="no-js lt-ie9" lang="en" > <![endif]-->
-<!--[if gt IE 8]><!--> <html class="no-js" lang="en" > <!--<![endif]-->
-<head>
-  <meta charset="utf-8">
-  <meta http-equiv="X-UA-Compatible" content="IE=edge">
-  <meta name="viewport" content="width=device-width, initial-scale=1.0">
-  
-  
-  <link rel="shortcut icon" href="../img/favicon.ico">
-  <title>Usage & Examples - mailcow: dockerized</title>
-  <link href='https://fonts.googleapis.com/css?family=Lato:400,700|Roboto+Slab:400,700|Inconsolata:400,700' rel='stylesheet' type='text/css'>
-
-  <link rel="stylesheet" href="../css/theme.css" type="text/css" />
-  <link rel="stylesheet" href="../css/theme_extra.css" type="text/css" />
-  <link rel="stylesheet" href="../css/highlight.css">
-  
-  <script>
-    // Current page data
-    var mkdocs_page_name = "Usage & Examples";
-    var mkdocs_page_input_path = "u_and_e.md";
-    var mkdocs_page_url = "/u_and_e/";
-  </script>
-  
-  <script src="../js/jquery-2.1.1.min.js"></script>
-  <script src="../js/modernizr-2.8.3.min.js"></script>
-  <script type="text/javascript" src="../js/highlight.pack.js"></script> 
-  
-</head>
-
-<body class="wy-body-for-nav" role="document">
-
-  <div class="wy-grid-for-nav">
-
-    
-    <nav data-toggle="wy-nav-shift" class="wy-nav-side stickynav">
-      <div class="wy-side-nav-search">
-        <a href=".." class="icon icon-home"> mailcow: dockerized</a>
-        <div role="search">
-  <form id ="rtd-search-form" class="wy-form" action="../search.html" method="get">
-    <input type="text" name="q" placeholder="Search docs" />
-  </form>
-</div>
-      </div>
-
-      <div class="wy-menu wy-menu-vertical" data-spy="affix" role="navigation" aria-label="main navigation">
-	<ul class="current">
-          
-            <li>
-    <li class="toctree-l1 ">
-        <a class="" href="..">Overview</a>
-        
-    </li>
-<li>
-          
-            <li>
-    <li class="toctree-l1 ">
-        <a class="" href="../install/">Installation</a>
-        
-    </li>
-<li>
-          
-            <li>
-    <li class="toctree-l1 ">
-        <a class="" href="../first_steps/">First Steps</a>
-        
-    </li>
-<li>
-          
-            <li>
-    <li class="toctree-l1 current">
-        <a class="current" href="./">Usage & Examples</a>
-        
-            <ul>
-            
-                <li class="toctree-l3"><a href="#anonymize-headers-for-smtp-relayed">Anonymize headers for smtp relayed</a></li>
-                
-            
-                <li class="toctree-l3"><a href="#backup-and-restore-maildir-simple-tar-file">Backup and restore maildir (simple tar file)</a></li>
-                
-                    <li><a class="toctree-l4" href="#backup">Backup</a></li>
-                
-                    <li><a class="toctree-l4" href="#restore">Restore</a></li>
-                
-            
-                <li class="toctree-l3"><a href="#black-and-whitelist">Black- and Whitelist</a></li>
-                
-            
-                <li class="toctree-l3"><a href="#change-default-theme">Change default theme</a></li>
-                
-            
-                <li class="toctree-l3"><a href="#customize-dockerfiles">Customize Dockerfiles</a></li>
-                
-            
-                <li class="toctree-l3"><a href="#do-not-check-sender-addresses-for-any-domain">Do not check sender addresses for any domain</a></li>
-                
-            
-                <li class="toctree-l3"><a href="#install-roundcube">Install Roundcube</a></li>
-                
-                    <li><a class="toctree-l4" href="#enable-password-changing">Enable password changing</a></li>
-                
-            
-                <li class="toctree-l3"><a href="#learn-spam-and-ham">Learn spam and ham</a></li>
-                
-            
-                <li class="toctree-l3"><a href="#mysql">MySQL</a></li>
-                
-                    <li><a class="toctree-l4" href="#connect-to-the-mysql-database">Connect to the MySQL database:</a></li>
-                
-                    <li><a class="toctree-l4" href="#backup-the-database">Backup the database:</a></li>
-                
-                    <li><a class="toctree-l4" href="#restore-the-database">Restore the database:</a></li>
-                
-            
-                <li class="toctree-l3"><a href="#read-logs">Read logs</a></li>
-                
-            
-                <li class="toctree-l3"><a href="#redirect-port-80-to-443">Redirect port 80 to 443</a></li>
-                
-            
-                <li class="toctree-l3"><a href="#redis">Redis</a></li>
-                
-                    <li><a class="toctree-l4" href="#connect-to-redis-key-store">Connect to redis key store:</a></li>
-                
-            
-                <li class="toctree-l3"><a href="#remove-persistent-data">Remove persistent data</a></li>
-                
-            
-                <li class="toctree-l3"><a href="#rspamd">Rspamd</a></li>
-                
-                    <li><a class="toctree-l4" href="#rspamd-cli-tools">Rspamd CLI tools</a></li>
-                
-            
-                <li class="toctree-l3"><a href="#adjust-service-configurations">Adjust service configurations</a></li>
-                
-            
-                <li class="toctree-l3"><a href="#tagging">Tagging</a></li>
-                
-            
-                <li class="toctree-l3"><a href="#two-factor-authentication">Two-factor authentication</a></li>
-                
-                    <li><a class="toctree-l4" href="#yubi-otp">Yubi OTP</a></li>
-                
-                    <li><a class="toctree-l4" href="#u2f">U2F</a></li>
-                
-            
-            </ul>
-        
-    </li>
-<li>
-          
-        </ul>
-      </div>
-      &nbsp;
-    </nav>
-
-    <section data-toggle="wy-nav-shift" class="wy-nav-content-wrap">
-
-      
-      <nav class="wy-nav-top" role="navigation" aria-label="top navigation">
-        <i data-toggle="wy-nav-top" class="fa fa-bars"></i>
-        <a href="..">mailcow: dockerized</a>
-      </nav>
-
-      
-      <div class="wy-nav-content">
-        <div class="rst-content">
-          <div role="navigation" aria-label="breadcrumbs navigation">
-  <ul class="wy-breadcrumbs">
-    <li><a href="..">Docs</a> &raquo;</li>
-    
-      
-    
-    <li>Usage & Examples</li>
-    <li class="wy-breadcrumbs-aside">
-      
-        <a href="https://github.com/andryyy/mailcow-dockerized/edit/master/docs/u_and_e.md"
-          class="icon icon-github"> Edit on GitHub</a>
-      
-    </li>
-  </ul>
-  <hr/>
-</div>
-          <div role="main">
-            <div class="section">
-              
-                <h1 id="anonymize-headers-for-smtp-relayed">Anonymize headers for smtp relayed</h1>
-<p>Save as <code>data/conf/postfix/mailcow_anonymize_headers.pcre</code>:</p>
-<pre><code>/^\s*Received:[^\)]+\)\s+\(Authenticated sender:(.+)/
-    REPLACE Received: from localhost (localhost [127.0.0.1]) (Authenticated sender:$1
-/^\s*User-Agent/        IGNORE
-/^\s*X-Enigmail/        IGNORE
-/^\s*X-Mailer/          IGNORE
-/^\s*X-Originating-IP/  IGNORE
-/^\s*X-Forward/         IGNORE
-/^\s*Mime-Version:/     IGNORE
-</code></pre>
-
-<p>Add this to <code>data/conf/postfix/main.cf</code>:</p>
-<pre><code>smtp_header_checks = pcre:/opt/postfix/conf/mailcow_anonymize_headers.pcre
-</code></pre>
-
-<h1 id="backup-and-restore-maildir-simple-tar-file">Backup and restore maildir (simple tar file)</h1>
-<h3 id="backup"><strong>Backup</strong></h3>
-<p>This line backups the vmail directory to a file backup_vmail.tar.gz in the mailcow root directory:</p>
-<pre><code>cd /path/to/mailcow-dockerized
-source mailcow.conf
-DATE=$(date +&quot;%Y%m%d_%H%M%S&quot;)
-docker run --rm -it -v $(docker inspect --format '{{ range .Mounts }}{{ if eq .Destination &quot;/var/vmail&quot; }}{{ .Name }}{{ end }}{{ end }}' $(docker-compose ps -q dovecot-mailcow)):/vmail -v ${PWD}:/backup debian:jessie tar cvfz /backup/backup_vmail.tar.gz /vmail
-</code></pre>
-
-<p>You can change the path by adjusting ${PWD} (which equals to the current directory) to any path you have write-access to.
-Set the filename <code>backup_vmail.tar.gz</code> to any custom name, but leave the path as it is. Example: <code>[...] tar cvfz /backup/my_own_filename_.tar.gz</code></p>
-<h3 id="restore"><strong>Restore</strong></h3>
-<pre><code>cd /path/to/mailcow-dockerized
-source mailcow.conf
-DATE=$(date +&quot;%Y%m%d_%H%M%S&quot;)
-docker run --rm -it -v $(docker inspect --format '{{ range .Mounts }}{{ if eq .Destination &quot;/var/vmail&quot; }}{{ .Name }}{{ end }}{{ end }}' $(docker-compose ps -q dovecot-mailcow)):/vmail -v ${PWD}:/backup debian:jessie tar xvfz /backup/backup_vmail.tar.gz
-</code></pre>
-
-<p>For the tab-tab... :-)</p>
-<pre><code>curl -L https://raw.githubusercontent.com/docker/compose/$(docker-compose version --short)/contrib/completion/bash/docker-compose -o /etc/bash_completion.d/docker-compose
-</code></pre>
-
-<h1 id="black-and-whitelist">Black- and Whitelist</h1>
-<p>Edit a domain as (domain) administrator to add an item to the filter table.</p>
-<p>Beware that a mailbox user can login to mailcow and override a domain policy filter item. </p>
-<h1 id="change-default-theme">Change default theme</h1>
-<p>mailcow uses <a href="http://getbootstrap.com/">Bootstrap</a>, a HTML, CSS, and JS framework.</p>
-<p>Open or create the file <code>data/web/inc/vars.local.inc.php</code> and change <code>DEFAULT_THEME</code> to either cerulean, cosmo, custom, cyborg, darkly, flatly, journal, paper, readable, sandstone, simplex, slate, spacelab, superhero, united or yeti (see https://bootswatch.com/):</p>
-<pre><code>&lt;?php
-$DEFAULT_THEME = &quot;paper&quot;;
-</code></pre>
-
-<h1 id="customize-dockerfiles">Customize Dockerfiles</h1>
-<p>Make your changes in <code>data/Dockerfiles/$service</code> and build the image locally:</p>
-<pre><code>docker build data/Dockerfiles/service -t andryyy/mailcow-dockerized:$service
-</code></pre>
-
-<p>Now auto-recreate modified containers:</p>
-<pre><code>docker-compose up -d
-</code></pre>
-
-<h1 id="do-not-check-sender-addresses-for-any-domain">Do not check sender addresses for any domain</h1>
-<p>This option is not best-practice and should only be implemented when there is no other option available to archive whatever you are trying to do.</p>
-<p>Simply create a file <code>data/conf/postfix/check_sender_access</code> and enter the following content:</p>
-<pre><code>[email protected] OK
-</code></pre>
-
-<p>Open <code>data/conf/postfix/main.cf</code> and find <code>smtpd_sender_restrictions</code>. Prepend <code>check_sasl_access hash:/opt/postfix/conf/check_sender_access</code> like this:</p>
-<pre><code>smtpd_sender_restrictions = check_sasl_access hash:/opt/postfix/conf/check_sender_access reject_authenticated_sender [...]
-</code></pre>
-
-<p>Run postmap on check_sasl_access:</p>
-<pre><code>docker-compose exec postfix-mailcow postmap /opt/postfix/conf/check_sasl_access
-</code></pre>
-
-<p>Restart the Postfix container.</p>
-<h1 id="install-roundcube">Install Roundcube</h1>
-<p>Download Roundcube 1.3.x (beta at the time of Feb 2017) to the web htdocs directory and extract it (here <code>rc/</code>):</p>
-<pre><code>cd data/web/rc
-wget -O - https://github.com/roundcube/roundcubemail/releases/download/1.3-beta/roundcubemail-1.3-beta-complete.tar.gz | tar xfvz -
-# Change folder name
-mv roundcubemail-1.3* rc
-# Change permissions
-chown -R root: rc/
-</code></pre>
-
-<p>Create a file <code>data/web/rc/config/config.inc.php</code> with the following content.</p>
-<p><strong>Change the <code>des_key</code> parameter to a random value.</strong> It is used to temporarily store your IMAP password.</p>
-<pre><code>&lt;?php
-error_reporting(0);
-if (!file_exists('/tmp/mime.types')) {
-file_put_contents(&quot;/tmp/mime.types&quot;, fopen(&quot;http://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types&quot;, 'r'));
-}
-$config = array();
-$config['db_dsnw'] = 'mysql://' . getenv('DBUSER') . ':' . getenv('DBPASS') . '@mysql/' . getenv('DBNAME');
-$config['default_host'] = 'tls://dovecot';
-$config['default_port'] = '143';
-$config['smtp_server'] = 'tls://postfix';
-$config['smtp_port'] = 587;
-$config['smtp_user'] = '%u';
-$config['smtp_pass'] = '%p';
-$config['support_url'] = '';
-$config['product_name'] = 'Roundcube Webmail';
-$config['des_key'] = 'rcmail-!24ByteDESkey*Str';
-$config['log_dir'] = '/dev/null';
-$config['temp_dir'] = '/tmp';
-$config['plugins'] = array(
-    'archive',
-);
-$config['skin'] = 'larry';
-$config['mime_types'] = '/tmp/mime.types';
-$config['imap_conn_options'] = array(
-'ssl' =&gt; array('verify_peer' =&gt; false, 'verify_peer_name' =&gt; false, 'allow_self_signed' =&gt; true)
-);
-$config['enable_installer'] = false;
-$config['smtp_conn_options'] = array(
-'ssl' =&gt; array('verify_peer' =&gt; false, 'verify_peer_name' =&gt; false, 'allow_self_signed' =&gt; true)
-);
-</code></pre>
-
-<p>Point your browser to <code>https://myserver/rc/installer</code> and follow the instructions.
-Initialize the database and leave the installer.
-<strong>Delete the directory <code>data/web/rc/installer</code> after a successful installation!</strong></p>
-<h2 id="enable-password-changing">Enable password changing</h2>
-<p>Open <code>data/web/rc/config.inc.php</code> and enable the password plugin:</p>
-<pre><code>...
-$config['plugins'] = array(
-    'archive',
-    'password',
-);
-...
-</code></pre>
-
-<p>Open <code>data/web/rc/plugins/password/password.php</code>, search for <code>case 'ssha':</code> and add above:</p>
-<pre><code>        case 'ssha256':
-            $salt = rcube_utils::random_bytes(8);
-            $crypted = base64_encode( hash('sha256', $password . $salt, TRUE ) . $salt );
-            $prefix  = '{SSHA256}';
-            break;
-</code></pre>
-
-<p>Open <code>data/web/rc/plugins/password/config.inc.php</code> and change the following parameters (or add them at the bottom of that file):</p>
-<pre><code>$config['password_driver'] = 'sql';
-$config['password_algorithm'] = 'ssha256';
-$config['password_algorithm_prefix'] = '{SSHA256}';
-$config['password_query'] = &quot;UPDATE mailbox SET password = %P WHERE username = %u&quot;;
-</code></pre>
-
-<h1 id="learn-spam-and-ham">Learn spam and ham</h1>
-<p>Rspamd learns mail as spam or ham when you move a message in or out of the junk folder to any mailbox besides trash.
-This is archived by using the Dovecot plugin "antispam" and a simple parser script.</p>
-<p>Rspamd also auto-learns mail when a high or low score is detected (see https://rspamd.com/doc/configuration/statistic.html#autolearning)</p>
-<p>The bayes statistics are written to Redis as keys <code>BAYES_HAM</code> and <code>BAYES_SPAM</code>.</p>
-<p>You can also use Rspamds web ui to learn ham and/or spam.</p>
-<h1 id="mysql">MySQL</h1>
-<h3 id="connect-to-the-mysql-database">Connect to the MySQL database:</h3>
-<pre><code>source mailcow.conf
-docker-compose exec mysql-mailcow mysql -u${DBUSER} -p${DBPASS} ${DBNAME}
-</code></pre>
-
-<h3 id="backup-the-database">Backup the database:</h3>
-<pre><code>cd /path/to/mailcow-dockerized
-source mailcow.conf
-DATE=$(date +&quot;%Y%m%d_%H%M%S&quot;)
-docker-compose exec mysql-mailcow mysqldump --default-character-set=utf8mb4 -u${DBUSER} -p${DBPASS} ${DBNAME} &gt; backup_${DBNAME}_${DATE}.sql
-</code></pre>
-
-<h3 id="restore-the-database">Restore the database:</h3>
-<pre><code>cd /path/to/mailcow-dockerized
-source mailcow.conf
-docker-compose exec mysql-mailcow mysql -u${DBUSER} -p${DBPASS} ${DBNAME} &lt; backup_file.sql
-</code></pre>
-
-<h1 id="read-logs">Read logs</h1>
-<p>You can use <code>docker-compose logs $service-name</code> for all containers.</p>
-<p>Run <code>docker-compose logs</code> for all logs at once.</p>
-<p>Follow the log output by running docker-compose with <code>logs -f</code>.</p>
-<h1 id="redirect-port-80-to-443">Redirect port 80 to 443</h1>
-<p>Since February the 28th 2017 mailcow does come with port 80 and 443 enabled.</p>
-<p>Open <code>mailcow.conf</code> and set <code>HTTP_BIND=0.0.0.0</code>.</p>
-<p>Open <code>data/conf/nginx/site.conf</code> and add a new "catch-all" site at the top of that file:</p>
-<pre><code>server {
-    listen 80 default_server;
-    server_name _;
-    return 301 https://$host$request_uri;
-}
-</code></pre>
-
-<p>Restart the stack, changed containers will be updated:</p>
-<p><code>docker-compose up -d</code></p>
-<h1 id="redis">Redis</h1>
-<h2 id="connect-to-redis-key-store">Connect to redis key store:</h2>
-<pre><code>docker-compose exec redis-mailcow redis-cli
-</code></pre>
-
-<h1 id="remove-persistent-data">Remove persistent data</h1>
-<ul>
-<li>Remove volume <code>mysql-vol-1</code> to remove all MySQL data.</li>
-<li>Remove volume <code>redis-vol-1</code> to remove all Redis data.</li>
-<li>Remove volume <code>vmail-vol-1</code> to remove all contents of <code>/var/vmail</code> mounted to <code>dovecot-mailcow</code>.</li>
-<li>Remove volume <code>dkim-vol-1</code> to remove all DKIM keys.</li>
-<li>Remove volume <code>rspamd-vol-1</code> to remove all Rspamd data.</li>
-</ul>
-<p>Running <code>docker-compose down -v</code> will <strong>destroy all mailcow: dockerized volumes</strong> and delete any related containers.Reset mailcow admin to <code>admin:moohoo</code>:</p>
-<ol>
-<li>Drop admin table</li>
-</ol>
-<pre><code>source mailcow.conf
-docker-compose exec mysql-mailcow mysql -u${DBUSER} -p${DBPASS} ${DBNAME} -e &quot;DROP TABLE admin;&quot;
-</code></pre>
-
-<ol>
-<li>Open mailcow UI to auto-init the db</li>
-</ol>
-<h1 id="rspamd">Rspamd</h1>
-<h2 id="rspamd-cli-tools">Rspamd CLI tools</h2>
-<pre><code>docker-compose exec rspamd-mailcow rspamc --help
-docker-compose exec rspamd-mailcow rspamadm --help
-</code></pre>
-
-<p>See <a href="https://rspamd.com/doc/index.html">Rspamd documentation</a></p>
-<h1 id="adjust-service-configurations">Adjust service configurations</h1>
-<p>The most important configuration files are mounted from the host into the related containers:</p>
-<pre><code>data/conf
-├── bind9
-│   └── named.conf
-├── dovecot
-│   ├── dovecot.conf
-│   ├── dovecot-master.passwd
-│   ├── sieve_after
-│   └── sql
-│       ├── dovecot-dict-sql.conf
-│       └── dovecot-mysql.conf
-├── mysql
-│   └── my.cnf
-├── nginx
-│   ├── dynmaps.conf
-│   ├── site.conf
-│   └── templates
-│       ├── listen_plain.template
-│       ├── listen_ssl.template
-│       └── server_name.template
-├── pdns
-│   ├── pdns_custom.lua
-│   └── recursor.conf
-├── postfix
-│   ├── main.cf
-│   ├── master.cf
-│   ├── postscreen_access.cidr
-│   ├── smtp_dsn_filter
-│   └── sql
-│       ├── mysql_relay_recipient_maps.cf
-│       ├── mysql_tls_enforce_in_policy.cf
-│       ├── mysql_tls_enforce_out_policy.cf
-│       ├── mysql_virtual_alias_domain_catchall_maps.cf
-│       ├── mysql_virtual_alias_domain_maps.cf
-│       ├── mysql_virtual_alias_maps.cf
-│       ├── mysql_virtual_domains_maps.cf
-│       ├── mysql_virtual_mailbox_maps.cf
-│       ├── mysql_virtual_relay_domain_maps.cf
-│       ├── mysql_virtual_sender_acl.cf
-│       └── mysql_virtual_spamalias_maps.cf
-├── rmilter
-│   └── rmilter.conf
-├── rspamd
-│   ├── dynmaps
-│   │   ├── authoritative.php
-│   │   ├── settings.php
-│   │   ├── tags.php
-│   │   └── vars.inc.php -&gt; ../../../web/inc/vars.inc.php
-│   ├── local.d
-│   │   ├── dkim.conf
-│   │   ├── metrics.conf
-│   │   ├── options.inc
-│   │   ├── redis.conf
-│   │   ├── rspamd.conf.local
-│   │   └── statistic.conf
-│   ├── lua
-│   │   └── rspamd.local.lua
-│   └── override.d
-│       ├── logging.inc
-│       ├── worker-controller.inc
-│       └── worker-normal.inc
-└── sogo
-    ├── sieve.creds
-    └── sogo.conf
-
-</code></pre>
-
-<p>Just change the according configuration file on the host and restart the related service: <code>docker-compose restart service-mailcow</code></p>
-<h1 id="tagging">Tagging</h1>
-<p>Mailbox users can tag their mail address like in <code>[email protected]</code> and choose between to setups to handle this tag:</p>
-<ol>
-<li>Move this message to a subfolder "facebook" (will be created lower case if not existing)</li>
-<li>Prepend the tag to the subject: "[facebook] Subject"</li>
-</ol>
-<h1 id="two-factor-authentication">Two-factor authentication</h1>
-<p>So far two methods for TFA are impelemented. Both work with the fantastic <a href="https://www.yubico.com">Yubikey</a>. </p>
-<p>While Yubi OTP needs an active internet connection and an API ID/key, U2F will work with any FIDO U2F USB key out of the box.</p>
-<p>Both methods support mulitple YubiKeys.</p>
-<p>As administrator you are able to temporary disable a domain adminsitrators TFA login until they successfully logged in.</p>
-<p>The key used to login will be displayed in green, while other keys remain grey.</p>
-<h2 id="yubi-otp">Yubi OTP</h2>
-<p>The Yubi API ID and Key will be checked against the Yubico Cloud API. When setting up TFA you will be asked for your personal API account for this key.
-The API ID, API key and the first 12 characters (your YubiKeys ID in modhex) are stored in the MySQL table as secret.</p>
-<h2 id="u2f">U2F</h2>
-<p>Only Google Chrome (+derivates) and Opera support U2F authentication to this day natively.
-For Firefox you will need to install the "U2F Support Add-on" as provided on <a href="https://addons.mozilla.org/en-US/firefox/addon/u2f-support-add-on/">mozilla.org</a>.
-U2F works without an internet connection.# Why does mailcow come with a DNS resolver?</p>
-<p>For DNS blacklist lookups and DNSSEC.</p>
-<p>Most systems use either a public or a local caching DNS resolver.
-That's a very bad idea when it comes to filter spam using DNS-based blackhole lists (DNSBL) or similar technics.
-Most if not all providers apply a rate limit based on the DNS resolver that is used to query their service.
-Using a public resolver like Googles 4x8, OpenDNS or any other shared DNS resolver like your ISPs will hit that limit very soon.</p>
-              
-            </div>
-          </div>
-          <footer>
-  
-    <div class="rst-footer-buttons" role="navigation" aria-label="footer navigation">
-      
-      
-        <a href="../first_steps/" class="btn btn-neutral" title="First Steps"><span class="icon icon-circle-arrow-left"></span> Previous</a>
-      
-    </div>
-  
-
-  <hr/>
-
-  <div role="contentinfo">
-    <!-- Copyright etc -->
-    
-  </div>
-
-  Built with <a href="http://www.mkdocs.org">MkDocs</a> using a <a href="https://github.com/snide/sphinx_rtd_theme">theme</a> provided by <a href="https://readthedocs.org">Read the Docs</a>.
-</footer>
-	  
-        </div>
-      </div>
-
-    </section>
-    
-  </div>
-
-  <div class="rst-versions" role="note" style="cursor: pointer">
-    <span class="rst-current-version" data-toggle="rst-current-version">
-      
-          <a href="https://github.com/andryyy/mailcow-dockerized/" class="icon icon-github" style="float: left; color: #fcfcfc"> GitHub</a>
-      
-      
-        <span><a href="../first_steps/" style="color: #fcfcfc;">&laquo; Previous</a></span>
-      
-      
-    </span>
-</div>
-    <script src="../js/theme.js"></script>
-
-</body>
-</html>