diff --git a/.github/stale.yml b/.github/stale.yml
deleted file mode 100644
index 24db8c03..00000000
--- a/.github/stale.yml
+++ /dev/null
@@ -1,18 +0,0 @@
-# Number of days of inactivity before an issue becomes stale
-daysUntilStale: 60
-# Number of days of inactivity before a stale issue is closed
-daysUntilClose: 7
-# Issues with these labels will never be considered stale
-exemptLabels:
- - pinned
- - security
- - enhancement
-# Label to use when marking an issue as stale
-staleLabel: dunno
-# Comment to post when marking an issue as stale. Set to `false` to disable
-markComment: >
- This issue has been automatically marked as stale because it has not had
- recent activity. It will be closed if no further activity occurs. Thank you
- for your contributions.
-# Comment to post when closing a stale issue. Set to `false` to disable
-closeComment: false
diff --git a/.github/workflows/close_old_issues_and_prs.yml b/.github/workflows/close_old_issues_and_prs.yml
new file mode 100644
index 00000000..f9630618
--- /dev/null
+++ b/.github/workflows/close_old_issues_and_prs.yml
@@ -0,0 +1,35 @@
+name: 'Close stale issues and PRs'
+on:
+ schedule:
+ # Once every day at midnight UTC
+ - cron: "0 0 * * *"
+ workflow_dispatch: # Allow to run workflow manually
+ issue_comment: # Run workflow on comments
+
+jobs:
+ stale:
+ runs-on: ubuntu-latest
+ permissions:
+ issues: write
+ pull-requests: write
+ steps:
+ - name: Mark/Close Stale Issues and Pull Requests 🗑️
+ uses: actions/stale@v4
+ with:
+ repo-token: ${{ secrets.GITHUB_TOKEN }}
+ days-before-stale: 60
+ days-before-close: 7
+ stale-issue-message: >
+ This issue has been automatically marked as stale because it has not had
+ recent activity. It will be closed if no further activity occurs.
+ stale-pr-message: >
+ This pull request has been automatically marked as stale because it has not had
+ recent activity. It will be closed if no further activity occurs.
+ exempt-issue-labels: "pinned,security,enhancement"
+ exempt-pr-labels: "pinned,security,enhancement"
+ stale-issue-label: "stale"
+ stale-pr-label: "stale"
+ operations-per-run: "250"
+ ascending: "true"
+ #DRY-RUN
+ debug-only: "false"
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 0cb9e7b6..06328b85 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,10 +1,10 @@
!data/conf/nginx/dynmaps.conf
!data/conf/nginx/meta_exporter.conf
!data/conf/nginx/site.conf
+!/**/.gitkeep
*.iml
.idea
.vscode/*
-data/assets/ejabberd/sqlite/sqlite.db
data/assets/ssl-example/*
data/assets/ssl/*
data/conf/borgmatic/
@@ -22,7 +22,6 @@ data/conf/dovecot/sni.conf
data/conf/dovecot/sogo-sso.conf
data/conf/dovecot/sogo_trusted_ip.conf
data/conf/dovecot/sql
-data/conf/ejabberd/autogen/*
data/conf/nextcloud-*.bak
data/conf/nginx/*.active
data/conf/nginx/*.bak
@@ -46,11 +45,12 @@ data/conf/sogo/sieve.creds
data/conf/sogo/sogo-full.svg
data/gitea/
data/gogs/
-data/hooks/dovecot
-data/hooks/phpfpm
-data/hooks/postfix
-data/hooks/rspamd
-data/hooks/unbound
+data/hooks/dovecot/*
+data/hooks/phpfpm/*
+data/hooks/postfix/*
+data/hooks/rspamd/*
+data/hooks/sogo/*
+data/hooks/unbound/*
data/web/.well-known/acme-challenge
data/web/css/build/0081-custom-mailcow.css
data/web/inc/vars.local.inc.php
diff --git a/data/Dockerfiles/dovecot/Dockerfile b/data/Dockerfiles/dovecot/Dockerfile
index 5a1df99e..19900973 100644
--- a/data/Dockerfiles/dovecot/Dockerfile
+++ b/data/Dockerfiles/dovecot/Dockerfile
@@ -2,7 +2,7 @@ FROM debian:buster-slim
LABEL maintainer "Andre Peters "
ARG DEBIAN_FRONTEND=noninteractive
-ARG DOVECOT=2.3.14
+ARG DOVECOT=2.3.14.1
ENV LC_ALL C
ENV GOSU_VERSION 1.12
@@ -74,6 +74,7 @@ RUN groupadd -g 5000 vmail \
liburi-perl \
libwww-perl \
lua-sql-mysql \
+ lua-socket \
mariadb-client \
procps \
python3-pip \
@@ -112,7 +113,6 @@ COPY clean_q_aged.sh /usr/local/bin/clean_q_aged.sh
COPY syslog-ng.conf /etc/syslog-ng/syslog-ng.conf
COPY syslog-ng-redis_slave.conf /etc/syslog-ng/syslog-ng-redis_slave.conf
COPY imapsync /usr/local/bin/imapsync
-COPY postlogin.sh /usr/local/bin/postlogin.sh
COPY imapsync_runner.pl /usr/local/bin/imapsync_runner.pl
COPY report-spam.sieve /usr/lib/dovecot/sieve/report-spam.sieve
COPY report-ham.sieve /usr/lib/dovecot/sieve/report-ham.sieve
diff --git a/data/Dockerfiles/dovecot/docker-entrypoint.sh b/data/Dockerfiles/dovecot/docker-entrypoint.sh
index 5df135cf..5ea1609d 100755
--- a/data/Dockerfiles/dovecot/docker-entrypoint.sh
+++ b/data/Dockerfiles/dovecot/docker-entrypoint.sh
@@ -60,14 +60,6 @@ map {
}
EOF
-# Write last logins to Redis
-if [[ ! -z ${REDIS_SLAVEOF_IP} ]]; then
- cp /etc/syslog-ng/syslog-ng-redis_slave.conf /etc/syslog-ng/syslog-ng.conf
- echo -n "redis:host=${REDIS_SLAVEOF_IP}:port=${REDIS_SLAVEOF_PORT}" > /etc/dovecot/last_login
-else
- echo -n "redis:host=${IPV4_NETWORK}.249:port=6379" > /etc/dovecot/last_login
-fi
-
# Create dict used for sieve pre and postfilters
cat < /etc/dovecot/sql/dovecot-dict-sql-sieve_before.conf
# Autogenerated by mailcow
@@ -118,13 +110,13 @@ EOF
echo -n ${ACL_ANYONE} > /etc/dovecot/acl_anyone
if [[ "${SKIP_SOLR}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
-echo -n 'quota acl zlib listescape mail_crypt mail_crypt_acl mail_log notify replication last_login' > /etc/dovecot/mail_plugins
-echo -n 'quota imap_quota imap_acl acl zlib imap_zlib imap_sieve listescape mail_crypt mail_crypt_acl notify replication mail_log last_login' > /etc/dovecot/mail_plugins_imap
-echo -n 'quota sieve acl zlib listescape mail_crypt mail_crypt_acl notify replication' > /etc/dovecot/mail_plugins_lmtp
+echo -n 'quota acl zlib mail_crypt mail_crypt_acl mail_log notify listescape replication' > /etc/dovecot/mail_plugins
+echo -n 'quota imap_quota imap_acl acl zlib imap_zlib imap_sieve mail_crypt mail_crypt_acl notify listescape replication mail_log' > /etc/dovecot/mail_plugins_imap
+echo -n 'quota sieve acl zlib mail_crypt mail_crypt_acl notify listescape replication' > /etc/dovecot/mail_plugins_lmtp
else
-echo -n 'quota acl zlib listescape mail_crypt mail_crypt_acl mail_log notify fts fts_solr replication last_login' > /etc/dovecot/mail_plugins
-echo -n 'quota imap_quota imap_acl acl zlib imap_zlib imap_sieve listescape mail_crypt mail_crypt_acl notify mail_log fts fts_solr replication last_login' > /etc/dovecot/mail_plugins_imap
-echo -n 'quota sieve acl zlib listescape mail_crypt mail_crypt_acl fts fts_solr notify replication' > /etc/dovecot/mail_plugins_lmtp
+echo -n 'quota acl zlib mail_crypt mail_crypt_acl mail_log notify fts fts_solr listescape replication' > /etc/dovecot/mail_plugins
+echo -n 'quota imap_quota imap_acl acl zlib imap_zlib imap_sieve mail_crypt mail_crypt_acl notify mail_log fts fts_solr listescape replication' > /etc/dovecot/mail_plugins_imap
+echo -n 'quota sieve acl zlib mail_crypt mail_crypt_acl fts fts_solr notify listescape replication' > /etc/dovecot/mail_plugins_lmtp
fi
chmod 644 /etc/dovecot/mail_plugins /etc/dovecot/mail_plugins_imap /etc/dovecot/mail_plugins_lmtp /templates/quarantine.tpl
@@ -136,36 +128,82 @@ user_query = SELECT CONCAT(JSON_UNQUOTE(JSON_VALUE(attributes, '$.mailbox_format
iterate_query = SELECT username FROM mailbox WHERE active = '1' OR active = '2';
EOF
-# Create pass dict for Dovecot
-cat < /etc/dovecot/sql/dovecot-dict-sql-passdb.conf
-# Autogenerated by mailcow
-driver = mysql
-connect = "host=/var/run/mysqld/mysqld.sock dbname=${DBNAME} user=${DBUSER} password=${DBPASS}"
-default_pass_scheme = ${MAILCOW_PASS_SCHEME}
-password_query = SELECT password FROM mailbox WHERE active = '1' AND username = '%u' AND domain IN (SELECT domain FROM domain WHERE domain='%d' AND active='1') AND JSON_UNQUOTE(JSON_VALUE(attributes, '$.force_pw_update')) != '1' AND (JSON_UNQUOTE(JSON_VALUE(attributes, '$.%s_access')) = '1' OR ('%s' != 'imap' AND '%s' != 'pop3'))
-EOF
-
-cat < /etc/dovecot/lua/app-passdb.lua
+cat < /etc/dovecot/lua/passwd-verify.lua
function auth_password_verify(req, pass)
+
if req.domain == nil then
return dovecot.auth.PASSDB_RESULT_USER_UNKNOWN, "No such user"
end
+
if cur == nil then
script_init()
end
- local cur,errorString = con:execute(string.format([[SELECT mailbox, password FROM app_passwd
- WHERE mailbox = '%s'
+
+ if req.user == nil then
+ req.user = ''
+ end
+
+ respbody = {}
+
+ -- check against mailbox passwds
+ local cur,errorString = con:execute(string.format([[SELECT password FROM mailbox
+ WHERE username = '%s'
AND active = '1'
- AND domain IN (SELECT domain FROM domain WHERE domain='%s' AND active='1')]], con:escape(req.user), con:escape(req.domain)))
+ AND domain IN (SELECT domain FROM domain WHERE domain='%s' AND active='1')
+ AND IFNULL(JSON_UNQUOTE(JSON_VALUE(mailbox.attributes, '$.force_pw_update')), 0) != '1'
+ AND IFNULL(JSON_UNQUOTE(JSON_VALUE(attributes, '$.%s_access')), 1) = '1']], con:escape(req.user), con:escape(req.domain), con:escape(req.service)))
local row = cur:fetch ({}, "a")
while row do
if req.password_verify(req, row.password, pass) == 1 then
cur:close()
+ con:execute(string.format([[REPLACE INTO sasl_log (service, app_password, username, real_rip)
+ VALUES ("%s", 0, "%s", "%s")]], con:escape(req.service), con:escape(req.user), con:escape(req.real_rip)))
return dovecot.auth.PASSDB_RESULT_OK, "password=" .. pass
end
row = cur:fetch (row, "a")
end
- return dovecot.auth.PASSDB_RESULT_USER_UNKNOWN, "No such user"
+
+ -- check against app passwds
+ local cur,errorString = con:execute(string.format([[SELECT app_passwd.id, app_passwd.password FROM app_passwd
+ INNER JOIN mailbox ON mailbox.username = app_passwd.mailbox
+ WHERE mailbox = '%s'
+ AND IFNULL(JSON_UNQUOTE(JSON_VALUE(mailbox.attributes, '$.%s_access')), 1) = '1'
+ AND IFNULL(JSON_UNQUOTE(JSON_VALUE(mailbox.attributes, '$.force_pw_update')), 0) != '1'
+ AND app_passwd.active = '1'
+ AND mailbox.active = '1'
+ AND app_passwd.domain IN (SELECT domain FROM domain WHERE domain='%s' AND active='1')]], con:escape(req.user), con:escape(req.service), con:escape(req.domain)))
+ local row = cur:fetch ({}, "a")
+ while row do
+ if req.password_verify(req, row.password, pass) == 1 then
+ cur:close()
+ con:execute(string.format([[REPLACE INTO sasl_log (service, app_password, username, real_rip)
+ VALUES ("%s", %d, "%s", "%s")]], con:escape(req.service), row.id, con:escape(req.user), con:escape(req.real_rip)))
+ return dovecot.auth.PASSDB_RESULT_OK, "password=" .. pass
+ end
+ row = cur:fetch (row, "a")
+ end
+
+ return dovecot.auth.PASSDB_RESULT_PASSWORD_MISMATCH, "Failed to authenticate"
+
+ -- PoC
+ -- local reqbody = string.format([[{
+ -- "success":0,
+ -- "service":"%s",
+ -- "app_password":false,
+ -- "username":"%s",
+ -- "real_rip":"%s"
+ -- }]], con:escape(req.service), con:escape(req.user), con:escape(req.real_rip))
+ -- http.request {
+ -- method = "POST",
+ -- url = "http://nginx:8081/sasl_log.php",
+ -- source = ltn12.source.string(reqbody),
+ -- headers = {
+ -- ["content-type"] = "application/json",
+ -- ["content-length"] = tostring(#reqbody)
+ -- },
+ -- sink = ltn12.sink.table(respbody)
+ -- }
+
end
function auth_passdb_lookup(req)
@@ -174,6 +212,9 @@ end
function script_init()
mysql = require "luasql.mysql"
+ http = require "socket.http"
+ http.TIMEOUT = 5
+ ltn12 = require "ltn12"
env = mysql.mysql()
con = env:connect("__DBNAME__","__DBUSER__","__DBPASS__","localhost")
return 0
@@ -186,9 +227,9 @@ end
EOF
# Replace patterns in app-passdb.lua
-sed -i "s/__DBUSER__/${DBUSER}/g" /etc/dovecot/lua/app-passdb.lua
-sed -i "s/__DBPASS__/${DBPASS}/g" /etc/dovecot/lua/app-passdb.lua
-sed -i "s/__DBNAME__/${DBNAME}/g" /etc/dovecot/lua/app-passdb.lua
+sed -i "s/__DBUSER__/${DBUSER}/g" /etc/dovecot/lua/passwd-verify.lua
+sed -i "s/__DBPASS__/${DBPASS}/g" /etc/dovecot/lua/passwd-verify.lua
+sed -i "s/__DBNAME__/${DBNAME}/g" /etc/dovecot/lua/passwd-verify.lua
# Migrate old sieve_after file
@@ -259,22 +300,16 @@ remote ${IPV4_NETWORK}.248 {
}
EOF
-if [[ "${ALLOW_ADMIN_EMAIL_LOGIN}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
- # Create random master Password for SOGo 'login as user' via proxy auth
- RAND_PASS=$(cat /dev/urandom | tr -dc 'a-z0-9' | fold -w 32 | head -n 1)
- echo -n ${RAND_PASS} > /etc/phpfpm/sogo-sso.pass
- cat < /etc/dovecot/sogo-sso.conf
+# Create random master Password for SOGo SSO
+RAND_PASS=$(cat /dev/urandom | tr -dc 'a-z0-9' | fold -w 32 | head -n 1)
+echo -n ${RAND_PASS} > /etc/phpfpm/sogo-sso.pass
+cat < /etc/dovecot/sogo-sso.conf
# Autogenerated by mailcow
passdb {
driver = static
args = allow_real_nets=${IPV4_NETWORK}.248/32 password={plain}${RAND_PASS}
}
EOF
-else
- rm -f /etc/dovecot/sogo-sso.pass
- rm -f /etc/dovecot/sogo-sso.conf
-fi
-
if [[ "${MASTER}" =~ ^([nN][oO]|[nN])+$ ]]; then
# Toggling MASTER will result in a rebuild of containers, so the quota script will be recreated
@@ -302,8 +337,8 @@ sievec /usr/lib/dovecot/sieve/report-ham.sieve
# Fix permissions
chown root:root /etc/dovecot/sql/*.conf
-chown root:dovecot /etc/dovecot/sql/dovecot-dict-sql-sieve* /etc/dovecot/sql/dovecot-dict-sql-quota* /etc/dovecot/lua/app-passdb.lua
-chmod 640 /etc/dovecot/sql/*.conf /etc/dovecot/lua/app-passdb.lua
+chown root:dovecot /etc/dovecot/sql/dovecot-dict-sql-sieve* /etc/dovecot/sql/dovecot-dict-sql-quota* /etc/dovecot/lua/passwd-verify.lua
+chmod 640 /etc/dovecot/sql/*.conf /etc/dovecot/lua/passwd-verify.lua
chown -R vmail:vmail /var/vmail/sieve
chown -R vmail:vmail /var/volatile
chown -R vmail:vmail /var/vmail_index
@@ -313,7 +348,6 @@ chown root:tty /dev/console
chmod +x /usr/lib/dovecot/sieve/rspamd-pipe-ham \
/usr/lib/dovecot/sieve/rspamd-pipe-spam \
/usr/local/bin/imapsync_runner.pl \
- /usr/local/bin/postlogin.sh \
/usr/local/bin/imapsync \
/usr/local/bin/trim_logs.sh \
/usr/local/bin/sa-rules.sh \
@@ -373,6 +407,6 @@ done
# For some strange, unknown and stupid reason, Dovecot may run into a race condition, when this file is not touched before it is read by dovecot/auth
# May be related to something inside Docker, I seriously don't know
-touch /etc/dovecot/lua/app-passdb.lua
+touch /etc/dovecot/lua/passwd-verify.lua
exec "$@"
diff --git a/data/Dockerfiles/dovecot/imapsync b/data/Dockerfiles/dovecot/imapsync
index 4c941f44..07cf58e7 100755
--- a/data/Dockerfiles/dovecot/imapsync
+++ b/data/Dockerfiles/dovecot/imapsync
@@ -1,6 +1,6 @@
#!/usr/bin/env perl
-# $Id: imapsync,v 1.977 2019/12/23 20:18:02 gilles Exp gilles $
+# $Id: imapsync,v 2.148 2021/07/22 14:21:09 gilles Exp gilles $
# structure
# pod documentation
# use pragmas
@@ -25,7 +25,7 @@ and without duplicates.
=head1 VERSION
-This documentation refers to Imapsync $Revision: 1.977 $
+This documentation refers to Imapsync $Revision: 2.148 $
=head1 USAGE
@@ -47,54 +47,82 @@ one another.
Imapsync command is a tool allowing incremental and
recursive imap transfers from one mailbox to another.
If you don't understand the previous sentence, it's normal,
-it's pedantic computer oriented jargon.
+it's pedantic computer-oriented jargon.
All folders are transferred, recursively, meaning
the whole folder hierarchy is taken, all messages in them,
-and all messages flags (\Seen \Answered \Flagged etc.)
+and all message flags (\Seen \Answered \Flagged etc.)
are synced too.
Imapsync reduces the amount of data transferred by not transferring
a given message if it already resides on the destination side.
Messages that are on the destination side but not on the
-source side stay as they are (see the --delete2
-option to have a strict sync).
+source side stay as they are. See the --delete2
+option to have strict sync and delete them.
-How imapsync knows a message is already on both sides?
+How imapsync know a message is already on both sides?
Same specific headers and the transfer is done only once.
By default, the identification headers are
"Message-Id:" and "Received:" lines
-but this choice can be changed with the --useheader option.
+but this choice can be changed with the --useheader option,
+most often a duplicate problem is solved by using
+--useheader "Message-Id"
+
All flags are preserved, unread messages will stay unread,
read ones will stay read, deleted will stay deleted.
+In the IMAP protocol, a deleted message is not really deleted,
+it is marked \Deleted and can be undelete. Real destruction
+comes with the EXPUNGE or UIDEXPUNGE IMAP commands.
You can abort the transfer at any time and restart it later,
imapsync works well with bad connections and interruptions,
by design. On a terminal hit Ctr-c twice within two seconds
-in order to abort the program. Hit Ctr-c just once makes
+to abort the program. Hit Ctr-c just once makes
imapsync reconnect to both imap servers.
+How do you know the sync is finished and well done?
+When imapsync ends by itself it mentions it with lines like those:
+
+ Exiting with return value 0 (EX_OK: successful termination) 0/50 nb_errors/max_errors PID 301
+ Removing pidfile /tmp/imapsync.pid
+ Log file is LOG_imapsync/2020_11_17_15_59_22_761_test1_test2.txt ( to change it, use --logfile filepath ; or use --nolog to turn off logging )
+
+If you don't have those lines it means that either the sync process is still
+running (or eventually hanging indefinitely) or that it ended without
+a whisper, a strong kill -9 on Linux for example.
+
+If you have those final lines then it means the sync process is properly
+finished. It may have encountered problems though.
+
+A good synchronization is mentioned by some lines above the last ones, especially
+those three lines:
+
+ The sync looks good, all 1745 identified messages in host1 are on host2.
+ There is no unidentified message on host1.
+ Detected 0 errors
+
+
A classical scenario is synchronizing a mailbox B from another mailbox A
where you just want to keep a strict copy of A in B. Strict meaning
all messages in A will be in B but no more.
-For this, option --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
+For this, option --delete2 can be used, it deletes messages in the host2
+folder B that are not in the host1 folder A. If you also need to destroy
host2 folders that are not in host1 then use --delete2folders. See also
--delete2foldersonly and --delete2foldersbutnot to set up exceptions
-on folders to destroy. INBOX will never be destroy, it's a mandatory
-folder in IMAP.
+on folders to destroy. INBOX will never be destroyed, it's a mandatory
+folder in IMAP so imapsync doesn't even try to remove it.
A different scenario is to delete the messages from the source mailbox
after a successful transfer, it can be a good feature when migrating
mailboxes since messages will be only on one side. The source account
will only have messages that are not on the destination yet, ie,
-messages that arrived after a sync or that failed to be copied.
+messages that arrived after a sync or that failed to be transferred.
In that case, use the --delete1 option. Option --delete1 implies also
-option --expunge1 so all messages marked deleted on host1 will be really
-deleted. In IMAP protocol deleting a message does not really delete it,
+the option --expunge1 so all messages marked deleted on host1 will be
+deleted. In IMAP protocol deleting a message does not delete it,
it marks it with the flag \Deleted, allowing an undelete. Expunging
a folder removes, definitively, all the messages marked as \Deleted
in this folder.
@@ -115,17 +143,18 @@ Michael R. Elkins) for a 2 ways synchronization.
usage: imapsync [options]
The standard options are the six values forming the credentials.
-Three values on each side are needed in order to log in into the IMAP
-servers. These six values are a host, a username, and a password, two times.
+Three values on each side are needed in order to login into the IMAP
+servers. These six values are a hostname, a username, and a password, two times.
Conventions used in the following descriptions of the options:
str means string
- int means integer
+ int means integer number
+ flo means float number
reg means regular expression
cmd means command
- --dry : Makes imapsync doing nothing for real, just print what
+ --dry : Makes imapsync doing nothing for real; it just print what
would be done without --dry.
=head2 OPTIONS/credentials
@@ -136,18 +165,18 @@ Conventions used in the following descriptions of the options:
Optional since default ports are the
well known ports imap/143 or imaps/993.
--user1 str : User to login on host1.
- --password1 str : Password for the user1.
+ --password1 str : Password of user1.
--host2 str : "destination" imap server.
--port2 int : Port to connect on host2. Optional
--user2 str : User to login on host2.
- --password2 str : Password for the user2.
+ --password2 str : Password of user2.
--showpasswords : Shows passwords on output instead of "MASKED".
Useful to restart a complete run by just reading
the command line used in the log,
or to debug passwords.
- It's not a secure practice at all.
+ It's not a secure practice at all!
--passfile1 str : Password file for the user1. It must contain the
password on the first line. This option avoids showing
@@ -155,7 +184,10 @@ Conventions used in the following descriptions of the options:
--passfile2 str : Password file for the user2.
You can also pass the passwords in the environment variables
-IMAPSYNC_PASSWORD1 and IMAPSYNC_PASSWORD2
+IMAPSYNC_PASSWORD1 and IMAPSYNC_PASSWORD2. If you don't pass
+the user1 password via --password1 nor --passfile1 nor $IMAPSYNC_PASSWORD1
+then imapsync will prompt to enter the password on the terminal.
+Same thing for user2 password.
=head2 OPTIONS/encryption
@@ -180,11 +212,16 @@ IMAPSYNC_PASSWORD1 and IMAPSYNC_PASSWORD2
--sslargs2 str : Pass any ssl parameter for host2 ssl or tls connection.
See --sslargs1
- --timeout1 int : Connection timeout in seconds for host1.
+ --timeout1 flo : Connection timeout in seconds for host1.
Default is 120 and 0 means no timeout at all.
- --timeout2 int : Connection timeout in seconds for host2.
+ --timeout2 flo : Connection timeout in seconds for host2.
Default is 120 and 0 means no timeout at all.
+ Caveat, under CGI context, you may encounter a timeout
+ from the webserver, killing imapsync and the imap connexions.
+ See the document INSTALL.OnlineUI.txt and search
+ for "Timeout" for how to deal with this issue.
+
=head2 OPTIONS/authentication
@@ -205,6 +242,28 @@ IMAPSYNC_PASSWORD1 and IMAPSYNC_PASSWORD2
--domain1 str : Domain on host1 (NTLM authentication).
--domain2 str : Domain on host2 (NTLM authentication).
+ --oauthaccesstoken1 str : The access token to authenticate with OAUTH2.
+ It will be combined with the --user1 value to form the
+ string to pass with XOAUTH2 authentication.
+ The password given by --password1 or --passfile1
+ is ignored.
+ Instead of the access token itself, the value can be a
+ file containing the access token on the first line.
+ If the value is a file, imapsync reads its first line
+ and take this line as the access token. The advantage
+ of the file is that if the access token changes then
+ imapsync can read it again when it needs to reconnect
+ during a run.
+
+
+ --oauthaccesstoken2 str : same thing as --oauthaccesstoken1
+
+ --oauthdirect1 str : The direct string to pass with XOAUTH2 authentication.
+ The password given by --password1 or --passfile1 and
+ the user given by --user1 are ignored.
+
+ --oauthdirect2 str : same thing as oauthdirect1
+
=head2 OPTIONS/folders
@@ -241,6 +300,9 @@ IMAPSYNC_PASSWORD1 and IMAPSYNC_PASSWORD2
--f1f2 str1=str2 : Force folder str1 to be synced to str2,
--f1f2 overrides --automap and --regextrans2.
+ Use several --f1f2 options to map several folders.
+ Option --f1f2 is a one to one only folder mapping,
+ str1 and str2 have to be full path folder names.
--subfolder2 str : Syncs the whole host1 folders hierarchy under the
host2 folder named str.
@@ -285,7 +347,7 @@ IMAPSYNC_PASSWORD1 and IMAPSYNC_PASSWORD2
--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 for a run, then
+ Then, when happy, remove --dry for a run, then
remove --justfolders for the next ones.
Have in mind that --regextrans2 is applied after
the automatic prefix and separator inversion.
@@ -309,9 +371,11 @@ IMAPSYNC_PASSWORD1 and IMAPSYNC_PASSWORD2
Default is system specific, Unix is /tmp but
/tmp is often too small and deleted at reboot.
--tmpdir /var/tmp should be better.
+
--pidfile str : The file where imapsync pid is written,
- it can be dirname/filename.
- Default name is imapsync.pid in tmpdir.
+ it can be dirname/filename complete path.
+ The default name is imapsync.pid in tmpdir.
+
--pidfilelocking : Abort if pidfile already exists. Useful to avoid
concurrent transfers on the same mailbox.
@@ -329,7 +393,7 @@ The default logfile name is for example
where:
2019_12_22_23_57_59_532 is nearly the date of the start
- YYYY_MM_DD_HH_MM_SS_mmm
+ YYYY_MM_DD_HH_MM_SS_mmm
year_month_day_hour_minute_seconde_millisecond
and user1 user2 are the --user1 --user2 values.
@@ -337,18 +401,18 @@ and user1 user2 are the --user1 --user2 values.
=head2 OPTIONS/messages
--skipmess reg : Skips messages matching the regex.
- Example: 'm/[\x80-ff]/' # to avoid 8bits messages.
+ Example: 'm/[\x80-\xff]/' # to avoid 8bits messages.
--skipmess is applied before --regexmess
--skipmess reg : or this one, etc.
--skipcrossduplicates : Avoid copying messages that are already copied
- in another folder, good from Gmail to X when
- X is not also Gmail.
+ in another folder, good from Gmail to XYZ when
+ XYZ is not also Gmail.
Activated with --gmail1 unless --noskipcrossduplicates
--debugcrossduplicates : Prints which messages (UIDs) are skipped with
- --skipcrossduplicates (and in what other folders
- they are).
+ --skipcrossduplicates and in what other folders
+ they are.
--pipemess cmd : Apply this cmd command to each message content
before the copy.
@@ -364,20 +428,21 @@ and user1 user2 are the --user1 --user2 values.
--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.
+ Example: 's/\000/ /g' # to replace null characters
+ by spaces.
--regexmess reg : and this one, etc.
=head2 OPTIONS/labels
-Gmail present labels as folders in imap. Imapsync can accelerate the sync
+Gmail present labels as folders in imap. Imapsync can accelerate the sync
by syncing X-GM-LABELS, it will avoid to transfer messages when they are
-already on host2.
+already on host2 in another folder.
--synclabels : Syncs also Gmail labels when a message is copied to host2.
Activated by default with --gmail1 --gmail2 unless
--nosynclabels is added.
-
+
--resynclabels : Resyncs Gmail labels when a message is already on host2.
Activated by default with --gmail1 --gmail2 unless
--noresynclabels is added.
@@ -400,6 +465,9 @@ https://imapsync.lamiral.info/FAQ.d/FAQ.Gmail.txt
May be useful when a user has already started to play
with its host2 account.
+ --filterbuggyflags : Filter flags known to be buggy and generators of errors
+ "BAD Invalid system flag" or "NO APPEND Invalid flag list".
+
=head2 OPTIONS/deletions
--delete1 : Deletes messages on host1 server after a successful
@@ -424,16 +492,18 @@ https://imapsync.lamiral.info/FAQ.d/FAQ.Gmail.txt
Useful with --delete1 since what remains on host1
is only what failed to be synced.
- --delete2 : Delete messages in host2 that are not in
- host1 server. Useful for backup or pre-sync.
+ --delete2 : Delete messages in the host2 account that are not in
+ the host1 account. Useful for backup or pre-sync.
--delete2 implies --uidexpunge2
- --delete2duplicates : Delete messages in host2 that are duplicates.
+ --delete2duplicates : Deletes messages in host2 that are duplicates in host2.
Works only without --useuid since duplicates are
detected with an header part of each message.
+ NB: --delete2duplicates is far less violent than --delete2
+ since it removes only duplicates.
- --delete2folders : Delete folders in host2 that are not in host1 server.
- For safety, first try it like this (it is safe):
+ --delete2folders : Delete folders in host2 that are not in host1.
+ For safety, first try it like this, it is safe:
--delete2folders --dry --justfolders --nofoldersizes
and see what folders will be deleted.
@@ -455,10 +525,10 @@ https://imapsync.lamiral.info/FAQ.d/FAQ.Gmail.txt
If you encounter problems with dates, see also:
https://imapsync.lamiral.info/FAQ.d/FAQ.Dates.txt
- --syncinternaldates : Sets the internal dates on host2 same as host1.
+ --syncinternaldates : Sets the internal dates on host2 as the same as host1.
Turned on by default. Internal date is the date
- a message arrived on a host (Unix mtime).
- --idatefromheader : Sets the internal dates on host2 same as the
+ a message arrived on a host (Unix mtime usually).
+ --idatefromheader : Sets the internal dates on host2 as same as the
ones in "Date:" headers.
@@ -467,6 +537,7 @@ https://imapsync.lamiral.info/FAQ.d/FAQ.Gmail.txt
--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
@@ -487,18 +558,30 @@ https://imapsync.lamiral.info/FAQ.d/FAQ.Gmail.txt
--search2 str : Same as --search but for selecting host2 messages only.
So --search CRIT equals --search1 CRIT --search2 CRIT
+ --noabletosearch : Makes --minage and --maxage options use the internal
+ dates given by a FETCH imap command instead of the
+ "Date:" header. Internal date is the arrival date
+ in the mailbox.
+ --noabletosearch equals --noabletosearch1 --noabletosearch2
+
+ --noabletosearch1 : Like --noabletosearch but for host1 only.
+ --noabletosearch2 : Like --noabletosearch but for host2 only.
+
--maxlinelength int : skip messages with a line length longer than int bytes.
RFC 2822 says it must be no more than 1000 bytes but
real life servers and email clients do more.
--useheader str : Use this header to compare messages on both sides.
- Ex: Message-ID or Subject or Date.
+ Example: "Message-Id" or "Received" or "Date".
--useheader str and this one, etc.
- --usecache : Use cache to speed up next syncs. Not set by default.
+ --syncduplicates : Sync also duplicates. Off by default.
+
+ --usecache : Use cache to speed up next syncs. Off by default.
--nousecache : Do not use cache. Caveat: --useuid --nousecache creates
duplicates on multiple runs.
+
--useuid : Use UIDs instead of headers as a criterion to recognize
messages. Option --usecache is then implied unless
--nousecache is used.
@@ -516,6 +599,7 @@ https://imapsync.lamiral.info/FAQ.d/FAQ.Gmail.txt
--addheader adds a "Message-Id" header,
like "Message-Id: 12345@imapsync", where 12345
is the imap UID of the message on the host1 folder.
+ Useful to sync folders "Sent" or "Draft".
=head2 OPTIONS/debugging
@@ -534,7 +618,7 @@ https://imapsync.lamiral.info/FAQ.d/FAQ.Gmail.txt
--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 connection.
- --testslive6 : Run a live test with ks2ipv6.lamiral.info imap server.
+ --testslive6 : Run a live test with ks6ipv6.lamiral.info imap server.
Useful to check the ipv6 connectivity. Needs internet.
@@ -543,8 +627,8 @@ https://imapsync.lamiral.info/FAQ.d/FAQ.Gmail.txt
--gmail1 : sets --host1 to Gmail and other options. See FAQ.Gmail.txt
--gmail2 : sets --host2 to Gmail and other options. See FAQ.Gmail.txt
- --office1 : sets --host1 to Office365 and other options. See FAQ.Exchange.txt
- --office2 : sets --host2 to Office365 and other options. See FAQ.Exchange.txt
+ --office1 : sets --host1 to Office365 and other options. See FAQ.Office365.txt
+ --office2 : sets --host2 to Office365 and other options. See FAQ.Office365.txt
--exchange1 : sets options for Exchange. See FAQ.Exchange.txt
--exchange2 : sets options for Exchange. See FAQ.Exchange.txt
@@ -555,13 +639,14 @@ https://imapsync.lamiral.info/FAQ.d/FAQ.Gmail.txt
=head2 OPTIONS/behavior
- --maxmessagespersecond int : limits the number of messages transferred per second.
+ --maxmessagespersecond flo : limits the average number of messages
+ transferred per second.
--maxbytespersecond int : limits the average transfer rate per second.
--maxbytesafter int : starts --maxbytespersecond limitation only after
--maxbytesafter amount of data transferred.
- --maxsleep int : do not sleep more than int seconds.
+ --maxsleep flo : do not sleep more than int seconds.
On by default, 2 seconds max, like --maxsleep 2
--abort : terminates a previous call still running.
@@ -570,13 +655,13 @@ https://imapsync.lamiral.info/FAQ.d/FAQ.Gmail.txt
--exitwhenover int : Stop syncing and exits when int total bytes
transferred is reached.
- --version : Print only software version.
+ --version : Print only the software version.
--noreleasecheck : Do not check for any new imapsync release.
--releasecheck : Check for new imapsync release.
it's an http request to
http://imapsync.lamiral.info/prj/imapsync/VERSION
- --noid : Do not send/receive ID command to imap servers.
+ --noid : Do not send/receive IMAP "ID" command to imap servers.
--justconnect : Just connect to both servers and print useful
information. Need only --host1 and --host2 options.
@@ -609,7 +694,7 @@ https://imapsync.lamiral.info/FAQ.d/FAQ.Gmail.txt
=head1 SECURITY
-You can use --passfile1 instead of --password1 to give the
+You can use --passfile1 instead of --password1 to mention the
password since it is safer. With --password1 option, on Linux,
any user on your host can see the password by using the 'ps auxwwww'
command. Using a variable (like IMAPSYNC_PASSWORD1) is also
@@ -625,10 +710,10 @@ Imapsync activates ssl if the well known port imaps port (993) is open
on the imap servers. If the imaps port is closed then it open a
normal (clear) connection on port 143 but it looks for TLS support
in the CAPABILITY list of the servers. If TLS is supported
-then imapsync goes to encryption.
+then imapsync goes to encryption with STARTTLS.
If the automatic ssl and the tls detections fail then imapsync will
-not protect against sniffing activities on the network, especially
+not protect against sniffing activities on the network, especially
for passwords.
If you want to force ssl or tls just use --ssl1 --ssl2 or --tls1 --tls2
@@ -641,12 +726,14 @@ or at https://imapsync.lamiral.info/FAQ.d/FAQ.Security.txt
Imapsync will exit with a 0 status (return code) if everything went good.
Otherwise, it exits with a non-zero status. That's classical Unix behavior.
Here is the list of the exit code values (an integer between 0 and 255).
+In Bourne Shells, this exit code value can be retrieved within the variable
+value "$?" if you read it just after the imapsync call.
+
The names reflect their meaning:
=for comment
egrep '^Readonly my.*\$EX' imapsync | egrep -o 'EX.*' | sed 's_^_ _'
-
EX_OK => 0 ; #/* successful termination */
EX_USAGE => 64 ; #/* command line usage error */
EX_NOINPUT => 66 ; #/* cannot open input */
@@ -654,6 +741,7 @@ egrep '^Readonly my.*\$EX' imapsync | egrep -o 'EX.*' | sed 's_^_ _'
EX_SOFTWARE => 70 ; #/* internal software error */
EXIT_CATCH_ALL => 1 ; # Any other error
EXIT_BY_SIGNAL => 6 ; # Should be 128+n where n is the sig_num
+ EXIT_BY_FILE => 7 ;
EXIT_PID_FILE_ERROR => 8 ;
EXIT_CONNECTION_FAILURE => 10 ;
EXIT_TLS_FAILURE => 12 ;
@@ -661,8 +749,18 @@ egrep '^Readonly my.*\$EX' imapsync | egrep -o 'EX.*' | sed 's_^_ _'
EXIT_SUBFOLDER1_NO_EXISTS => 21 ;
EXIT_WITH_ERRORS => 111 ;
EXIT_WITH_ERRORS_MAX => 112 ;
+ EXIT_OVERQUOTA => 113 ;
+ EXIT_ERR_APPEND => 114 ;
+ EXIT_ERR_FETCH => 115 ;
+ EXIT_ERR_CREATE => 116 ;
+ EXIT_ERR_SELECT => 117 ;
+ EXIT_TRANSFER_EXCEEDED => 118 ;
+ EXIT_ERR_APPEND_VIRUS => 119 ;
EXIT_TESTS_FAILED => 254 ; # Like Test::More API
-
+ EXIT_CONNECTION_FAILURE_HOST1 => 101 ;
+ EXIT_CONNECTION_FAILURE_HOST2 => 102 ;
+ EXIT_AUTHENTICATION_FAILURE_USER1 => 161 ;
+ EXIT_AUTHENTICATION_FAILURE_USER2 => 162 ;
=head1 LICENSE AND COPYRIGHT
@@ -688,11 +786,11 @@ Good feedback is always welcome.
Bad feedback is very often welcome.
Gilles LAMIRAL earns his living by writing, installing,
-configuring and teaching free, open and often gratis
+configuring and sometimes teaching free, open and often gratis
software. Imapsync used to be "always gratis" but now it is
only "often gratis" because imapsync is sold by its author,
-a good way to maintain and support free open public
-software over decades.
+your servitor, a good way to maintain and support free open public
+software tools over decades.
=head1 BUGS AND LIMITATIONS
@@ -745,8 +843,8 @@ https://imapsync.lamiral.info/examples/
and all Server releases 2000, 2003, 2008 and R2, 2012 and R2, 2016)
as a standalone binary software called imapsync.exe,
usually launched from a batch file in order to avoid always typing
- the options. There is also a 64bit binary called imapsync_64bit.exe
-
+ the options. There is also a 32bit binary called imapsync_32bit.exe
+
Imapsync works under OS X as a standalone binary
software called imapsync_bin_Darwin
@@ -783,8 +881,7 @@ Feel free to hack imapsync as the NOLIMIT license permits it.
See also https://imapsync.lamiral.info/S/external.shtml
for a better up to date list.
-Last updated and verified on Sun Dec 8, 2019.
-
+List verified on Friday July 1, 2021.
imapsync: https://github.com/imapsync/imapsync (this is an imapsync copy, sometimes delayed, with --noreleasecheck by default since release 1.592, 2014/05/22)
imap_tools: https://web.archive.org/web/20161228145952/http://www.athensfbc.com/imap_tools/. The imap_tools code is now at https://github.com/andrewnimmo/rick-sanders-imap-tools
@@ -792,6 +889,7 @@ Last updated and verified on Sun Dec 8, 2019.
Doveadm-Sync: https://wiki2.dovecot.org/Tools/Doveadm/Sync ( Dovecot sync tool )
davmail: http://davmail.sourceforge.net/
offlineimap: http://offlineimap.org/
+ fdm: https://github.com/nicm/fdm
mbsync: http://isync.sourceforge.net/
mailsync: http://mailsync.sourceforge.net/
mailutil: https://www.washington.edu/imap/ part of the UW IMAP toolkit. (well, seems abandoned now)
@@ -813,8 +911,8 @@ Last updated and verified on Sun Dec 8, 2019.
imapbackup: https://github.com/rcarmo/imapbackup (A Python script for incremental backups of IMAP mailboxes)
BitRecover email-backup 99 USD, 299 USD https://www.bitrecover.com/email-backup/.
ImportExportTools: https://addons.thunderbird.net/en-us/thunderbird/addon/importexporttools/ ImportExportTools for Mozilla Thunderbird by Paolo Kaosmos. ImportExportTools does not do IMAP.
-
-
+ rximapmail: https://sourceforge.net/projects/rximapmail/
+ CodeTwo: https://www.codetwo.com/ but CodeTwo does imap source to Office365 only.
=head1 HISTORY
@@ -825,7 +923,7 @@ away remote imap server, accessible by an
often broken low-bandwidth ISDN link.
I had to verify every mailbox was well transferred, all folders, all messages,
-without wasting bandwidth or creating duplicates upon resyncs. The imapsync
+without wasting bandwidth or creating duplicates upon resyncs. The imapsync
design was made with the beautiful rsync command in mind.
Imapsync started its life as a patch of the copy_folder.pl
@@ -833,7 +931,7 @@ script. The script copy_folder.pl comes from the Mail-IMAPClient-2.1.3 perl
module tarball source (more precisely in the examples/ directory of the
Mail-IMAPClient tarball).
-So many happened since then that I wonder
+So many changes happened since then that I wonder
if it remains any lines of the original
copy_folder.pl in imapsync source code.
@@ -847,9 +945,12 @@ copy_folder.pl in imapsync source code.
use strict ;
use warnings ;
use Carp ;
+use Cwd ;
use Data::Dumper ;
use Digest::HMAC_SHA1 qw( hmac_sha1 hmac_sha1_hex ) ;
use Digest::MD5 qw( md5 md5_hex md5_base64 ) ;
+use Encode ;
+use Encode::IMAPUTF7 ;
use English qw( -no_match_vars ) ;
use Errno qw(EAGAIN EPIPE ECONNRESET) ;
use Fcntl ;
@@ -866,25 +967,23 @@ use IO::Socket::INET6 ;
use IO::Socket::SSL ;
use IO::Tee ;
use IPC::Open3 'open3' ;
+#use locale ;
use Mail::IMAPClient 3.30 ;
use MIME::Base64 ;
use Pod::Usage qw(pod2usage) ;
-use POSIX qw(uname SIGALRM :sys_wait_h) ;
+use POSIX qw( uname SIGALRM :sys_wait_h ) ;
use Sys::Hostname ;
use Term::ReadKey ;
use Test::More ;
use Time::HiRes qw( time sleep ) ;
use Time::Local ;
use Unicode::String ;
-use Cwd ;
use Readonly ;
use Sys::MemInfo ;
use Regexp::Common ;
use Text::ParseWords ; # for quotewords()
use File::Tail ;
-use Encode ;
-use Encode::IMAPUTF7 ;
local $OUTPUT_AUTOFLUSH = 1 ;
@@ -918,6 +1017,7 @@ Readonly my $EX_SOFTWARE => 70 ; #/* internal software error */
# Mine
Readonly my $EXIT_CATCH_ALL => 1 ; # Any other error
Readonly my $EXIT_BY_SIGNAL => 6 ; # Should be 128+n where n is the sig_num
+Readonly my $EXIT_BY_FILE => 7 ;
Readonly my $EXIT_PID_FILE_ERROR => 8 ;
Readonly my $EXIT_CONNECTION_FAILURE => 10 ;
Readonly my $EXIT_TLS_FAILURE => 12 ;
@@ -925,10 +1025,22 @@ Readonly my $EXIT_AUTHENTICATION_FAILURE => 16 ;
Readonly my $EXIT_SUBFOLDER1_NO_EXISTS => 21 ;
Readonly my $EXIT_WITH_ERRORS => 111 ;
Readonly my $EXIT_WITH_ERRORS_MAX => 112 ;
+Readonly my $EXIT_OVERQUOTA => 113 ;
+Readonly my $EXIT_ERR_APPEND => 114 ;
+Readonly my $EXIT_ERR_FETCH => 115 ;
+Readonly my $EXIT_ERR_CREATE => 116 ;
+Readonly my $EXIT_ERR_SELECT => 117 ;
+Readonly my $EXIT_TRANSFER_EXCEEDED => 118 ;
+Readonly my $EXIT_ERR_APPEND_VIRUS => 119 ;
Readonly my $EXIT_TESTS_FAILED => 254 ; # Like Test::More API
+Readonly my $EXIT_CONNECTION_FAILURE_HOST1 => 101 ;
+Readonly my $EXIT_CONNECTION_FAILURE_HOST2 => 102 ;
+Readonly my $EXIT_AUTHENTICATION_FAILURE_USER1 => 161 ;
+Readonly my $EXIT_AUTHENTICATION_FAILURE_USER2 => 162 ;
+
Readonly my %EXIT_TXT => (
$EX_OK => 'EX_OK: successful termination',
@@ -939,6 +1051,7 @@ Readonly my %EXIT_TXT => (
$EXIT_CATCH_ALL => 'EXIT_CATCH_ALL',
$EXIT_BY_SIGNAL => 'EXIT_BY_SIGNAL',
+ $EXIT_BY_FILE => 'EXIT_BY_FILE',
$EXIT_PID_FILE_ERROR => 'EXIT_PID_FILE_ERROR' ,
$EXIT_CONNECTION_FAILURE => 'EXIT_CONNECTION_FAILURE',
$EXIT_TLS_FAILURE => 'EXIT_TLS_FAILURE',
@@ -946,7 +1059,37 @@ Readonly my %EXIT_TXT => (
$EXIT_SUBFOLDER1_NO_EXISTS => 'EXIT_SUBFOLDER1_NO_EXISTS',
$EXIT_WITH_ERRORS => 'EXIT_WITH_ERRORS',
$EXIT_WITH_ERRORS_MAX => 'EXIT_WITH_ERRORS_MAX',
+ $EXIT_OVERQUOTA => 'EXIT_OVERQUOTA',
+ $EXIT_ERR_APPEND => 'EXIT_ERR_APPEND',
+ $EXIT_ERR_APPEND_VIRUS => 'EXIT_ERR_APPEND_VIRUS',
+ $EXIT_ERR_FETCH => 'EXIT_ERR_FETCH',
+ $EXIT_ERR_CREATE => 'EXIT_ERR_CREATE',
+ $EXIT_ERR_SELECT => 'EXIT_ERR_SELECT',
$EXIT_TESTS_FAILED => 'EXIT_TESTS_FAILED',
+ $EXIT_TRANSFER_EXCEEDED => 'EXIT_TRANSFER_EXCEEDED',
+ $EXIT_CONNECTION_FAILURE_HOST1 => 'EXIT_CONNECTION_FAILURE_HOST1',
+ $EXIT_CONNECTION_FAILURE_HOST2 => 'EXIT_CONNECTION_FAILURE_HOST2',
+ $EXIT_AUTHENTICATION_FAILURE_USER1 => 'EXIT_AUTHENTICATION_FAILURE_USER1',
+ $EXIT_AUTHENTICATION_FAILURE_USER2 => 'EXIT_AUTHENTICATION_FAILURE_USER2',
+) ;
+
+
+Readonly my %EXIT_VALUE_OF_ERR_TYPE => (
+ ERR_APPEND_SIZE => $EXIT_ERR_APPEND,
+ ERR_OVERQUOTA => $EXIT_OVERQUOTA,
+ ERR_APPEND => $EXIT_ERR_APPEND,
+ ERR_APPEND_VIRUS => $EXIT_ERR_APPEND_VIRUS,
+ ERR_CREATE => $EXIT_ERR_CREATE,
+ ERR_SELECT => $EXIT_ERR_SELECT,
+ ERR_Host1_FETCH => $EXIT_ERR_FETCH,
+ ERR_UNCLASSIFIED => $EXIT_WITH_ERRORS,
+ ERR_NOTHING_REPORTED => $EXIT_WITH_ERRORS,
+ ERR_TRANSFER_EXCEEDED => $EXIT_TRANSFER_EXCEEDED,
+ ERR_CONNECTION_FAILURE_HOST1 => $EXIT_CONNECTION_FAILURE_HOST1,
+ ERR_CONNECTION_FAILURE_HOST2 => $EXIT_CONNECTION_FAILURE_HOST2,
+ ERR_AUTHENTICATION_FAILURE_USER1 => $EXIT_AUTHENTICATION_FAILURE_USER1,
+ ERR_AUTHENTICATION_FAILURE_USER2 => $EXIT_AUTHENTICATION_FAILURE_USER2,
+ ERR_EXIT_TLS_FAILURE => $EXIT_TLS_FAILURE,
) ;
@@ -976,7 +1119,7 @@ Readonly my $RELEASE_NUMBER_EXAMPLE_2 => 42.4242 ;
Readonly my $TCP_PING_TIMEOUT => 5 ;
Readonly my $DEFAULT_TIMEOUT => 120 ;
Readonly my $DEFAULT_NB_RECONNECT_PER_IMAP_COMMAND => 3 ;
-Readonly my $DEFAULT_UIDNEXT => 999_999 ;
+
Readonly my $DEFAULT_BUFFER_SIZE => 4096 ;
Readonly my $MAX_SLEEP => 2 ; # 2 seconds max for limiting too long sleeps from --maxbytespersecond and --maxmessagespersecond
@@ -1029,15 +1172,13 @@ Readonly my $FORCE => 1 ;
# global variables
-# Currently working to finish with only $sync
+# Currently working to finish with only $sync, $acc1, $acc2
# Not finished yet...
my(
- $sync,
- $timestart_str,
- $debugimap, $debugimap1, $debugimap2, $debugcontent, $debugflags,
+ $sync, $acc1, $acc2,
+ $debugcontent, $debugflags,
$debuglist, $debugdev, $debugmaxlinelength, $debugcgi,
- $domain1, $domain2,
@include, @exclude, @folderrec,
@folderfirst, @folderlast,
@@ -1052,46 +1193,39 @@ my(
%h2_folders_from_1_several,
$prefix1, $prefix2,
- @regexmess, @regexflag, @skipmess, @pipemess, $pipemesscheck,
- $flagscase, $filterflags, $syncflagsaftercopy,
+ @regexmess, @skipmess, @pipemess, $pipemesscheck,
+ $syncflagsaftercopy,
$syncinternaldates,
$idatefromheader,
- $syncacls,
- $fastio1, $fastio2,
+
$minsize, $maxage, $minage,
$search,
- $skipheader, @useheader, %useheader,
+ @useheader, %useheader,
$skipsize, $allowsizemismatch, $buffersize,
$authmd5, $authmd51, $authmd52,
$subscribed, $subscribe, $subscribeall,
$help,
- $justbanner,
+
$fast,
$nb_msg_skipped_dry_mode,
- $h1_nb_msg_duplicate,
- $h2_nb_msg_duplicate,
- $h2_nb_msg_noheader,
- $h2_nb_msg_deleted,
+ $h2_nb_msg_noheader,
$h1_bytes_processed,
$h1_nb_msg_end, $h1_bytes_end,
$h2_nb_msg_end, $h2_bytes_end,
- $timeout,
$timestart_int,
$uid1, $uid2,
- $authuser1, $authuser2,
- $proxyauth1, $proxyauth2,
- $authmech1, $authmech2,
+
+
$split1, $split2,
- $reconnectretry1, $reconnectretry2,
- $max_msg_size_in_bytes,
+
$modulesversion,
$delete2folders, $delete2foldersonly, $delete2foldersbutnot,
$usecache, $debugcache, $cacheaftercopy,
@@ -1101,7 +1235,6 @@ my(
$fixInboxINBOX,
$maxlinelength, $maxlinelengthcmd,
$minmaxlinelength,
- $uidnext_default,
$fixcolonbug,
$create_folder_old,
$skipcrossduplicates, $debugcrossduplicates,
@@ -1114,7 +1247,9 @@ my(
$warn_release,
) ;
-single_sync( ) ;
+single_sync( $sync, $acc1, $acc2 );
+
+
sub single_sync
{
@@ -1122,21 +1257,36 @@ sub single_sync
# main program
# global variables initialization
-# I'm currently removing all global variables except $sync
-# passing each of them under $sync->{variable_name}
+# I'm currently removing all global variables except $sync $acc1 $acc2
+# passing each of them under
+# $sync->{variable_name}
+# or $acc1->{variable_name}
+# or $acc1->{variable_name}
+
+#
+$acc1 = {} ;
+$acc2 = {} ;
+$sync->{ acc1 } = $acc1 ;
+$sync->{ acc2 } = $acc2 ;
+
+$acc1->{ Side } = 'Host1' ;
+$acc2->{ Side } = 'Host2' ;
$sync->{timestart} = time ; # Is a float because of use Time::HiRres
-$sync->{rcs} = q{$Id: imapsync,v 1.977 2019/12/23 20:18:02 gilles Exp gilles $} ;
+$sync->{rcs} = q{$Id: imapsync,v 2.148 2021/07/22 14:21:09 gilles Exp gilles $} ;
$sync->{ memory_consumption_at_start } = memory_consumption( ) || 0 ;
+
my @loadavg = loadavg( ) ;
-$sync->{cpu_number} = cpu_number( ) ;
-$sync->{loaddelay} = load_and_delay( $sync->{cpu_number}, @loadavg ) ;
-$sync->{loadavg} = join( q{ }, $loadavg[ 0 ] )
+$sync->{ cpu_number } = cpu_number( ) ;
+$sync->{ loaddelay } = load_and_delay( $sync->{ cpu_number }, @loadavg ) ;
+$sync->{ loaddelay } = 0 ;
+
+$sync->{ loadavg } = join( q{ }, $loadavg[ 0 ] )
. " on $sync->{cpu_number} cores and "
. ram_memory_info( ) ;
@@ -1146,10 +1296,13 @@ $sync->{ total_bytes_transferred } = 0 ;
$sync->{ total_bytes_skipped } = 0 ;
$sync->{ nb_msg_transferred } = 0 ;
$sync->{ nb_msg_skipped } = $nb_msg_skipped_dry_mode = 0 ;
-$sync->{ h1_nb_msg_deleted } = 0 ;
-$h2_nb_msg_deleted = 0 ;
-$h1_nb_msg_duplicate = 0 ;
-$h2_nb_msg_duplicate = 0 ;
+
+$sync->{ acc1 }->{ nb_msg_deleted } = 0 ;
+$sync->{ acc2 }->{ nb_msg_deleted } = 0 ;
+
+$sync->{ acc1 }->{ nb_msg_duplicate } = 0 ;
+$sync->{ acc2 }->{ nb_msg_duplicate } = 0 ;
+
$sync->{ h1_nb_msg_noheader } = 0 ;
$h2_nb_msg_noheader = 0 ;
@@ -1165,8 +1318,8 @@ $sync->{ h2_nb_msg_crossdup } = 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;
+$sync->{ nb_errors } = 0;
+$sync->{ biggest_message_transferred } = 0;
%month_abrev = (
Jan => '00',
@@ -1192,14 +1345,14 @@ cgibegin( $sync ) ;
# In cgi context, printing must start by the header so we delay other prints by using output() storage
my $options_good = get_options( $sync, @ARGV ) ;
# Is it the first myprint?
-docker_context( $sync ) ;
cgibuildheader( $sync ) ;
+docker_context( $sync ) ;
+
+print_output_if_needed( $sync ) ;
+
-myprint( output( $sync ) ) ;
output_reset_with( $sync ) ;
-# Old place for cgiload( $sync ) ;
-
# don't go on if options are not all known.
if ( ! defined $options_good ) { exit $EX_USAGE ; }
@@ -1214,7 +1367,7 @@ $sync->{releasecheck} = defined $sync->{releasecheck} ? $sync->{releasecheck}
# just the version
if ( $sync->{ version } ) {
myprint( imapsync_version( $sync ), "\n" ) ;
- exit 0 ;
+ return 0 ;
}
#$sync->{debugenv} = 1 ;
@@ -1224,6 +1377,8 @@ load_modules( ) ;
# after_get_options call usage and exit if --help or options were not well got
after_get_options( $sync, $options_good ) ;
+#local $ENV{TZ} = 'GMT' if ( under_cgi_context( $sync ) and 'MSWin32' ne $OSNAME ) ;
+#output( $sync, localtime(time) . " " . gmtime(time) . "\n" ) ;
# Under CGI environment, fix caveat emptor potential issues
cgisetcontext( $sync ) ;
@@ -1237,28 +1392,40 @@ sanitize( $sync ) ;
$sync->{ tmpdir } ||= File::Spec->tmpdir( ) ;
# Unit tests
-testsexit( $sync ) ;
+my $unittestssuite = unittestssuite( $sync ) ;
+
+
+if ( condition_to_leave_after_tests( $sync ) )
+{
+ return $unittestssuite ;
+}
# init live varaiables
-testslive_init( $sync ) if ( $sync->{testslive} ) ;
-testslive6_init( $sync ) if ( $sync->{testslive6} ) ;
-#
+if ( $sync->{ testslive } )
+{
+ testslive_init( $sync ) ;
+}
-pidfile( $sync ) ;
+if ( $sync->{ testslive6 } )
+{
+ testslive6_init( $sync ) ;
+}
-# old abort place
+define_pidfile( $sync ) ;
+if ( $sync->{ abortbyfile } ) { $sync->{ abort } = 1 ; }
install_signals( $sync ) ;
-$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->{ log } = defined $sync->{ log } ? $sync->{ log } : 1 ;
+$sync->{ errorsdump } = defined $sync->{ errorsdump } ? $sync->{ errorsdump } : 1 ;
+$sync->{ errorsmax } = defined $sync->{ errorsmax } ? $sync->{ errorsmax } : $ERRORS_MAX ;
# log and output
binmode STDOUT, ":encoding(UTF-8)" ;
-if ( $sync->{log} ) {
+
+if ( $sync->{ log } ) {
setlogfile( $sync ) ;
teelaunch( $sync ) ;
# now $sync->{tee} is a filehandle to STDOUT and the logfile
@@ -1266,7 +1433,7 @@ if ( $sync->{log} ) {
#binmode STDERR, ":encoding(UTF-8)" ;
# STDERR goes to the same place: LOG and STDOUT (if logging is on)
-# Useful only for --debugssl
+# Useful only for --debugssl
$sync->{tee} and local *STDERR = *${$sync->{tee}}{IO} ;
@@ -1275,14 +1442,14 @@ $timestart_int = int( $sync->{timestart} ) ;
$sync->{timebefore} = $sync->{timestart} ;
-$timestart_str = localtime( $sync->{timestart} ) ;
+$sync->{ timestart_str } = localtimez( $sync->{timestart} ) ;
# The prints in the log starts here
myprint( localhost_info( $sync ), "\n" ) ;
-myprint( "Transfer started at $timestart_str\n" ) ;
+myprint( "Transfer started at $sync->{ timestart_str }\n" ) ;
myprint( "PID is $PROCESS_ID my PPID is ", mygetppid( ), "\n" ) ;
-myprint( "Log file is $sync->{logfile} ( to change it, use --logfile path ; or use --nolog to turn off logging )\n" ) if ( $sync->{log} ) ;
+announcelogfile( $sync ) ;
myprint( "Load is " . ( join( q{ }, loadavg( ) ) || 'unknown' ), " on $sync->{cpu_number} cores\n" ) ;
#myprintf( "Memory consumption so far: %.1f MiB\n", memory_consumption( ) / $KIBI / $KIBI ) ;
myprint( 'Current directory is ' . getcwd( ) . "\n" ) ;
@@ -1296,11 +1463,13 @@ $warn_release = ( $sync->{releasecheck} ) ? check_last_release( ) : $STR_use_re
$wholeheaderifneeded = defined $wholeheaderifneeded ? $wholeheaderifneeded : 1;
-# Activate --usecache if --useuid is set and no --nousecache
+# Activate --usecache if --useuid is set and there is no --nousecache
$usecache = 1 if ( $useuid and ( ! defined $usecache ) ) ;
$cacheaftercopy = 1 if ( $usecache and ( ! defined $cacheaftercopy ) ) ;
-$sync->{ checkselectable } = defined $sync->{ checkselectable } ? $sync->{ checkselectable } : 1 ;
+
+
+
$sync->{ checkfoldersexist } = defined $sync->{ checkfoldersexist } ? $sync->{ checkfoldersexist } : 1 ;
$checkmessageexists = defined $checkmessageexists ? $checkmessageexists : 0 ;
$sync->{ expungeaftereach } = defined $sync->{ expungeaftereach } ? $sync->{ expungeaftereach } : 1 ;
@@ -1312,6 +1481,7 @@ $sync->{abletosearch2} = defined $sync->{abletosearch2} ? $sync->{abletosear
$checkmessageexists = 0 if ( not $sync->{abletosearch1} ) ;
+$sync->{ trylogin } = defined $sync->{ trylogin } ? $sync->{ trylogin } : 1 ;
$sync->{showpasswords} = defined $sync->{showpasswords} ? $sync->{showpasswords} : 0 ;
$sync->{ fixslash2 } = defined $sync->{ fixslash2 } ? $sync->{ fixslash2 } : 1 ;
$fixInboxINBOX = defined $fixInboxINBOX ? $fixInboxINBOX : 1 ;
@@ -1335,19 +1505,18 @@ output_reset_with( $sync ) ;
do_valid_directory( $sync->{ tmpdir } ) || croak "Error creating tmpdir $sync->{ tmpdir } : $OS_ERROR" ;
-remove_pidfile_not_running( $sync->{pidfile} ) ;
+remove_pidfile_not_running( $sync->{ pidfile } ) ;
# if another imapsync is running then tail -f its logfile and exit
# useful in cgi context
if ( $sync->{ tail } and tail( $sync ) )
{
- $sync->{nb_errors}++ ;
exit_clean( $sync, $EX_OK, "Tail -f finished. Now finishing myself processus $PROCESS_ID\n" ) ;
exit $EX_OK ;
}
if ( ! write_pidfile( $sync ) ) {
- myprint( "Exiting with return value $EXIT_PID_FILE_ERROR ($EXIT_TXT{$EXIT_PID_FILE_ERROR}) $sync->{nb_errors}/$sync->{errorsmax} nb_errors/max_errors\n" ) ;
+ myprint( "Exiting with return value $EXIT_PID_FILE_ERROR ($EXIT_TXT{$EXIT_PID_FILE_ERROR}) $sync->{nb_errors}/$sync->{errorsmax} nb_errors/max_errors PID $PROCESS_ID\n" ) ;
exit $EXIT_PID_FILE_ERROR ;
}
@@ -1357,18 +1526,22 @@ if ( ! write_pidfile( $sync ) ) {
if ( $sync->{ abort } )
{
abort( $sync ) ;
+ # well, the abort job is done, because even when not succeeded
+ # in aborting another run, this run has to end without doing any
+ # thing else
+
+ exit $EX_OK ;
}
# simulong is just a loop printing some lines for xx seconds with option "--simulong xx".
-if ( $sync->{ simulong } )
-{
- simulong( $sync->{ simulong } ) ;
-}
+simulong( $sync ) ;
+
# New place for cgiload 2019_03_03
# because I want to log it
# Can break here if load is too heavy
+# Have in mind the CGI header has already a 503 Service Unavailable
cgiload( $sync ) ;
@@ -1376,20 +1549,28 @@ $fixcolonbug = defined $fixcolonbug ? $fixcolonbug : 1 ;
if ( $usecache and $fixcolonbug ) { tmpdir_fix_colon_bug( $sync ) } ;
-$modulesversion and myprint( "Modules version list:\n", modulesversion(), "( use --no-modulesversion to turn off printing this Perl modules list )\n" ) ;
+$modulesversion and myprint( "Modules version list ( use --no-modulesversion to turn off printing this Perl modules list ):\n", modulesversion(), "\n" ) ;
check_lib_version( $sync ) or
croak "imapsync needs perl lib Mail::IMAPClient release 3.30 or superior.\n";
-exit_clean( $sync, $EX_OK ) if ( $justbanner ) ;
+
+if ( $sync->{ justbanner } )
+{
+ myprint( "Exiting because of --justbanner\n" ) ;
+ exit_clean( $sync, $EX_OK ) ;
+}
# turn on RFC standard flags correction like \SEEN -> \Seen
-$flagscase = defined $flagscase ? $flagscase : 1 ;
+$sync->{ flagscase } = defined $sync->{ flagscase } ? $sync->{ flagscase } : 1 ;
# Use PERMANENTFLAGS if available
-$filterflags = defined $filterflags ? $filterflags : 1 ;
+$sync->{ filterflags } = defined $sync->{ filterflags } ? $sync->{ filterflags } : 1 ;
+
+filterbuggyflags( $sync ) ;
+
# sync flags just after an APPEND, some servers ignore the flags given in the APPEND
# like MailEnable IMAP server.
@@ -1412,13 +1593,18 @@ $split1 ||= $SPLIT ;
$split2 ||= $SPLIT ;
#$sync->{host1} || missing_option( $sync, '--host1' ) ;
+$sync->{host1} = sanitize_host( $sync->{host1} ) ;
$sync->{port1} ||= ( $sync->{ssl1} ) ? $IMAP_SSL_PORT : $IMAP_PORT ;
#$sync->{host2} || missing_option( $sync, '--host2' ) ;
+$sync->{host2} = sanitize_host( $sync->{host2} ) ;
$sync->{port2} ||= ( $sync->{ssl2} ) ? $IMAP_SSL_PORT : $IMAP_PORT ;
-$debugimap1 = $debugimap2 = 1 if ( $debugimap ) ;
-$sync->{ debug } = 1 if ( $debugimap1 or $debugimap2 ) ;
+
+$acc1->{ debugimap } = $acc2->{ debugimap } = 1 if ( $sync->{ debugimap } ) ;
+# Set on debug mode if one of the imap dialogs are in debug.
+# imap dialog without the debug mode is scary and useless.
+$sync->{ debug } = 1 if ( $acc1->{ debugimap } or $acc2->{ debugimap } ) ;
# By default, don't take size to compare
$skipsize = (defined $skipsize) ? $skipsize : 1;
@@ -1455,6 +1641,7 @@ if ( $sync->{ssl1} or $sync->{ssl2} or $sync->{tls1} or $sync->{tls2}) {
if ( $sync->{ssl1} ) {
myprint( qq{Host1: SSL default mode is like --sslargs1 "SSL_verify_mode=$SSL_VERIFY_POLICY", meaning for host1 $SSL_VERIFY_STR{$SSL_VERIFY_POLICY}\n} ) ;
myprint( 'Host1: Use --sslargs1 SSL_verify_mode=' . IO::Socket::SSL::SSL_VERIFY_PEER( ) . " to have $SSL_VERIFY_STR{IO::Socket::SSL::SSL_VERIFY_PEER( )} of host1\n" ) ;
+ # $sync->{ acc1 }->{sslargs}->{SSL_verify_mode}
}
if ( $sync->{ssl2} ) {
@@ -1516,8 +1703,11 @@ if ( ( $sync->{ delete2 } or $sync->{ delete2duplicates } ) and not defined $sy
}
if ( $sync->{ delete1 } and $sync->{ delete2 } ) {
- myprint( "Warning: using --delete1 and --delete2 together is almost always a bad idea, exiting imapsync\n" ) ;
- $sync->{nb_errors}++ ;
+ myprint( "Warning: using --delete1 and --delete2 together is almost always a bad idea. "
+ . "You should probably launch two runs, the first with --delete2 for a strict sync, "
+ . "then the second with --delete1 to remove messages from the source account. "
+ . "Exiting imapsync.\n" ) ;
+ $sync->{ nb_errors }++ ;
exit_clean( $sync, $EX_USAGE ) ;
}
@@ -1540,44 +1730,48 @@ if ( defined $authmd5 and $authmd5 ) {
}
if ( defined $authmd51 and $authmd51 ) {
- $authmech1 ||= 'CRAM-MD5';
+ $acc1->{ authmech } ||= 'CRAM-MD5' ;
}
else{
- $authmech1 ||= $authuser1 ? 'PLAIN' : 'LOGIN';
+ $acc1->{ authmech } ||= $acc1->{ authuser } ? 'PLAIN' : 'LOGIN' ;
}
if ( defined $authmd52 and $authmd52 ) {
- $authmech2 ||= 'CRAM-MD5';
+ $acc2->{ authmech } ||= 'CRAM-MD5';
}
else{
- $authmech2 ||= $authuser2 ? 'PLAIN' : 'LOGIN';
+ $acc2->{ authmech } ||= $acc2->{ authuser } ? 'PLAIN' : 'LOGIN';
}
-$authmech1 = uc $authmech1;
-$authmech2 = uc $authmech2;
+$acc1->{ authmech } = uc $acc1->{ authmech } ;
+$acc2->{ authmech } = uc $acc2->{ authmech } ;
-if (defined $proxyauth1 && !$authuser1) {
+if ( defined $acc1->{ proxyauth } && !$acc1->{ authuser } )
+{
missing_option( $sync, 'With --proxyauth1, --authuser1' ) ;
}
-if (defined $proxyauth2 && !$authuser2) {
+if ( defined $acc2->{ proxyauth } && !$acc2->{ authuser } )
+{
missing_option( $sync, 'With --proxyauth2, --authuser2' ) ;
}
-#$authuser1 ||= $sync->{user1};
-#$authuser2 ||= $sync->{user2};
+myprint( "Host1: will try to use $acc1->{ authmech } authentication on host1\n") ;
+myprint( "Host2: will try to use $acc2->{ authmech } authentication on host2\n") ;
-myprint( "Host1: will try to use $authmech1 authentication on host1\n") ;
-myprint( "Host2: will try to use $authmech2 authentication on host2\n") ;
+$sync->{ timeout } = defined $sync->{ timeout } ?$sync->{ timeout } : $DEFAULT_TIMEOUT ;
-$timeout = defined $timeout ? $timeout : $DEFAULT_TIMEOUT ;
+$sync->{ acc1 }->{timeout} = defined $sync->{ acc1 }->{timeout} ? $sync->{ acc1 }->{timeout} : $sync->{ timeout } ;
+myprint( "Host1: imap connection timeout is $sync->{ acc1 }->{timeout} seconds\n") ;
+$sync->{ acc2 }->{timeout} = defined $sync->{ acc2 }->{timeout} ? $sync->{ acc2 }->{timeout} : $sync->{ timeout } ;
+myprint( "Host2: imap connection timeout is $sync->{ acc2 }->{timeout} seconds\n" ) ;
-$sync->{h1}->{timeout} = defined $sync->{h1}->{timeout} ? $sync->{h1}->{timeout} : $timeout ;
-myprint( "Host1: imap connection timeout is $sync->{h1}->{timeout} seconds\n") ;
-$sync->{h2}->{timeout} = defined $sync->{h2}->{timeout} ? $sync->{h2}->{timeout} : $timeout ;
-myprint( "Host2: imap connection timeout is $sync->{h2}->{timeout} seconds\n" ) ;
+if ( under_cgi_context( $sync ) )
+{
+ myprint( "Under CGI context, a timeout can occur from the webserver, see https://imapsync.lamiral.info/INSTALL.d/INSTALL.OnlineUI.txt\n" ) ;
+}
-$syncacls = defined $syncacls ? $syncacls : 0 ;
+$sync->{ syncacls } = defined $sync->{ syncacls } ? $sync->{ syncacls } : 0 ;
# No folders sizes if --justfolders, unless really wanted.
if (
@@ -1592,21 +1786,21 @@ if (
$sync->{ foldersizes } = ( defined $sync->{ foldersizes } ) ? $sync->{ foldersizes } : 1 ;
$sync->{ foldersizesatend } = ( defined $sync->{ foldersizesatend } ) ? $sync->{ foldersizesatend } : $sync->{ foldersizes } ;
+$sync->{ checknoabletosearch } = ( defined $sync->{ checknoabletosearch } ) ? $sync->{ checknoabletosearch } : 1 ;
-$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 ;
+$acc1->{ fastio } = defined $acc1->{ fastio } ? $acc1->{ fastio } : 0 ;
+$acc2->{ fastio } = defined $acc2->{ fastio } ? $acc2->{ fastio } : 0 ;
+
+
+$acc1->{ reconnectretry } = defined $acc1->{ reconnectretry } ? $acc1->{ reconnectretry } : $DEFAULT_NB_RECONNECT_PER_IMAP_COMMAND ;
+$acc2->{ reconnectretry } = defined $acc2->{ reconnectretry } ? $acc2->{ reconnectretry } : $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 ;
if ( ! @useheader ) { @useheader = qw( Message-Id Received ) ; }
# Make a hash %useheader of each --useheader 'key' in uppercase
-for ( @useheader ) { $useheader{ uc $_ } = undef } ;
+for ( @useheader ) { $sync->{useheader}->{ uc $_ } = undef } ;
#myprint( Data::Dumper->Dump( [ \%useheader ] ) ) ;
#exit ;
@@ -1617,6 +1811,10 @@ myprint( "Host2: IMAP server [$sync->{host2}] port [$sync->{port2}] user [$sync-
get_password1( $sync ) ;
get_password2( $sync ) ;
+# --dry1 make imapsync not fetching messages from host1, it is on when --dry is on.
+# Use --dry --nodry1 to make imapsync fetching messages from host1,
+# It is useful when debugging transformation options like --pipemess or --regexmess
+$sync->{dry1} = defined $sync->{dry1} ? $sync->{dry1} : $sync->{dry} ;
$sync->{dry_message} = q{} ;
if( $sync->{dry} ) {
@@ -1626,7 +1824,8 @@ if( $sync->{dry} ) {
$sync->{ search1 } ||= $search if ( $search ) ;
$sync->{ search2 } ||= $search if ( $search ) ;
-if ( $disarmreadreceipts ) {
+if ( $disarmreadreceipts )
+{
push @regexmess, q{s{\A((?:[^\n]+\r\n)+|)(^Disposition-Notification-To:[^\n]*\n)(\r?\n|.*\n\r?\n)}{$1X-$2$3}ims} ;
}
@@ -1688,9 +1887,9 @@ if ( @skipmess ) {
myprint( "Ok with each --skipmess\n" ) ;
}
-if ( @regexflag ) {
+if ( $sync->{ regexflag } ) {
myprint( "Checking each --regexflag command with an space string.\n" ) ;
- my $string = flags_regex( q{ } ) ;
+ my $string = regexflags( $sync, q{ } ) ;
# string undef means one of the eval regex was bad.
if ( not ( defined $string ) ) {
$sync->{nb_errors}++ ;
@@ -1701,33 +1900,26 @@ if ( @regexflag ) {
myprint( "Ok with each --regexflag\n" ) ;
}
-$sync->{imap1} = login_imap( $sync->{host1}, $sync->{port1}, $sync->{user1}, $domain1, $sync->{password1},
- $debugimap1, $sync->{h1}->{timeout}, $fastio1, $sync->{ssl1}, $sync->{tls1},
- $authmech1, $authuser1, $reconnectretry1,
- $proxyauth1, $uid1, $split1, 'Host1', $sync->{h1}, $sync ) ;
+$sync->{imap1} = login_imap( $sync->{host1}, $sync->{port1}, $sync->{user1}, $sync->{password1},
+ $sync->{ssl1}, $sync->{tls1},
+ $uid1, $split1, $sync->{ acc1 }, $sync ) ;
-$sync->{imap2} = login_imap( $sync->{host2}, $sync->{port2}, $sync->{user2}, $domain2, $sync->{password2},
- $debugimap2, $sync->{h2}->{timeout}, $fastio2, $sync->{ssl2}, $sync->{tls2},
- $authmech2, $authuser2, $reconnectretry2,
- $proxyauth2, $uid2, $split2, 'Host2', $sync->{h2}, $sync ) ;
+$sync->{imap2} = login_imap( $sync->{host2}, $sync->{port2}, $sync->{user2}, $sync->{password2},
+ $sync->{ssl2}, $sync->{tls2},
+ $uid2, $split2, $sync->{ acc2 }, $sync ) ;
-$sync->{ debug } and myprint( 'Host1 Buffer I/O: ', $sync->{imap1}->Buffer(), "\n" ) ;
-$sync->{ debug } and myprint( 'Host2 Buffer I/O: ', $sync->{imap2}->Buffer(), "\n" ) ;
+$sync->{ debug } and $sync->{imap1} and myprint( 'Host1 Buffer I/O: ', $sync->{imap1}->Buffer(), "\n" ) ;
+$sync->{ debug } and $sync->{imap2} and myprint( 'Host2 Buffer I/O: ', $sync->{imap2}->Buffer(), "\n" ) ;
-if ( ! $sync->{imap1}->IsAuthenticated( ) )
+if ( ! $sync->{imap1} || ! $sync->{imap2} )
{
- $sync->{nb_errors}++ ;
- exit_clean( $sync, $EXIT_AUTHENTICATION_FAILURE, "Not authenticated on host1\n" ) ;
+ exit_most_errors( $sync ) ;
}
+
+
myprint( "Host1: state Authenticated\n" ) ;
-
-if ( ! $sync->{imap2}->IsAuthenticated( ) )
-{
- $sync->{nb_errors}++ ;
- exit_clean( $sync, $EXIT_AUTHENTICATION_FAILURE, "Not authenticated on host2\n" ) ;
-}
myprint( "Host2: state Authenticated\n" ) ;
myprint( 'Host1 capability once authenticated: ', join(q{ }, @{ $sync->{imap1}->capability() || [] }), "\n" ) ;
@@ -1886,29 +2078,17 @@ if ( $sync->{ checkfoldersexist } ) {
myprint( "Host1: Not checking that wanted folders exist. Remove --nocheckfoldersexist to get this check.\n" ) ;
}
+setcheckselectable( $sync ) ;
-if ( $sync->{ checkselectable } ) {
- my @h1_folders_wanted_selectable ;
- myprint( "Host1: Checking wanted folders are selectable. Use --nocheckselectable to avoid this check.\n" ) ;
- foreach my $folder ( @{ $sync->{h1_folders_wanted} } ) {
- ( $sync->{ debug } or $sync->{debugfolders} ) and myprint( "Checking $folder is selectable on host1\n" ) ;
- # It does an imap command LIST "" $folder and then search for no \Noselect
- if ( ! $sync->{imap1}->selectable( $folder ) ) {
- myprint( "Host1: warning! ignoring folder $folder because it is not selectable\n" ) ;
- }else{
- push @h1_folders_wanted_selectable, $folder ;
- }
- }
- @{ $sync->{h1_folders_wanted} } = @h1_folders_wanted_selectable ;
- ( $sync->{ debug } or $sync->{debugfolders} ) and myprint( 'Host1: checking folders took ', timenext( $sync ), " s\n" ) ;
-}else{
- myprint( "Host1: Not checking that wanted folders are selectable. Remove --nocheckselectable to get this check.\n" ) ;
-}
+checkselectable( $sync ) ;
-# Old place of private_folders_separators_and_prefixes( ) call.
-#private_folders_separators_and_prefixes( ) ;
+# Bugfix OpenFind folders named like "kk \*123" are in fact "kk *123" (no \)
+#foreach my $folder ( @{ $sync->{ h1_folders_wanted } } )
+#{
+# $folder =~ s{ \\\*}{ *}g ;
+#}
# this hack is because LWP post does not pass well a hash in the $form parameter
@@ -2021,6 +2201,25 @@ if ( $sync->{ skipemptyfolders } )
myprint( "Host1: will not syncing empty folders on host1. Use --noskipemptyfolders to create them anyway on host2\n") ;
}
+if ( $sync->{ checknoabletosearch } )
+{
+ myprint( "Checking SEARCH ALL works on both accounts. To avoid that check, use --nochecknoabletosearch\n" ) ;
+ my $check1 = checknoabletosearch( $sync, $sync->{ imap1 }, 'INBOX', 'Host1' ) ;
+ my $check2 = checknoabletosearch( $sync, $sync->{ imap2 }, 'INBOX', 'Host2' ) ;
+ if ( $check1 or $check2 )
+ {
+ myprint( "At least one account can not SEARCH ALL. So acting like --noabletosearch\n" ) ;
+ $sync->{abletosearch} = 0 ;
+ $sync->{abletosearch1} = 0 ;
+ $sync->{abletosearch2} = 0 ;
+ }
+ else
+ {
+ myprint( "Good! SEARCH ALL works on both accounts.\n" ) ;
+ }
+}
+
+
if ( $sync->{ foldersizes } ) {
@@ -2035,7 +2234,7 @@ if ( $sync->{ justfoldersizes } )
exit_clean( $sync, $EX_OK, "Exiting because of --justfoldersizes\n" ) ;
}
-$sync->{stats} = 1 ;
+$sync->{can_do_stats} = 1 ;
if ( $sync->{ delete1emptyfolders } ) {
delete1emptyfolders( $sync ) ;
@@ -2060,6 +2259,7 @@ FOLDER: foreach my $h1_fold ( @{ $sync->{h1_folders_wanted} } )
{
$sync->{ h1_current_folder } = $h1_fold ;
eta_print( $sync ) ;
+ abortifneeded( $sync ) ;
if ( ! reconnect_12_if_needed( $sync ) ) { last FOLDER ; }
my $h2_fold = imap2_folder_name( $sync, $h1_fold ) ;
@@ -2075,10 +2275,15 @@ FOLDER: foreach my $h1_fold ( @{ $sync->{h1_folders_wanted} } )
debugsleep( $sync ) ;
- my $h1_fold_nb_messages = count_from_select( $sync->{imap1}->History ) ;
- myprint( "Host1: folder [$h1_fold] has $h1_fold_nb_messages messages in total (mentioned by SELECT)\n" ) ;
+ my $h1_msgs_all_hash_ref ;
+ my @h1_msgs ;
+ my $h1_msgs_nb ;
+ my $h1_msgs_nb_from_select ;
- if ( $sync->{ skipemptyfolders } and 0 == $h1_fold_nb_messages ) {
+ $h1_msgs_nb_from_select = count_from_select( $sync->{imap1}->History ) ;
+ myprint( "Host1: folder [$h1_fold] has $h1_msgs_nb_from_select messages in total (mentioned by SELECT)\n" ) ;
+
+ if ( $sync->{ skipemptyfolders } and 0 == $h1_msgs_nb_from_select ) {
myprint( "Host1: skipping empty host1 folder [$h1_fold]\n" ) ;
next FOLDER ;
}
@@ -2087,22 +2292,32 @@ FOLDER: foreach my $h1_fold ( @{ $sync->{h1_folders_wanted} } )
# Thanks jh1995
# Goal: do not create folder if --search or --max/minage return 0 message.
# even if there are messages by SELECT (no not real empty, empty for the user point of vue).
- if ( $sync->{ skipemptyfolders } )
+ if ( $sync->{ skipemptyfolders } or $sync->{ dry } )
{
- my $h1_msgs_all_hash_ref_tmp = { } ;
- my @h1_msgs_tmp = select_msgs( $sync->{imap1}, $h1_msgs_all_hash_ref_tmp, $sync->{ search1 }, $h1_fold ) ;
- my $h1_fold_nb_messages_tmp = scalar( @h1_msgs_tmp ) ;
- if ( 0 == $h1_fold_nb_messages_tmp ) {
+ $h1_msgs_all_hash_ref = { } ;
+ @h1_msgs = select_msgs( $sync->{imap1}, $h1_msgs_all_hash_ref, $sync->{ search1 }, $sync->{abletosearch1}, $h1_fold ) ;
+
+ $h1_msgs_nb = scalar( @h1_msgs ) ;
+ if ( 0 == $h1_msgs_nb and $sync->{ skipemptyfolders } ) {
myprint( "Host1: skipping empty host1 folder [$h1_fold] (0 message found by SEARCH)\n" ) ;
next FOLDER ;
}
}
if ( ! exists $h2_folders_all{ $h2_fold } ) {
- create_folder( $sync, $sync->{imap2}, $h2_fold, $h1_fold ) or next FOLDER ;
+ # In --dry mode I could count the messages to be transfered instead of 0
+ # Messages transferred : 0 (could be 0 without dry mode)
+ if ( ! create_folder( $sync, $sync->{imap2}, $h2_fold, $h1_fold ) )
+ {
+ if ( $sync->{ dry } )
+ {
+ $nb_msg_skipped_dry_mode += $h1_msgs_nb ;
+ }
+ next FOLDER ;
+ }
}
- acls_sync( $h1_fold, $h2_fold ) ;
+ acls_sync( $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)
@@ -2138,13 +2353,18 @@ FOLDER: foreach my $h1_fold ( @{ $sync->{h1_folders_wanted} } )
if ( ! reconnect_12_if_needed( $sync ) ) { last FOLDER ; }
- my $h1_msgs_all_hash_ref = { } ;
- my @h1_msgs = select_msgs( $sync->{imap1}, $h1_msgs_all_hash_ref, $sync->{ search1 }, $sync->{abletosearch1}, $h1_fold );
+
+ if ( ! defined $h1_msgs_nb )
+ {
+ $h1_msgs_all_hash_ref = { } ;
+ @h1_msgs = select_msgs( $sync->{imap1}, $h1_msgs_all_hash_ref, $sync->{ search1 }, $sync->{abletosearch1}, $h1_fold );
+ $h1_msgs_nb = scalar @h1_msgs ;
+ }else{
+ # select_msgs already done.
+ }
if ( ! reconnect_12_if_needed( $sync ) ) { last FOLDER ; }
- my $h1_msgs_nb = scalar @h1_msgs ;
-
myprint( "Host1: folder [$h1_fold] considering $h1_msgs_nb messages\n" ) ;
( $sync->{ debug } or $debuglist ) and myprint( "Host1: folder [$h1_fold] considering $h1_msgs_nb messages, LIST gives: @h1_msgs\n" ) ;
$sync->{ debug } and myprint( "Host1: selecting messages of folder [$h1_fold] took ", timenext( $sync ), " s\n" ) ;
@@ -2188,7 +2408,7 @@ FOLDER: foreach my $h1_fold ( @{ $sync->{h1_folders_wanted} } )
@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 @h2_msgs_in_cache = sort { $a <=> $b } keys %{ $cache_2_1_ref } ;
my ( %h1_msgs_not_in_cache, %h2_msgs_not_in_cache ) ;
%h1_msgs_not_in_cache = %h1_msgs ;
@@ -2196,9 +2416,9 @@ FOLDER: foreach my $h1_fold ( @{ $sync->{h1_folders_wanted} } )
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 ;
+ my @h1_msgs_not_in_cache = sort { $a <=> $b } 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_not_in_cache = sort { $a <=> $b } keys %h2_msgs_not_in_cache ;
my @h2_msgs_delete2_not_in_cache = () ;
%h1_msgs_copy_by_uid = ( ) ;
@@ -2233,8 +2453,7 @@ FOLDER: foreach my $h1_fold ( @{ $sync->{h1_folders_wanted} } )
}
else
{
- my $uidnext = $sync->{imap1}->uidnext( $h1_fold ) || $uidnext_default ;
- my $fetch_hash_uids = $fetch_hash_set || "1:$uidnext" ;
+ my $fetch_hash_uids = $fetch_hash_set || "1:*" ;
$h1_fir_ref = $sync->{imap1}->fetch_hash( $fetch_hash_uids, @h1_common_fetch_param, $h1_fir_ref )
if ( @h1_msgs ) ;
}
@@ -2260,22 +2479,30 @@ FOLDER: foreach my $h1_fold ( @{ $sync->{h1_folders_wanted} } )
$sync->{ nb_msg_skipped } += 1 ;
$sync->{ h1_nb_msg_noheader } +=1 ;
$sync->{ h1_nb_msg_processed } +=1 ;
- } elsif(0 == $rc)
+ } elsif( 0 == $rc )
{
# duplicate
push @h1_msgs_duplicate, $m;
# duplicate, same id same size?
my $h1_size = $h1_fir_ref->{$m}->{'RFC822.SIZE'} || 0;
- $sync->{ nb_msg_skipped } += 1;
- $h1_nb_msg_duplicate += 1;
- $sync->{ h1_nb_msg_processed } +=1 ;
+
+ $sync->{ acc1 }->{ nb_msg_duplicate } += 1;
+ if ( ! $sync->{ syncduplicates } ) {
+ $sync->{ nb_msg_skipped } += 1 ;
+ $sync->{ h1_nb_msg_processed } +=1 ;
+ }
}
}
+
+
my $h1_msgs_duplicate_nb = scalar @h1_msgs_duplicate ;
myprint( "Host1: folder [$h1_fold] selected $h1_msgs_nb messages, duplicates $h1_msgs_duplicate_nb\n" ) ;
$sync->{ debug } and myprint( 'Host1: whole time parsing headers took ', timenext( $sync ), " s\n" ) ;
+
+
+
# Getting headers and metada can be so long that host2 might be disconnected here
if ( ! reconnect_12_if_needed( $sync ) ) { last FOLDER ; }
@@ -2296,8 +2523,7 @@ FOLDER: foreach my $h1_fold ( @{ $sync->{h1_folders_wanted} } )
if ( $sync->{abletosearch2} and scalar( @h2_msgs ) ) {
$h2_fir_ref = $sync->{imap2}->fetch_hash( \@h2_msgs, @h2_common_fetch_param, $h2_fir_ref) ;
}else{
- my $uidnext = $sync->{imap2}->uidnext( $h2_fold ) || $uidnext_default ;
- my $fetch_hash_uids = $fetch_hash_set || "1:$uidnext" ;
+ my $fetch_hash_uids = $fetch_hash_set || "1:*" ;
$h2_fir_ref = $sync->{imap2}->fetch_hash( $fetch_hash_uids, @h2_common_fetch_param, $h2_fir_ref )
if ( @h2_msgs ) ;
}
@@ -2313,7 +2539,7 @@ FOLDER: foreach my $h1_fold ( @{ $sync->{h1_folders_wanted} } )
$h2_nb_msg_noheader += 1 ;
} elsif( 0 == $rc ) {
# duplicate
- $h2_nb_msg_duplicate += 1 ;
+ $sync->{ acc2 }->{ nb_msg_duplicate } += 1 ;
push @h2_msgs_duplicate, $m ;
}
}
@@ -2353,9 +2579,9 @@ FOLDER: foreach my $h1_fold ( @{ $sync->{h1_folders_wanted} } )
foreach my $h2_msg ( @h2_msgs_duplicate ) {
myprint( "Host2: msg $h2_fold/$h2_msg marked \\Deleted [duplicate] on host2 $sync->{dry_message}\n" ) ;
push @h2_expunge, $h2_msg if $sync->{ uidexpunge2 } ;
- if ( ! $sync->{dry} ) {
- $sync->{imap2}->delete_message( $h2_msg ) ;
- $h2_nb_msg_deleted += 1 ;
+ if ( ! $sync->{ dry } ) {
+ $sync->{ imap2 }->delete_message( $h2_msg ) ;
+ $sync->{ acc2 }->{ nb_msg_deleted } += 1 ;
}
}
my $cnt = scalar @h2_expunge ;
@@ -2381,9 +2607,9 @@ FOLDER: foreach my $h1_fold ( @{ $sync->{h1_folders_wanted} } )
myprint( "Host2: msg $h2_fold/$h2_msg marked \\Deleted on host2 [$m_id] $sync->{dry_message}\n" )
if ! $isdel;
push @h2_expunge, $h2_msg if $sync->{ uidexpunge2 };
- if ( ! ( $sync->{dry} or $isdel ) ) {
- $sync->{imap2}->delete_message($h2_msg);
- $h2_nb_msg_deleted += 1;
+ if ( ! ( $sync->{ dry } or $isdel ) ) {
+ $sync->{ imap2 }->delete_message( $h2_msg );
+ $sync->{ acc2 }->{ nb_msg_deleted } += 1;
}
}
}
@@ -2391,8 +2617,8 @@ FOLDER: foreach my $h1_fold ( @{ $sync->{h1_folders_wanted} } )
myprint( "Host2: msg $h2_fold/$h2_msg marked \\Deleted [not in cache] on host2 $sync->{dry_message}\n" ) ;
push @h2_expunge, $h2_msg if $sync->{ uidexpunge2 };
if ( ! $sync->{dry} ) {
- $sync->{imap2}->delete_message($h2_msg);
- $h2_nb_msg_deleted += 1;
+ $sync->{ imap2 }->delete_message( $h2_msg );
+ $sync->{ acc2 }->{ nb_msg_deleted } += 1;
}
}
my $cnt = scalar @h2_expunge ;
@@ -2445,9 +2671,9 @@ FOLDER: foreach my $h1_fold ( @{ $sync->{h1_folders_wanted} } )
}else{
myprint( "Host2: msg $h2_fold/$h2_msg marked \\Deleted $sync->{dry_message}\n" ) ;
push @h2_expunge, $h2_msg if $sync->{ uidexpunge2 } ;
- if ( ! $sync->{dry} ) {
- $sync->{imap2}->delete_message( $h2_msg ) ;
- $h2_nb_msg_deleted += 1 ;
+ if ( ! $sync->{ dry} ) {
+ $sync->{ imap2 }->delete_message( $h2_msg ) ;
+ $sync->{ acc2 }->{ nb_msg_deleted } += 1 ;
}
}
}
@@ -2475,6 +2701,7 @@ FOLDER: foreach my $h1_fold ( @{ $sync->{h1_folders_wanted} } )
my @h1_msgs_to_delete ;
MESS: foreach my $m_id (@h1_hash_keys_sorted_by_uid) {
+ abortifneeded( $sync ) ;
if ( ! reconnect_12_if_needed( $sync ) ) { last FOLDER ; }
#myprint( "h1_nb_msg_processed: $sync->{ h1_nb_msg_processed }\n" ) ;
@@ -2510,7 +2737,7 @@ FOLDER: foreach my $h1_fold ( @{ $sync->{h1_folders_wanted} } )
}
if ( total_bytes_max_reached( $sync ) ) {
- # a bug when using --delete1 --noexpungeaftereach
+ # Still a bug when using --delete1 --noexpungeaftereach
# same thing below on all total_bytes_max_reached!
last FOLDER ;
}
@@ -2558,7 +2785,7 @@ FOLDER: foreach my $h1_fold ( @{ $sync->{h1_folders_wanted} } )
}
}
- if ( ! reconnect_12_if_needed( $sync ) ) { last FOLDER ; }
+ if ( ! reconnect_12_if_needed( $sync ) ) { last FOLDER ; }
if ( $sync->{ delete1 } ) {
push @h1_msgs_to_delete, $h1_msg ;
@@ -2566,6 +2793,7 @@ FOLDER: foreach my $h1_fold ( @{ $sync->{h1_folders_wanted} } )
}
# END MESS: loop
+ # @h1_msgs_in_cache are already synced too.
delete_message_on_host1( $sync, $h1_fold, $sync->{ expunge1 }, @h1_msgs_to_delete, @h1_msgs_in_cache ) ;
if ( ! reconnect_12_if_needed( $sync ) ) { last FOLDER ; }
@@ -2595,6 +2823,7 @@ FOLDER: foreach my $h1_fold ( @{ $sync->{h1_folders_wanted} } )
# MESS_BY_UID:
foreach my $h1_msg ( sort { $a <=> $b } keys %h1_msgs_copy_by_uid )
{
+ abortifneeded( $sync ) ;
$sync->{ debug } and myprint( "Copy by uid $h1_fold/$h1_msg\n" ) ;
if ( ! reconnect_12_if_needed( $sync ) ) { last FOLDER ; }
@@ -2638,18 +2867,30 @@ END_SIZE
foldersizesatend( $sync ) ;
}
+#$sync->{imap1}->State( 0 ); # Unconnected
if ( ! lost_connection( $sync, $sync->{imap1}, "for host1 [$sync->{host1}]" ) ) { $sync->{imap1}->logout( ) ; }
if ( ! lost_connection( $sync, $sync->{imap2}, "for host2 [$sync->{host2}]" ) ) { $sync->{imap2}->logout( ) ; }
-stats( $sync ) ;
-myprint( errorsdump( $sync->{nb_errors}, errors_log( $sync ) ) ) if ( $sync->{errorsdump} ) ;
-tests_live_result( $sync->{nb_errors} ) if ( $sync->{testslive} or $sync->{testslive6} ) ;
+do_and_print_stats( $sync ) ;
+
+
+if ( $sync->{errorsdump} and $sync->{nb_errors} )
+{
+ myprint( errors_listing( $sync ) ) ;
+}
+
+
+if ( $sync->{testslive} or $sync->{testslive6} )
+{
+ tests_live_result( $sync->{nb_errors} ) ;
+}
if ( $sync->{nb_errors} )
{
- exit_clean( $sync, $EXIT_WITH_ERRORS ) ;
+ my $exit_value = $EXIT_VALUE_OF_ERR_TYPE{ $sync->{most_common_error} } || $EXIT_CATCH_ALL ;
+ exit_clean( $sync, $exit_value ) ;
}
else
{
@@ -2768,9 +3009,56 @@ sub output_reset_with
return $mysync->{ output } ;
}
-sub pidfile
+
+sub tests_print_output_if_needed
{
- my $mysync = shift ;
+ note( 'Entering tests_print_output_if_needed()' ) ;
+
+ is( undef, print_output_if_needed( ), 'print_output_if_needed: no args => undef' ) ;
+ my $mysync = { } ;
+ is( q{}, print_output_if_needed( $mysync ), 'print_output_if_needed: undef => undef' ) ;
+
+ output( $mysync, "Hello\n" ) ;
+ is( "Hello\n", print_output_if_needed( $mysync ), 'print_output_if_needed: Hello => Hello' ) ;
+
+ $mysync->{ dockercontext } = 1 ;
+ is( "Hello\n", print_output_if_needed( $mysync ), 'print_output_if_needed: dockercontext + Hello => Hello' ) ;
+
+ $mysync->{ version } = 1 ;
+ is( q{}, print_output_if_needed( $mysync ), 'print_output_if_needed: dockercontext + Hello + --version => ""' ) ;
+
+ $mysync->{ dockercontext } = 0 ;
+ is( "Hello\n", print_output_if_needed( $mysync ), 'print_output_if_needed: Hello + --version => Hello' ) ;
+
+ note( 'Leaving tests_print_output_if_needed()' ) ;
+ return ;
+}
+
+
+sub print_output_if_needed
+{
+
+ my $mysync = shift @ARG ;
+ if ( ! defined $mysync ) { return ; }
+ my $output = output( $mysync ) ;
+
+ if ( $mysync->{ version } && under_docker_context( $mysync ) )
+ {
+ return q{} ;
+ }
+ else
+ {
+ myprint( $output ) ;
+ return $output ;
+ }
+
+}
+
+
+
+sub define_pidfile
+{
+ my $mysync = shift @ARG ;
$mysync->{ pidfilelocking } = defined $mysync->{ pidfilelocking } ? $mysync->{ pidfilelocking } : 0 ;
@@ -2795,9 +3083,26 @@ sub pidfile
}
$mysync->{ pidfile } = defined $mysync->{ pidfile } ? $mysync-> { pidfile } : $mysync->{ tmpdir } . "/$pidfile_basename" ;
+ $mysync->{ abortfile } = abortfile( $mysync, $PROCESS_ID ) ;
return ;
}
+sub abortfile
+{
+ my $mysync = shift @ARG ;
+ my $pid = shift @ARG ;
+
+ my $abortfile ;
+ if ( $mysync->{ abort } )
+ {
+ $abortfile = $mysync->{ pidfile } . "abort$pid" ;
+ }
+ else
+ {
+ $abortfile = $mysync->{ pidfile } . "abort$PROCESS_ID" ;
+ }
+ return $abortfile ;
+}
sub tests_kill_zero
{
@@ -2975,7 +3280,7 @@ sub killpid
my $pidtokill = shift ;
if ( ! $pidtokill ) {
- myprint( "No process to abort.\n" ) ;
+ myprint( "No process to kill.\n" ) ;
return ;
}
@@ -2989,7 +3294,7 @@ sub killpid
if ( kill( 'ZERO', $pidtokill ) or ( 'MSWin32' eq $OSNAME ) ) {
myprint( "Sending signal QUIT to PID $pidtokill \n" ) ;
kill 'QUIT', $pidtokill ;
- sleep 2 ;
+ sleep 3 ;
waitpid( $pidtokill, WNOHANG) ;
}else{
myprint( "Can not send signal kill ZERO to PID $pidtokill.\n" ) ;
@@ -3023,7 +3328,7 @@ sub killpid
sub tests_abort
{
note( 'Entering tests_abort()' ) ;
-
+ # Well, the abort behavior is tested by test.sh
is( undef, abort( ), 'abort: no args => undef' ) ;
note( 'Leaving tests_abort()' ) ;
return ;
@@ -3036,31 +3341,87 @@ sub abort
{
my $mysync = shift @ARG ;
+ myprint( "In abort\n" ) ;
if ( not $mysync ) { return ; }
if ( ! -r $mysync->{pidfile} ) {
- myprint( "Can not read pidfile $mysync->{pidfile}. Exiting.\n" ) ;
- exit $EX_OK ;
+ myprint( "In abort: Can not read pidfile $mysync->{pidfile}\n" ) ;
+ return ;
}
my $pidtokill = firstline( $mysync->{pidfile} ) ;
if ( ! $pidtokill ) {
- myprint( "No process to abort. Exiting.\n" ) ;
- exit $EX_OK ;
+ myprint( "In abort: No process to abort in $mysync->{pidfile}\n" ) ;
+ return ;
}
- killpid( $pidtokill ) ;
+ if ( ! match_a_pid_number( $pidtokill ) )
+ {
+ myprint( "In abort: pid $pidtokill in $mysync->{pidfile} is not a pid number\n" ) ;
+ return ;
+ }
- # well, the abort job is done anyway, because even when not succeeded
- # in aborting another run, this run has to end without doing any
- # thing else
- exit $EX_OK ;
+ if ( $mysync->{abortbyfile} )
+ {
+ abortbyfile( $mysync, $pidtokill ) ;
+ }
+ else
+ {
+ killpid( $pidtokill ) ;
+ }
+ return ;
+}
+
+sub abortbyfile
+{
+ my $mysync = shift @ARG ;
+ my $pidtokill = shift @ARG ;
+
+ my $abortfile = abortfile( $mysync, $pidtokill ) ;
+ myprint( "touching $abortfile\n" ) ;
+ touch( $abortfile ) ;
+ return ;
+}
+
+
+sub tests_under_docker_context
+{
+ note( 'Entering tests_under_docker_context()' ) ;
+
+ is( undef, under_docker_context( ), 'under_docker_context: no args => undef' ) ;
+
+ my $mysync = { } ;
+ $mysync->{ dockercontext } = 1 ;
+ is( 1, under_docker_context( $mysync ), 'under_docker_context: --dockercontext => 1' ) ;
+ $mysync->{ dockercontext } = 0 ;
+ is( 0, under_docker_context( $mysync ), 'under_docker_context: --nodockercontext => 0' ) ;
+
+ $mysync = { } ;
+ # Is not it a stupid test?
+ if ( under_docker_context( $mysync ) )
+ {
+ is( 1, under_docker_context( $mysync ), 'under_docker_context: docker context => 1' ) ;
+ }
+ else
+ {
+ is( 0, under_docker_context( $mysync ), 'under_docker_context: not docker context => 0' ) ;
+ }
+
+ note( 'Leaving tests_under_docker_context()' ) ;
+ return ;
}
sub under_docker_context
{
my $mysync = shift ;
+
+ if ( ! defined $mysync ) { return ; }
+
+ if ( defined $mysync->{ dockercontext } )
+ {
+ return( $mysync->{ dockercontext } ) ;
+ }
if ( -e '/.dockerenv' )
{
@@ -3075,27 +3436,35 @@ sub under_docker_context
}
-sub docker_context
+sub docker_context
{
- my $mysync = shift ;
-
- #-e '/.dockerenv' || return ;
+ my $mysync = shift ;
if ( ! under_docker_context( $mysync ) )
{
return ;
}
- $mysync->{ debug } and myprint( "Docker context detected with /.dockerenv\n" ) ;
- # No pidfile
- $mysync->{pidfile} = q{} ;
- # No log
- $mysync->{log} = 0 ;
- # In case
- $mysync->{ debug } and myprint( "Changing current directory to /var/tmp/\n" ) ;
- chdir '/var/tmp/' ;
+ output( $mysync, "Docker context detected with the file /.dockerenv\n" ) ;
+ # No pidfile by default
+
+ $mysync->{ pidfile } = defined( $mysync->{ pidfile } ) ? $mysync->{ pidfile } : q{} ;
+ # No log by default
+ if ( defined( $mysync->{ log } ) )
+ {
+ output( $mysync, "Logging in Docker context. Be sure you added access to it with a mount or similar. See https://docs.docker.com/storage/volumes/\n" ) ;
+ }
+ else
+ {
+ output( $mysync, "No log by default in Docker context. Use --log to trigger logging to the logfile.\n" ) ;
+ $mysync->{ log } = 0 ;
+ }
- return ;
+ # In case something is written relatively to .
+ output( $mysync, "Changing current directory to /var/tmp/\n" ) ;
+ chdir '/var/tmp/' ;
+
+ return ;
}
sub cgibegin
@@ -3151,7 +3520,7 @@ sub under_cgi_context
return ;
}
-sub cgibuildheader
+sub cgibuildheader
{
my $mysync = shift ;
if ( ! under_cgi_context( $mysync ) ) { return ; }
@@ -3166,7 +3535,7 @@ sub cgibuildheader
my $httpheader ;
if ( $mysync->{ abort } ) {
$httpheader = $mysync->{cgi}->header(
- -type => 'text/plain',
+ -type => 'text/plain; charset=UTF-8',
-status => '200 OK to abort syncing IMAP boxes' . ". Here is " . hostname(),
) ;
}elsif( $mysync->{ loaddelay } ) {
@@ -3174,7 +3543,7 @@ sub cgibuildheader
# 503 Service Unavailable
# The server is currently unable to handle the request due to a temporary overloading or maintenance of the server.
$httpheader = $mysync->{cgi}->header(
- -type => 'text/plain',
+ -type => 'text/plain; charset=UTF-8',
-status => '503 Service Unavailable' . ". Be back in $mysync->{ loaddelay } min. Load on " . hostname() . " is $mysync->{ loadavg }",
) ;
}else{
@@ -3306,40 +3675,56 @@ sub tests_umask
return ;
}
-sub cgisetcontext
+sub buggyflagsregex
{
- my $mysync = shift ;
- if ( ! under_cgi_context( $mysync ) ) { return ; }
+ # From /X analyse
+ # cut -d: -f1 Error_112_all_syncs.txt | xargs egrep -oih 'Invalid system flag [^( ]+' | sort | uniq -c | sort -g
+ my @buggyflagsregex = ( 's/\\\\RECEIPTCHECKED|\\\\Indexed|\\\\X-EON-HAS-ATTACHMENT|\\\\UNSEEN|\\\\ATTACHED|\\\\X-HAS-ATTACH|\\\\FORWARDED|\\\\FORWARD|\\\\X-FORWARDED|\\\\\$FORWARDED|\\\\PRIORITY|\\\\READRCPT//g' ) ;
+ return( @buggyflagsregex ) ;
+}
- output( $mysync, "Under cgi context\n" ) ;
- set_umask( $mysync ) ;
+sub cgisetcontext
+{
+ my $mysync = shift ;
+ if ( ! under_cgi_context( $mysync ) ) { return ; }
+
+ output( $mysync, "Under cgi context\n" ) ;
+
+
+ set_umask( $mysync ) ;
# Remove all content in unsafe evaled options
@{ $mysync->{ regextrans2 } } = ( ) ;
- @regexflag = ( ) ;
+
+ @{ $mysync->{ regexflag } } = buggyflagsregex( ) ;
+
@regexmess = ( ) ;
@skipmess = ( ) ;
@pipemess = ( ) ;
$delete2foldersonly = undef ;
$delete2foldersbutnot = undef ;
- $maxlinelengthcmd = undef ;
+ $maxlinelengthcmd = undef ;
- # Set safe default values (I hope...)
+ # Set safe default values (I hope...)
#$mysync->{pidfile} = 'imapsync.pid' ;
- $mysync->{pidfilelocking} = 1 ;
- $mysync->{errorsmax} = $ERRORS_MAX_CGI ;
- $modulesversion = 0 ;
- $mysync->{releasecheck} = defined $mysync->{releasecheck} ? $mysync->{releasecheck} : 1 ;
- $usecache = 0 ;
- $mysync->{showpasswords} = 0 ;
- $debugimap1 = $debugimap2 = $debugimap = 0 ;
- $reconnectretry1 = $reconnectretry2 = $DEFAULT_NB_RECONNECT_PER_IMAP_COMMAND ;
- $pipemesscheck = 0 ;
+ $mysync->{ pidfilelocking } = 1 ;
+ $mysync->{ errorsmax } = $ERRORS_MAX_CGI ;
+ $modulesversion = 0 ;
+ $mysync->{ releasecheck } = defined $mysync->{ releasecheck } ? $mysync->{ releasecheck } : 1 ;
+ $usecache = 0 ;
+ $mysync->{ showpasswords } = 0 ;
+ $mysync->{ acc1 }->{ debugimap } = 0 ;
+ $mysync->{ acc2 }->{ debugimap } = 0 ;
- $mysync->{hashfile} = $CGI_HASHFILE ;
- my $hashsynclocal = hashsynclocal( $mysync ) || die "Can not get hashsynclocal. Exiting\n" ;
+ $mysync->{ acc1 }->{ reconnectretry } = $DEFAULT_NB_RECONNECT_PER_IMAP_COMMAND ;
+ $mysync->{ acc2 }->{ reconnectretry } = $DEFAULT_NB_RECONNECT_PER_IMAP_COMMAND ;
+
+ $pipemesscheck = 0 ;
+
+ $mysync->{ hashfile } = $CGI_HASHFILE ;
+ my $hashsynclocal = hashsynclocal( $mysync ) || die "Can not get hashsynclocal. Exiting\n" ;
if ( $ENV{ 'NET_SERVER_SOFTWARE' } and ( $ENV{ 'NET_SERVER_SOFTWARE' } =~ /Net::Server::HTTP/ ) )
{
@@ -3352,7 +3737,8 @@ sub cgisetcontext
}
-d $cgidir or mkpath $cgidir or die "Can not create $cgidir: $OS_ERROR\n" ;
$mysync->{ tmpdir } = $cgidir ;
-
+ $mysync->{ logdir } = '' ;
+
chdir $cgidir or die "Can not cd to $cgidir: $OS_ERROR\n" ;
cgioutputenvcontext( $mysync ) ;
$mysync->{ debug } and output( $mysync, 'Current directory is ' . getcwd( ) . "\n" ) ;
@@ -3368,11 +3754,16 @@ sub cgisetcontext
$mysync->{ tail } = defined $mysync->{ tail } ? $mysync->{ tail } : 1 ;
# not sure it's for good
- @useheader = qw( Message-Id ) ;
+ @useheader = qw( Message-Id Received ) ;
# addheader on by default
$mysync->{ addheader } = defined $mysync->{ addheader } ? $mysync->{ addheader } : 1 ;
+
+ # sync duplicates by default in cgi context
+ $mysync->{ syncduplicates } = defined $mysync->{ syncduplicates } ? $mysync->{ syncduplicates } : 1 ;
+ # log the logfile name by default in cgi context
+ $mysync->{ loglogfile } = defined $mysync->{ loglogfile } ? $mysync->{ loglogfile } : 1 ;
return ;
}
@@ -3389,6 +3780,100 @@ sub cgioutputenvcontext
return ;
}
+sub announcelogfile
+{
+ my $mysync = shift ;
+
+ if ( $mysync->{ log } )
+ {
+ myprint( "Log file is $mysync->{ logfile } ( to change it, use --logfile path ; or use --nolog to turn off logging )\n" ) ;
+ loglogfile( $mysync ) ;
+ }
+ else
+ {
+ myprint( "No log file because of option --nolog\n" ) ;
+ }
+ return ;
+}
+
+
+sub loglogfile
+{
+ my $mysync = shift ;
+ if ( ! $mysync->{ loglogfile } ) { return ; }
+ if ( ! $mysync->{ log } ) { return ; }
+
+ my $cwd = getcwd( ) ;
+ my $absolutelogfilepath ;
+ # Fixme: add case when the logfile name is already absolute
+ $absolutelogfilepath = "$cwd/$mysync->{ logfile }" ;
+ my $loglogfilename = '../list_all_logs_auto.txt' ;
+ myprint( "Writing log file name $absolutelogfilepath to $loglogfilename\n" ) ;
+ if ( open( my $fh, '>>', $loglogfilename ) )
+ {
+ print $fh "$absolutelogfilepath\n" ;
+ close $fh ;
+ }
+ else
+ {
+ myprint( "Could not open loglogfile $loglogfilename $!\n" ) ;
+ }
+ return ;
+}
+
+
+sub checkselectable
+{
+ my $mysync = shift ;
+
+ if ( $mysync->{ checkselectable } ) {
+ my @h1_folders_wanted_selectable ;
+ myprint( "Host1: Checking wanted folders are selectable. Use --nocheckselectable to avoid this check.\n" ) ;
+ foreach my $folder ( @{ $mysync->{ h1_folders_wanted } } )
+ {
+ ( $mysync->{ debug } or $mysync->{ debugfolders } ) and myprint( "Checking $folder is selectable on host1\n" ) ;
+ # It does an imap command LIST "" $folder and then search for no \Noselect
+ if ( ! $mysync->{ imap1 }->selectable( $folder ) )
+ {
+ myprint( "Host1: warning! ignoring folder $folder because it is not selectable\n" ) ;
+ }else
+ {
+ push @h1_folders_wanted_selectable, $folder ;
+ }
+ }
+ @{ $mysync->{ h1_folders_wanted } } = @h1_folders_wanted_selectable ;
+ ( $mysync->{ debug } or $mysync->{ debugfolders } )
+ and myprint( 'Host1: checking folders took ', timenext( $mysync ), " s\n" ) ;
+ }
+ else
+ {
+ myprint( "Host1: Not checking that wanted folders are selectable. Use --checkselectable to force this check.\n" ) ;
+ }
+ return ;
+}
+
+sub setcheckselectable
+{
+ my $mysync = shift ;
+
+ my $h1_folders_wanted_nb = scalar @{ $mysync->{ h1_folders_wanted } } ;
+ # 152 because 98% of host1 accounts have less than 152 folders on /X service.
+ # command to get this value:
+ # datamash_file_op_index G_Host1_Nb_folders.txt perc:98 4 %16.1f
+ if ( ! defined $mysync->{ checkselectable } )
+ {
+ if ( 152 >= $h1_folders_wanted_nb )
+ {
+ $mysync->{ checkselectable } = 1 ;
+ }else{
+ myprint( "Host1: Not checking that $h1_folders_wanted_nb wanted folders are selectable. Use --checkselectable to force this check.\n" ) ;
+ $mysync->{ checkselectable } = 0 ;
+ }
+ }
+ return ;
+}
+
+
sub debugsleep
{
@@ -3416,7 +3901,6 @@ sub tests_foldersize
# Globals:
-# $uidnext_default
# $fetch_hash_set
#
sub foldersize
@@ -3450,6 +3934,7 @@ sub foldersize
my $biggest_in_folder = 0 ;
@{ $hash_ref }{ @msgs } = ( undef ) if @msgs ;
+
my $stot = 0 ;
if ( $imap->IsUnconnected( ) )
@@ -3467,8 +3952,7 @@ sub foldersize
}
else
{
- my $uidnext = $imap->uidnext( $folder ) || $uidnext_default ;
- my $fetch_hash_uids = $fetch_hash_set || "1:$uidnext" ;
+ my $fetch_hash_uids = $fetch_hash_set || "1:*" ;
if ( ! $imap->fetch_hash( $fetch_hash_uids, 'RFC822.SIZE', $hash_ref ) ) {
my $error = "$side failure with fetch_hash: $EVAL_ERROR\n" ;
errors_incr( $mysync, $error ) ;
@@ -3477,8 +3961,11 @@ sub foldersize
}
for ( keys %{ $hash_ref } ) {
my $size = $hash_ref->{ $_ }->{ 'RFC822.SIZE' } ;
- $stot += $size ;
- $biggest_in_folder = max( $biggest_in_folder, $size ) ;
+ if ( defined $size )
+ {
+ $stot += $size ;
+ $biggest_in_folder = max( $biggest_in_folder, $size ) ;
+ }
}
}
return( $stot, $nb_msgs, $biggest_in_folder ) ;
@@ -3717,6 +4204,66 @@ sub add
return $x + $y ;
}
+sub tests_checknoabletosearch
+{
+ note( 'Entering checknoabletosearch()' ) ;
+
+ is( undef, checknoabletosearch( ), 'checknoabletosearch: no args => undef' ) ;
+
+ note( 'Leaving checknoabletosearch()' ) ;
+ return ;
+}
+
+
+
+
+sub checknoabletosearch
+{
+ # call example: checknoabletosearch( $sync, $sync->{ imap1 }, 'INBOX', 'Host1' ) ;
+ # output:
+ # * undef if something is not ok to decide
+ # * 1 if SEARCH ALL failed
+
+ my( $mysync, $imap, $folder, $HostX ) = @ARG ;
+
+ if ( ! all_defined( $mysync, $imap, $folder, $HostX ) )
+ {
+ return ;
+ }
+
+ myprint( "$HostX: checking if SEARCH ALL works on $folder\n" ) ;
+ if ( ! select_folder( $mysync, $imap, $folder, $HostX ) )
+ {
+ myprint( "$HostX: can not SELECT folder [$folder]\n" ) ;
+ return ;
+ }
+ my $count_from_select = count_from_select( $imap->History ) ;
+ myprint( "$HostX: folder [$folder] has $count_from_select messages mentioned by SELECT\n" ) ;
+
+ my $msgs_all = $imap->messages( ) ;
+ if ( ! $msgs_all )
+ {
+ myprint( "$HostX: can not SEARCH ALL folder [$folder]\n" ) ;
+ myprint( "$HostX: ", $imap->LastError(), "\n" ) ;
+ return 1 ;
+ }
+
+ my $count_from_search_all = scalar( @{ $msgs_all } ) ;
+ myprint( "$HostX: folder [$folder] has $count_from_search_all messages found by SEARCH ALL\n" ) ;
+
+ if ( $count_from_select == $count_from_search_all )
+ {
+ myprint( "$HostX: folder [$folder] has the same messages count ($count_from_select) by SELECT and SEARCH ALL\n" ) ;
+ }
+ else
+ {
+ myprint( "$HostX: Warning, folder [$folder] has not the same count by SELECT ($count_from_select) and SEARCH ALL ($count_from_search_all)\n" ) ;
+ return 1 ;
+ }
+
+ return ;
+}
+
sub foldersizes_diff_list
{
@@ -3864,7 +4411,7 @@ END_SIZE
return ;
}
- my $h2_bytes_limit = $mysync->{h2}->{quota_limit_bytes} || 0 ;
+ my $h2_bytes_limit = $mysync->{ acc2 }->{quota_limit_bytes} || 0 ;
if ( $h2_bytes_limit and ( $h2_bytes_limit < $mysync->{ h1_bytes_start } ) )
{
my $quota_percent = mysprintf( '%.0f', $NUMBER_100 * $mysync->{ h1_bytes_start } / $h2_bytes_limit ) ;
@@ -3906,7 +4453,7 @@ END_SIZE
return ;
}
- my $h2_bytes_limit = $mysync->{h2}->{quota_limit_bytes} || 0 ;
+ my $h2_bytes_limit = $mysync->{ acc2 }->{quota_limit_bytes} || 0 ;
if ( $h2_bytes_limit and ( $h2_bytes_limit < $mysync->{ h1_bytes_start } ) )
{
my $quota_percent = mysprintf( '%.0f', $NUMBER_100 * $mysync->{ h1_bytes_start } / $h2_bytes_limit ) ;
@@ -3917,15 +4464,51 @@ END_SIZE
}
+sub tests_total_bytes_max_reached
+{
+ note( 'Entering tests_total_bytes_max_reached()' ) ;
+
+ is( undef, total_bytes_max_reached( ), 'total_bytes_max_reached: no args => undef' ) ;
+
+ my $mysync = {} ;
+ is( undef, total_bytes_max_reached( $mysync ), 'total_bytes_max_reached: no exitwhenover => undef' ) ;
+
+ $mysync->{ exitwhenover } = 300 ;
+ is( undef, total_bytes_max_reached( $mysync ), 'total_bytes_max_reached: exitwhenover 300 but no total_bytes_transferred => undef' ) ;
+
+ $mysync->{ total_bytes_transferred } = 200 ;
+ is( undef, total_bytes_max_reached( $mysync ), 'total_bytes_max_reached: exitwhenover 300 but total_bytes_transferred 200 => undef' ) ;
+
+ $mysync->{ total_bytes_transferred } = 400 ;
+ is( 1, total_bytes_max_reached( $mysync ), 'total_bytes_max_reached: exitwhenover 300 but total_bytes_transferred 400 => 1' ) ;
+
+
+
+ note( 'Leaving tests_total_bytes_max_reached()' ) ;
+ return ;
+}
+
+
sub total_bytes_max_reached
{
my $mysync = shift ;
- if ( ! $mysync->{ exitwhenover } ) {
- return( 0 ) ;
+ if ( ! defined $mysync ) { return ; }
+
+ if ( ! $mysync->{ exitwhenover } )
+ {
+ return ;
}
- if ( $mysync->{ total_bytes_transferred } >= $mysync->{ exitwhenover } ) {
- myprint( "Maximum bytes transferred reached, $mysync->{total_bytes_transferred} >= $mysync->{ exitwhenover }, ending sync\n" ) ;
+
+ if ( ! $mysync->{ total_bytes_transferred } )
+ {
+ return ;
+ }
+
+ if ( $mysync->{ total_bytes_transferred } >= $mysync->{ exitwhenover } )
+ {
+ my $error = "Maximum bytes transferred reached, $mysync->{total_bytes_transferred} >= $mysync->{ exitwhenover }, ending sync\n" ;
+ errors_incr( $mysync, $error ) ;
return( 1 ) ;
}
return ;
@@ -4166,7 +4749,7 @@ sub appendlimit_from_capability
#myprint( Data::Dumper->Dump( [ \$myimap ] ) ) ;
my $appendlimit = capability_of( $myimap, 'APPENDLIMIT' ) ;
#myprint( "has_capability APPENDLIMIT $appendlimit\n" ) ;
- if ( is_an_integer( $appendlimit ) )
+ if ( is_integer( $appendlimit ) )
{
return $appendlimit ;
}
@@ -4291,8 +4874,6 @@ sub tests_maxsize_setting
# Now --truncmess stuff
-
-
note( 'Leaving tests_maxsize_setting()' ) ;
return ;
@@ -4432,20 +5013,70 @@ sub hashsynclocal
sub tests_hashsync
{
- note( 'Entering tests_hashsync()' ) ;
+ note( 'Entering tests_hashsync()' ) ;
+
+ is( 'fbdb1d1b18aa6c08324b7d64b71fb76370690e1d', hashsync( ), 'hashsync: no args' ) ;
+
+ is( 'fbdb1d1b18aa6c08324b7d64b71fb76370690e1d', hashsync( {}, q{} ), 'hashsync: empty args' ) ;
+ my $mysync ;
+ $mysync->{ host1 } = 'zzz' ;
+ is( 'e86a28a3611c1e7bbaf8057cd00ae122781a11fe', hashsync( $mysync, q{} ), 'hashsync: host1 zzz => ' ) ;
+ is( '6a7b451ac99eab1531ad8e6cd544b32420c552ac', hashsync( $mysync, q{A} ), 'hashsync: host1 zzz => ' ) ;
+ $mysync->{ host2 } = 'zzz' ;
+ is( '15959573e4a86763253a7aedb1a2b0c60d133dc2', hashsync( $mysync, q{} ), 'hashsync: + host2 zzz => ' ) ;
+ is( 'b8d4ab541b209c75928528020ca28ee43488bd8f', hashsync( $mysync, 'A' ), 'hashsync: + hashkey A => ' ) ;
+
+ $mysync = undef ;
+ is( 'fbdb1d1b18aa6c08324b7d64b71fb76370690e1d', hashsync( $mysync, q{} ), 'hashsync: undef $mysync' ) ;
+ $mysync->{ password1 } = 'abcd' ;
+ is( 'afa29ab8534495251ac8346a985717c54bc49c26', hashsync( $mysync, q{} ), 'hashsync: password1: abcd' ) ;
+
+ # A user reported a massive failure on /X (Thomas V. 21/04/2020 Ã 21:41 Subject: Error)
+ # "Wide character in subroutine entry at /usr/local/lib/perl5/site_perl/Digest/HMAC.pm"
+ # I can reproduce it now
- is( 'fbdb1d1b18aa6c08324b7d64b71fb76370690e1d', hashsync( {}, q{} ), 'hashsync: empty args' ) ;
- my $mysync ;
- $mysync->{ host1 } = 'zzz' ;
- is( 'e86a28a3611c1e7bbaf8057cd00ae122781a11fe', hashsync( $mysync, q{} ), 'hashsync: host1 zzz => ' ) ;
- is( 'e86a28a3611c1e7bbaf8057cd00ae122781a11fe', hashsync( $mysync, q{} ), 'hashsync: host1 zzz => ' ) ;
- $mysync->{ host2 } = 'zzz' ;
- is( '15959573e4a86763253a7aedb1a2b0c60d133dc2', hashsync( $mysync, q{} ), 'hashsync: + host2 zzz => ' ) ;
- is( 'b8d4ab541b209c75928528020ca28ee43488bd8f', hashsync( $mysync, 'A' ), 'hashsync: + hashkey A => ' ) ;
+ # The eval is there to avoid a complete crash
+ # this one is fatal so it is commented
+ # is( 'f1a3f3dac3f137fd658027c11678b895f773ce55', 1 / 0 , 'hashsync: 1 / 0 fatal' ) ;
- note( 'Leaving tests_hashsync()' ) ;
- return ;
+ my $eval ;
+ # this one is not fatal
+ is( undef, $eval = eval { 1 / 0 } , 'hashsync: 1/0 not fatal' ) ;
+ # this one neither
+ $mysync->{ password1 } = 'Ö' ;
+ is( 'f1a3f3dac3f137fd658027c11678b895f773ce55', $eval = eval { hashsync( $mysync, q{} ) } , 'hashsync: password1: Ö with eval' ) ;
+
+ $mysync->{ password1 } = 'Ö' ;
+ is( 'f1a3f3dac3f137fd658027c11678b895f773ce55', hashsync( $mysync, q{} ), 'hashsync: password1: Ö without eval' ) ;
+
+ $mysync->{ password1 } = qq{\x{00D6}} ;
+ is( 'bb5bfb461e79ecd3dbc6ade2aabb52d22fa8be1a', $eval = eval { hashsync( $mysync, q{} ) }, 'hashsync: password1: \x{00D6}' ) ; #
+
+ print qq{1 00D6:Ö\n} ;
+ print encode_utf8( qq{2 00D6:Ö\n} ) ;
+ print qq{3 00D6:\x{00D6}\n} ;
+ print encode_utf8( qq{4 00D6:\x{00D6}\n} ) ;
+
+
+ print qq{5 6536:æ”¶\n} ;
+ print encode_utf8( qq{6 6536:æ”¶\n} ) ;
+ # the next one prints "Wide character in print at ./imapsync line xxxx"
+ print qq{7 6536:\x{6536}\n} ;
+ print encode_utf8( qq{8 6536:\x{6536}\n} ) ;
+
+ $mysync->{ password1 } = qq{æ”¶} ;
+ is( '4199f02773d1cd5599b1a8f2d024bdceb8b48e0b', hashsync( $mysync, q{} ), 'hashsync: password1: æ”¶' ) ;
+
+ $mysync->{ password1 } = qq{\x{6536}} ;
+ is( '4199f02773d1cd5599b1a8f2d024bdceb8b48e0b', $eval = eval{ hashsync( $mysync, q{} ) }, 'hashsync: password1: \x{6536} with eval' ) ;
+
+ # No side effect.
+ $mysync->{ password1 } = 'abcd' ;
+ is( 'afa29ab8534495251ac8346a985717c54bc49c26', hashsync( $mysync, q{} ), 'hashsync: password1: abcd again' ) ;
+
+ note( 'Leaving tests_hashsync()' ) ;
+ return ;
}
sub hashsync
@@ -4461,12 +5092,119 @@ sub hashsync
$mysync->{ user2 } || q{},
$mysync->{ password2 } || q{},
) ;
- my $hashsync = hmac_sha1_hex( $mystring, $hashkey ) ;
+ #my $hashsync = hmac_sha1_hex( $mystring, $hashkey ) ;
+ my $hashsync = hmac_sha1_hex_robust( $mystring, $hashkey ) ;
#myprint( "$hashsync\n" ) ;
return( $hashsync ) ;
}
+sub tests_hmac_sha1_hex
+{
+ note( 'Entering tests_hmac_sha1_hex()' ) ;
+
+ is( 'fbdb1d1b18aa6c08324b7d64b71fb76370690e1d', hmac_sha1_hex( ), 'hmac_sha1_hex: no args => fbdb1d1b18aa6c08324b7d64b71fb76370690e1d' ) ;
+ is( 'fbdb1d1b18aa6c08324b7d64b71fb76370690e1d', hmac_sha1_hex( '' ), 'hmac_sha1_hex: empty string => fbdb1d1b18aa6c08324b7d64b71fb76370690e1d' ) ;
+ is( 'fbdb1d1b18aa6c08324b7d64b71fb76370690e1d', hmac_sha1_hex( '', '' ), 'hmac_sha1_hex: empty strings => fbdb1d1b18aa6c08324b7d64b71fb76370690e1d' ) ;
+ is( 'fbdb1d1b18aa6c08324b7d64b71fb76370690e1d', hmac_sha1_hex( '', '', 'caca' ), 'hmac_sha1_hex: empty strings + caca => fbdb1d1b18aa6c08324b7d64b71fb76370690e1d' ) ;
+
+ # Good
+ is( 'f1a3f3dac3f137fd658027c11678b895f773ce55', hmac_sha1_hex( 'Ö' ), 'hmac_sha1_hex: Ö => f1a3f3dac3f137fd658027c11678b895f773ce55' ) ;
+ is( 'f1a3f3dac3f137fd658027c11678b895f773ce55', hmac_sha1_hex( encode_utf8(qq{\x{00D6}}) ), 'hmac_sha1_hex: encode_utf8 \x{00D6} => f1a3f3dac3f137fd658027c11678b895f773ce55' ) ;
+ # Bad
+ is( 'fe8dc3b9ba3e8850bb4a7b070b2279e911003af2', hmac_sha1_hex( encode_utf8( 'Ö' ) ), 'hmac_sha1_hex: encode_utf8 Ö => fe8dc3b9ba3e8850bb4a7b070b2279e911003af2' ) ;
+ is( 'bb5bfb461e79ecd3dbc6ade2aabb52d22fa8be1a', hmac_sha1_hex( qq{\x{00D6}} ), 'hmac_sha1_hex: qq{\x{00D6}} => bb5bfb461e79ecd3dbc6ade2aabb52d22fa8be1a' ) ;
+
+ # Good
+ is( 'a6fda2a6acdd74630b20aac0c68716048ecd0333', hmac_sha1_hex( 'A' ), 'hmac_sha1_hex: A => a6fda2a6acdd74630b20aac0c68716048ecd0333' ) ;
+ is( 'a6fda2a6acdd74630b20aac0c68716048ecd0333', hmac_sha1_hex( encode_utf8(qq{\x{0041}}) ), 'hmac_sha1_hex: encode_utf8 \x{0041} => a6fda2a6acdd74630b20aac0c68716048ecd0333' ) ;
+ is( 'a6fda2a6acdd74630b20aac0c68716048ecd0333', hmac_sha1_hex( encode_utf8( 'A' ) ), 'hmac_sha1_hex: encode_utf8 A => a6fda2a6acdd74630b20aac0c68716048ecd0333' ) ;
+ is( 'a6fda2a6acdd74630b20aac0c68716048ecd0333', hmac_sha1_hex( qq{\x{0041}} ), 'hmac_sha1_hex: \x{0041} => a6fda2a6acdd74630b20aac0c68716048ecd0333' ) ;
+
+ # Good
+ is( '36c54f255b575a2db58921d116b37c8af94c08cd', hmac_sha1_hex( 'A', 'B' ), 'hmac_sha1_hex: A B => 36c54f255b575a2db58921d116b37c8af94c08cd' ) ;
+ is( '36c54f255b575a2db58921d116b37c8af94c08cd', hmac_sha1_hex( encode_utf8(qq{\x{0041}}), 'B' ), 'hmac_sha1_hex: encode_utf8 \x{0041} B => 36c54f255b575a2db58921d116b37c8af94c08cd' ) ;
+ is( '36c54f255b575a2db58921d116b37c8af94c08cd', hmac_sha1_hex( encode_utf8( 'A' ), 'B' ), 'hmac_sha1_hex: encode_utf8 A B => 36c54f255b575a2db58921d116b37c8af94c08cd' ) ;
+ is( '36c54f255b575a2db58921d116b37c8af94c08cd', hmac_sha1_hex( qq{\x{0041}}, 'B' ), 'hmac_sha1_hex: \x{0041} B => 36c54f255b575a2db58921d116b37c8af94c08cd' ) ;
+
+ # http://unicode.scarfboy.com/?s=U%2B6536
+ # Good
+ is( '4199f02773d1cd5599b1a8f2d024bdceb8b48e0b', hmac_sha1_hex( 'æ”¶' ), 'hmac_sha1_hex: æ”¶ => 4199f02773d1cd5599b1a8f2d024bdceb8b48e0b' ) ;
+ is( '4199f02773d1cd5599b1a8f2d024bdceb8b48e0b', hmac_sha1_hex( encode_utf8(qq{\x{6536}}) ), 'hmac_sha1_hex: encode_utf8 \x{6536} => 4199f02773d1cd5599b1a8f2d024bdceb8b48e0b' ) ;
+ # Bad
+ is( 'e82217119628ad03e659cc89671d05ea4cee7238', hmac_sha1_hex( encode_utf8( 'æ”¶' ) ), 'hmac_sha1_hex: encode_utf8 æ”¶ => e82217119628ad03e659cc89671d05ea4cee7238' ) ;
+ # Very very bad, perl dies...
+ #is( '4199f02773d1cd5599b1a8f2d024bdceb8b48e0b', hmac_sha1_hex( qq{\x{6536}} ), 'hmac_sha1_hex: \x{6536} => 4199f02773d1cd5599b1a8f2d024bdceb8b48e0b' ) ;
+ # Ok but well, bad indeed
+ is( undef, my $eval = eval{ hmac_sha1_hex( qq{\x{6536}} ) }, 'hmac_sha1_hex: \x{6536} => undef' ) ;
+
+
+ note( 'Leaving tests_hmac_sha1_hex()' ) ;
+ return ;
+}
+
+sub tests_hmac_sha1_hex_robust
+{
+ note( 'Entering tests_hmac_sha1_hex_robust()' ) ;
+
+ is( 'fbdb1d1b18aa6c08324b7d64b71fb76370690e1d', hmac_sha1_hex_robust( ), 'hmac_sha1_hex_robust: no args => fbdb1d1b18aa6c08324b7d64b71fb76370690e1d' ) ;
+ is( 'fbdb1d1b18aa6c08324b7d64b71fb76370690e1d', hmac_sha1_hex_robust( '' ), 'hmac_sha1_hex_robust: empty string => fbdb1d1b18aa6c08324b7d64b71fb76370690e1d' ) ;
+ is( 'fbdb1d1b18aa6c08324b7d64b71fb76370690e1d', hmac_sha1_hex_robust( '', '' ), 'hmac_sha1_hex_robust: empty strings => fbdb1d1b18aa6c08324b7d64b71fb76370690e1d' ) ;
+ is( 'fbdb1d1b18aa6c08324b7d64b71fb76370690e1d', hmac_sha1_hex_robust( '', '', 'caca' ), 'hmac_sha1_hex_robust: empty strings + caca => fbdb1d1b18aa6c08324b7d64b71fb76370690e1d' ) ;
+
+ # Good
+ is( 'f1a3f3dac3f137fd658027c11678b895f773ce55', hmac_sha1_hex_robust( 'Ö' ), 'hmac_sha1_hex_robust: Ö => f1a3f3dac3f137fd658027c11678b895f773ce55' ) ;
+ is( 'f1a3f3dac3f137fd658027c11678b895f773ce55', hmac_sha1_hex_robust( encode_utf8(qq{\x{00D6}}) ), 'hmac_sha1_hex_robust: encode_utf8 \x{00D6} => f1a3f3dac3f137fd658027c11678b895f773ce55' ) ;
+ # Bad
+ is( 'fe8dc3b9ba3e8850bb4a7b070b2279e911003af2', hmac_sha1_hex_robust( encode_utf8( 'Ö' ) ), 'hmac_sha1_hex_robust: encode_utf8 Ö => fe8dc3b9ba3e8850bb4a7b070b2279e911003af2' ) ;
+ is( 'bb5bfb461e79ecd3dbc6ade2aabb52d22fa8be1a', hmac_sha1_hex_robust( qq{\x{00D6}} ), 'hmac_sha1_hex_robust: qq{\x{00D6}} => bb5bfb461e79ecd3dbc6ade2aabb52d22fa8be1a' ) ;
+
+ # Good
+ is( 'a6fda2a6acdd74630b20aac0c68716048ecd0333', hmac_sha1_hex_robust( 'A' ), 'hmac_sha1_hex_robust: A => a6fda2a6acdd74630b20aac0c68716048ecd0333' ) ;
+ is( 'a6fda2a6acdd74630b20aac0c68716048ecd0333', hmac_sha1_hex_robust( encode_utf8(qq{\x{0041}}) ), 'hmac_sha1_hex_robust: encode_utf8 \x{0041} => a6fda2a6acdd74630b20aac0c68716048ecd0333' ) ;
+ is( 'a6fda2a6acdd74630b20aac0c68716048ecd0333', hmac_sha1_hex_robust( encode_utf8( 'A' ) ), 'hmac_sha1_hex_robust: encode_utf8 A => a6fda2a6acdd74630b20aac0c68716048ecd0333' ) ;
+ is( 'a6fda2a6acdd74630b20aac0c68716048ecd0333', hmac_sha1_hex_robust( qq{\x{0041}} ), 'hmac_sha1_hex_robust: \x{0041} => a6fda2a6acdd74630b20aac0c68716048ecd0333' ) ;
+
+ # Good
+ is( '36c54f255b575a2db58921d116b37c8af94c08cd', hmac_sha1_hex_robust( 'A', 'B' ), 'hmac_sha1_hex_robust: A B => 36c54f255b575a2db58921d116b37c8af94c08cd' ) ;
+ is( '36c54f255b575a2db58921d116b37c8af94c08cd', hmac_sha1_hex_robust( encode_utf8(qq{\x{0041}}), 'B' ), 'hmac_sha1_hex_robust: encode_utf8 \x{0041} B => 36c54f255b575a2db58921d116b37c8af94c08cd' ) ;
+ is( '36c54f255b575a2db58921d116b37c8af94c08cd', hmac_sha1_hex_robust( encode_utf8( 'A' ), 'B' ), 'hmac_sha1_hex_robust: encode_utf8 A B => 36c54f255b575a2db58921d116b37c8af94c08cd' ) ;
+ is( '36c54f255b575a2db58921d116b37c8af94c08cd', hmac_sha1_hex_robust( qq{\x{0041}}, 'B' ), 'hmac_sha1_hex_robust: \x{0041} B => 36c54f255b575a2db58921d116b37c8af94c08cd' ) ;
+
+ # http://unicode.scarfboy.com/?s=U%2B6536
+ # Good
+ is( '4199f02773d1cd5599b1a8f2d024bdceb8b48e0b', hmac_sha1_hex_robust( 'æ”¶' ), 'hmac_sha1_hex_robust: æ”¶ => 4199f02773d1cd5599b1a8f2d024bdceb8b48e0b' ) ;
+ is( '4199f02773d1cd5599b1a8f2d024bdceb8b48e0b', hmac_sha1_hex_robust( encode_utf8(qq{\x{6536}}) ), 'hmac_sha1_hex_robust: encode_utf8 \x{6536} => 4199f02773d1cd5599b1a8f2d024bdceb8b48e0b' ) ;
+ # Bad
+ is( 'e82217119628ad03e659cc89671d05ea4cee7238', hmac_sha1_hex_robust( encode_utf8( 'æ”¶' ) ), 'hmac_sha1_hex_robust: encode_utf8 æ”¶ => e82217119628ad03e659cc89671d05ea4cee7238' ) ;
+ # Good
+ is( '4199f02773d1cd5599b1a8f2d024bdceb8b48e0b', hmac_sha1_hex_robust( qq{\x{6536}} ), 'hmac_sha1_hex_robust: \x{6536} => 4199f02773d1cd5599b1a8f2d024bdceb8b48e0b' ) ;
+ # Good again
+ is( '4199f02773d1cd5599b1a8f2d024bdceb8b48e0b', my $eval = eval{ hmac_sha1_hex_robust( qq{\x{6536}} ) }, 'hmac_sha1_hex_robust: \x{6536} => undef' ) ;
+
+ note( 'Leaving tests_hmac_sha1_hex_robust()' ) ;
+ return ;
+}
+
+
+sub hmac_sha1_hex_robust
+{
+ my $string = shift ;
+ my $val ;
+ if ( defined( $val = eval{ hmac_sha1_hex( $string, @ARG ) } ) )
+ {
+ return $val ;
+ }
+ elsif( defined( $val = eval{ hmac_sha1_hex( encode_utf8( $string ), @ARG ) } ) )
+ {
+ return $val ;
+ }
+ else
+ {
+ return ;
+ }
+}
+
sub tests_createhashfileifneeded
{
note( 'Entering tests_createhashfileifneeded()' ) ;
@@ -4588,7 +5326,7 @@ sub imapsync_id
vendor => 'Gilles LAMIRAL',
'support-url' => 'https://imapsync.lamiral.info/',
# Example of date-time: 19-Sep-2015 08:56:07
- date => date_from_rcs( q{$Date: 2019/12/23 20:18:02 $ } ),
+ date => date_from_rcs( q{$Date: 2021/07/22 14:21:09 $ } ),
} ;
my $imapsync_id_github = {
@@ -4597,7 +5335,7 @@ sub imapsync_id
os => $OSNAME,
vendor => 'github',
'support-url' => 'https://github.com/imapsync/imapsync',
- date => date_from_rcs( q{$Date: 2019/12/23 20:18:02 $ } ),
+ date => date_from_rcs( q{$Date: 2021/07/22 14:21:09 $ } ),
} ;
$imapsync_id = $imapsync_id_lamiral ;
@@ -5003,12 +5741,16 @@ sub errors_incr
$mysync->{errorsmax} ||= $ERRORS_MAX ;
if ( $mysync->{nb_errors} >= $mysync->{errorsmax} ) {
myprint( "Maximum number of errors $mysync->{errorsmax} reached ( you can change $mysync->{errorsmax} to any value, for example 100 with --errorsmax 100 ). Exiting.\n" ) ;
+ my $most_common_error = errorsanalyse( errors_log( $mysync ) ) ;
if ( $mysync->{errorsdump} ) {
- myprint( errorsdump( $mysync->{nb_errors}, errors_log( $mysync ) ) ) ;
+ myprint( errorsdump( errors_log( $mysync ) ) ) ;
+ myprint( "The most frequent error is $most_common_error\n" ) ;
# again since errorsdump( ) can be very verbose and masquerade previous warning
myprint( "Maximum number of errors $mysync->{errorsmax} reached ( you can change $mysync->{errorsmax} to any value, for example 100 with --errorsmax 100 ). Exiting.\n" ) ;
}
- exit_clean( $mysync, $EXIT_WITH_ERRORS_MAX ) ;
+ my $exit_value = $EXIT_VALUE_OF_ERR_TYPE{ $most_common_error } || $EXIT_CATCH_ALL ;
+ #exit_clean( $mysync, $EXIT_WITH_ERRORS_MAX ) ;
+ exit_clean( $mysync, $exit_value ) ;
}
return ;
}
@@ -5048,14 +5790,288 @@ sub errors_log
}
+sub tests_error_type
+{
+ note( 'Entering tests_error_type()' ) ;
+
+ is( 'ERR_NOTHING_REPORTED', error_type( ), 'error_type: no args => ERR_NOTHING_REPORTED' ) ;
+ is( 'ERR_NOTHING_REPORTED', error_type( '' ), 'error_type: empty string => ERR_NOTHING_REPORTED' ) ;
+
+ is( 'ERR_UNCLASSIFIED', error_type( 'ERR_UNCLASSIFIED' ), 'error_type: ERR_UNCLASSIFIED => ERR_UNCLASSIFIED' ) ;
+ is( 'ERR_UNCLASSIFIED', error_type( 'aie' ), 'error_type: aie => ERR_UNCLASSIFIED' ) ;
+ is( 'ERR_UNCLASSIFIED', error_type( 'ouille' ), 'error_type: ouille => ERR_UNCLASSIFIED' ) ;
+
+ is( 'ERR_Host1_FETCH', error_type( 'Message xxx could not be fetched: blabla' ),
+ 'error_type: could not be fetched => ERR_Host1_FETCH'
+ ) ;
+
+ is( 'ERR_APPEND_SIZE',
+ error_type( 'could not append message xxx: BAD maximum message size exceeded' ),
+ 'error_type: could not append message xxx: BAD maximum message size exceeded => ERR_APPEND_SIZE'
+ ) ;
+
+ is( 'ERR_OVERQUOTA',
+ error_type( 'Quota limit will be exceeded' ),
+ 'error_type: Quota limit will be exceeded => ERR_OVERQUOTA'
+ ) ;
+
+ is( 'ERR_APPEND', error_type( 'could not append' ), 'error_type: could not append => ERR_APPEND' ) ;
+
+ is( 'ERR_CREATE',
+ error_type( 'Could not create folder' ),
+ 'error_type: Could not create folder => ERR_CREATE'
+ ) ;
+
+ is( 'ERR_SELECT',
+ error_type( 'Could not select: blabla' ),
+ 'error_type: Could not select: blabla => ERR_SELECT'
+ ) ;
+
+
+ #
+ #Maximum bytes transferred reached, 423 >= 100, ending sync
+ is( 'ERR_TRANSFER_EXCEEDED',
+ error_type( 'Maximum bytes transferred reached, blabla' ),
+ 'error_type: Maximum bytes transferred reached, blabla => ERR_TRANSFER_EXCEEDED'
+ ) ;
+
+ #
+ is( 'ERR_CONNECTION_FAILURE_HOST1',
+ error_type( 'Host1 failure: can not open imap connection on host1 [badhostkaka] with user [tata]: Unable to connect to badhostkaka: Invalid argument' ),
+ 'error_type: can not open imap connection on host1 => ERR_CONNECTION_FAILURE_HOST1'
+ ) ;
+
+ is( 'ERR_CONNECTION_FAILURE_HOST2',
+ error_type( 'Host2 failure: can not open imap connection on host2 [badhostkiki] with user [titi]: Unable to connect to badhostkiki: Invalid argument' ),
+ 'error_type: can not open imap connection on host2 => ERR_CONNECTION_FAILURE_HOST2'
+ ) ;
+
+ is( 'ERR_APPEND_VIRUS',
+ error_type( 'could not append ( Subject:[For Your Consideration], Date:["29-Nov-2016 03:21:10 -0800"], Size:[5505], Flags:[\Seen] ) to folder INBOX: 275 NO Message refused because it contains a virus' ),
+ 'error_type: could not append ... virus => ERR_APPEND_VIRUS'
+ ) ;
+
+ note( 'Leaving tests_error_type()' ) ;
+ return ;
+}
+
+
+
+# Could be implemented with https://metacpan.org/pod/Tie::RegexpHash
+# with just a hash of error regexes as keys and types as values.
+
+sub error_type
+{
+ my $error = shift ;
+
+ if ( ! defined $error ) { return 'ERR_NOTHING_REPORTED' ; }
+ if ( ! $error ) { return 'ERR_NOTHING_REPORTED' ; }
+
+ #
+ if ( $error =~ m{Host1 failure: Error login on} ) { return 'ERR_AUTHENTICATION_FAILURE_USER1' } ;
+ if ( $error =~ m{Host2 failure: Error login on} ) { return 'ERR_AUTHENTICATION_FAILURE_USER2' } ;
+
+ if ( $error =~ m{Host. failure: Can not go to tls encryption on host.} ) { return 'ERR_EXIT_TLS_FAILURE' } ;
+ #
+
+ if ( $error =~ m{could not be fetched:} ) { return 'ERR_Host1_FETCH' } ;
+
+ # could not append .*BAD maximum message size exceeded
+ # could not append.*Maximum size of appendable message has been exceeded
+ if ( $error =~ m{could not append .*BAD maximum message size exceeded} )
+ { return 'ERR_APPEND_SIZE' ; } ;
+
+ if ( $error =~ m{could not append.*Maximum size of appendable message has been exceeded} )
+ { return 'ERR_APPEND_SIZE' ; } ;
+
+ # Could not create folder *[OVERQUOTA] Not enough disk quota
+ # could not append .*[OVERQUOTA] Not enough disk quota
+ # could not append .*[OVERQUOTA] Mailbox is full / Blocks limit exceeded / Inode limit exceeded
+ if ( $error =~ m{OVERQUOTA} ) { return 'ERR_OVERQUOTA' ; } ;
+ if ( $error =~ m{Quota limit will be exceeded} ) { return 'ERR_OVERQUOTA' ; } ;
+ if ( $error =~ m{full: it is time to find a bigger place} ) { return 'ERR_OVERQUOTA' ; } ;
+
+ # could not append ... to folder INBOX: 276 NO Message refused because it contains a virus
+ if ( $error =~ m{could not append.*virus} )
+ { return 'ERR_APPEND_VIRUS' ; } ;
+
+ # could not append .*Write failed 'Broken pipe'
+ # could not append .*timeout waiting .* for data from server
+ # could not append .*BAD Invalid Arguments: Unable to parse message
+ # could not append .*BAD Command Argument Error. 11
+ # could not append .*NO header limit reached
+ if ( $error =~ m{could not append} ) { return 'ERR_APPEND' ; } ;
+
+ # Could not create folder .*Invalid mailbox name
+ if ( $error =~ m{Could not create folder} ) { return 'ERR_CREATE' ; } ;
+
+
+ # Could not select:.*NO [NOPERM] Permission denied
+ # Could not select:.*NO Mailbox doesn't exist
+ # Could not select:.*NO [SERVERBUG] Internal error occurred.
+ # Could not select:.*[CANNOT] Mailbox isn't a valid mbox file
+ if ( $error =~ m{Could not select:} ) { return 'ERR_SELECT' ; } ;
+
+ #Maximum bytes transferred reached, 423 >= 100, ending sync
+ if ( $error =~ m{Maximum bytes transferred reached} ) { return 'ERR_TRANSFER_EXCEEDED' ; } ;
+
+ if ( $error =~ m{can not open imap connection on host1} ) { return 'ERR_CONNECTION_FAILURE_HOST1' ; } ;
+ if ( $error =~ m{can not open imap connection on host2} ) { return 'ERR_CONNECTION_FAILURE_HOST2' ; } ;
+
+ # Default is ERR_UNCLASSIFIED
+ return 'ERR_UNCLASSIFIED' ;
+
+}
+
+sub tests_errorclassify
+{
+ note( 'Entering tests_errorclassify()' ) ;
+
+ is( undef, errorclassify( ), 'errorclassify: no args => undef' ) ;
+
+ is_deeply( { 'ERR_UNCLASSIFIED' => 1 }, errorclassify( 'aie' ), 'errorclassify: aie => { ERR_UNCLASSIFIED => 1 }' ) ;
+ is_deeply( { 'ERR_UNCLASSIFIED' => 2 }, errorclassify( 'aie', 'ouille' ), 'errorclassify: aie ouille => { ERR_UNCLASSIFIED => 2 }' ) ;
+ is_deeply( { 'ERR_UNCLASSIFIED' => 2, 'ERR_NOTHING_REPORTED' => 1 }, errorclassify( 'aie', 'ouille', '' ), 'errorclassify: aie ouille "" => { ERR_UNCLASSIFIED => 2 }' ) ;
+ is_deeply( { 'ERR_UNCLASSIFIED' => 3 }, errorclassify( 'aie', 'ouille', 'aie' ), 'errorclassify: aie ouille aie => { ERR_UNCLASSIFIED => 3 }' ) ;
+ is_deeply( { 'ERR_UNCLASSIFIED' => 1, 'ERR_OVERQUOTA' => 2 }, errorclassify( 'aie', 'OVERQUOTA pipi', 'OVERQUOTA caca' ), 'errorclassify: aie OVERQUOTA OVERQUOTA' ) ;
+ is_deeply( { 'ERR_NOTHING_REPORTED' => 1 }, errorclassify( '' ), 'errorclassify: "" => { ERR_NOTHING_REPORTED => 1 }' ) ;
+ is_deeply( { 'ERR_NOTHING_REPORTED' => 2 }, errorclassify( '', '' ), 'errorclassify: "", "" => { ERR_NOTHING_REPORTED => 1 }' ) ;
+
+ note( 'Leaving tests_errorclassify()' ) ;
+ return ;
+}
+
+
+
+sub errorclassify
+{
+ my @errors = @ARG ;
+
+ if ( ! @errors ) { return ; } ;
+
+ my $error_type_count = { } ;
+ foreach my $error ( @errors )
+ {
+ my $error_type = error_type( $error ) ;
+ $error_type_count->{ $error_type }++ ;
+ }
+
+ return $error_type_count ;
+}
+
+sub tests_most_common_error
+{
+ note( 'Entering tests_most_common_error()' ) ;
+
+ is( 'ERR_NOTHING_REPORTED', most_common_error( ), 'most_common_error: no args => ERR_NOTHING_REPORTED' ) ;
+ is( 'ERR_NOTHING_REPORTED', most_common_error( {} ), 'most_common_error: empty hash ref => ERR_NOTHING_REPORTED' ) ;
+ is( 'ERR_NOTHING_REPORTED', most_common_error( 'blabla' ), 'most_common_error: not a hash ref => ERR_NOTHING_REPORTED' ) ;
+
+ is( 'ERR_FOO', most_common_error( { ERR_FOO => 1 } ), 'most_common_error: { ERR_FOO => 1 } => ERR_FOO' ) ;
+ is( 'ERR_BAR', most_common_error( { ERR_FOO => 1, ERR_BAR => 2 } ), 'most_common_error: { ERR_FOO => 1, ERR_BAR => 2 } => ERR_BAR' ) ;
+ is( 'ERR_FOO', most_common_error( { ERR_FOO => 2, ERR_BAR => 1 } ), 'most_common_error: { ERR_FOO => 2, ERR_BAR => 1 } => ERR_FOO' ) ;
+ # exaequo => first lexical wins. ERR_BAR <= ERR_FOO
+ is( 'ERR_BAR', most_common_error( { ERR_FOO => 2, ERR_BAR => 2 } ), 'most_common_error: { ERR_FOO => 2, ERR_BAR => 2 } => ERR_BAR' ) ;
+
+ is( 'A', most_common_error( { A => 5, B => 5, C => 5 } ), 'most_common_error: { A => 5, B => 5, C => 5 } => A' ) ;
+ is( 'B', most_common_error( { A => 5, B => 6, C => 6 } ), 'most_common_error: { A => 5, B => 6, C => 6 } => B' ) ;
+ is( 'C', most_common_error( { A => 5, B => 5, C => 7 } ), 'most_common_error: { A => 5, B => 5, C => 7 } => C' ) ;
+ is( 'C', most_common_error( { A => 5, B => 6, C => 7 } ), 'most_common_error: { A => 5, B => 5, C => 7 } => C' ) ;
+
+ note( 'Leaving tests_most_common_error()' ) ;
+ return ;
+}
+
+
+
+sub most_common_error
+{
+ my $errors_counted_ref = shift ;
+
+ if ( ! defined $errors_counted_ref ) { return 'ERR_NOTHING_REPORTED' ; }
+
+ if ( 'HASH' ne ref $errors_counted_ref ) { return 'ERR_NOTHING_REPORTED' ; }
+
+ # empty hash
+ if ( !%{ $errors_counted_ref } ) { return 'ERR_NOTHING_REPORTED' ; }
+
+ # non empty hash
+ my $most_common_error = ( sort
+ {
+ $errors_counted_ref->{$b} <=> $errors_counted_ref->{$a}
+ || $a cmp $b
+ } keys %{$errors_counted_ref} )[0] ;
+
+ return $most_common_error ;
+
+}
+
+
+
+sub tests_errorsanalyse
+{
+ note( 'Entering tests_errorsanalyse()' ) ;
+
+ is( 'ERR_NOTHING_REPORTED', errorsanalyse( ), 'errorsanalyse: no args => ERR_NOTHING_REPORTED' ) ;
+ is( 'ERR_NOTHING_REPORTED', errorsanalyse( ( ) ), 'errorsanalyse: empty list => ERR_NOTHING_REPORTED' ) ;
+ is( 'ERR_UNCLASSIFIED', errorsanalyse( 'aie' ), 'errorsanalyse: aie => ERR_UNCLASSIFIED' ) ;
+
+ # in case of equality, empty wins
+ is( 'ERR_NOTHING_REPORTED', errorsanalyse( 'aie', '' ), 'errorsanalyse: aie => ERR_UNCLASSIFIED' ) ;
+ is( 'ERR_NOTHING_REPORTED', errorsanalyse( '', 'aie' ), 'errorsanalyse: aie => ERR_UNCLASSIFIED' ) ;
+
+
+ is( 'ERR_UNCLASSIFIED', errorsanalyse( 'aie', 'ouille' ), 'errorsanalyse: aie, ouille => ERR_UNCLASSIFIED' ) ;
+ is( 'ERR_UNCLASSIFIED', errorsanalyse( 'aie', 'ouille', '' ), 'errorsanalyse: aie, ouille, "" => ERR_UNCLASSIFIED' ) ;
+ is( 'ERR_UNCLASSIFIED', errorsanalyse( '', 'aie', 'ouille' ), 'errorsanalyse: aie, ouille, "" => ERR_UNCLASSIFIED' ) ;
+
+ is( 'ERR_NOTHING_REPORTED', errorsanalyse( '' ), 'errorsanalyse: "" => ERR_NOTHING_REPORTED' ) ;
+ is( 'ERR_NOTHING_REPORTED', errorsanalyse( ( '' ) ), 'errorsanalyse: ( "" ) => ERR_NOTHING_REPORTED' ) ;
+ is( 'ERR_NOTHING_REPORTED', errorsanalyse( ( '', '' ) ), 'errorsanalyse: ( "", "" ) => ERR_NOTHING_REPORTED' ) ;
+
+ note( 'Leaving tests_errorsanalyse()' ) ;
+ return ;
+}
+
+
+
+sub errorsanalyse
+{
+ my @errors = @ARG ;
+ my $errors_types_counted = errorclassify( @errors ) ;
+
+ my $most_common_error = most_common_error( $errors_types_counted ) ;
+
+ return $most_common_error ;
+}
+
+
+
+sub tests_errorsdump
+{
+ note( 'Entering tests_errorsdump()' ) ;
+
+ is( undef, errorsdump( ), 'errorsdump: no args => undef' ) ;
+ is( undef, errorsdump( ( ) ), 'errorsdump: empty list => undef' ) ;
+ is( "Err 1/1: ", errorsdump( '' ), 'errorsdump: one empty string => "Err 1/1: "' ) ;
+ is( "Err 1/1: aieaieaie", errorsdump( 'aieaieaie' ), 'errorsdump: aieaieaie => "Err 1/1: aieaieaie"' ) ;
+ is( "Err 1/2: Aie Err 2/2: Ouille", errorsdump( 'Aie ', 'Ouille' ), 'errorsdump: Aie Ouille => "Err 1/2: Aie Err 2/2: Ouille"' ) ;
+ note( 'Leaving tests_errorsdump()' ) ;
+ return ;
+}
+
+
sub errorsdump
{
- my( $nb_errors, @errors_log ) = @ARG ;
+ if ( ! @ARG ) { return ; }
+
+ my @errors_log = @ARG ;
+ my $nb_errors = @errors_log ;
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 ) {
+ foreach my $error ( @errors_log )
+ {
$error_num++ ;
$errors_list .= "Err $error_num/$nb_errors: $error" ;
}
@@ -5064,9 +6080,26 @@ sub errorsdump
}
+
+sub errors_listing
+{
+ my $mysync = shift ;
+ $mysync->{most_common_error} = errorsanalyse( errors_log( $sync ) ) ;
+
+ my $errors_listing = join( '',
+ "++++ Listing $mysync->{nb_errors} errors encountered during the sync ( avoid this listing with --noerrorsdump ).\n",
+ errorsdump( errors_log( $mysync ) ),
+ "The most frequent error is $mysync->{most_common_error}\n",
+ ) ;
+ return $errors_listing ;
+}
+
+
+
+
sub tests_live_result
{
- note( 'Entering tests_live_result()' ) ;
+ note( 'Entering tests_live_result()' ) ;
my $nb_errors = shift ;
if ( $nb_errors ) {
@@ -5074,7 +6107,7 @@ sub tests_live_result
} else {
myprint( "Live tests ended successfully\n" ) ;
}
- note( 'Leaving tests_live_result()' ) ;
+ note( 'Leaving tests_live_result()' ) ;
return ;
}
@@ -5140,7 +6173,7 @@ sub sync_flags
( $mysync->{ debug } or $debugflags ) and
myprint( "Host1: flags init msg $h1_fold/$h1_msg flags( $h1_flags ) Host2 msg $h2_fold/$h2_msg flags( $h2_flags )\n" ) ;
- $h1_flags = flags_for_host2( $h1_flags, $permanentflags2 ) ;
+ $h1_flags = flags_for_host2( $mysync, $h1_flags, $permanentflags2 ) ;
$h2_flags = flagscase( $h2_flags ) ;
@@ -5195,7 +6228,8 @@ sub lost_connection
if ( $imap->IsUnconnected( ) ) {
$mysync->{nb_errors}++ ;
my $lcomm = $imap->LastIMAPCommand || q{} ;
- my $einfo = $imap->LastError || @{$imap->History}[$LAST] || q{} ;
+
+ my $einfo = imap_last_error( $imap ) ;
# if string is long try reduce to a more reasonable size
$lcomm = _filter( $mysync, $lcomm ) ;
@@ -5209,6 +6243,14 @@ sub lost_connection
}
}
+sub imap_last_error
+{
+ my $imap = shift ;
+ my $einfo = $imap->LastError || @{$imap->History}[$LAST] || q{} ;
+ chomp( $einfo ) ;
+ return( $einfo ) ;
+}
+
sub tests_max
{
note( 'Entering tests_max()' ) ;
@@ -5569,10 +6611,10 @@ sub get_password1
my $mysync = shift ;
- $mysync->{password1}
+ $mysync->{ password1 }
|| $mysync->{ passfile1 }
- || 'PREAUTH' eq $authmech1
- || 'EXTERNAL' eq $authmech1
+ || 'PREAUTH' eq $mysync->{ acc1 }->{ authmech }
+ || 'EXTERNAL' eq $mysync->{ acc1 }->{ authmech }
|| $ENV{IMAPSYNC_PASSWORD1}
|| do
{
@@ -5583,8 +6625,8 @@ password of user1 in a file named file1 and use "--passfile1 file1" instead of t
Then give this file restrictive permissions with the command "chmod 600 file1".
An other solution is to set the environment variable IMAPSYNC_PASSWORD1
FIN_PASSFILE
- my $user = $authuser1 || $mysync->{user1} ;
- my $host = $mysync->{host1} ;
+ my $user = $mysync->{ acc1 }->{ authuser } || $mysync->{ user1 } ;
+ my $host = $mysync->{ host1 } ;
my $prompt = "What's the password for $user" . ' at ' . "$host? (not visible while you type, then enter RETURN) " ;
$mysync->{password1} = ask_for_password( $prompt ) ;
} ;
@@ -5613,8 +6655,8 @@ sub get_password2
$mysync->{password2}
|| $mysync->{ passfile2 }
- || 'PREAUTH' eq $authmech2
- || 'EXTERNAL' eq $authmech2
+ || 'PREAUTH' eq $mysync->{ acc2 }->{ authmech }
+ || 'EXTERNAL' eq $mysync->{ acc2 }->{ authmech }
|| $ENV{IMAPSYNC_PASSWORD2}
|| do
{
@@ -5625,8 +6667,8 @@ password of user2 in a file named file2 and use "--passfile2 file2" instead of t
Then give this file restrictive permissions with the command "chmod 600 file2".
An other solution is to set the environment variable IMAPSYNC_PASSWORD2
FIN_PASSFILE
- my $user = $authuser2 || $mysync->{user2} ;
- my $host = $mysync->{host2} ;
+ my $user = $mysync->{ acc2 }->{ authuser } || $mysync->{ user2 } ;
+ my $host = $mysync->{ host2 } ;
my $prompt = "What's the password for $user" . ' at ' . "$host? (not visible while you type, then enter RETURN) " ;
$mysync->{password2} = ask_for_password( $prompt ) ;
} ;
@@ -5656,9 +6698,15 @@ sub remove_tmp_files
{
my $mysync = shift or return ;
$mysync->{pidfile} or return ;
+
if ( -e $mysync->{pidfile} ) {
+ myprint( "Removing pidfile $mysync->{pidfile}\n" ) ;
unlink $mysync->{pidfile} ;
}
+ if ( -e $mysync->{abortfile} ) {
+ myprint( "Removing pidfile $mysync->{abortfile}\n" ) ;
+ unlink $mysync->{abortfile} ;
+ }
return ;
}
@@ -5679,14 +6727,28 @@ sub cleanup_before_exit
if ( $mysync->{log} ) {
myprint( "Log file is $mysync->{logfile} ( to change it, use --logfile filepath ; or use --nolog to turn off logging )\n" ) ;
}
+ else
+ {
+ myprint( "No log file because of option --nolog\n" ) ;
+ }
+
if ( $mysync->{log} and $mysync->{logfile_handle} ) {
- #myprint( "Closing $mysync->{ logfile }\n" ) ;
- close $mysync->{logfile_handle} ;
+ #print( "Closing $mysync->{ logfile }\n" ) ;
+ teefinish( $mysync ) ;
}
return ;
}
+sub exit_most_errors
+{
+ my $mysync = shift @ARG ;
+
+ myprint( errors_listing( $mysync ) ) ;
+ my $exit_value = $EXIT_VALUE_OF_ERR_TYPE{ $mysync->{most_common_error} } || $EXIT_CATCH_ALL ;
+ exit_clean( $mysync, $exit_value ) ;
+ return ;
+}
sub exit_clean
{
@@ -5697,7 +6759,7 @@ sub exit_clean
{
myprint( @messages ) ;
}
- myprint( "Exiting with return value $status ($EXIT_TXT{$status}) $mysync->{nb_errors}/$mysync->{errorsmax} nb_errors/max_errors\n" ) ;
+ myprint( "Exiting with return value $status ($EXIT_TXT{$status}) $mysync->{nb_errors}/$mysync->{errorsmax} nb_errors/max_errors PID $PROCESS_ID\n" ) ;
cleanup_before_exit( $mysync ) ;
exit $status ;
@@ -5721,11 +6783,12 @@ sub catch_ignore
my $sigcounter = ++$mysync->{ sigcounter }{ $signame } ;
myprint( "\nGot a signal $signame (my PID is $PROCESS_ID my PPID is ", getppid( ),
"). Received $sigcounter $signame signals so far. Thanks!\n" ) ;
- stats( $mysync ) ;
+ do_and_print_stats( $mysync ) ;
return ;
}
+
sub catch_exit
{
my $mysync = shift ;
@@ -5733,22 +6796,26 @@ sub catch_exit
if ( $signame ) {
myprint( "\nGot a signal $signame (my PID is $PROCESS_ID my PPID is ", getppid( ),
"). Asked to terminate\n" ) ;
- if ( $mysync->{stats} ) {
- myprint( "Here are the final stats of this sync not completely finished so far\n" ) ;
- stats( $mysync ) ;
+ if ( $mysync->{can_do_stats} ) {
+ do_and_print_stats( $mysync ) ;
myprint( "Ended by a signal $signame (my PID is $PROCESS_ID my PPID is ",
getppid( ), "). I am asked to terminate immediately.\n" ) ;
- myprint( "You should resynchronize those accounts by running a sync again,\n",
- "since some messages and entire folders might still be missing on host2.\n" ) ;
}
+ myprint( "You should resynchronize those accounts by running a sync again,\n",
+ "since some messages and entire folders might still be missing on host2.\n"
+ ) ;
## no critic (RequireLocalizedPunctuationVars)
+ # Well, restore default action does not work well
$SIG{ $signame } = 'DEFAULT'; # restore default action
+ #$SIG{ 'TERM' } = 'DEFAULT'; # restore default action
# kill myself with $signame
# https://www.cons.org/cracauer/sigint.html
myprint( "Killing myself with signal $signame\n" ) ;
- cleanup_before_exit( $mysync ) ;
+ #cleanup_before_exit( $mysync ) ;
kill( $signame, $PROCESS_ID ) ;
- sleep 1 ;
+ #kill( 'TERM', $PROCESS_ID ) ;
+ #sleep 1 ;
+ #while ( 1 ) { } ;
$mysync->{nb_errors}++ ;
exit_clean( $mysync, $EXIT_BY_SIGNAL,
"Still there after killing myself with signal $signame...\n"
@@ -5861,6 +6928,7 @@ sub install_signals
# --sigignore can override sigexit, sigreconnect and sigprint (for the same signals only)
sig_install( $mysync, 'catch_ignore', @{ $mysync->{ sigignore } } ) ;
+ # remove/add sleeping mechanism when receiving USR1 signal (except on Win32)
sig_install_toggle_sleep( $mysync ) ;
}
@@ -5955,10 +7023,6 @@ sub reconnect_if_needed
}
-
-# $sync->{id} = defined $sync->{id} ? $sync->{id} : 1 ;
-# imap_id_stuff( $sync ) ;
-
sub justconnect
{
my $mysync = shift ;
@@ -5974,10 +7038,11 @@ sub justconnect1
{
myprint( "Host1: Will just connect to $mysync->{host1} without login\n" ) ;
$mysync->{imap1} = connect_imap(
- $mysync->{host1}, $mysync->{port1}, $debugimap1,
- $mysync->{ssl1}, $mysync->{tls1}, 'Host1',
- $mysync->{h1}->{timeout}, $mysync->{h1} ) ;
- imap_id( $mysync, $mysync->{imap1}, 'Host1' ) ;
+ $mysync->{host1}, $mysync->{port1},
+ $mysync->{ssl1}, $mysync->{tls1},
+ $mysync->{ acc1 } ) ;
+
+ imap_id( $mysync, $mysync->{imap1}, $mysync->{ acc1 }->{ Side } ) ;
$mysync->{imap1}->logout( ) ;
return $mysync->{host1} ;
}
@@ -5992,10 +7057,11 @@ sub justconnect2
{
myprint( "Host2: Will just connect to $mysync->{host2} without login\n" ) ;
$mysync->{imap2} = connect_imap(
- $mysync->{host2}, $mysync->{port2}, $debugimap2,
- $mysync->{ssl2}, $mysync->{tls2}, 'Host2',
- $mysync->{h2}->{timeout}, $mysync->{h2} ) ;
- imap_id( $mysync, $mysync->{imap2}, 'Host2' ) ;
+ $mysync->{host2}, $mysync->{port2},
+ $mysync->{ssl2}, $mysync->{tls2},
+ $mysync->{ acc2 } ) ;
+
+ imap_id( $mysync, $mysync->{imap2}, $mysync->{ acc2 }->{ Side } ) ;
$mysync->{imap2}->logout( ) ;
return $mysync->{host2} ;
}
@@ -6006,9 +7072,18 @@ sub justconnect2
sub skip_macosx
{
#return ;
- return( 'macosx.polarhome.com' eq hostname() ) ;
+ # hostname used to be macosx.polarhome.com
+ return( 'macosx' eq hostname( ) && ( 'darwin' eq $OSNAME ) ) ;
}
+sub skip_macosx_binary
+{
+ #return ;
+ return( skip_macosx( ) && ( $PROGRAM_NAME =~ m{imapsync_bin_Darwin} ) ) ;
+}
+
+
+
sub tests_mailimapclient_connect
{
note( 'Entering tests_mailimapclient_connect()' ) ;
@@ -6026,7 +7101,7 @@ sub tests_mailimapclient_connect
is( 'test.lamiral.info', $imap->Server( 'test.lamiral.info' ), 'mailimapclient_connect ipv4: setting Server(test.lamiral.info)' ) ;
is( 1, $imap->Debug( 1 ), 'mailimapclient_connect ipv4: setting Debug( 1 )' ) ;
is( 143, $imap->Port( 143 ), 'mailimapclient_connect ipv4: setting Port( 143 )' ) ;
- is( 3, $imap->Timeout( 3 ), 'mailimapclient_connect ipv4: setting Timout( 3 )' ) ;
+ is( 10, $imap->Timeout( 10 ), 'mailimapclient_connect ipv4: setting Timeout( 10 )' ) ;
like( ref( $imap->connect( ) ), qr/IO::Socket::INET|IO::Socket::IP/, 'mailimapclient_connect ipv4: connect to test.lamiral.info' ) ;
like( $imap->logout( ), qr/Mail::IMAPClient/, 'mailimapclient_connect ipv4: logout' ) ;
is( undef, undef $imap, 'mailimapclient_connect ipv4: free variable' ) ;
@@ -6042,11 +7117,10 @@ sub tests_mailimapclient_connect
is( undef, undef $imap, 'mailimapclient_connect ipv4 + ssl: free variable' ) ;
# ipv6 + ssl
- # Fails often on ks2ipv6.lamiral.info
-
+
ok( $imap = Mail::IMAPClient->new( ), 'mailimapclient_connect ipv6 + ssl: new' ) ;
is( 'petiteipv6.lamiral.info', $imap->Server( 'petiteipv6.lamiral.info' ), 'mailimapclient_connect ipv6 + ssl: setting Server petiteipv6.lamiral.info' ) ;
- is( 3, $imap->Timeout( 3 ), 'mailimapclient_connect ipv4: setting Timout( 3 )' ) ;
+ is( 10, $imap->Timeout( 10 ), 'mailimapclient_connect ipv6: setting Timeout( 10 )' ) ;
ok( $imap->Ssl( [ SSL_verify_mode => SSL_VERIFY_NONE, SSL_cipher_list => 'DEFAULT:!DH' ] ), 'mailimapclient_connect ipv6 + ssl: setting Ssl( SSL_VERIFY_NONE )' ) ;
is( 993, $imap->Port( 993 ), 'mailimapclient_connect ipv6 + ssl: setting Port( 993 )' ) ;
SKIP: {
@@ -6062,9 +7136,9 @@ sub tests_mailimapclient_connect
{
skip( 'Tests avoided on CUILLERE/pcHPDV7-HP/macosx.polarhome.com/docker cannot do ipv6', 4 ) ;
}
-
+
is( 1, $imap->Debug( 1 ), 'mailimapclient_connect ipv4 + ssl: setting Debug( 1 )' ) ;
-
+
# It sounds stupid but it avoids failures on the next test about $imap->connect
is( '2a01:e34:ecde:70d0:223:54ff:fec2:36d7', resolv( 'petiteipv6.lamiral.info' ), 'resolv: petiteipv6.lamiral.info => 2001:41d0:8:bebd::1' ) ;
@@ -6089,7 +7163,7 @@ sub tests_mailimapclient_connect_bug
# ipv6
ok( $imap = Mail::IMAPClient->new( ), 'mailimapclient_connect_bug ipv6: new' ) ;
- is( 'ks2ipv6.lamiral.info', $imap->Server( 'ks2ipv6.lamiral.info' ), 'mailimapclient_connect_bug ipv6: setting Server(ks2ipv6.lamiral.info)' ) ;
+ is( 'ks6ipv6.lamiral.info', $imap->Server( 'ks6ipv6.lamiral.info' ), 'mailimapclient_connect_bug ipv6: setting Server(ks6ipv6.lamiral.info)' ) ;
is( 143, $imap->Port( 143 ), 'mailimapclient_connect_bug ipv6: setting Port( 993 )' ) ;
SKIP: {
@@ -6105,7 +7179,7 @@ sub tests_mailimapclient_connect_bug
{
skip( 'Tests avoided on CUILLERE/pcHPDV7-HP/macosx.polarhome.com/docker cannot do ipv6', 1 ) ;
}
- like( ref( $imap->connect( ) ), qr/IO::Socket::INET/, 'mailimapclient_connect_bug ipv6: connect to ks2ipv6.lamiral.info' )
+ like( ref( $imap->connect( ) ), qr/IO::Socket::INET/, 'mailimapclient_connect_bug ipv6: connect to ks6ipv6.lamiral.info' )
or diag( 'mailimapclient_connect_bug ipv6: ', $imap->LastError( ), $!, ) ;
}
#is( $imap->logout( ), undef, 'mailimapclient_connect_bug ipv6: logout in ssl causes failure' ) ;
@@ -6140,12 +7214,12 @@ sub tests_connect_socket
}
$socket = IO::Socket::INET6->new(
- PeerAddr => 'ks2ipv6.lamiral.info',
+ PeerAddr => 'ks6ipv6.lamiral.info',
PeerPort => 143,
) ;
- ok( $imap = connect_socket( $socket ), 'connect_socket: ks2ipv6.lamiral.info port 143 IO::Socket::INET6' ) ;
+ ok( $imap = connect_socket( $socket ), 'connect_socket: ks6ipv6.lamiral.info port 143 IO::Socket::INET6' ) ;
#$imap->Debug( 1 ) ;
# myprint( $imap->capability( ) ) ;
if ( $imap ) {
@@ -6154,13 +7228,13 @@ sub tests_connect_socket
$IO::Socket::SSL::DEBUG = 4 ;
$socket = IO::Socket::SSL->new(
- PeerHost => 'ks2ipv6.lamiral.info',
+ PeerHost => 'ks6ipv6.lamiral.info',
PeerPort => 993,
SSL_verify_mode => SSL_VERIFY_NONE,
SSL_cipher_list => 'DEFAULT:!DH',
) ;
# myprint( $socket ) ;
- ok( $imap = connect_socket( $socket ), 'connect_socket: ks2ipv6.lamiral.info port 993 IO::Socket::SSL' ) ;
+ ok( $imap = connect_socket( $socket ), 'connect_socket: ks6ipv6.lamiral.info port 993 IO::Socket::SSL' ) ;
#$imap->Debug( 1 ) ;
# myprint( $imap->capability( ) ) ;
# $socket->close( ) ;
@@ -6216,10 +7290,10 @@ sub tests_probe_imapssl
skip( 'Tests avoided on CUILLERE or pcHPDV7-HP or Mac or docker: cannot do ipv6', 0 ) ;
}
# fed up with this one
- #like( probe_imapssl( 'ks2ipv6.lamiral.info' ), qr/^\* OK/, 'probe_imapssl: ks2ipv6.lamiral.info matches "* OK"' ) ;
+ #like( probe_imapssl( 'ks6ipv6.lamiral.info' ), qr/^\* OK/, 'probe_imapssl: ks6ipv6.lamiral.info matches "* OK"' ) ;
} ;
-
+
# It sounds stupid but it avoids failures on the next test about $imap->connect
ok( resolv( 'imap.gmail.com' ), 'resolv: imap.gmail.com => something' ) ;
like( probe_imapssl( 'imap.gmail.com' ), qr/^\* OK/, 'probe_imapssl: imap.gmail.com matches "* OK"' ) ;
@@ -6257,193 +7331,604 @@ sub probe_imapssl
sub connect_imap
{
- my( $host, $port, $mydebugimap, $ssl, $tls, $Side, $mytimeout, $h ) = @_ ;
+ my( $host, $port, $ssl, $tls, $acc ) = @_ ;
my $imap = Mail::IMAPClient->new( ) ;
-
- if ( $ssl ) { set_ssl( $imap, $h ) }
+
+ if ( $ssl ) { set_ssl( $imap, $acc ) }
$imap->Server( $host ) ;
$imap->Port( $port ) ;
- $imap->Debug( $mydebugimap ) ;
- $imap->Timeout( $mytimeout ) ;
+ $imap->Debug( $acc->{ debugimap } ) ;
+ $imap->Timeout( $acc->{ timeout } ) ;
- my $side = lc $Side ;
- myprint( "$Side: connecting on $side [$host] port [$port]\n" ) ;
+ my $side = lc $acc->{ Side } ;
+
+ myprint( "$acc->{ Side }: connecting on $side [$host] port [$port]\n" ) ;
if ( ! $imap->connect( ) )
{
$sync->{nb_errors}++ ;
exit_clean( $sync, $EXIT_CONNECTION_FAILURE,
- "$Side: Can not open imap connection on [$host]: ",
+ "$acc->{ Side }: Can not open imap connection on [$host]: ",
$imap->LastError,
" $OS_ERROR\n"
) ;
}
- myprint( "$Side IP address: ", $imap->Socket->peerhost(), "\n" ) ;
+ myprint( "$acc->{ Side } IP address: ", $imap->Socket->peerhost(), "\n" ) ;
my $banner = $imap->Results()->[0] ;
- myprint( "$Side banner: $banner" ) ;
- myprint( "$Side capability: ", join(q{ }, @{ $imap->capability() || [] }), "\n" ) ;
+ myprint( "$acc->{ Side } banner: $banner" ) ;
+ myprint( "$acc->{ Side } capability: ", join(q{ }, @{ $imap->capability() || [] }), "\n" ) ;
if ( $tls ) {
- set_tls( $imap, $h ) ;
+ set_tls( $imap, $acc ) ;
if ( ! $imap->starttls( ) )
{
$sync->{nb_errors}++ ;
exit_clean( $sync, $EXIT_TLS_FAILURE,
- "$Side: Can not go to tls encryption on $side [$host]:",
+ "$acc->{ Side }: Can not go to tls encryption on $side [$host]:",
$imap->LastError, "\n"
) ;
}
- myprint( "$Side: Socket successfuly converted to SSL\n" ) ;
+ myprint( "$acc->{ Side }: Socket successfully converted to SSL\n" ) ;
}
return( $imap ) ;
}
+
+sub tests_login_imap
+{
+ note( 'Entering tests_login_imap()' ) ;
+
+ is( undef, login_imap( ), 'login_imap: no args => undef' ) ;
+
+ SKIP: {
+ if ( skip_macosx_binary( ) )
+ {
+ skip( 'Tests avoided only on binary on host polarhome macosx, no clue "ssl3_get_server_certificate:certificate verify failed"', 11 ) ;
+ }
+ else{
+
+ my $myimap ;
+ my $acc = {} ;
+ $acc->{ Side } = 'HostK' ;
+ $acc->{ authmech } = 'LOGIN' ;
+ #$IO::Socket::SSL::DEBUG = 4 ;
+ # Each month (trimester?):
+ # echo | openssl s_client -crlf -connect test1.lamiral.info:993
+ # ...
+ # certificate has expired
+ # Fix:
+ # ssh root@test1.lamiral.info 'apt update && apt upgrade && /etc/init.d/dovecot restart'
+ ok(
+ $myimap = login_imap( 'test1.lamiral.info', 993, 'test1', 'secret1',
+ 1, undef,
+ 1, 100, $acc, {},
+ ), 'login_imap: test1.lamiral.info test1 ssl' ) ;
+ ok( defined( $myimap ) && $myimap->IsAuthenticated( ), 'login_imap: test1.lamiral.info test1 ssl IsAuthenticated' ) ;
+
+ ok(
+ $myimap = login_imap( 'test1.lamiral.info', 143, 'test1', 'secret1',
+ 0, undef,
+ 1, 100, $acc, {},
+ ), 'login_imap: test1.lamiral.info test1 tls' ) ;
+ ok( $myimap && $myimap->IsAuthenticated( ), 'login_imap: test1.lamiral.info test1 tls IsAuthenticated' ) ;
+
+ #$IO::Socket::SSL::DEBUG = 4 ;
+ $acc->{sslargs} = { SSL_version => 'SSLv2' } ;
+ # SSLv2 not supported
+ is(
+ undef, $myimap = login_imap( 'test1.lamiral.info', 143, 'test1', 'secret1',
+ 0, undef,
+ 1, 100, $acc, {},
+ ), 'login_imap: test1.lamiral.info test1 tls SSLv2 not supported' ) ;
+#SSL_verify_mode => 1
+#SSL_version => 'TLSv1_1'
+
+
+
+ # I have left ? exit_clean to be replaced by errors_incr( $mysync, 'error message' )
+ # 1 in login_imap()
+
+
+ my $mysync = {} ;
+ $acc = {} ;
+ $acc->{ Side } = 'Host2' ;
+ $acc->{ authmech } = 'LOGIN' ;
+ is(
+ undef, login_imap( 'noresol.lamiral.info', 143, 'test1', 'secret1',
+ 0, undef,
+ 1, 100, $acc, $mysync,
+ ), 'login_imap: noresol.lamiral.info undef' ) ;
+
+ is( 'ERR_CONNECTION_FAILURE_HOST2', errorsanalyse( errors_log( $mysync ) ), 'login_imap: Host2 noresol.lamiral.info => ERR_CONNECTION_FAILURE_HOST2' ) ;
+
+ # authentication failure for user2
+ $mysync = {} ;
+ is(
+ undef, login_imap( 'test1.lamiral.info', 143, 'test1', 'Ce crétin',
+ 0, undef,
+ 1, 100, $acc, $mysync,
+ ), 'login_imap: user2 bad passord => undef' ) ;
+
+ is( 'ERR_AUTHENTICATION_FAILURE_USER2', errorsanalyse( errors_log( $mysync ) ), 'login_imap: Host2 bad password => ERR_AUTHENTICATION_FAILURE_USER2' ) ;
+
+ # authentication failure for user1
+ $mysync = {} ;
+ $acc = {} ;
+ $acc->{ Side } = 'Host1' ;
+ $acc->{ authmech } = 'LOGIN' ;
+ is(
+ undef, login_imap( 'test1.lamiral.info', 143, 'test1', 'Ce crétin',
+ 0, undef,
+ 1, 100, $acc, $mysync,
+ ), 'login_imap: user1 bad passord => undef' ) ;
+
+ is( 'ERR_AUTHENTICATION_FAILURE_USER1', errorsanalyse( errors_log( $mysync ) ), 'login_imap: Host1 bad password => ERR_AUTHENTICATION_FAILURE_USER1' ) ;
+
+ }
+ }
+
+ note( 'Leaving tests_login_imap()' ) ;
+ return ;
+}
+
+sub oauthgenerateaccess
+{
+ if ( "petite" eq hostname() )
+ {
+ myprint( "oauthgenerateaccess\n" ) ;
+ my @output = backtick( 'cd oauth2 && pwd && ./generate_gmail_token imapsync.gl0@gmail.com' ) ;
+ myprint( @output ) ;
+ }
+ return ;
+}
+
+sub tests_login_imap_oauth
+{
+ note( 'Entering tests_login_imap_oauth()' ) ;
+
+ oauthgenerateaccess() ;
+
+ SKIP: {
+ if ( skip_macosx_binary( ) )
+ {
+ skip( 'Tests avoided only on binary on host polarhome macosx, no clue "ssl3_get_server_certificate:certificate verify failed"', 6 ) ;
+ }
+ else
+ {
+
+ my $mysync ;
+ my $acc ;
+ # oauthdirect authentication failure for user2
+ $mysync = {} ;
+ $acc = {} ;
+ $acc->{ oauthdirect } = 'caca2' ;
+ $acc->{ debugimap } = 1 ;
+ $mysync->{ showpasswords } = 1 ;
+ $acc->{ Side } = 'Host2' ;
+ $acc->{ authmech } = 'QQQ' ;
+ is(
+ undef, login_imap( 'imap.gmail.com', 993, 'test1', 'Ce crétin',
+ 1, undef,
+ 1, 100, $acc, $mysync,
+ ), 'login_imap: user2 bad oauthdirect => undef' ) ;
+
+ is( 'ERR_AUTHENTICATION_FAILURE_USER2', errorsanalyse( errors_log( $mysync ) ), 'login_imap: Host2 bad oauthdirect => ERR_AUTHENTICATION_FAILURE_USER2' ) ;
+
+ # oauthdirect authentication failure for user1
+ $mysync = {} ;
+ $acc = {} ;
+ $acc->{ Side } = 'Host1' ;
+ $acc->{ oauthdirect } = 'caca1' ;
+ $acc->{ debugimap } = 1 ;
+ $mysync->{ showpasswords } = 1 ;
+ $acc->{ authmech } = 'QQQ' ;
+ is(
+ undef, login_imap( 'imap.gmail.com', 993, 'test1', 'Ce crétin',
+ 1, undef,
+ 1, 100, $acc, $mysync,
+ ), 'login_imap: user1 bad oauthdirect => undef' ) ;
+
+ is( 'ERR_AUTHENTICATION_FAILURE_USER1', errorsanalyse( errors_log( $mysync ) ), 'login_imap: Host1 bad oauthdirect => ERR_AUTHENTICATION_FAILURE_USER1' ) ;
+
+ # oauthdirect authentication failure for user1
+ $mysync = {} ;
+ $acc = {} ;
+ $acc->{ Side } = 'Host1' ;
+ $acc->{ oauthdirect } = '' ;
+ $acc->{ debugimap } = 1 ;
+ $mysync->{ showpasswords } = 1 ;
+ $acc->{ authmech } = 'QQQ' ;
+ is(
+ undef, login_imap( 'imap.gmail.com', 993, 'test1', 'Ce crétin',
+ 1, undef,
+ 1, 100, $acc, $mysync,
+ ), 'login_imap: user1 bad oauthdirect => undef' ) ;
+
+ is( 'ERR_AUTHENTICATION_FAILURE_USER1', errorsanalyse( errors_log( $mysync ) ), 'login_imap: Host1 no oauthdirect value => ERR_AUTHENTICATION_FAILURE_USER1' ) ;
+
+ }
+ }
+
+ # oauthdirect authentication success for user1
+ SKIP: {
+ if ( ! -r 'oauth2/D_oauth2_oauthdirect_imapsync.gl0@gmail.com.txt' )
+ {
+ skip( 'oauthdirect: no oauthdirect file', 2 ) ;
+ }
+ my $myimap ;
+ my $mysync = {} ;
+ my $acc = {} ;
+ $acc->{ Side } = 'Host1' ;
+ $acc->{ oauthdirect } = 'oauth2/D_oauth2_oauthdirect_imapsync.gl0@gmail.com.txt' ;
+ $acc->{ debugimap } = 1 ;
+ $mysync->{ showpasswords } = 1 ;
+ $acc->{ authmech } = 'QQQ' ;
+ isa_ok(
+ $myimap = login_imap( 'imap.gmail.com', 993, 'user_useless', 'password_useless',
+ 1, undef,
+ 1, 100, $acc, $mysync,
+ ), 'Mail::IMAPClient', 'login_imap: user1 good oauthdirect => Mail::IMAPClient' ) ;
+
+ ok( defined( $myimap ) && $myimap->IsAuthenticated( ), 'login_imap: gmail oauth2 IsAuthenticated' ) ;
+ }
+
+ # oauthaccesstoken authentication success for user1
+ SKIP: {
+ if ( ! -r 'oauth2/D_oauth2_access_token_imapsync.gl0@gmail.com.txt' )
+ {
+ skip( 'oauthaccesstoken: no access_token file', 2 ) ;
+ }
+ my $myimap ;
+ my $mysync = {} ;
+ my $acc = {} ;
+ $acc->{ Side } = 'Host1' ;
+ $acc->{ oauthaccesstoken } = 'oauth2/D_oauth2_access_token_imapsync.gl0@gmail.com.txt' ;
+ $acc->{ debugimap } = 1 ;
+ $mysync->{ showpasswords } = 1 ;
+ $acc->{ authmech } = 'QQQ' ;
+ isa_ok(
+ $myimap = login_imap( 'imap.gmail.com', 993, 'imapsync.gl0@gmail.com', 'password_useless',
+ 1, undef,
+ 1, 100, $acc, $mysync,
+ ), 'Mail::IMAPClient', 'login_imap: user1 good oauthaccesstoken => Mail::IMAPClient' ) ;
+
+ ok( defined( $myimap ) && $myimap->IsAuthenticated( ), 'login_imap: gmail oauth2 oauthaccesstoken IsAuthenticated' ) ;
+
+ }
+
+
+ note( 'Leaving tests_login_imap_oauth()' ) ;
+ return ;
+}
+
+
+
sub login_imap
{
-
my @allargs = @_ ;
my(
- $host, $port, $user, $domain, $password,
- $mydebugimap, $mytimeout, $fastio,
- $ssl, $tls, $authmech, $authuser, $reconnectretry,
- $proxyauth, $uid, $split, $Side, $h, $mysync ) = @allargs ;
+ $host, $port, $user, $password,
+ $ssl, $tls,
+ $uid, $split, $acc, $mysync ) = @allargs ;
- my $side = lc $Side ;
- myprint( "$Side: connecting and login on $side [$host] port [$port] with user [$user]\n" ) ;
+ if ( ! all_defined( $host, $port, $user, $acc->{ Side } ) )
+ {
+ return ;
+ }
+
+ my $side = lc $acc->{ Side } ;
+ myprint( "$acc->{ Side }: connecting and login on $side [$host] port [$port] with user [$user]\n" ) ;
my $imap = init_imap( @allargs ) ;
if ( ! $imap->connect() )
{
- $mysync->{nb_errors}++ ;
- exit_clean( $mysync, $EXIT_CONNECTION_FAILURE,
- "$Side failure: can not open imap connection on $side [$host] with user [$user]: ",
- $imap->LastError . " $OS_ERROR\n"
- ) ;
+ my $error = "$acc->{ Side } failure: can not open imap connection on $side [$host] with user [$user]: "
+ . $imap->LastError . " $OS_ERROR\n" ;
+ errors_incr( $mysync, $error ) ;
+ return ;
}
- myprint( "$Side IP address: ", $imap->Socket->peerhost(), "\n" ) ;
+ myprint( "$acc->{ Side } IP address: ", $imap->Socket->peerhost(), "\n" ) ;
my $banner = $imap->Results()->[0] ;
- myprint( "$Side banner: $banner" ) ;
- myprint( "$Side capability before authentication: ", join(q{ }, @{ $imap->capability() || [] }), "\n" ) ;
+ myprint( "$acc->{ Side } banner: $banner" ) ;
+ myprint( "$acc->{ Side } capability before authentication: ", join(q{ }, @{ $imap->capability() || [] }), "\n" ) ;
if ( (! $ssl) and (! defined $tls ) and $imap->has_capability( 'STARTTLS' ) ) {
- myprint( "$Side: going to ssl because STARTTLS is in CAPABILITY. Use --notls1 or --notls2 to avoid that behavior\n" ) ;
+ myprint( "$acc->{ Side }: going to ssl because STARTTLS is in CAPABILITY. Use --notls1 or --notls2 to avoid that behavior\n" ) ;
$tls = 1 ;
}
- if ( $authmech eq 'PREAUTH' ) {
+
+ #myprint( Data::Dumper->Dump( [ @allargs ] ) ) ;
+ if ( $tls ) {
+ set_tls( $imap, $acc ) ;
+
+ if ( ! $imap->starttls( ) )
+ {
+ my $error = "$acc->{ Side } failure: Can not go to tls encryption on $side [$host]: "
+ . $imap->LastError . "\n" ;
+
+ errors_incr( $mysync, $error ) ;
+ return ;
+ }
+ myprint( "$acc->{ Side }: Socket successfully converted to SSL\n" ) ;
+ }
+
+ if ( $acc->{ authmech } eq 'PREAUTH' ) {
if ( $imap->IsAuthenticated( ) ) {
$imap->Socket ;
- myprintf("%s: Assuming PREAUTH for %s\n", $Side, $imap->Server ) ;
+ myprintf("%s: Assuming PREAUTH for %s\n", $acc->{ Side }, $imap->Server ) ;
}else{
$mysync->{nb_errors}++ ;
exit_clean(
$mysync, $EXIT_AUTHENTICATION_FAILURE,
- "$Side failure: error login on $side [$host] with user [$user] auth [PREAUTH]\n"
+ "$acc->{ Side } failure: error login on $side [$host] with user [$user] auth [PREAUTH]\n"
) ;
}
}
- if ( $tls ) {
- set_tls( $imap, $h ) ;
- if ( ! $imap->starttls( ) )
- {
- $mysync->{nb_errors}++ ;
- exit_clean( $mysync, $EXIT_TLS_FAILURE,
- "$Side failure: Can not go to tls encryption on $side [$host]:",
- $imap->LastError, "\n"
- ) ;
- }
- myprint( "$Side: Socket successfuly converted to SSL\n" ) ;
+
+
+ if ( authenticate_imap( $imap, @allargs ) )
+ {
+ myprint( "$acc->{ Side }: success login on [$host] with user [$user] auth [$acc->{ authmech }] or [LOGIN]\n" ) ;
+ return( $imap ) ;
+ }
+ else
+ {
+ # The errors are already printed
+ myprint( "$acc->{ Side }: failed login on [$host] with user [$user] auth [$acc->{ authmech }]\n" ) ;
+ return ;
}
-
- authenticate_imap( $imap, @allargs ) ;
-
- myprint( "$Side: success login on [$host] with user [$user] auth [$authmech]\n" ) ;
- return( $imap ) ;
}
+
+sub init_imap
+{
+ my(
+ $host, $port, $user, $password,
+ $ssl, $tls,
+ $uid, $split, $acc, $mysync ) = @_ ;
+
+ my ( $imap ) ;
+
+ $imap = Mail::IMAPClient->new() ;
+
+ if ( $mysync->{ tee } )
+ {
+ # Well, it does not change anything, does it?
+ # It does when suppressing the hack with *STDERR
+ $imap->Debug_fh( $mysync->{ tee } ) ;
+ }
+
+ if ( $ssl ) { set_ssl( $imap, $acc ) }
+ if ( $tls ) { } # 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( $acc->{ fastio } ) ;
+ $imap->Buffer( $buffersize || $DEFAULT_BUFFER_SIZE ) ;
+ $imap->Uid( $uid ) ;
+
+
+ $imap->Peek( 1 ) ;
+ $imap->Debug( $acc->{ debugimap } ) ;
+ if ( $mysync->{ showpasswords } ) {
+ $imap->Showcredentials( 1 ) ;
+ }
+
+ defined $acc->{ timeout } and $imap->Timeout( $acc->{ timeout } ) ;
+
+ if ( defined $acc->{ reconnectretry } )
+ {
+ $imap->Reconnectretry( $acc->{ reconnectretry } ) ;
+ }
+ $imap->{IMAPSYNC_RECONNECT_COUNT} = 0 ;
+ $imap->Ignoresizeerrors( $allowsizemismatch ) ;
+ $split and $imap->Maxcommandlength( $SPLIT_FACTOR * $split ) ;
+
+
+ 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, $mysync ) = @_ ;
+ $host, $port, $user, $password,
+ $ssl, $tls,
+ $uid, $split, $acc, $mysync ) = @_ ;
- check_capability( $imap, $authmech, $Side ) ;
+ check_capability( $imap, $acc->{ authmech }, $acc->{ Side } ) ;
$imap->User( $user ) ;
- $imap->Domain( $domain ) if ( defined $domain ) ;
- $imap->Authuser( $authuser ) ;
+
+ if ( defined $acc->{ domain } )
+ {
+ $imap->Domain( $acc->{ domain } ) ;
+ $mysync->{ debug } and myprint( "Domain: $acc->{ domain }\n" ) ;
+ }
+
+ $imap->Authuser( $acc->{ authuser } ) ;
$imap->Password( $password ) ;
- if ( 'X-MASTERAUTH' eq $authmech )
+ if ( 'X-MASTERAUTH' eq $acc->{ authmech } )
{
xmasterauth( $imap ) ;
- return ;
+ return 1 ;
}
- if ( $proxyauth ) {
+
+ if ( defined $acc->{ oauthdirect } )
+ {
+ $acc->{ authmech } = 'XOAUTH2 direct' ;
+ return( oauthdirect( $mysync, $acc, $imap, $host, $user ) ) ;
+ }
+
+
+ if ( defined $acc->{ oauthaccesstoken } )
+ {
+ $acc->{ authmech } = 'XOAUTH2 accesstoken' ;
+ return( oauthaccesstoken( $mysync, $acc, $imap, $host, $user ) ) ;
+ }
+
+
+
+
+ if ( $acc->{ proxyauth } ) {
$imap->Authmechanism(q{}) ;
- $imap->User( $authuser ) ;
+ $imap->User( $acc->{ authuser } ) ;
} else {
- $imap->Authmechanism( $authmech ) unless ( $authmech eq 'LOGIN' or $authmech eq 'PREAUTH' ) ;
+ $imap->Authmechanism( $acc->{ authmech } ) unless ( $acc->{ authmech } eq 'LOGIN' or $acc->{ authmech } eq 'PREAUTH' ) ;
}
- $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->Authcallback(\&xoauth2) if ( 'XOAUTH2' eq $acc->{ authmech } ) ;
+ $imap->Authcallback(\&plainauth) if ( ( 'PLAIN' eq $acc->{ authmech } ) or ( 'EXTERNAL' eq $acc->{ authmech } ) ) ;
- 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 ) {
- $authuser ||= "" ;
- myprint( "$Side info: authmech [$authmech] user [$user] authuser [$authuser] IsUnconnected [", $imap->IsUnconnected( ), "]\n" ) ;
- $mysync->{nb_errors}++ ;
- exit_clean( $mysync, $EXIT_AUTHENTICATION_FAILURE, $error ) ;
+ unless ( $acc->{ authmech } eq 'PREAUTH' or $imap->login( ) ) {
+ my $info = "$acc->{ Side } failure: Error login on [$host] with user [$user] auth" ;
+ my $einfo = imap_last_error( $imap ) ;
+ my $error = "$info [$acc->{ authmech }]: $einfo\n" ;
+
+
+ if ( ( $acc->{ authmech } eq 'LOGIN' ) or $imap->IsUnconnected( ) or $acc->{ authuser } ) {
+ $acc->{ authuser } ||= "" ;
+ myprint( "$acc->{ Side } info: authmech [$acc->{ authmech }] user [$user] authuser [$acc->{ authuser }] IsUnconnected [", $imap->IsUnconnected( ), "]\n" ) ;
+ errors_incr( $mysync, $error ) ;
+ return ;
}else{
- myprint( $error ) ;
+ errors_incr( $mysync, $error ) ;
}
+
# It is not secure to try plain text LOGIN when another authmech failed
- # but I do it.
- # I shell remove this code one day.
- myprint( "$Side info: trying LOGIN Auth mechanism on [$host] with user [$user]\n" ) ;
- $imap->Authmechanism(q{}) ;
- if ( ! $imap->login( ) )
+ # but I do it anyway. This behavior is optional as option --notrylogin will skip it.
+ if ( $mysync->{ trylogin } )
{
- $mysync->{nb_errors}++ ;
- exit_clean( $mysync, $EXIT_AUTHENTICATION_FAILURE,
- "$info [LOGIN]: ",
- $imap->LastError, "\n"
- ) ;
+ myprint( "$acc->{ Side } info: trying LOGIN Auth mechanism on [$host] with user [$user]. Use option --notrylogin to avoid this second chance to login via LOGIN auth\n" ) ;
+ $imap->Authmechanism(q{}) ;
+ if ( ! $imap->login( ) )
+ {
+ failure_login( $mysync, $acc, 'LOGIN', $imap, $host, $user ) ;
+ return ;
+ }
+ else
+ {
+ myprint( "$acc->{ Side }: success login on [$host] with user [$user] auth [LOGIN] after [$acc->{ authmech }] failure\n" ) ;
+ }
+ }
+ else
+ {
+ myprint( "$acc->{ Side } info: not trying LOGIN Auth mechanism on [$host] with user [$user]. Use option --trylogin to have this second chance to login via LOGIN auth\n" ) ;
+ return ;
}
}
- if ( $proxyauth ) {
+ if ( $acc->{ 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 ;
- $mysync->{nb_errors}++ ;
- exit_clean( $mysync,
- $EXIT_AUTHENTICATION_FAILURE,
- "$info: $einfo\n"
- ) ;
+ failure_proxyauth( $mysync, $acc, $acc->{ authmech }, $imap, $host, $user ) ;
+ return ;
}
}
+ return 1;
+}
+
+
+sub failure_login
+{
+ my( $mysync, $acc, $authmech, $imap, $host, $user ) = @ARG ;
+ my $info = "$acc->{ Side } failure: Error login on [$host] with user [$user] auth" ;
+ my $einfo = imap_last_error( $imap ) ;
+ my $error = "$info [$authmech]: $einfo\n" ;
+ errors_incr( $mysync, $error ) ;
return ;
}
+# failure_login and failure_proxyauth function are similar but
+# variable $error so no factoring
+sub failure_proxyauth
+{
+ my( $mysync, $acc, $authmech, $imap, $host, $user ) = @ARG ;
+ my $info = "$acc->{ Side } failure: Error login on [$host] with user [$user] auth" ;
+ my $einfo = imap_last_error( $imap ) ;
+ my $error = "$info [$authmech] using proxy-login as [$acc->{ authuser }]: $einfo\n" ;
+ errors_incr( $mysync, $error ) ;
+ return ;
+}
+
+
+
+
+sub oauthdirect
+{
+ my( $mysync, $acc, $imap, $host, $user ) = @_ ;
+
+ my $oauthdirect_str ;
+ if ( -f -r $acc->{ oauthdirect } )
+ {
+ $oauthdirect_str = firstline( $acc->{ oauthdirect } ) ;
+ }
+ else
+ {
+ $oauthdirect_str = $acc->{ oauthdirect } || 'Please define oauthdirect value' ;
+ }
+ if ( $imap->authenticate('XOAUTH2', sub { return $oauthdirect_str } ) )
+ {
+ return 1 ;
+ }
+ else
+ {
+ failure_login( $mysync, $acc, $acc->{ authmech }, $imap, $host, $user ) ;
+ return ;
+ }
+ return ;
+}
+
+
+
+
+sub oauthaccesstoken
+{
+ my( $mysync, $acc, $imap, $host, $user ) = @_ ;
+
+ my $oauthaccesstoken_str ;
+ if ( -f -r $acc->{ oauthaccesstoken } )
+ {
+ $oauthaccesstoken_str = firstline( $acc->{ oauthaccesstoken } ) ;
+ }
+ else
+ {
+ $oauthaccesstoken_str = $acc->{ oauthaccesstoken } || 'Please define oauthaccesstoken value' ;
+ }
+
+ my $oauth_string = "user=" . $user . "\x01auth=Bearer ". $oauthaccesstoken_str . "\x01\x01" ;
+ #myprint "oauth_string: $oauth_string\n" ;
+
+ my $oauth_string_base64 = encode_base64( $oauth_string , '' ) ;
+ #myprint "oauth_string_base64: $oauth_string_base64\n" ;
+
+ my $oauthdirect_str = $oauth_string_base64 ;
+
+ if ( $imap->authenticate('XOAUTH2', sub { return $oauthdirect_str } ) )
+ {
+ return 1 ;
+ }
+ else
+ {
+ failure_login( $mysync, $acc, $acc->{ authmech }, $imap, $host, $user ) ;
+ return ;
+ }
+ return ;
+}
+
+
+
+
sub check_capability
{
@@ -6479,12 +7964,12 @@ sub check_capability
sub set_ssl
{
- my ( $imap, $h ) = @_ ;
+ my ( $imap, $acc ) = @_ ;
# 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_hash = $acc->{sslargs} ;
my $sslargs_default = {
SSL_verify_mode => $SSL_VERIFY_POLICY,
@@ -6509,14 +7994,15 @@ sub set_ssl
sub set_tls
{
- my ( $imap, $h ) = @_ ;
+ my ( $imap, $acc ) = @_ ;
- my $sslargs_hash = $h->{sslargs} ;
+ my $sslargs_hash = $acc->{sslargs} ;
my $sslargs_default = {
SSL_verify_mode => $SSL_VERIFY_POLICY,
- SSL_cipher_list => 'DEFAULT:!DH',
+ SSL_cipher_list => 'DEFAULT:!DH',
} ;
+ #myprint( Data::Dumper->Dump( [ $acc, $sslargs_hash, $sslargs_default ] ) ) ;
# initiate with default values
my %sslargs_mix = %{ $sslargs_default } ;
@@ -6535,53 +8021,6 @@ sub set_tls
-
-sub init_imap
-{
- my(
- $host, $port, $user, $domain, $password,
- $mydebugimap, $mytimeout, $fastio,
- $ssl, $tls, $authmech, $authuser, $reconnectretry,
- $proxyauth, $uid, $split, $Side, $h, $mysync ) = @_ ;
-
- my ( $imap ) ;
-
- $imap = Mail::IMAPClient->new() ;
-
- if ( $mysync->{ tee } )
- {
- # Well, it does not change anything, does it?
- # It does when suppressing the hack with *STDERR
- $imap->Debug_fh( $mysync->{ tee } ) ;
- }
-
- if ( $ssl ) { set_ssl( $imap, $h ) }
- if ( $tls ) { } # 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);
- if ( $mysync->{ showpasswords } ) {
- $imap->Showcredentials( 1 ) ;
- }
- defined $mytimeout and $imap->Timeout( $mytimeout ) ;
-
- $imap->Reconnectretry( $reconnectretry ) if ( $reconnectretry ) ;
- $imap->{IMAPSYNC_RECONNECT_COUNT} = 0 ;
- $imap->Ignoresizeerrors( $allowsizemismatch ) ;
- $split and $imap->Maxcommandlength( $SPLIT_FACTOR * $split ) ;
-
-
- return( $imap ) ;
-
-}
-
sub plainauth
{
my $code = shift;
@@ -6714,80 +8153,6 @@ sub xoauth2
-# 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 ;
- $sync->{ 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);
- $sync->{ 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;
-
- $sync->{ debug } and myprint( "XOAUTH: authentication string: $string\n" ) ;
-
- # It must be base64 encoded
- return encode_base64("$string", q{});
-}
-
-
sub xmasterauth
{
# This is Kerio auth admin
@@ -6839,29 +8204,6 @@ sub xmasterauth
}
-sub tests_do_valid_directory
-{
- note( 'Entering tests_do_valid_directory()' ) ;
-
- Readonly my $NB_UNIX_tests_do_valid_directory => 2 ;
- SKIP: {
- skip( 'Tests only for Unix', $NB_UNIX_tests_do_valid_directory ) if ( 'MSWin32' eq $OSNAME ) ;
- ok( 1 == do_valid_directory( '.'), 'do_valid_directory: . good' ) ;
- ok( 1 == do_valid_directory( './W/tmp/tests/valid/sub'), 'do_valid_directory: ./W/tmp/tests/valid/sub good' ) ;
- }
- Readonly my $NB_UNIX_tests_do_valid_directory_non_root => 2 ;
- SKIP: {
- skip( 'Tests only for Unix', $NB_UNIX_tests_do_valid_directory_non_root ) if ( 'MSWin32' eq $OSNAME or '0' eq $EFFECTIVE_USER_ID ) ;
- diag( 'Error / not writable is on purpose' ) ;
- ok( 0 == do_valid_directory( '/'), 'do_valid_directory: / bad' ) ;
- diag( 'Error permission denied on /noway is on purpose' ) ;
- ok( 0 == do_valid_directory( '/noway'), 'do_valid_directory: /noway bad' ) ;
- }
-
-
- note( 'Leaving tests_do_valid_directory()' ) ;
- return ;
-}
sub banner_imapsync
{
@@ -6870,8 +8212,8 @@ sub banner_imapsync
my $banner_imapsync = join q{},
q{$RCSfile: imapsync,v $ },
- q{$Revision: 1.977 $ },
- q{$Date: 2019/12/23 20:18:02 $ },
+ q{$Revision: 2.148 $ },
+ q{$Date: 2021/07/22 14:21:09 $ },
"\n",
"Command line used, run by $EXECUTABLE_NAME:\n",
"$PROGRAM_NAME ", command_line_nopassword( $mysync, @argv ), "\n" ;
@@ -6879,6 +8221,29 @@ sub banner_imapsync
return( $banner_imapsync ) ;
}
+sub tests_do_valid_directory
+{
+ note( 'Entering tests_do_valid_directory()' ) ;
+
+ is( 1, do_valid_directory( '.'), 'do_valid_directory: . good' ) ;
+ is( 1, do_valid_directory( './W/tmp/tests/valid/sub'), 'do_valid_directory: ./W/tmp/tests/valid/sub good' ) ;
+
+ Readonly my $NB_UNIX_tests_do_valid_directory_non_root => 2 ;
+ diag( "OSNAME=$OSNAME EFFECTIVE_USER_ID=$EFFECTIVE_USER_ID" ) ;
+
+ SKIP: {
+ skip( 'Tests only for non roor user', $NB_UNIX_tests_do_valid_directory_non_root ) if ( '0' eq $EFFECTIVE_USER_ID ) ;
+ diag( 'The "Error / is not writable" is on purpose' ) ;
+ ok( 0 == do_valid_directory( '/'), 'do_valid_directory: / bad' ) ;
+ diag( 'The "Error permission denied" on /noway is on purpose' ) ;
+ ok( 0 == do_valid_directory( '/noway'), 'do_valid_directory: /noway bad' ) ;
+ }
+
+
+ note( 'Leaving tests_do_valid_directory()' ) ;
+ return ;
+}
+
sub do_valid_directory
{
my $dir = shift @ARG ;
@@ -6898,7 +8263,7 @@ sub do_valid_directory
return( 0 ) ;
}
# Trying to create it
- myprint( "Creating directory $dir\n" ) ;
+ myprint( "Creating directory $dir (current directory is " . getcwd( ) . ")\n" ) ;
if ( ! eval { mkpath( $dir ) } ) {
myprint( "$EVAL_ERROR" ) if ( $EVAL_ERROR ) ;
}
@@ -6924,11 +8289,14 @@ sub tests_match_a_pid_number
is( 1, match_a_pid_number( 99999 ), 'match_a_pid_number: 99999 => 1' ) ;
is( 1, match_a_pid_number( -99999 ), 'match_a_pid_number: -99999 => 1' ) ;
is( undef, match_a_pid_number( 0 ), 'match_a_pid_number: 0 => undef' ) ;
- is( undef, match_a_pid_number( 100000 ), 'match_a_pid_number: 100000 => undef' ) ;
- is( undef, match_a_pid_number( 123456 ), 'match_a_pid_number: 123456 => undef' ) ;
+ is( 1, match_a_pid_number( 100000 ), 'match_a_pid_number: 100000 => 1' ) ;
+ is( 1, match_a_pid_number( 123456 ), 'match_a_pid_number: 123456 => 1' ) ;
is( undef, match_a_pid_number( '-0' ), 'match_a_pid_number: "-0" => undef' ) ;
- is( undef, match_a_pid_number( -100000 ), 'match_a_pid_number: -100000 => undef' ) ;
- is( undef, match_a_pid_number( -123456 ), 'match_a_pid_number: -123456 => undef' ) ;
+ is( 1, match_a_pid_number( -100000 ), 'match_a_pid_number: -100000 => 1' ) ;
+ is( 1, match_a_pid_number( -123456 ), 'match_a_pid_number: -123456 => 1' ) ;
+ is( 1, match_a_pid_number( 2**22 ), 'match_a_pid_number: 2**22 => 1' ) ;
+ is( undef, match_a_pid_number( 2**22 + 1 ), 'match_a_pid_number: 2**22 + 1 => undef' ) ;
+ is( undef, match_a_pid_number( 4194304 + 1 ), 'match_a_pid_number: 2**22 + 1 = 4194305 => undef' ) ;
note( 'Leaving tests_match_a_pid_number()' ) ;
return ;
@@ -6944,7 +8312,7 @@ sub match_a_pid_number
# can be negative on Windows
#if ( 0 > $pid ) { return ; }
#if ( 65535 < $pid ) { return ; }
- if ( 99999 < abs( $pid ) ) { return ; }
+ if ( 2**22 < abs( $pid ) ) { return ; }
if ( 0 == abs( $pid ) ) { return ; }
return 1 ;
}
@@ -6974,13 +8342,20 @@ sub remove_pidfile_not_running
{
#
my $pid_filename = shift @ARG ;
-
+
+ #myprint( "In remove_pidfile_not_running $pid_filename\n" ) ;
if ( ! $pid_filename ) { myprint( "No variable pid_filename\n" ) ; return } ;
- if ( ! -e $pid_filename ) { myprint( "File $pid_filename does not exist\n" ) ; return } ;
+ if ( ! -e $pid_filename )
+ {
+ myprint( "File $pid_filename does not exist\n" ) ;
+ return ;
+ }
+ #myprint( "Still In remove_pidfile_not_running $pid_filename\n" ) ;
+
if ( ! -f $pid_filename ) { myprint( "File $pid_filename is not a file\n" ) ; return } ;
my $pid = firstline( $pid_filename ) ;
- if ( ! match_a_pid_number( $pid ) ) { myprint( "pid $pid in $pid_filename is not a number\n" ) ; return } ;
+ if ( ! match_a_pid_number( $pid ) ) { myprint( "In remove_pidfile_not_running: pid $pid in $pid_filename is not a pid number\n" ) ; return } ;
# can't kill myself => do nothing
if ( ! kill 'ZERO', $PROCESS_ID ) { myprint( "Can not kill ZERO myself $PROCESS_ID\n" ) ; return } ;
@@ -7061,6 +8436,8 @@ sub tail
if ( ! $lock ) { return ; }
if ( ! $tail ) { return ; }
+ if ( ! -e $pidfile ) { return ; }
+
my $pidtotail = firstline( $pidfile ) ;
if ( ! $pidtotail ) { return ; }
@@ -7173,13 +8550,14 @@ sub write_pidfile
{
# returns undef if something is considered fatal
# returns 1 otherwise
-
+
+ #myprint( "In write_pidfile\n" ) ;
if ( ! @ARG ) { return 1 ; }
my $mysync = shift @ARG ;
- # Do not write the pid file if this process goal is to abort the process designed by the pid file
- if ( $mysync->{abort} ) { return 1 ; }
+ # Do not write the pid file if the current process goal is to abort the process designed by the pid file
+ if ( $mysync->{ abort } ) { return 1 ; }
#
my $pid_filename = $mysync->{ pidfile } ;
@@ -7384,6 +8762,9 @@ sub tests_jux_utf8
is( '[&ZTZO9nux-] = [æ”¶ä»¶ç®±]', jux_utf8( '&ZTZO9nux-'), 'jux_utf8: => [&ZTZO9nux-] = [æ”¶ä»¶ç®±]' ) ;
#
+ #
+ is( '[!Old Emails]', jux_utf8( '!Old Emails'), 'jux_utf8: !Old Emails => [!Old Emails]' ) ;
+ is( '[2006 Budget & Fcst]', jux_utf8( '2006 Budget & Fcst'), 'jux_utf8: 2006 Budget & Fcst => [2006 Budget & Fcst]' ) ;
note( 'Leaving tests_jux_utf8()' ) ;
return ;
}
@@ -7530,6 +8911,7 @@ sub create_folder
if ( $create_folder_old ) {
return( create_folder_old( $mysync, $myimap2 , $h2_fold , $h1_fold ) ) ;
}
+
myprint( "Creating folder [$h2_fold] on host2\n" ) ;
if ( ( 'INBOX' eq uc $h2_fold )
and ( $myimap2->exists( $h2_fold ) ) ) {
@@ -7577,6 +8959,7 @@ sub create_folder
if ( ! $mysync->{ 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" ) ;
+ # The messages that could be transferred are counted and the number is given at the end.
}
return( 0 ) ;
}
@@ -7639,7 +9022,7 @@ sub sort_requested_folders
{
my @requested_folders_sorted = () ;
- #myprint "folderfirst: @folderfirst\n" ;
+ $sync->{ debug } and myprint "folderfirst: @folderfirst\n" ;
my @folderfirst_requested = remove_from_requested_folders( @folderfirst ) ;
#myprint "folderfirst_requested: @folderfirst_requested\n" ;
@@ -7648,7 +9031,7 @@ sub sort_requested_folders
my @middle = sort keys %requested_folder ;
@requested_folders_sorted = ( @folderfirst_requested, @middle, @folderlast_requested ) ;
- #myprint "requested_folders_sorted: @requested_folders_sorted\n" ;
+ $sync->{ debug } and myprint "requested_folders_sorted: @requested_folders_sorted\n" ;
add_to_requested_folders( @requested_folders_sorted ) ;
return( @requested_folders_sorted ) ;
@@ -7707,7 +9090,7 @@ sub tests_remove_from_requested_folders
is_deeply( [ 'F1', 'F2' ], [ remove_from_requested_folders( 'F1', 'F2' ) ], 'remove_from_requested_folders: remove F1 F2 among F1 F2 F3 => F1 F2' ) ;
is_deeply( { 'F3' => 1 }, { %requested_folder }, 'remove_from_requested_folders: remove F1 F2 among F1 F2 F3 => %requested_folder F3' ) ;
-
+ undef %requested_folder ;
note( 'Leaving tests_remove_from_requested_folders()' ) ;
return ;
@@ -7784,13 +9167,13 @@ sub tests_compare_lists
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( 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( $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]' ) ;
@@ -8015,6 +9398,7 @@ sub folders_list_to_help
return( $listing ) ;
}
+# Globals are $sync @h1_folders_all @h2_folders_all $prefix1 $prefix2
sub private_folders_separators_and_prefixes
{
# what are the private folders separators and prefixes for each server ?
@@ -8121,8 +9505,32 @@ sub sanitize_subfolder
}
}
+sub tests_sanitize_host
+{
+ note( 'Entering tests_sanitize_host()' ) ;
+
+ is( undef, sanitize_host( ), 'sanitize_host: no args => undef' ) ;
+ is( '', sanitize_host( '' ), 'sanitize_host: empty => empty' ) ;
+ is( 'imap.example.org', sanitize_host( 'imap.example.org' ), 'sanitize_host: imap.example.org => imap.example.org' ) ;
+ is( 'imap.example.org', sanitize_host( ' imap.example.org' ), 'sanitize_host: imap.example.org 1 => imap.example.org' ) ;
+ is( 'imap.example.org', sanitize_host( 'imap.example.org ' ), 'sanitize_host: imap.example.org 2 => imap.example.org' ) ;
+ is( 'imap.example.org', sanitize_host( 'imap.exam ple.org' ), 'sanitize_host: imap.example.org 3 => imap.example.org' ) ;
+ is( 'imap.example.org', sanitize_host( ' imap.exam ple.org ' ), 'sanitize_host: imap.example.org 4 => imap.example.org' ) ;
+ is( 'imap.example.org', sanitize_host( 'imap.exa/mple.org/' ), 'sanitize_host: imap.example.org/ => imap.example.org' ) ;
+
+ note( 'Leaving tests_sanitize_host()' ) ;
+ return ;
+}
+sub sanitize_host
+{
+ my $host = shift ;
+ if ( ! defined $host ) { return ; }
+
+ $host =~ tr{ /}{}d ;
+ return $host ;
+}
sub tests_add_subfolder1_to_folderrec
@@ -8707,86 +10115,89 @@ sub timesince
-sub tests_flags_regex
+sub tests_regexflags
{
- note( 'Entering tests_flags_regex()' ) ;
+ note( 'Entering tests_regexflags()' ) ;
- ok( q{} eq flags_regex(q{} ), 'flags_regex, null string q{}' ) ;
- ok( q{\Seen NonJunk $Spam} eq flags_regex( q{\Seen NonJunk $Spam} ), q{flags_regex, nothing to do} ) ;
+ my $mysync = {} ;
+
+ ok( q{} eq regexflags( $mysync, q{} ), 'regexflags, null string q{}' ) ;
+ ok( q{\Seen NonJunk $Spam} eq regexflags( $mysync, q{\Seen NonJunk $Spam} ), q{regexflags, nothing to do} ) ;
- @regexflag = ('I am BAD' ) ;
- ok( not ( defined flags_regex( q{} ) ), 'flags_regex, bad regex' ) ;
+ @{ $mysync->{ regexflag } } = ('I am BAD' ) ;
+ ok( not ( defined regexflags( $mysync, q{} ) ), 'regexflags, 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( q{\Seen NonJunk } eq flags_regex( q{\Seen NonJunk $Spam} ), q{flags_regex, remove $Spam: 's/\$Spam//g'} ) ;
+ @{ $mysync->{ regexflag } } = ( 's/NonJunk//g' ) ;
+ ok( q{\Seen $Spam} eq regexflags( $mysync, q{\Seen NonJunk $Spam} ), q{regexflags, remove NonJunk: 's/NonJunk//g'} ) ;
+ @{ $mysync->{ regexflag } } = ( q{s/\$Spam//g} ) ;
+ ok( q{\Seen NonJunk } eq regexflags( $mysync, q{\Seen NonJunk $Spam} ), q{regexflags, remove $Spam: 's/\$Spam//g'} ) ;
- @regexflag = ( 's/\\\\Seen//g' ) ;
+ @{ $mysync->{ regexflag } } = ( 's/\\\\Seen//g' ) ;
- ok( q{ NonJunk $Spam} eq flags_regex( q{\Seen NonJunk $Spam} ), q{flags_regex, remove \Seen: 's/\\\\\\\\Seen//g'} ) ;
+ ok( q{ NonJunk $Spam} eq regexflags( $mysync, q{\Seen NonJunk $Spam} ), q{regexflags, remove \Seen: 's/\\\\\\\\Seen//g'} ) ;
- @regexflag = ( 's/(\s|^)[^\\\\]\w+//g' ) ;
- ok( q{\Seen \Middle \End} eq flags_regex( q{\Seen NonJunk \Middle $Spam \End} ), q{flags_regex: only \word among \Seen NonJunk \Middle $Spam \End} ) ;
- ok( q{ \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} ) ;
+ @{ $mysync->{ regexflag } } = ( 's/(\s|^)[^\\\\]\w+//g' ) ;
+ ok( q{\Seen \Middle \End} eq regexflags( $mysync, q{\Seen NonJunk \Middle $Spam \End} ), q{regexflags: only \word among \Seen NonJunk \Middle $Spam \End} ) ;
+ ok( q{ \Seen \Middle \End1} eq regexflags( $mysync, q{Begin \Seen NonJunk \Middle $Spam \End1 End} ),
+ q{regexflags: 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' ) ;
+ @{ $mysync->{ regexflag } } = ( q{s/.*?(Keep1|Keep2|Keep3)/$1 /g} ) ;
+ ok( 'Keep1 Keep2 ReB' eq regexflags( $mysync, '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' ) ;
+ ok( 'Keep1 Keep2 ' eq regexflags( $mysync, 'REM REM Keep1 Keep2' ), 'Keep only regex' ) ;
+ ok( 'Keep1 Keep2 ' eq regexflags( $mysync, 'Keep1 REM REM Keep2' ), 'Keep only regex' ) ;
+ ok( 'Keep1 Keep2 ' eq regexflags( $mysync, 'REM Keep1 REM REM Keep2' ), 'Keep only regex' ) ;
+ ok( 'Keep1 Keep2 ' eq regexflags( $mysync, 'Keep1 Keep2' ), 'Keep only regex' ) ;
+ ok( 'Keep1 ' eq regexflags( $mysync, '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' ) ;
+ @{ $mysync->{ regexflag } } = ( q{s/(Keep1|Keep2|Keep3) (?!(Keep1|Keep2|Keep3)).*/$1 /g} ) ;
+ ok( 'Keep1 Keep2 ' eq regexflags( $mysync, 'Keep1 Keep2 ReB' ), 'Keep only regex' ) ;
+ ok( 'Keep1 Keep2 ' eq regexflags( $mysync, 'Keep1 Keep2 REM REM REM' ), 'Keep only regex' ) ;
+ ok( 'Keep2 ' eq regexflags( $mysync, 'Keep2 REM REM REM' ), 'Keep only regex' ) ;
- @regexflag = ( q{s/.*?(Keep1|Keep2|Keep3)/$1 /g},
+ @{ $mysync->{ 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' ) ;
+ ok( 'Keep1 Keep2 ' eq regexflags( $mysync, 'REM Keep1 REM Keep2 REM' ), 'Keep only regex' ) ;
+ ok( 'Keep1 Keep2 ' eq regexflags( $mysync, 'Keep1 REM Keep2 REM' ), 'Keep only regex' ) ;
+ ok( 'Keep1 Keep2 ' eq regexflags( $mysync, 'REM Keep1 Keep2 REM' ), 'Keep only regex' ) ;
+ ok( 'Keep1 Keep2 ' eq regexflags( $mysync, 'REM Keep1 REM Keep2' ), 'Keep only regex' ) ;
+ ok( 'Keep1 Keep2 Keep3 ' eq regexflags( $mysync, 'REM Keep1 REM Keep2 REM REM Keep3 REM' ), 'Keep only regex' ) ;
+ ok( 'Keep1 ' eq regexflags( $mysync, 'REM REM Keep1 REM REM REM ' ), 'Keep only regex' ) ;
+ ok( 'Keep1 Keep3 ' eq regexflags( $mysync, '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{Add jrdH8u 's/(.*)/\$1 jrdH8u/'} ) ;
- @regexflag = ('s/jrdH8u *//');
- ok('REM REM REM REM REM ' eq flags_regex('REM REM REM REM REM jrdH8u'), q{Remove jrdH8u s/jrdH8u *//} ) ;
+ @{ $mysync->{ regexflag } } = ( 's/(.*)/$1 jrdH8u/' ) ;
+ ok('REM REM REM REM REM jrdH8u' eq regexflags( $mysync, 'REM REM REM REM REM' ), q{Add jrdH8u 's/(.*)/\$1 jrdH8u/'} ) ;
+ @{ $mysync->{ regexflag } } = ('s/jrdH8u *//' );
+ ok('REM REM REM REM REM ' eq regexflags( $mysync, 'REM REM REM REM REM jrdH8u' ), q{Remove jrdH8u s/jrdH8u *//} ) ;
- @regexflag = (
+ @{ $mysync->{ 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'),
+ eq regexflags( $mysync, '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 regexflags( $mysync, q{} ), 'Keep only regex: Exchange case, null string (Phil)' ) ;
ok( q{}
- eq flags_regex('Blabla $Junk machin truc'),
+ eq regexflags( $mysync, '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 '),
+ eq regexflags( $mysync, '\\Deleted \\Answered \\Draft \\Flagged ' ),
'Keep only regex: Exchange case (Phil)' ) ;
- @regexflag = ( 's/\\\\Flagged//g' ) ;
+ @{ $mysync->{ regexflag } } = ( 's/\\\\Flagged//g' ) ;
is('\Deleted \Answered \Draft ',
- flags_regex('\\Deleted \\Answered \\Draft \\Flagged '),
- 'flags_regex: remove \Flagged 1' ) ;
+ regexflags( $mysync, '\\Deleted \\Answered \\Draft \\Flagged ' ),
+ 'regexflags: remove \Flagged 1' ) ;
+
is('\\Deleted \\Answered \\Draft',
- flags_regex('\\Deleted \\Flagged \\Answered \\Draft'),
- 'flags_regex: remove \Flagged 2' ) ;
+ regexflags( $mysync, '\\Deleted \\Flagged \\Answered \\Draft' ),
+ 'regexflags: remove \Flagged 2' ) ;
# I didn't understand why it gives \F
# https://perldoc.perl.org/perlrebackslash.html
@@ -8795,49 +10206,136 @@ sub tests_flags_regex
# \F Not available in old Perl so I comment the test
- # @regexflag = ( 's/\\Flagged/X/g' ) ;
+ # @{ $mysync->{ regexflag } } = ( 's/\\Flagged/X/g' ) ;
#is('\Deleted FX \Answered \FX \Draft \FX',
- #flags_regex( '\Deleted Flagged \Answered \Flagged \Draft \Flagged' ),
- # 'flags_regex: remove \Flagged 3 mistery...' ) ;
+ #regexflags( '\Deleted Flagged \Answered \Flagged \Draft \Flagged' ),
+ # 'regexflags: remove \Flagged 3 mistery...' ) ;
- note( 'Leaving tests_flags_regex()' ) ;
+ $mysync->{ regexflag } = [ ] ;
+ $mysync->{ filterbuggyflags } = 1 ;
+ filterbuggyflags( $mysync ) ;
+
+ is( '\Deleted \Answered \Draft \Flagged',
+ regexflags( $mysync, '\\Deleted \\Answered \\RECEIPTCHECKED \\Draft \\Indexed \\Flagged' ),
+ 'regexflags: remove famous /X 1' ) ;
+
+ is( '\\Deleted \\Flagged \\Answered \\Draft',
+ regexflags( $mysync, '\\Deleted \\RECEIPTCHECKED \\Flagged \\Answered \\Indexed \\Draft' ),
+ 'regexflags: remove famous /X 2' ) ;
+
+ is( '\ ', '\\ ', 'regexflags: \ is \\ ' ) ;
+ is( '\\ ', '\\ ', 'regexflags: \\ is \\ ' ) ;
+ is( '\\ \ ', '\ \\ ', 'regexflags: \\ \ is \ \\ ' ) ;
+ note( 'Leaving tests_regexflags()' ) ;
return ;
}
-sub flags_regex
+sub regexflags
{
- 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" ) ;
+ my $mysync = shift ;
+ my $flags = shift ;
+
+ foreach my $regexflag ( @{ $mysync->{ regexflag } } )
+ {
+ my $flags_orig = $flags ;
+ $debugflags and myprint( "eval \$flags =~ $regexflag\n" ) ;
+ my $ret = eval "\$flags =~ $regexflag ; 1 " ;
+ $debugflags and myprint( "regexflag $regexflag [$flags_orig] -> [$flags]\n" ) ;
if( not ( defined $ret ) or $EVAL_ERROR ) {
myprint( "Error: eval regexflag '$regexflag': $EVAL_ERROR\n" ) ;
return( undef ) ;
}
}
- return( $h1_flags ) ;
+ return( $flags ) ;
}
+
+sub filterbuggyflags
+{
+ my $mysync = shift ;
+ if ( $mysync->{ filterbuggyflags } )
+ {
+ unshift @{ $mysync->{ regexflag } }, buggyflagsregex( ) ;
+ }
+ return ;
+}
+
+
+sub tests_remove_doublequotes_if_any
+{
+ note( 'Entering tests_remove_doublequotes_if_any()' ) ;
+ # the number of tests is stupid here
+ is( undef, remove_doublequotes_if_any( ), 'remove_doublequotes_if_any: no args => undef' ) ;
+ is( q{}, remove_doublequotes_if_any( q{} ), 'remove_doublequotes_if_any: empty string => empty string' ) ;
+ is( q{}, remove_doublequotes_if_any( q{""} ), 'remove_doublequotes_if_any: double-quotes => empty string' ) ;
+ is( q{}, remove_doublequotes_if_any( q{"""} ), 'remove_doublequotes_if_any: double-quotes => empty string' ) ;
+ is( q{}, remove_doublequotes_if_any( q{"""} ), 'remove_doublequotes_if_any: double-quotes => empty string' ) ;
+ is( q{toto}, remove_doublequotes_if_any( q{"toto"} ), 'remove_doublequotes_if_any: "toto" => toto' ) ;
+ is( q{toto}, remove_doublequotes_if_any( q{toto} ), 'remove_doublequotes_if_any: toto => toto' ) ;
+ is( q{toto}, remove_doublequotes_if_any( q{to"to} ), 'remove_doublequotes_if_any: to"to => toto' ) ;
+ is( q{toto}, remove_doublequotes_if_any( q{toto"} ), 'remove_doublequotes_if_any: toto" => toto' ) ;
+ is( q{toto}, remove_doublequotes_if_any( q{"toto} ), 'remove_doublequotes_if_any: "toto => toto' ) ;
+ is( q{toto}, remove_doublequotes_if_any( q{"to"to} ), 'remove_doublequotes_if_any: "to"to => toto' ) ;
+ is( q{toto}, remove_doublequotes_if_any( q{to"to"} ), 'remove_doublequotes_if_any: to"to" => toto' ) ;
+
+ is( q{toto}, remove_doublequotes_if_any( q{to\"to} ), 'remove_doublequotes_if_any: to\"to => toto' ) ;
+ is( q{toto}, remove_doublequotes_if_any( q{toto\"} ), 'remove_doublequotes_if_any: toto\" => toto' ) ;
+ is( q{toto}, remove_doublequotes_if_any( q{\"toto} ), 'remove_doublequotes_if_any: \"toto => toto' ) ;
+ is( q{toto}, remove_doublequotes_if_any( q{\"to\"to} ), 'remove_doublequotes_if_any: \"to\"to => toto' ) ;
+ is( q{toto}, remove_doublequotes_if_any( q{to\"to\"} ), 'remove_doublequotes_if_any: to\"to" => toto' ) ;
+
+
+ note( 'Leaving tests_remove_doublequotes_if_any()' ) ;
+ return ;
+}
+
+
+
+sub remove_doublequotes_if_any
+{
+ my $string = shift ;
+
+ if ( ! defined $string ) { return ; }
+ $string =~ s/\\\"//g ;
+ $string =~ tr/"//d ;
+ return $string ;
+}
+
+
+# No globals here
sub acls_sync
{
- my($h1_fold, $h2_fold) = @_ ;
- if ( $syncacls ) {
- my $h1_hash = $sync->{imap1}->getacl($h1_fold)
- or myprint( "Could not getacl for $h1_fold: $EVAL_ERROR\n" ) ;
- my $h2_hash = $sync->{imap2}->getacl($h2_fold)
- or myprint( "Could not getacl for $h2_fold: $EVAL_ERROR\n" ) ;
+# https://tools.ietf.org/html/rfc4314
+# Standard Rights:
+# https://tools.ietf.org/html/rfc4314#section-2.1
+
+ my( $mysync, $h1_fold, $h2_fold ) = @_ ;
+ if ( $mysync->{ syncacls } ) {
+ my $h1_hash = $mysync->{imap1}->getacl($h1_fold)
+ or myprint( "Host1: Could not getacl for $h1_fold: $EVAL_ERROR\n" ) ;
+ my $h2_hash = $mysync->{imap2}->getacl($h2_fold)
+ or myprint( "Host2: Could not getacl for $h2_fold: $EVAL_ERROR\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 ($sync->{dry}) {
- myprint( "setting acl $h2_fold $user $acl\n" ) ;
- $sync->{imap2}->setacl($h2_fold, $user, $acl)
- or myprint( "Could not set acl: $EVAL_ERROR\n" ) ;
+ my $h1_acl = remove_doublequotes_if_any( $h1_hash->{$user} ) || '' ;
+ my $h2_acl = remove_doublequotes_if_any( $h2_hash->{$user} ) || '' ;
+ myprint( "Host1: user $user has acl [$h1_acl] on host1\n" ) ;
+ myprint( "Host2: user $user has acl [$h2_acl] on host2\n" ) ;
+ # removes surrounding double-quotes if any
+ my $user_no_quotes = remove_doublequotes_if_any( $user ) ;
+
+ if ( $h1_hash->{$user}
+ && $h2_hash->{$user}
+ && $h1_hash->{$user} eq $h2_hash->{$user} )
+ {
+ myprint( "Host2: user $user_no_quotes has already the same acl, no need to set it.\n" ) ;
+ next ;
+ }
+ myprint( "Host2: setting acl for folder $h2_fold user $user_no_quotes acl $h1_acl $mysync->{dry_message}\n" ) ;
+ unless ( $mysync->{dry} ) {
+ $mysync->{imap2}->setacl( $h2_fold, $user_no_quotes, $h1_acl )
+ or myprint( "Could not set acl for user $user_no_quotes on host2: $EVAL_ERROR\n" ) ;
}
}
}
@@ -8874,10 +10372,11 @@ sub permanentflags
if ( $line =~ m{\[PERMANENTFLAGS\s\(([^)]+?)\)\]}x ) {
( $debugflags or $sync->{ debug } ) and myprint( "permanentflags: $line" ) ;
my $permanentflags = $1 ;
- if ( $permanentflags =~ m{\\\*}x ) {
+ if ( $permanentflags =~ m{\\\*}x )
+ {
$permanentflags = q{} ;
}
- return($permanentflags) ;
+ return( $permanentflags ) ;
} ;
}
return( q{} ) ;
@@ -8913,19 +10412,6 @@ sub flags_filter
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
{
note( 'Entering tests_flagscase()' ) ;
@@ -8943,6 +10429,93 @@ sub tests_flagscase
return ;
}
+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_flags_for_host2
+{
+ note( 'Entering tests_flags_for_host2()' ) ;
+
+ is( undef, flags_for_host2( ), 'flags_for_host2: no args => undef' ) ;
+
+ my $mysync ;
+ is( undef, flags_for_host2( $mysync ), 'flags_for_host2: undef => undef' ) ;
+
+ $mysync = { } ;
+ is( undef, flags_for_host2( $mysync ), 'flags_for_host2: nothing => undef' ) ;
+
+ is( q{}, flags_for_host2( $mysync, '' ), 'flags_for_host2: no flags => empty string' ) ;
+
+ is( q{}, flags_for_host2( $mysync, '\Recent' ), 'flags_for_host2: \Recent => empty string' ) ;
+
+ is( q{\Seen}, flags_for_host2( $mysync, '\Recent \Seen' ), 'flags_for_host2: \Recent \Seen => \Seen' ) ;
+
+ is( q{\Deleted \Seen}, flags_for_host2( $mysync, '\Deleted \Recent \Seen' ), 'flags_for_host2: \Deleted \Recent \Seen => \Deleted \Seen' ) ;
+
+ $mysync->{ flagscase } = 0 ;
+ is( q{\DELETED \Seen}, flags_for_host2( $mysync, '\DELETED \Seen' ), 'flags_for_host2: flagscase = 0 \DELETED \Seen => \DELETED \Seen' ) ;
+
+ $mysync->{ flagscase } = 1 ;
+ is( q{\Deleted \Seen}, flags_for_host2( $mysync, '\DELETED \Seen' ), 'flags_for_host2: flagscase = 1 \DELETED \Seen => \Deleted \Seen' ) ;
+
+ $mysync->{ filterflags } = 0 ;
+ is( q{\Seen \Blabla}, flags_for_host2( $mysync, '\Seen \Blabla', '\Seen \Junk' ), 'flags_for_host2: filterflags = 0 \Seen \Blabla among \Seen \Junk => \Seen \Blabla' ) ;
+
+ $mysync->{ filterflags } = 1 ;
+ is( q{\Seen}, flags_for_host2( $mysync, '\Seen \Blabla', '\Seen \Junk' ), 'flags_for_host2: filterflags = 1 \Seen \Blabla among \Seen \Junk => \Seen' ) ;
+
+ $mysync->{ filterflags } = 1 ;
+ is( q{\Seen \Blabla}, flags_for_host2( $mysync, '\Seen \Blabla', '' ), 'flags_for_host2: filterflags = 1 \Seen \Blabla among "" => \Seen \Blabla' ) ;
+
+
+ note( 'Leaving tests_flags_for_host2()' ) ;
+ return ;
+}
+
+
+
+
+sub flags_for_host2
+{
+ my $mysync = shift ;
+ my $h1_flags = shift ;
+ my $permanentflags2 = shift ;
+
+ if ( ! all_defined( $mysync, $h1_flags ) ) { return ; } ;
+
+ # RFC 2060: This flag can not be altered by any client
+ $h1_flags =~ s@\\Recent\s?@@xgi ;
+
+ my $h1_flags_re ;
+ if ( $mysync->{ regexflag } and defined( $h1_flags_re = regexflags( $mysync, $h1_flags ) ) ) {
+ $h1_flags = $h1_flags_re ;
+ }
+
+ if ( $mysync->{ flagscase } )
+ {
+ $h1_flags = flagscase( $h1_flags ) ;
+ }
+
+ if ( $permanentflags2 and $mysync->{ filterflags } )
+ {
+ $h1_flags = flags_filter( $h1_flags, $permanentflags2 ) ;
+ }
+
+ return( $h1_flags ) ;
+}
+
sub ucsecond
@@ -9041,8 +10614,7 @@ sub select_msgs_by_fetch
$debugdev and myprint( "Calling fetch_hash()\n" ) ;
- my $uidnext = $imap->uidnext( $folder ) || $uidnext_default ;
- my $fetch_hash_uids = $fetch_hash_set || "1:$uidnext" ;
+ my $fetch_hash_uids = $fetch_hash_set || "1:*" ;
%fetch = %{$imap->fetch_hash( $fetch_hash_uids, 'INTERNALDATE' ) } ;
@msgs_all = sort { $a <=> $b } keys %fetch ;
@@ -9108,6 +10680,10 @@ sub msgs_from_maxmin
@min = @{ $min_ref } ;
SWITCH: {
+ if ( not ( defined $minage or defined $maxage ) )
+ {
+ return ;
+ }
unless( defined $minage ) { @msgs = @max ; last SWITCH } ;
unless( defined $maxage ) { @msgs = @min ; last SWITCH } ;
my ( %union, %inter ) ;
@@ -9125,23 +10701,38 @@ sub msgs_from_maxmin
sub tests_msgs_from_maxmin
{
- note( 'Entering tests_msgs_from_maxmin()' ) ;
+ note( 'Entering tests_msgs_from_maxmin()' ) ;
+
my @msgs ;
+
+ # no maxage nor minage
+ @msgs = msgs_from_maxmin( [ '1', '2' ], [ '2', '3' ] ) ;
+ is_deeply( [ ], \@msgs , 'msgs_from_maxmin: no maxage nor minage => empty result' ) ;
+
+ # maxage alone
$maxage = $NUMBER_200 ;
@msgs = msgs_from_maxmin( [ '1', '2' ], [ '2', '3' ] ) ;
- ok( 0 == compare_lists( [ '1', '2' ], \@msgs ), 'msgs_from_maxmin: maxage++' ) ;
+ is_deeply( [ '1', '2' ], \@msgs , 'msgs_from_maxmin: maxage++' ) ;
+
+ # maxage > minage -> intersection
$minage = $NUMBER_100 ;
@msgs = msgs_from_maxmin( [ '1', '2' ], [ '2', '3' ] ) ;
- ok( 0 == compare_lists( [ '2' ], \@msgs ), 'msgs_from_maxmin: -maxage++minage-' ) ;
+ is_deeply( [ '2' ], \@msgs , 'msgs_from_maxmin: -maxage++minage-' ) ;
+
+ # maxage < minage -> union
$minage = $NUMBER_300 ;
@msgs = msgs_from_maxmin( [ '1', '2' ], [ '2', '3' ] ) ;
- ok( 0 == compare_lists( [ '1', '2', '3' ], \@msgs ), 'msgs_from_maxmin: ++maxage-minage++' ) ;
+ is_deeply( [ '1', '2', '3' ], \@msgs, 'msgs_from_maxmin: ++maxage-minage++' ) ;
+
+
+ # minage alone
$maxage = undef ;
@msgs = msgs_from_maxmin( [ '1', '2' ], [ '2', '3' ] ) ;
- ok( 0 == compare_lists( [ '2', '3' ], \@msgs ), 'msgs_from_maxmin: ++minage-' ) ;
+ is_deeply( [ '2', '3' ], \@msgs, 'msgs_from_maxmin: ++minage-' ) ;
- note( 'Leaving tests_msgs_from_maxmin()' ) ;
+
+ note( 'Leaving tests_msgs_from_maxmin()' ) ;
return ;
}
@@ -9249,6 +10840,13 @@ sub copy_message
( $mysync->{ debug } or $mysync->{dry} )
and myprint( "msg $h1_fold/$h1_msg copying to $h2_fold $mysync->{dry_message} " . eta( $mysync ) . "\n" ) ;
+ if ( $mysync->{dry1} )
+ {
+ $mysync->{ h1_nb_msg_processed } +=1 ;
+ $nb_msg_skipped_dry_mode += 1 ;
+ return ;
+ }
+
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{} ;
@@ -9294,7 +10892,7 @@ sub copy_message
( $mysync->{ 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 ) ;
+ $h1_flags = flags_for_host2( $mysync, $h1_flags, $permanentflags2 ) ;
( $mysync->{ debug } or $debugflags ) and
myprint( "Host1: flags filt msg $h1_fold/$h1_msg date [$h1_date] flags [$h1_flags] size [$h1_size]\n" ) ;
@@ -9447,7 +11045,7 @@ sub message_for_host2
${ $string_ref } = $header . "\r\n" . ${ $string_ref } ;
}
- if ( ( defined $mysync->{ truncmess } ) and is_an_integer( $mysync->{ truncmess } ) )
+ if ( ( defined $mysync->{ truncmess } ) and is_integer( $mysync->{ truncmess } ) )
{
${ $string_ref } = truncmess( ${ $string_ref }, $mysync->{ truncmess } ) ;
}
@@ -10094,20 +11692,6 @@ sub date_for_host2
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
{
@@ -10116,7 +11700,7 @@ sub subject
my $header = extract_header( $string ) ;
- if( $header =~ m/^Subject:\s*([^\n\r]*)\r?$/msx ) {
+ if( $header =~ m/^Subject:[ \t]*([^\n\r]*)\r?$/msx ) {
#myprint( "MMM[$1]\n" ) ;
$subject = $1 ;
}
@@ -10125,12 +11709,14 @@ sub subject
sub tests_subject
{
- note( 'Entering tests_subject()' ) ;
+ note( 'Entering 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') ;
+ is( '', subject( 'Subject:' ), 'Subject:') ;
+ is( '', subject( "Subject:\r\n" ), 'Subject:\r\n') ;
+ 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') ;
+ ok( 'toto le hero' eq subject( "Subject:toto le hero\r\n" ), 'Subject: toto le hero\r\n') ;
my $MESS ;
$MESS = <<'EOF';
@@ -10169,13 +11755,24 @@ Subject: toto le hero
EOF
ok( q{} eq subject( $MESS ), 'subject: null but body could') ;
+
+ $MESS = <<'EOF';
+From: lalala
+Subject:
+Date: zzzzzz
+
+Subject: toto le hero
+EOF
+ is( '', subject( $MESS ), 'Subject:') ;
+
+
+
note( 'Leaving tests_subject()' ) ;
return ;
}
# GlobVar
-# $max_msg_size_in_bytes
# $h2_uidguess
# ...
#
@@ -10187,10 +11784,9 @@ sub append_message_on_host2
my $new_id ;
if ( ! $mysync->{dry} ) {
- $max_msg_size_in_bytes = max( $string_len, $max_msg_size_in_bytes ) ;
$new_id = $mysync->{imap2}->append_string( $h2_fold, ${ $string_ref }, $h1_flags, $h1_date ) ;
myprint( debugmemory( $mysync, " at A2" ) ) ;
- if ( ! $new_id){
+ if ( ! defined $new_id ){
my $subject = subject( ${ $string_ref } ) ;
my $error_imap = $mysync->{imap2}->LastError || q{} ;
my $error = "- msg $h1_fold/$h1_msg {$string_len} could not append ( Subject:[$subject], Date:[$h1_date], Size:[$h1_size], Flags:[$h1_flags] ) to folder $h2_fold: $error_imap\n" ;
@@ -10210,6 +11806,7 @@ sub append_message_on_host2
$mysync->{ total_bytes_transferred } += $string_len ;
$mysync->{ nb_msg_transferred } += 1 ;
$mysync->{ h1_nb_msg_processed } +=1 ;
+ $mysync->{ biggest_message_transferred } = max( $string_len, $mysync->{ biggest_message_transferred } ) ;
my $time_spent = timesince( $mysync->{begin_transfer_time} ) ;
my $rate = bytes_display_string( $mysync->{total_bytes_transferred} / $time_spent ) ;
@@ -10279,15 +11876,18 @@ sub sleep_if_needed
{
my( $mysync ) = shift ;
- if ( ! $mysync ) {
- return ;
- }
- # No need to go further if there is no limit set
- if ( not ( $mysync->{maxmessagespersecond}
- or $mysync->{maxbytespersecond} )
- ) {
- return ;
- }
+ if ( ! $mysync ) {
+ return ;
+ }
+ # No need to go further if there is no limit set
+ if (
+ not (
+ $mysync->{maxmessagespersecond}
+ or $mysync->{maxbytespersecond}
+ )
+ ) {
+ return ;
+ }
$mysync->{maxsleep} = defined $mysync->{maxsleep} ? $mysync->{maxsleep} : $MAX_SLEEP ;
# Must be positive
@@ -10360,7 +11960,7 @@ sub sleep_max_bytes
sub tests_sleep_max_bytes
{
- note( 'Entering tests_sleep_max_bytes()' ) ;
+ note( 'Entering tests_sleep_max_bytes()' ) ;
ok( 0 == sleep_max_bytes( 4000, 2, undef ), 'sleep_max_bytes: maxbytespersecond == undef => sleep 0' ) ;
ok( 0 == sleep_max_bytes( 4000, 2, 0 ), 'sleep_max_bytes: maxbytespersecond = 0 => sleep 0') ;
@@ -10371,7 +11971,7 @@ sub tests_sleep_max_bytes
ok( 0 == sleep_max_bytes( 2000, 2, 2000 ), 'sleep_max_bytes: maxbytespersecond = 2k max not reached => sleep 0') ;
ok( 0 == sleep_max_bytes( -2000, 2, 1000 ), 'sleep_max_bytes: maxbytespersecond = 1k max not reached => sleep 0') ;
- note( 'Leaving tests_sleep_max_bytes()' ) ;
+ note( 'Leaving tests_sleep_max_bytes()' ) ;
return ;
}
@@ -10383,7 +11983,8 @@ sub delete_message_on_host1
if ( ! @h1_msg ) { return ; }
delete_messages_on_any(
$mysync,
- $mysync->{imap1},
+ $mysync->{ acc1 },
+ $mysync->{ imap1 },
"Host1: $h1_fold",
$expunge,
$split1,
@@ -10419,18 +12020,22 @@ sub tests_operators_and_exclam_precedence
return ;
}
+
sub delete_messages_on_any
{
- my( $mysync, $imap, $hostX_folder, $expunge, $split, @messages ) = @_ ;
+ # $acc is not used yet,
+ #
+ my( $mysync, $acc, $imap, $hostX_folder, $expunge, $split, @messages ) = @_ ;
my $expunge_message = q{} ;
my $dry_message = $mysync->{ dry_message } ;
$expunge_message = 'and expunged' if ( $expunge ) ;
# "Host1: msg "
- $imap->Debug( 1 ) ;
+ # $imap->Debug( 1 ) ;
- while ( my @messages_part = splice @messages, 0, $split )
+ my @messages_to_mark_deleted = @messages ;
+ while ( my @messages_part = splice @messages_to_mark_deleted, 0, $split )
{
foreach my $message ( @messages_part )
{
@@ -10442,7 +12047,7 @@ sub delete_messages_on_any
if ( defined $nb_deleted )
{
# $nb_deleted is not accurate
- $mysync->{ h1_nb_msg_deleted } += scalar @messages_part ;
+ $acc->{ nb_msg_deleted } += scalar @messages_part ;
}
else
{
@@ -10458,7 +12063,7 @@ sub delete_messages_on_any
uidexpunge_or_expunge( $mysync, $imap, @messages ) ;
}
- $imap->Debug( 0 ) ;
+ #$imap->Debug( 0 ) ;
return ;
}
@@ -10547,13 +12152,13 @@ sub tests_eta
$mysync->{ begin_transfer_time } = time ; # Now
$mysync->{ h1_nb_msg_processed } = 0 ;
- is( "ETA: " . localtime( time ) . " 0 s 0/0 msgs left",
+ is( "ETA: " . localtimez( time ) . " 0 s 0/0 msgs left",
eta( $mysync ),
'eta: no args => ETA: "Now" 0 s 0/0 msgs left' ) ;
$mysync->{ h1_nb_msg_processed } = 1 ;
$mysync->{ h1_nb_msg_start } = 2 ;
- is( "ETA: " . localtime( time ) . " 0 s 1/2 msgs left",
+ is( "ETA: " . localtimez( time ) . " 0 s 1/2 msgs left",
eta( $mysync ),
'eta: 1, 1, 2 => ETA: "Now" 0 s 1/2 msgs left' ) ;
@@ -10584,7 +12189,7 @@ sub eta
my $time_remaining = time_remaining( $time_spent, $h1_nb_processed, $h1_nb_msg_start, $nb_msg_transferred ) ;
$mysync->{ debug } and myprint( "time_spent: $time_spent time_remaining: $time_remaining\n" ) ;
my $nb_msg_remaining = $h1_nb_msg_start - $h1_nb_processed ;
- my $eta_date = localtime( time + $time_remaining ) ;
+ my $eta_date = localtimez( 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 ) ) ;
}
@@ -11171,6 +12776,7 @@ sub touch
}
+
sub tests_tmpdir_has_colon_bug
{
note( 'Entering tests_tmpdir_has_colon_bug()' ) ;
@@ -11237,7 +12843,7 @@ sub tmpdir_fix_colon_bug
myprint( "Old cache directory $cachedir_old still exists\n" ) ;
$err++ ;
}else{
- myprint( "Old cache directory $cachedir_old successfuly moved\n" ) ;
+ myprint( "Old cache directory $cachedir_old successfully moved\n" ) ;
}
}
return( not $err ) ;
@@ -11280,31 +12886,33 @@ sub cache_folder
sub tests_filter_forbidden_characters
{
- note( 'Entering tests_filter_forbidden_characters()' ) ;
+ note( 'Entering 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' ) ;
+ is( undef , filter_forbidden_characters( ), 'filter_forbidden_characters: no args -> undef' ) ;
- 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 "' ) ;
- } ;
+ is( 'a_b' , filter_forbidden_characters( 'a_b' ), 'filter_forbidden_characters: a_b -> a_b' ) ;
+ is( 'a_b' , filter_forbidden_characters( 'a*b' ), 'filter_forbidden_characters: a*b -> a_b' ) ;
+ is( 'a_b' , filter_forbidden_characters( 'a|b' ), 'filter_forbidden_characters: a|b -> a_b' ) ;
+ is( 'a_b' , filter_forbidden_characters( 'a?b' ), 'filter_forbidden_characters: a?b -> a_b' ) ;
+ is( 'a________b', filter_forbidden_characters( q{a*|?:"<>'b} ), q{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_"' ) ;
- } ;
- ok( 'a_b' eq filter_forbidden_characters( "a\tb" ), 'filter_forbidden_characters: a\tb -> a_b' ) ;
- ok( "a_b" eq filter_forbidden_characters( "a\rb" ), 'filter_forbidden_characters: a\rb -> a_b' ) ;
- ok( "a_b" eq filter_forbidden_characters( "a\nb" ), 'filter_forbidden_characters: a\nb -> a_b' ) ;
- ok( "a_b" eq filter_forbidden_characters( "a\\b" ), 'filter_forbidden_characters: a\b -> a_b' ) ;
+ is( 'a_b_' , filter_forbidden_characters( 'a b ' ), 'filter_forbidden_characters: "a b " -> "a_b_"' ) ;
- note( 'Leaving tests_filter_forbidden_characters()' ) ;
+
+ is( 'a_b' , filter_forbidden_characters( "a\tb" ), 'filter_forbidden_characters: a\tb -> a_b' ) ;
+ is( "a_b" , filter_forbidden_characters( "a\rb" ), 'filter_forbidden_characters: a\rb -> a_b' ) ;
+ is( "a_b" , filter_forbidden_characters( "a\nb" ), 'filter_forbidden_characters: a\nb -> a_b' ) ;
+ is( "a_b" , filter_forbidden_characters( "a\\b" ), 'filter_forbidden_characters: a\b -> a_b' ) ;
+
+ is( 'a-b' , filter_forbidden_characters( 'a-b' ), 'filter_forbidden_characters: a-b -> a-b' ) ;
+ is( 'a__-__-__-__-__b' , filter_forbidden_characters( 'aé-è-à -ç-Öb' ), 'filter_forbidden_characters: aé-è-à -ç-Öb -> a__-__-__-__-__b' ) ;
+
+ is( 'abcdABCDwxyzWXYZ012789' , filter_forbidden_characters( 'abcdABCDwxyzWXYZ012789' ),
+ 'filter_forbidden_characters: abcdABCDwxyzWXYZ012789 -> abcdABCDwxyzWXYZ012789' ) ;
+
+
+ note( 'Leaving tests_filter_forbidden_characters()' ) ;
return ;
}
@@ -11312,13 +12920,12 @@ sub filter_forbidden_characters
{
my $string = shift ;
- if ( ! defined $string ) { return ; }
+ if ( ! defined $string ) { return ; }
+
+ $string =~ s{[\Q*|?:"<>' \E\t\r\n\\]}{_}xg ;
+ # replace all non-ascii and control characters by _
+ $string =~ s/[[:^ascii:][:cntrl:]]/_/xg ;
- if ( 'MSWin32' eq $OSNAME ) {
- # Move trailing whitespace to _ " a b /c d " -> " a b_/c d_"
- $string =~ s{\ (/|$)}{_$1}xg ;
- }
- $string =~ s{[\Q*|?:"<>\E\t\r\n\\]}{_}xg ;
#myprint( "[$string]\n" ) ;
return( $string ) ;
}
@@ -11351,58 +12958,58 @@ sub convert_sep_to_slash
sub tests_regexmess
{
- note( 'Entering tests_regexmess()' ) ;
+ note( 'Entering tests_regexmess()' ) ;
- ok( 'blabla' eq regexmess( 'blabla' ), 'regexmess, no regexmess, nothing to do' ) ;
+ ok( 'blabla' eq regexmess( 'blabla' ), 'regexmess: no regexmess, nothing to do' ) ;
@regexmess = ( 'lalala' ) ;
- ok( not( defined regexmess( 'popopo' ) ), 'regexmess, bad regex 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' ) ;
+ 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: c->C');
@regexmess = ( 's{\AFrom\ }{From:}gxms' ) ;
ok( q{}
eq regexmess(q{}),
- 'From mbox 1 add colon blank');
+ 'regexmess: From mbox 1 add colon blank');
ok( 'From:'
eq regexmess('From '),
- 'From mbox 2 add colo');
+ 'regexmess: From mbox 2 add colo');
ok( "\n" . 'From '
eq regexmess("\n" . 'From '),
- 'From mbox 3 add colo') ;
+ 'regexmess: From mbox 3 add colo') ;
ok( "From: zzz\n" . 'From '
eq regexmess("From zzz\n" . 'From '),
- 'From mbox 4 add colo') ;
+ 'regexmess: From mbox 4 add colo') ;
@regexmess = ( 's{\AFrom\ [^\n]*(\n)?}{}gxms' ) ;
ok( q{}
eq regexmess(q{}),
- 'From mbox 1 remove, blank');
+ 'regexmess: From mbox 1 remove, blank');
ok( q{}
eq regexmess('From '),
- 'From mbox 2 remove');
+ 'regexmess: From mbox 2 remove');
ok( "\n" . 'From '
eq regexmess("\n" . 'From '),
- 'From mbox 3 remove');
+ 'regexmess: From mbox 3 remove');
#myprint( "[", regexmess("From zzz\n" . 'From '), "]" ) ;
ok( q{} . 'From '
eq regexmess("From zzz\n" . 'From '),
- 'From mbox 4 remove');
+ 'regexmess: From mbox 4 remove');
- ok(
+ is(
<<'EOM'
Date: Sat, 10 Jul 2010 05:34:45 -0700
From:
@@ -11410,7 +13017,7 @@ From:
Hello,
Bye.
EOM
- eq regexmess(
+ , regexmess(
<<'EOM'
From zzz
Date: Sat, 10 Jul 2010 05:34:45 -0700
@@ -11419,7 +13026,7 @@ From:
Hello,
Bye.
EOM
-), 'From mbox 5 remove');
+ ), 'regexmess: 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!
@@ -11460,7 +13067,7 @@ Disposition-Notification-To: Gilles LAMIRAL
Hello,
Bye.
EOM
-),
+ ),
'regexmess: 2 Delete header Disposition-Notification-To:');
ok(
@@ -11480,7 +13087,7 @@ From:
Hello,
Bye.
EOM
-),
+ ),
'regexmess: 3 Delete header Disposition-Notification-To:');
ok(
@@ -11500,7 +13107,7 @@ From:
Disposition-Notification-To: Gilles LAMIRAL
Bye.
EOM
-),
+ ),
'regexmess: 4 Delete header Disposition-Notification-To:');
@@ -11520,11 +13127,11 @@ From:
Disposition-Notification-To: Gilles LAMIRAL
Bye.
EOM
-),
+ ),
'regexmess: 5 Delete header Disposition-Notification-To:');
-ok(
+ ok(
<<'EOM'
Date: Sat, 10 Jul 2010 05:34:45 -0700
From:
@@ -11542,10 +13149,10 @@ Hello,
Disposition-Notification-To: Gilles LAMIRAL
Bye.
EOM
-),
+ ),
'regexmess: 6 Delete header Disposition-Notification-To:');
-ok(
+ ok(
<<'EOM'
Date: Sat, 10 Jul 2010 05:34:45 -0700
From:
@@ -11565,11 +13172,11 @@ Disposition-Notification-To: Gilles LAMIRAL
Bye.
EOM
-),
+ ),
'regexmess: 7 Delete header Disposition-Notification-To:');
-ok(
+ ok(
<<'EOM'
Date: Sat, 10 Jul 2010 05:34:45 -0700
From:
@@ -11589,7 +13196,7 @@ EOM
'regexmess: 8 Delete header Disposition-Notification-To:');
-ok(
+ ok(
<<'EOM'
Date: Sat, 10 Jul 2010 05:34:45 -0700
From:
@@ -11607,12 +13214,12 @@ Hello,
Disposition-Notification-To: Gilles LAMIRAL
Bye.
EOM
-),
+ ),
'regexmess: 9 Delete header Disposition-Notification-To:');
-ok(
+ ok(
<<'EOM'
Date: Sat, 10 Jul 2010 05:34:45 -0700
From:
@@ -11634,10 +13241,10 @@ Disposition-Notification-To: Gilles LAMIRAL
Bye.
EOM
-),
+ ),
'regexmess: 10 Delete header Disposition-Notification-To:');
-ok(
+ ok(
<<'EOM'
Date: Sat, 10 Jul 2010 05:34:45 -0700
From:
@@ -11662,7 +13269,7 @@ EOM
),
'regexmess: 11 Delete header Disposition-Notification-To:');
-ok(
+ ok(
<<'EOM'
Date: Sat, 10 Jul 2010 05:34:45 -0700
From:
@@ -11688,15 +13295,15 @@ Disposition-Notification-To: Gilles LAMIRAL
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' ) ;
+ @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(
+ ok(
<<'EOM'
Date: Sat, 10 Jul 2010 05:34:45 -0700
From:
@@ -11722,10 +13329,10 @@ Disposition-Notification-To: Gilles LAMIRAL
Bye.
EOM
-),
+ ),
'regexmess: 13 Delete header Disposition-Notification-To:');
-ok(
+ ok(
<<'EOM'
Date: Sat, 10 Jul 2010 05:34:45 -0700
X-Disposition-Notification-To: Gilles LAMIRAL
@@ -11753,10 +13360,10 @@ Disposition-Notification-To: Gilles LAMIRAL
Bye.
EOM
-),
+ ),
'regexmess: 14 Delete header Disposition-Notification-To:');
-ok(
+ ok(
<<'EOM'
Date: Sat, 10 Jul 2010 05:34:45 -0700
X-Disposition-Notification-To: Gilles LAMIRAL
@@ -11776,11 +13383,11 @@ Hello,
Bye.
EOM
-),
+ ),
'regexmess: 15 Delete header Disposition-Notification-To:');
-ok(
+ ok(
<<'EOM'
Date: Sat, 10 Jul 2010 05:34:45 -0700
From:
@@ -11800,10 +13407,10 @@ Hello,
Bye.
EOM
-),
+ ),
'regexmess: 16 Delete header Disposition-Notification-To:');
-ok(
+ ok(
<<'EOM'
X-Disposition-Notification-To: Gilles LAMIRAL
Date: Sat, 10 Jul 2010 05:34:45 -0700
@@ -11827,15 +13434,15 @@ EOM
'regexmess: 17 Delete header Disposition-Notification-To:');
@regexmess = ( 's/.{11}\K.*//gs' ) ;
- is( "0123456789\n", regexmess( "0123456789\n" x 100 ), 'regexmess, truncate whole message after 11 characters' ) ;
- is( "0123456789\n", regexmess( "0123456789\n" x 100_000 ), 'regexmess, truncate whole message after 11 characters ~ 1MB' ) ;
+ is( "0123456789\n", regexmess( "0123456789\n" x 100 ), 'regexmess: truncate whole message after 11 characters' ) ;
+ is( "0123456789\n", regexmess( "0123456789\n" x 100_000 ), 'regexmess: truncate whole message after 11 characters ~ 1MB' ) ;
@regexmess = ( 's/.{10000}\K.*//gs' ) ;
- is( "123456789\n" x 1000, regexmess( "123456789\n" x 100_000 ), 'regexmess, truncate whole message after 10000 characters ~ 1MB' ) ;
+ is( "123456789\n" x 1000, regexmess( "123456789\n" x 100_000 ), 'regexmess: truncate whole message after 10000 characters ~ 1MB' ) ;
-@regexmess = ( 's/^(X-Ham-Report.*?\n)^X-/X-/sm' ) ;
+ @regexmess = ( 's/^(X-Ham-Report.*?\n)^X-/X-/sm' ) ;
-is(
+ is(
<<'EOM'
X-Spam-Score: -1
X-Spam-Bar: /
@@ -11848,7 +13455,7 @@ Hello,
Bye.
EOM
,
-regexmess(
+ regexmess(
<<'EOM'
X-Spam-Score: -1
X-Spam-Bar: /
@@ -11868,20 +13475,247 @@ Hello,
Bye.
EOM
-),
- 'regexmess: 1 Delete header X-Ham-Report:');
+ ),
+ 'regexmess: Delete header X-Ham-Report:');
# regex to play with Date: from the FAQ
#@regexmess = 's{\A(.*?(?! ^$))^Date:(.*?)$}{$1Date:$2\nX-Date:$2}gxms'
+# Change 8bit characters in whole email to X characters
+ @regexmess = ( 's{[\x80-\xff]}{X}gxms' ) ;
+ is( 'X-8bit: kaka 1 XX kiki', regexmess('X-8bit: kaka 1 ¤ kiki'), 'regexmess: 1 Change 8bit characters in whole email to X characters');
+
+# Same change but using tr
+ @regexmess = ( 'tr [\x80-\xff] [X]' ) ;
+ is( 'X-8bit: kaka 1 XXXX kiki', regexmess('X-8bit: kaka 1 ¤£ kiki'), 'regexmess: 2 Change 8bit characters in whole email to X characters, using tr');
- note( 'Leaving tests_regexmess()' ) ;
- return ;
+# Add a final \r\n if missing
+ @regexmess = ( 's{(?
+LaSuite: super
+
+Hello,
+Bye.
+EOM
+ , regexmess(
+<<'EOM'
+Date: Sat, 10 Jul 2010 05:34:45 -0700
+From:
+X-Spam-Report: caca
+caca
+ caca
+caca
+LaSuite: super
+
+Hello,
+Bye.
+EOM
+ ), 'regexmess: 1 remove buggy X-Spam-Report: across several lines, not the final header');
+
+
+ is(
+<<'EOM'
+Date: Sat, 10 Jul 2010 05:34:45 -0700
+From:
+LaSuite: super
+LaSuite2: super 2
+
+Hello,
+Bye.
+EOM
+ , regexmess(
+<<'EOM'
+Date: Sat, 10 Jul 2010 05:34:45 -0700
+From:
+X-Spam-Report: caca
+caca
+ caca
+caca
+LaSuite: super
+LaSuite2: super 2
+
+Hello,
+Bye.
+EOM
+ ), 'regexmess: 2 remove buggy X-Spam-Report: across several lines, not the final header');
+
+
+ is(
+<<'EOM'
+Date: Sat, 10 Jul 2010 05:34:45 -0700
+From:
+LaSuite: super
+LaSuite2: super 2
+
+Hello,
+Bye.
+EOM
+ , regexmess(
+<<'EOM'
+X-Spam-Report: caca
+caca
+ caca
+caca
+Date: Sat, 10 Jul 2010 05:34:45 -0700
+From:
+LaSuite: super
+LaSuite2: super 2
+
+Hello,
+Bye.
+EOM
+ ), 'regexmess: 3 remove buggy X-Spam-Report: across several lines, first header');
+
+
+
+
+ is(
+<<'EOM'
+Date: Sat, 10 Jul 2010 05:34:45 -0700
+From:
+
+Hello,
+Bye.
+EOM
+ , regexmess(
+<<'EOM'
+Date: Sat, 10 Jul 2010 05:34:45 -0700
+From:
+X-Spam-Report: caca
+caca
+ caca
+caca
+
+Hello,
+Bye.
+EOM
+ ), 'regexmess: 4 remove buggy X-Spam-Report: across several lines, final header');
+
+
+ is(
+<<'EOM'
+Date: Sat, 10 Jul 2010 05:34:45 -0700
+From:
+
+Hello,
+Bye.
+EOM
+ , regexmess(
+<<'EOM'
+Date: Sat, 10 Jul 2010 05:34:45 -0700
+From:
+
+Hello,
+Bye.
+EOM
+ ), 'regexmess: 5 remove buggy X-Spam-Report: not there at all');
+
+
+ is(
+<<"EOM"
+Date: Sat, 10 Jul 2010 05:34:45 -0700\r
+From:\r
+LaSuite: super\r
+LaSuite2: super 2\r
+\r
+Hello,\r
+Bye.\r
+EOM
+ , regexmess(
+<<"EOM"
+X-Spam-Report: caca\r
+caca\r
+ caca\r
+caca\r
+Date: Sat, 10 Jul 2010 05:34:45 -0700\r
+From:\r
+LaSuite: super\r
+LaSuite2: super 2\r
+\r
+Hello,\r
+Bye.\r
+EOM
+ ), 'regexmess: 6 remove buggy X-Spam-Report: across several lines, first header, with \r');
+
+
+ is(
+<<"EOM"
+Date: Sat, 10 Jul 2010 05:34:45 -0700\r
+From:\r
+LaSuite: super\r
+LaSuite2: super 2\r
+\r
+Hello,\r
+Bye.\r
+EOM
+ , regexmess(
+<<"EOM"
+Date: Sat, 10 Jul 2010 05:34:45 -0700\r
+From:\r
+X-Spam-Report: caca\r
+caca\r
+ caca\r
+caca\r
+LaSuite: super\r
+LaSuite2: super 2\r
+\r
+Hello,\r
+Bye.\r
+EOM
+ ), 'regexmess: 7 remove buggy X-Spam-Report: across several lines, middle header, with \r');
+
+
+ is(
+<<"EOM"
+Date: Sat, 10 Jul 2010 05:34:45 -0700\r
+From:\r
+\r
+Hello,\r
+Bye.\r
+EOM
+ , regexmess(
+<<"EOM"
+Date: Sat, 10 Jul 2010 05:34:45 -0700\r
+From:\r
+X-Spam-Report: caca\r
+caca\r
+ caca\r
+caca\r
+\r
+Hello,\r
+Bye.\r
+EOM
+ ), 'regexmess: 8 remove buggy X-Spam-Report: across several lines, final header, with \r');
+
+
+ undef @regexmess ;
+ note( 'Leaving tests_regexmess()' ) ;
+ return ;
}
sub regexmess
@@ -12119,10 +13953,83 @@ EOM
# Complex regular subexpression recursion limit (32766) exceeded with more lines
# exit;
- note( 'Leaving tests_skipmess()' ) ;
+
+ undef @skipmess ;
+ note( 'Leaving tests_skipmess()' ) ;
return ;
}
+
+sub tests_skipmess_neg
+{
+ note( 'Entering tests_skipmess_neg()' ) ;
+
+
+ @skipmess = ('m{i}') ;
+ ok( 1 == skipmess( 'Hi!' ), 'skipmess: i string yes' ) ;
+ ok( 0 == skipmess( 'Ho!' ), 'skipmess: i string no' ) ;
+
+ @skipmess = ('m{\A(?!.*i)}') ;
+ ok( 0 == skipmess( 'Hi!' ), 'skipmess: not i string no' ) ;
+ ok( 1 == skipmess( 'Ho!' ), 'skipmess: not i string yes' ) ;
+
+
+ @skipmess = ('m{\A(?!.*^From:[^\n]*tartanpion\@machin\.truc)}xms') ;
+
+ ok( 0 == skipmess(
+<<'EOM'
+Date: Sat, 10 Jul 2010 05:34:45 -0700
+From:
+
+Bye.
+EOM
+),
+ 'skipmess: 1 not From tartanpion@machin.truc' ) ;
+
+ok( 1 == skipmess(
+<<'EOM'
+Date: Sat, 10 Jul 2010 05:34:45 -0700
+From:
+
+Bye.
+EOM
+),
+ 'skipmess: 2 not From tartanpion@machin.truc' ) ;
+
+
+
+
+ ok( 0 == skipmess(
+<<'EOM'
+Date: Sat, 10 Jul 2010 05:34:45 -0700
+From:
+
+ From:
+Bye.
+EOM
+),
+ 'skipmess: 3 not From tartanpion@machin.truc' ) ;
+
+ok( 1 == skipmess(
+<<'EOM'
+Date: Sat, 10 Jul 2010 05:34:45 -0700
+From:
+
+ From:
+Bye.
+EOM
+),
+ 'skipmess: 4 not From tartanpion@machin.truc' ) ;
+
+
+
+
+ undef @skipmess ;
+ note( 'Leaving tests_skipmess_neg()' ) ;
+ return ;
+}
+
+
sub skipmess
{
my ( $string ) = @_ ;
@@ -12154,25 +14061,25 @@ sub tests_bytes_display_string
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( 1_048_575 ), 'bytes_display_string: 1_048_575' ) ;
- ok( '1.000 MiB' eq bytes_display_string( 1_048_576 ), 'bytes_display_string: 1_048_576' ) ;
+ is( '0.000 KiB', bytes_display_string( 0 ), 'bytes_display_string: 0' ) ;
+ is( '0.001 KiB', bytes_display_string( 1 ), 'bytes_display_string: 1' ) ;
+ is( '0.010 KiB', bytes_display_string( 10 ), 'bytes_display_string: 10' ) ;
+ is( '1.000 MiB', bytes_display_string( 1_048_575 ), 'bytes_display_string: 1_048_575' ) ;
+ is( '1.000 MiB', bytes_display_string( 1_048_576 ), 'bytes_display_string: 1_048_576' ) ;
- ok( '1.000 GiB' eq bytes_display_string( 1_073_741_823 ), 'bytes_display_string: 1_073_741_823 ' ) ;
- ok( '1.000 GiB' eq bytes_display_string( 1_073_741_824 ), 'bytes_display_string: 1_073_741_824 ' ) ;
+ is( '1.000 GiB', bytes_display_string( 1_073_741_823 ), 'bytes_display_string: 1_073_741_823 ' ) ;
+ is( '1.000 GiB', bytes_display_string( 1_073_741_824 ), 'bytes_display_string: 1_073_741_824 ' ) ;
- ok( '1.000 TiB' eq bytes_display_string( 1_099_511_627_775 ), 'bytes_display_string: 1_099_511_627_775' ) ;
- ok( '1.000 TiB' eq bytes_display_string( 1_099_511_627_776 ), 'bytes_display_string: 1_099_511_627_776' ) ;
+ is( '1.000 TiB', bytes_display_string( 1_099_511_627_775 ), 'bytes_display_string: 1_099_511_627_775' ) ;
+ is( '1.000 TiB', bytes_display_string( 1_099_511_627_776 ), 'bytes_display_string: 1_099_511_627_776' ) ;
- ok( '1.000 PiB' eq bytes_display_string( 1_125_899_906_842_623 ), 'bytes_display_string: 1_125_899_906_842_623' ) ;
- ok( '1.000 PiB' eq bytes_display_string( 1_125_899_906_842_624 ), 'bytes_display_string: 1_125_899_906_842_624' ) ;
+ is( '1.000 PiB', bytes_display_string( 1_125_899_906_842_623 ), 'bytes_display_string: 1_125_899_906_842_623' ) ;
+ is( '1.000 PiB', bytes_display_string( 1_125_899_906_842_624 ), 'bytes_display_string: 1_125_899_906_842_624' ) ;
- ok( '1024.000 PiB' eq bytes_display_string( 1_152_921_504_606_846_975 ), 'bytes_display_string: 1_152_921_504_606_846_975' ) ;
- ok( '1024.000 PiB' eq bytes_display_string( 1_152_921_504_606_846_976 ), 'bytes_display_string: 1_152_921_504_606_846_976' ) ;
+ is( '1024.000 PiB', bytes_display_string( 1_152_921_504_606_846_975 ), 'bytes_display_string: 1_152_921_504_606_846_975' ) ;
+ is( '1024.000 PiB', bytes_display_string( 1_152_921_504_606_846_976 ), 'bytes_display_string: 1_152_921_504_606_846_976' ) ;
- ok( '1048576.000 PiB' eq bytes_display_string( 1_180_591_620_717_411_303_424 ), 'bytes_display_string: 1_180_591_620_717_411_303_424' ) ;
+ is( '1048576.000 PiB', bytes_display_string( 1_180_591_620_717_411_303_424 ), 'bytes_display_string: 1_180_591_620_717_411_303_424' ) ;
#myprint( bytes_display_string( 1_180_591_620_717_411_303_424 ), "\n" ) ;
note( 'Leaving tests_bytes_display_string()' ) ;
@@ -12257,27 +14164,31 @@ sub useheader_suggestion
return ;
}
-sub stats
+sub do_and_print_stats
{
my $mysync = shift ;
- if ( ! $mysync->{stats} ) {
+ if ( ! $mysync->{can_do_stats} ) {
return ;
}
my $timeend = time ;
my $timediff = $timeend - $mysync->{timestart} ;
- my $timeend_str = localtime $timeend ;
+ my $timeend_str = localtimez( $timeend ) ;
+
+ my $cpu_time = cpu_time( $mysync ) ;
+ my $cpu_percent = cpu_percent( $mysync, $cpu_time, $timediff ) ;
+ my $cpu_percent_global = cpu_percent_global( $mysync, $cpu_percent ) ;
my $memory_consumption_at_end = memory_consumption( ) || 0 ;
my $memory_consumption_at_start = $mysync->{ memory_consumption_at_start } || 0 ;
- my $memory_ratio = ($max_msg_size_in_bytes) ?
- mysprintf('%.1f', $memory_consumption_at_end / $max_msg_size_in_bytes) : 'NA' ;
+ my $memory_ratio = ( $mysync->{ biggest_message_transferred } ) ?
+ mysprintf( '%.1f', $memory_consumption_at_end / $mysync->{ biggest_message_transferred } ) : 'NA' ;
# my $useheader_suggestion = useheader_suggestion( $mysync ) ;
myprint( "++++ Statistics\n" ) ;
- myprint( "Transfer started on : $timestart_str\n" ) ;
+ myprint( "Transfer started on : $mysync->{ 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" ) ;
@@ -12285,8 +14196,8 @@ sub stats
myprint( "(could be $nb_msg_skipped_dry_mode without dry mode)" ) if ( $mysync->{dry} ) ;
myprint( "\n" ) ;
myprint( "Messages skipped : $mysync->{ 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 found duplicate on host1 : $mysync->{ acc1 }->{ nb_msg_duplicate }\n" ) ;
+ myprint( "Messages found duplicate on host2 : $mysync->{ acc2 }->{ nb_msg_duplicate }\n" ) ;
myprint( "Messages found crossduplicate on host2 : $mysync->{ h2_nb_msg_crossdup }\n" ) ;
myprint( "Messages void (noheader) on host1 : $mysync->{ h1_nb_msg_noheader } ", useheader_suggestion( $mysync ), "\n" ) ;
myprint( "Messages void (noheader) on host2 : $h2_nb_msg_noheader\n" ) ;
@@ -12294,8 +14205,8 @@ sub stats
nb_messages_in_2_not_in_1( $mysync ) ;
myprintf( "Messages found in host1 not in host2 : %s messages\n", $mysync->{ nb_messages_in_1_not_in_2 } ) ;
myprintf( "Messages found in host2 not in host1 : %s messages\n", $mysync->{ nb_messages_in_2_not_in_1 } ) ;
- myprint( "Messages deleted on host1 : $mysync->{ h1_nb_msg_deleted }\n" ) ;
- myprint( "Messages deleted on host2 : $h2_nb_msg_deleted\n" ) ;
+ myprint( "Messages deleted on host1 : $mysync->{ acc1 }->{ nb_msg_deleted }\n" ) ;
+ myprint( "Messages deleted on host2 : $mysync->{ acc2 }->{ nb_msg_deleted }\n" ) ;
myprintf( "Total bytes transferred : %s (%s)\n",
$mysync->{total_bytes_transferred},
bytes_display_string( $mysync->{total_bytes_transferred} ) ) ;
@@ -12311,10 +14222,10 @@ sub stats
$memory_consumption_at_end / $KIBI / $KIBI,
$memory_consumption_at_start / $KIBI / $KIBI ) ;
myprint( "Load end is : " . ( join( q{ }, loadavg( ) ) || 'unknown' ), " on $mysync->{cpu_number} cores\n" ) ;
-
- myprintf("Biggest message : %s bytes (%s)\n",
- $max_msg_size_in_bytes,
- bytes_display_string( $max_msg_size_in_bytes) ) ;
+ myprint( "CPU time and %cpu : $cpu_time sec $cpu_percent %cpu $cpu_percent_global %allcpus\n" ) ;
+ myprintf("Biggest message transferred : %s bytes (%s)\n",
+ $mysync->{ biggest_message_transferred },
+ bytes_display_string( $mysync->{ biggest_message_transferred } ) ) ;
myprint( "Memory/biggest message ratio : $memory_ratio\n" ) ;
if ( $mysync->{ foldersizesatend } and $mysync->{ foldersizes } ) {
@@ -12440,7 +14351,7 @@ sub load_modules
}
-
+# Globals: $skipsize $wholeheaderifneeded
sub parse_header_msg
{
my ( $mysync, $imap, $m_uid, $s_heads, $s_fir, $side, $s_hash ) = @_ ;
@@ -12463,9 +14374,7 @@ sub parse_header_msg
#myprint( Data::Dumper->Dump( [ $head, \%useheader ] ) ) ;
- my $headstr ;
-
- $headstr = header_construct( $head, $side, $m_uid ) ;
+ my $headstr = header_construct( $mysync, $head, $side, $m_uid ) ;
if ( ( ! $headstr ) and ( $mysync->{addheader} ) and ( $side eq 'Host1' ) ) {
my $header = add_header( $m_uid ) ;
@@ -12481,49 +14390,124 @@ sub parse_header_msg
my $idate = $s_fir->{$m_uid}->{'INTERNALDATE'} ;
$size = length $headstr unless ( $size ) ;
my $m_md5 = md5_base64( $headstr ) ;
- $mysync->{ debug } and myprint( "$side: uid $m_uid sig $m_md5 size $size idate $idate\n" ) ;
+
my $key ;
- if ($skipsize) {
+ 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 ) ;
+ if ( exists $s_hash->{"$key"} )
+ {
+ # 0 return code is used to identify duplicate message hash
+ my $dup_ref = $s_hash->{"$key"}->{'U'} ;
+ my $num = scalar( @{ $dup_ref } ) ;
+ push( @{ $dup_ref }, $m_uid ) ;
+ my $keydup = "$key#$num" ;
+ $mysync->{ debug } and myprint( "$side: uid $m_uid sig $keydup size $size idate $idate dup @{ $dup_ref }\n" ) ;
+ if ( $mysync->{ syncduplicates } )
+ {
+ $s_hash->{"$keydup"}{'5'} = $m_md5 ;
+ $s_hash->{"$keydup"}{'s'} = $size ;
+ $s_hash->{"$keydup"}{'D'} = $idate ;
+ $s_hash->{"$keydup"}{'F'} = $flags ;
+ $s_hash->{"$keydup"}{'m'} = $m_uid ;
+ }
+ return 0 ;
+ }
+ else
+ {
+ $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 ;
+ $s_hash->{"$key"}{'U'} = [ $m_uid ] ; # ? or [ ] ?
+ $mysync->{ debug } and myprint( "$side: uid $m_uid sig $key size $size idate $idate\n" ) ;
+ return( 1 ) ;
+ }
+
+ # we should not be here
+ return ;
}
+sub tests_header_construct
+{
+ note( 'Entering tests_header_construct()' ) ;
+
+ is( undef, header_construct( ), 'header_construct: no args => undef' ) ;
+ my $mysync = {} ;
+ my $head = {
+ 'key1' => [ 'val1_key1' ]
+ } ;
+ is( undef, header_construct( $mysync, $head, 'Host1', '1' ), 'header_construct: key1 val1_key1 no useheader => undef' ) ;
+
+ $mysync->{useheader}->{ 'KEY1' } = 1 ;
+ is( 'KEY1: VAL1_KEY1', header_construct( $mysync, $head, 'Host1', '1' ), 'header_construct: key1 val1_key1 => KEY1: VAL1_KEY1' ) ;
+
+
+
+ $head = {
+ 'key1' => [ 'val1_key1', 'val3_key1', 'val2_key1' ]
+ } ;
+ is( 'KEY1: VAL1_KEY1KEY1: VAL2_KEY1KEY1: VAL3_KEY1', header_construct( $mysync, $head, 'Host1', '1' ),
+ 'header_construct: key1 val1_key1 val3_key1 val2_key1 => KEY1: VAL1_KEY1KEY1: VAL2_KEY1KEY1: VAL3_KEY1' ) ;
+
+ $head = {
+ 'key1' => [ 'val1_key1', 'val3_key1', ' val2_key1' ]
+ } ;
+ is( 'KEY1: VAL1_KEY1KEY1: VAL2_KEY1KEY1: VAL3_KEY1', header_construct( $mysync, $head, 'Host1', '1' ),
+ 'header_construct: key1 val1_key1 val3_key1 val2_key1 => KEY1: VAL1_KEY1KEY1: VAL2_KEY1KEY1: VAL3_KEY1' ) ;
+
+ $mysync->{useheader}->{ 'ALL' } = 1 ;
+
+ is( 'KEY1: VAL1_KEY1KEY1: VAL2_KEY1KEY1: VAL3_KEY1', header_construct( $mysync, $head, 'Host1', '1' ),
+ 'header_construct: key1 val1_key1 val3_key1 val2_key1 useheader ALL => KEY1: VAL1_KEY1KEY1: VAL2_KEY1KEY1: VAL3_KEY1' ) ;
+
+ $mysync->{skipheader} = 'key1' ;
+ is( undef, header_construct( $mysync, $head, 'Host1', '1' ),
+ 'header_construct: key1 val1_key1 val3_key1 val2_key1 useheader ALL => undef' ) ;
+
+ $head = {
+ 'key1' => [ 'val1_key1', 'val3_key1', ' val2_key1' ],
+ 'key2' => [ 'val1_key2', 'val3_key2', ' val2_key2' ]
+ } ;
+ is( 'KEY2: VAL1_KEY2KEY2: VAL2_KEY2KEY2: VAL3_KEY2', header_construct( $mysync, $head, 'Host1', '1' ),
+ 'header_construct: ... useheader ALL skipheader key1 => KEY2: VAL1_KEY2KEY2: VAL2_KEY2KEY2: VAL3_KEY2' ) ;
+
+
+ note( 'Leaving tests_header_construct()' ) ;
+ return ;
+}
+
+
+# No global in header_construct
sub header_construct
{
+ my( $mysync, $head, $side, $m_uid ) = @_ ;
- my( $head, $side, $m_uid ) = @_ ;
-
- my $headstr ;
+ my @headstr ;
foreach my $h ( sort keys %{ $head } ) {
- next if ( not ( exists $useheader{ uc $h } )
- and ( not exists $useheader{ 'ALL' } )
+ next if ( not ( exists $mysync->{useheader}->{ uc $h } )
+ and ( not exists $mysync->{useheader}->{ 'ALL' } )
) ;
- foreach my $val ( sort @{$head->{$h}} ) {
+ foreach my $val ( @{$head->{$h}} ) {
my $H = header_line_normalize( $h, $val ) ;
# show stuff in debug mode
- $sync->{ debug } and myprint( "$side uid $m_uid header [$H]", "\n" ) ;
+ $mysync->{ debug } and myprint( "$side uid $m_uid header [$H]", "\n" ) ;
- if ($skipheader and $H =~ m/$skipheader/xi) {
- $sync->{ debug } and myprint( "$side uid $m_uid skipping header [$H]\n" ) ;
+ if ( $mysync->{skipheader} and $H =~ m/$mysync->{skipheader}/xi) {
+ $mysync->{ debug } and myprint( "$side uid $m_uid skipping header [$H]\n" ) ;
next ;
}
- $headstr .= "$H" ;
+ push @headstr, $H ;
}
}
+ my $headstr = join( '', sort @headstr ) || undef ;
return( $headstr ) ;
}
@@ -12655,7 +14639,6 @@ sub tests_nthline
is( q{}, nthline( 'W/tmp/tests/noexist.txt', 2 ), 'nthline: 2nd getting empty string from inexisting W/tmp/tests/noexist.txt' ) ;
ok( (-d 'W/tmp/tests/' or mkpath( 'W/tmp/tests/' ) ), 'nthline: mkpath W/tmp/tests/' ) ;
-
is( "L1\nL2\nL3\nL4\n" , string_to_file( "L1\nL2\nL3\nL4\n", 'W/tmp/tests/nthline.txt' ), 'nthline: put L1\nL2\nL3\nL4\n in W/tmp/tests/nthline.txt' ) ;
is( 'L3' , nthline( 'W/tmp/tests/nthline.txt', 3 ), 'nthline: get L3 from W/tmp/tests/nthline.txt' ) ;
@@ -12689,21 +14672,44 @@ sub nthline
}
}
+sub tests_file_to_array
+{
+ note( 'Entering tests_file_to_array()' ) ;
+
+ is( undef, file_to_array( ), 'file_to_array: no args => undef' ) ;
+ is( undef, file_to_array( '/noexist' ), 'file_to_array: /noexist => undef' ) ;
+ is( undef, file_to_array( '/' ), 'file_to_array: reading a directory => undef' ) ;
+
+ ok( (-d 'W/tmp/tests/' or mkpath( 'W/tmp/tests/' ) ), 'file_to_array: mkpath W/tmp/tests/' ) ;
+ is( "L1\nL2\nL3\nL4\n" , string_to_file( "L1\nL2\nL3\nL4\n", 'W/tmp/tests/file_to_array.txt' ), 'file_to_array: put L1\nL2\nL3\nL4\n in W/tmp/tests/file_to_array.txt' ) ;
+ is_deeply( [ "L1\n", "L2\n", "L3\n", "L4\n" ] , [ file_to_array( 'W/tmp/tests/file_to_array.txt' ) ], 'file_to_array: get back L1\n L2\n L3\n L4\n from W/tmp/tests/file_to_array.txt' ) ;
+
+ note( 'Leaving tests_file_to_array()' ) ;
+ return ;
+}
-# Should be unit tested and then be used by file_to_string, refactoring file_to_string
sub file_to_array
{
my( $file ) = shift ;
+ if ( ! $file ) { return ; }
+ if ( ! -e $file ) { return ; }
+ if ( ! -f $file ) { return ; }
+ if ( ! -r $file ) { return ; }
+
my @string ;
- open my $FILE, '<', $file or do {
+ if ( open my $FILE, '<', $file )
+ {
+ @string = <$FILE> ;
+ close $FILE ;
+ return( @string ) ;
+ }
+ else
+ {
myprint( "Error reading file $file : $OS_ERROR\n" ) ;
return ;
- } ;
- @string = <$FILE> ;
- close $FILE ;
- return( @string ) ;
+ }
}
@@ -12735,15 +14741,8 @@ sub file_to_string
if ( ! -e $file ) { return ; }
if ( ! -f $file ) { return ; }
if ( ! -r $file ) { return ; }
- my @string ;
- if ( open my $FILE, '<', $file ) {
- @string = <$FILE> ;
- close $FILE ;
- return( join q{}, @string ) ;
- }else{
- myprint( "Error reading file $file : $OS_ERROR\n" ) ;
- return ;
- }
+
+ return( join q{}, file_to_array( $file ) ) ;
}
@@ -13157,7 +15156,7 @@ sub tests_version_from_rcs
{
note( 'Entering tests_version_from_rcs()' ) ;
- is( undef, version_from_rcs( ), 'version_from_rcs: no args => UNKNOWN' ) ;
+ is( undef, version_from_rcs( ), 'version_from_rcs: no args => undef' ) ;
is( 1.831, version_from_rcs( q{imapsync,v 1.831 2017/08/27} ), 'version_from_rcs: imapsync,v 1.831 2017/08/27 => 1.831' ) ;
is( 'UNKNOWN', version_from_rcs( 1.831 ), 'version_from_rcs: 1.831 => UNKNOWN' ) ;
@@ -13221,47 +15220,104 @@ sub tests_cpu_number
{
note( 'Entering tests_cpu_number()' ) ;
- is( 1, is_an_integer( cpu_number( ) ), "cpu_number: is_an_integer" ) ;
+ is( 1, is_integer( cpu_number( ) ), "cpu_number: is_integer" ) ;
ok( 1 <= cpu_number( ), "cpu_number: 1 or more" ) ;
is( 1, cpu_number( 1 ), "cpu_number: 1 => 1" ) ;
is( 1, cpu_number( $MINUS_ONE ), "cpu_number: -1 => 1" ) ;
is( 1, cpu_number( 'lalala' ), "cpu_number: lalala => 1" ) ;
is( $NUMBER_42, cpu_number( $NUMBER_42 ), "cpu_number: $NUMBER_42 => $NUMBER_42" ) ;
+
+ note( "cpu_number = " . cpu_number( ) . "\n" ) ;
+ note( "hostname = " . hostname( ) . "\n" ) ;
+ SKIP: {
+ if ( ! ( 'i005' eq hostname() ) )
+ {
+ skip( 'cpu_number on host != i005 (FreeBSD)', 1 ) ;
+ }
+ is( 4, cpu_number( ), "cpu_number: on i005 (FreeBSD) => 4" ) ;
+ } ;
+
+ SKIP: {
+ if ( ! ( 'petite' eq hostname() ) )
+ {
+ skip( 'cpu_number on host != petite (Linux)', 1 ) ;
+ }
+ is( 2, cpu_number( ), "cpu_number: on petite (Linux) => 2" ) ;
+ } ;
+
+ SKIP: {
+ if ( ! ( skip_macosx( ) ) )
+ {
+ skip( 'cpu_number on host != polarhome macosx (Darwin MacOS X 10.7.5 Lion)', 1 ) ;
+ }
+ is( 2, cpu_number( ), "cpu_number: on polarhome macosx (Darwin MacOS X 10.7.5 Lion) => 2" ) ;
+ } ;
+
+ SKIP: {
+ if ( ! ( 'pcHPDV7-HP' eq hostname() ) )
+ {
+ skip( 'cpu_number on host != pcHPDV7-HP (Windows 7, 64bits)', 1 ) ;
+ }
+ is( 2, cpu_number( ), "cpu_number: on pcHPDV7-HP (Windows 7, 64bits) => 2" ) ;
+ } ;
+
+ SKIP: {
+ if ( ! ( 'CUILLERE' eq hostname() ) )
+ {
+ skip( 'cpu_number on host != CUILLERE (Windows XP, 32bits)', 1 ) ;
+ }
+ is( 1, cpu_number( ), "cpu_number: on CUILLERE (Windows XP, 32bits) => 1" ) ;
+ } ;
+
+
note( 'Leaving tests_cpu_number()' ) ;
return ;
}
-sub cpu_number
-{
+
+sub cpu_number {
my $cpu_number_forced = shift ;
# Well, here 1 is better than 0 or undef
my $cpu_number = 1 ; # Default value, erased if better found
my @cpuinfo ;
- if ( $ENV{"NUMBER_OF_PROCESSORS"} ) {
+ if ( $ENV{"NUMBER_OF_PROCESSORS"} )
+ {
# might be under a Windows system
$cpu_number = $ENV{"NUMBER_OF_PROCESSORS"} ;
- $sync->{ debug } and myprint( "Number of processors found by env var NUMBER_OF_PROCESSORS: $cpu_number\n" ) ;
- }elsif ( 'darwin' eq $OSNAME or 'freebsd' eq $OSNAME ) {
+ #myprint( "Number of processors found by env var NUMBER_OF_PROCESSORS: $cpu_number\n" ) ;
+ }
+
+ if ( 'darwin' eq $OSNAME )
+ {
$cpu_number = backtick( "sysctl -n hw.ncpu" ) ;
chomp( $cpu_number ) ;
- $sync->{ debug } and myprint( "Number of processors found by cmd 'sysctl -n hw.ncpu': $cpu_number\n" ) ;
- }elsif ( ! -e '/proc/cpuinfo' ) {
- $sync->{ debug } and myprint( "Number of processors not found so I might assume there is only 1\n" ) ;
- $cpu_number = 1 ;
- }elsif( @cpuinfo = file_to_array( '/proc/cpuinfo' ) ) {
- $cpu_number = grep { /^processor/mxs } @cpuinfo ;
- $sync->{ debug } and myprint( "Number of processors found via /proc/cpuinfo: $cpu_number\n" ) ;
+ #myprint( "Number of processors found by cmd 'sysctl -n hw.ncpu': $cpu_number\n" ) ;
}
-
- if ( defined $cpu_number_forced ) {
+
+ if ( 'freebsd' eq $OSNAME )
+ {
+ $cpu_number = backtick( "sysctl -n kern.smp.cpus" ) ;
+ chomp( $cpu_number ) ;
+ #myprint( "Number of processors found by cmd 'sysctl -n kern.smp.cpus': $cpu_number\n" ) ;
+ }
+
+ if ( 'linux' eq $OSNAME && -e '/proc/cpuinfo' )
+ {
+ @cpuinfo = file_to_array( '/proc/cpuinfo' ) ;
+ $cpu_number = grep { /^processor/mxs } @cpuinfo ;
+ #myprint( "Number of processors found via /proc/cpuinfo: $cpu_number\n" ) ;
+ }
+
+ if ( defined $cpu_number_forced )
+ {
$cpu_number = $cpu_number_forced ;
}
+
return( integer_or_1( $cpu_number ) ) ;
}
-
sub tests_integer_or_1
{
note( 'Entering tests_integer_or_1()' ) ;
@@ -13279,33 +15335,33 @@ sub tests_integer_or_1
sub integer_or_1
{
my $number = shift ;
- if ( is_an_integer( $number ) ) {
+ if ( is_integer( $number ) ) {
return $number ;
}
# else
return 1 ;
}
-sub tests_is_an_integer
+sub tests_is_integer
{
- note( 'Entering tests_is_an_integer()' ) ;
+ note( 'Entering tests_is_integer()' ) ;
- is( undef, is_an_integer( ), 'is_an_integer: no args => undef ' ) ;
- ok( is_an_integer( 1 ), 'is_an_integer: 1 => yes ') ;
- ok( is_an_integer( $NUMBER_42 ), 'is_an_integer: 42 => yes ') ;
- ok( is_an_integer( "$NUMBER_42" ), 'is_an_integer: "$NUMBER_42" => yes ') ;
- ok( is_an_integer( '42' ), 'is_an_integer: "42" => yes ') ;
- ok( is_an_integer( $NUMBER_104_857_600 ), 'is_an_integer: 104_857_600 => yes') ;
- ok( is_an_integer( "$NUMBER_104_857_600" ), 'is_an_integer: "$NUMBER_104_857_600" => yes') ;
- ok( is_an_integer( '104857600' ), 'is_an_integer: 104857600 => yes') ;
- ok( ! is_an_integer( 'blabla' ), 'is_an_integer: blabla => no' ) ;
- ok( ! is_an_integer( q{} ), 'is_an_integer: empty string => no' ) ;
+ is( undef, is_integer( ), 'is_integer: no args => undef ' ) ;
+ ok( is_integer( 1 ), 'is_integer: 1 => yes ') ;
+ ok( is_integer( $NUMBER_42 ), 'is_integer: 42 => yes ') ;
+ ok( is_integer( "$NUMBER_42" ), 'is_integer: "$NUMBER_42" => yes ') ;
+ ok( is_integer( '42' ), 'is_integer: "42" => yes ') ;
+ ok( is_integer( $NUMBER_104_857_600 ), 'is_integer: 104_857_600 => yes') ;
+ ok( is_integer( "$NUMBER_104_857_600" ), 'is_integer: "$NUMBER_104_857_600" => yes') ;
+ ok( is_integer( '104857600' ), 'is_integer: 104857600 => yes') ;
+ ok( ! is_integer( 'blabla' ), 'is_integer: blabla => no' ) ;
+ ok( ! is_integer( q{} ), 'is_integer: empty string => no' ) ;
- note( 'Leaving tests_is_an_integer()' ) ;
+ note( 'Leaving tests_is_integer()' ) ;
return ;
}
-sub is_an_integer
+sub is_integer
{
my $number = shift ;
if ( ! defined $number ) { return ; }
@@ -13470,48 +15526,38 @@ sub tests_load_and_delay
is( undef, load_and_delay( ), 'load_and_delay: no args => undef ' ) ;
is( undef, load_and_delay( 1 ), 'load_and_delay: not 4 args => undef ' ) ;
is( undef, load_and_delay( 0, 1, 1, 1 ), 'load_and_delay: division per 0 => undef ' ) ;
- is( 0, load_and_delay( 1, 1, 1, 1 ), 'load_and_delay: one core, loads are all 1 => ok ' ) ;
- is( 0, load_and_delay( 1, 1, 1, 1, 'lalala' ), 'load_and_delay: five arguments is ok' ) ;
- is( 0, load_and_delay( 2, 2, 2, 2 ), 'load_and_delay: two core, loads are all 2 => ok ' ) ;
- is( 0, load_and_delay( 2, 2, 4, 5 ), 'load_and_delay: two core, load1m is 2 => ok ' ) ;
-# Old behavior, rather strict
- # is( 0, load_and_delay( 1, 0, 0, 0 ), 'load_and_delay: one core, load1m=0 load5m=0 load15m=0 => 0 ' ) ;
- # is( 0, load_and_delay( 1, 0, 0, 2 ), 'load_and_delay: one core, load1m=0 load5m=0 load15m=2 => 0 ' ) ;
- # is( 0, load_and_delay( 1, 0, 2, 0 ), 'load_and_delay: one core, load1m=0 load5m=2 load15m=0 => 0 ' ) ;
- # is( 0, load_and_delay( 1, 0, 2, 2 ), 'load_and_delay: one core, load1m=0 load5m=2 load15m=2 => 0 ' ) ;
- # is( 1, load_and_delay( 1, 2, 0, 0 ), 'load_and_delay: one core, load1m=2 load5m=0 load15m=0 => 1 ' ) ;
- # is( 1, load_and_delay( 1, 2, 0, 2 ), 'load_and_delay: one core, load1m=2 load5m=0 load15m=2 => 1 ' ) ;
- # is( 5, load_and_delay( 1, 2, 2, 0 ), 'load_and_delay: one core, load1m=2 load5m=2 load15m=0 => 5 ' ) ;
- # is( 15, load_and_delay( 1, 2, 2, 2 ), 'load_and_delay: one core, load1m=2 load5m=2 load15m=2 => 15 ' ) ;
+# ( $cpu_num, $avg_1_min, $avg_5_min, $avg_15_min )
- # is( 0, load_and_delay( 4, 0, 2, 2 ), 'load_and_delay: four core, load1m=0 load5m=2 load15m=2 => 0 ' ) ;
- # is( 1, load_and_delay( 4, 8, 0, 0 ), 'load_and_delay: four core, load1m=2 load5m=0 load15m=0 => 1 ' ) ;
- # is( 1, load_and_delay( 4, 8, 0, 2 ), 'load_and_delay: four core, load1m=2 load5m=0 load15m=2 => 1 ' ) ;
- # is( 5, load_and_delay( 4, 8, 8, 0 ), 'load_and_delay: four core, load1m=2 load5m=2 load15m=0 => 5 ' ) ;
- # is( 15, load_and_delay( 4, 8, 8, 8 ), 'load_and_delay: four core, load1m=2 load5m=2 load15m=2 => 15 ' ) ;
+ is( 0, load_and_delay( 1, 1, 1, 1 ), 'load_and_delay: one core, loads are all 1 => ok ' ) ;
+ is( 0, load_and_delay( 1, 1, 1, 1, 'lalala' ), 'load_and_delay: five arguments is ok' ) ;
+ is( 0, load_and_delay( 2, 2, 2, 2 ), 'load_and_delay: two core, loads are all 2 => ok ' ) ;
+ is( 0, load_and_delay( 2, 2, 4, 5 ), 'load_and_delay: two core, load1m is 2 => ok ' ) ;
-# New behavior, tolerate more load
- is( 0, load_and_delay( 1, 0, 0, 0 ), 'load_and_delay: one core, load1m=0 load5m=0 load15m=0 => 0 ' ) ;
- is( 0, load_and_delay( 1, 0, 0, 2 ), 'load_and_delay: one core, load1m=0 load5m=0 load15m=2 => 0 ' ) ;
- is( 0, load_and_delay( 1, 0, 2, 0 ), 'load_and_delay: one core, load1m=0 load5m=2 load15m=0 => 0 ' ) ;
- is( 0, load_and_delay( 1, 0, 2, 2 ), 'load_and_delay: one core, load1m=0 load5m=2 load15m=2 => 0 ' ) ;
- is( 0, load_and_delay( 1, 2, 0, 0 ), 'load_and_delay: one core, load1m=2 load5m=0 load15m=0 => 1 ' ) ;
- is( 0, load_and_delay( 1, 2, 0, 2 ), 'load_and_delay: one core, load1m=2 load5m=0 load15m=2 => 1 ' ) ;
- is( 0, load_and_delay( 1, 2, 2, 0 ), 'load_and_delay: one core, load1m=2 load5m=2 load15m=0 => 5 ' ) ;
- is( 0, load_and_delay( 1, 2, 2, 2 ), 'load_and_delay: one core, load1m=2 load5m=2 load15m=2 => 15 ' ) ;
+ is( 0, load_and_delay( 1, 0, 0, 0 ), 'load_and_delay: one core, load1m=0 load5m=0 load15m=0 => 0 ' ) ;
+ is( 0, load_and_delay( 1, 0, 0, 2 ), 'load_and_delay: one core, load1m=0 load5m=0 load15m=2 => 0 ' ) ;
+ is( 0, load_and_delay( 1, 0, 2, 0 ), 'load_and_delay: one core, load1m=0 load5m=2 load15m=0 => 0 ' ) ;
+ is( 0, load_and_delay( 1, 0, 2, 2 ), 'load_and_delay: one core, load1m=0 load5m=2 load15m=2 => 0 ' ) ;
+ is( 0, load_and_delay( 1, 0, 3, 3 ), 'load_and_delay: one core, load1m=0 load5m=3 load15m=3 => 0 ' ) ;
+ is( 0, load_and_delay( 1, 0, 4, 4 ), 'load_and_delay: one core, load1m=0 load5m=3 load15m=3 => 0 ' ) ;
+ is( 0, load_and_delay( 1, 2, 0, 0 ), 'load_and_delay: one core, load1m=2 load5m=0 load15m=0 => 0 ' ) ;
+ is( 0, load_and_delay( 1, 2, 0, 2 ), 'load_and_delay: one core, load1m=2 load5m=0 load15m=2 => 0 ' ) ;
+ is( 0, load_and_delay( 1, 2, 2, 0 ), 'load_and_delay: one core, load1m=2 load5m=2 load15m=0 => 0 ' ) ;
+ is( 0, load_and_delay( 1, 2, 2, 2 ), 'load_and_delay: one core, load1m=2 load5m=2 load15m=2 => 0 ' ) ;
+ is( 0, load_and_delay( 1, 2.9, 2.9, 2.9 ), 'load_and_delay: one core, load1m=2.9 load5m=2.9 load15m=2.9 => 0 ' ) ;
+
+ is( 0, load_and_delay( 1, 3, 0, 0 ), 'load_and_delay: one core, load1m=3 load5m=0 load15m=0 => 0 ' ) ;
+ is( 0, load_and_delay( 1, 3, 2.9, 2.9 ), 'load_and_delay: one core, load1m=3 load5m=2.9 load15m=2.9 => 0 ' ) ;
+ is( 0, load_and_delay( 1, 3, 3, 2.9 ), 'load_and_delay: one core, load1m=3 load5m=3 load15m=2.9 => 0 ' ) ;
+ is( 0, load_and_delay( 1, 3, 3, 3 ), 'load_and_delay: one core, load1m=3 load5m=3 load15m=3 => 0 ' ) ;
+
+ is( 1, load_and_delay( 1, 6, 0, 0 ), 'load_and_delay: one core, load1m=3 load5m=0 load15m=0 => 1 ' ) ;
+ is( 1, load_and_delay( 1, 6, 5.9, 5.9 ), 'load_and_delay: one core, load1m=3 load5m=2.9 load15m=2.9 => 1 ' ) ;
+ is( 5, load_and_delay( 1, 6, 6, 5.9 ), 'load_and_delay: one core, load1m=3 load5m=3 load15m=2.9 => 5 ' ) ;
+ is( 15, load_and_delay( 1, 6, 6, 6 ), 'load_and_delay: one core, load1m=3 load5m=3 load15m=3 => 15 ' ) ;
- is( 1, load_and_delay( 1, 4, 0, 0 ), 'load_and_delay: one core, load1m=4 load5m=0 load15m=0 => 1 ' ) ;
- is( 1, load_and_delay( 1, 4, 0, 4 ), 'load_and_delay: one core, load1m=4 load5m=0 load15m=4 => 1 ' ) ;
- is( 5, load_and_delay( 1, 4, 4, 0 ), 'load_and_delay: one core, load1m=4 load5m=4 load15m=0 => 5 ' ) ;
- is( 15, load_and_delay( 1, 4, 4, 4 ), 'load_and_delay: one core, load1m=4 load5m=4 load15m=4 => 15 ' ) ;
- is( 0, load_and_delay( 4, 0, 9, 9 ), 'load_and_delay: four core, load1m=0 load5m=9 load15m=9 => 0 ' ) ;
- is( 1, load_and_delay( 4, 9, 0, 0 ), 'load_and_delay: four core, load1m=9 load5m=0 load15m=0 => 1 ' ) ;
- is( 1, load_and_delay( 4, 9, 0, 9 ), 'load_and_delay: four core, load1m=9 load5m=0 load15m=9 => 1 ' ) ;
- is( 5, load_and_delay( 4, 9, 9, 0 ), 'load_and_delay: four core, load1m=9 load5m=9 load15m=0 => 5 ' ) ;
- is( 15, load_and_delay( 4, 9, 9, 9 ), 'load_and_delay: four core, load1m=9 load5m=9 load15m=9 => 15 ' ) ;
note( 'Leaving tests_load_and_delay()' ) ;
return ;
@@ -13531,12 +15577,123 @@ sub load_and_delay
# Let divide by number of cores
( $avg_1_min, $avg_5_min, $avg_15_min ) = map { $_ / $cpu_num } ( $avg_1_min, $avg_5_min, $avg_15_min ) ;
# One of avg ok => ok, for now it is a OR
- if ( $avg_1_min <= 2 ) { return 0 ; }
- if ( $avg_5_min <= 2 ) { return 1 ; } # Retry in 1 minute
- if ( $avg_15_min <= 2 ) { return 5 ; } # Retry in 5 minutes
+ if ( $avg_1_min < 6 ) { return 0 ; }
+ if ( $avg_5_min < 6 ) { return 1 ; } # Retry in 1 minute
+ if ( $avg_15_min < 6 ) { return 5 ; } # Retry in 5 minutes
return 15 ; # Retry in 15 minutes
}
+
+sub tests_cpu_time
+{
+ note( 'Entering tests_cpu_time()' ) ;
+
+ ok( is_number( cpu_time( ) ), 'cpu_time: no args => a number' ) ;
+
+ my $mysync = { } ;
+ $mysync->{ debug } = 1 ;
+ ok( is_number( cpu_time( $mysync ) ), 'cpu_time: {} => a number' ) ;
+
+ note( 'Leaving tests_cpu_time()' ) ;
+ return ;
+}
+
+sub cpu_time
+{
+ my $mysync = shift ;
+
+ my @cpu_times = times ;
+ if ( ! @cpu_times ) { return ; }
+
+ my $cpu_time = 0 ;
+ # last element is the sum of all elements
+ $cpu_time = ( map { $cpu_time += $_ } @cpu_times )[ -1 ] ;
+ $mysync->{ debug } and myprint( join(' + ', @cpu_times), " = $cpu_time\n" ) ;
+
+ return $cpu_time ;
+}
+
+
+sub tests_cpu_percent
+{
+ note( 'Entering tests_cpu_percent()' ) ;
+
+ is( '0.0', cpu_percent( ), 'cpu_percent: no args => 0.0' ) ;
+ my $mysync = { } ;
+ $mysync->{ debug } = 1 ;
+ is( '0.0', cpu_percent( $mysync ), 'cpu_percent: {} => 0.0' ) ;
+ is( '0.0', cpu_percent( $mysync, 0 ), 'cpu_percent: {} 0 => 0.0' ) ;
+ is( '300.0', cpu_percent( $mysync, 3 ), 'cpu_percent: {} 3 => 300.0' ) ;
+ is( '30.0', cpu_percent( $mysync, 3, 10 ), 'cpu_percent: {} 3 10 => 30.0' ) ;
+ is( '0.0', cpu_percent( $mysync, 0, 10 ), 'cpu_percent: {} 0 10 => 0.0' ) ;
+
+ note( 'Leaving tests_cpu_percent()' ) ;
+ return ;
+}
+
+sub cpu_percent
+{
+ my $mysync = shift ;
+ my $cpu_time = shift || 0 ;
+ my $timediff = shift || 1 ; # no division by 0
+
+ if ( $cpu_time > $timediff )
+ {
+ myprint( "Strange: cpu_time $cpu_time > timediff $timediff\n" ) ;
+ }
+ my $cpu_percent = 0 ;
+ $cpu_percent = mysprintf( '%.1f', 100 * $cpu_time / $timediff ) ;
+ $mysync->{ debug } and myprint( "cpu_percent: $cpu_percent \n" ) ;
+
+ return $cpu_percent ;
+
+}
+
+sub tests_cpu_percent_global
+{
+ note( 'Entering tests_cpu_percent_global()' ) ;
+
+ is( '0.0', cpu_percent_global( ), 'cpu_percent_global: no args => 0' ) ;
+ my $mysync = { } ;
+ $mysync->{ debug } = 1 ;
+ is( '0.0', cpu_percent_global( $mysync ), 'cpu_percent_global: {} => 0' ) ;
+ is( '0.0', cpu_percent_global( $mysync, 0 ), 'cpu_percent_global: {} 0 => 0' ) ;
+
+ SKIP: {
+ if ( ! ( 'i005' eq hostname() ) )
+ {
+ skip( 'cpu_percent_global on host != i005', 1 ) ;
+ }
+ is( '25.0', cpu_percent_global( $mysync, 100 ), 'cpu_percent_global: {} 100 => 25 on host i005' ) ;
+ } ;
+
+ SKIP: {
+ if ( ! ( 'petite' eq hostname() ) )
+ {
+ skip( 'cpu_percent_global on host != petite', 1 ) ;
+ }
+ is( '50.0', cpu_percent_global( $mysync, 100 ), 'cpu_percent_global: {} 100 => 50 on host petite' ) ;
+ } ;
+
+ note( 'Leaving tests_cpu_percent_global()' ) ;
+ return ;
+}
+
+sub cpu_percent_global
+{
+ my $mysync = shift ;
+ my $cpu_percent = shift || 0 ;
+
+ my $cpu_number = cpu_number( ) ;
+
+ my $cpu_percent_global ;
+ $cpu_percent_global = mysprintf( '%.1f', $cpu_percent / $cpu_number ) ;
+ $mysync->{ debug } and myprint( "cpu_percent_global: $cpu_percent_global \n" ) ;
+
+ return( $cpu_percent_global ) ;
+}
+
+
sub ram_memory_info
{
# In GigaBytes so division by 1024 * 1024 * 1024
@@ -13623,7 +15780,7 @@ sub memory_consumption_of_pids
#myprint( "ps: @ps" ) ;
# Use IPC::Open3 from perlcrit -3
- # It stalls on Darwin, don't understand why!
+ # But it stalls on Darwin, I don't understand why!
#my @ps = backtick( "ps -o vsz -p @pid" ) ;
#myprint( "ps: @ps" ) ;
@@ -13795,7 +15952,7 @@ sub check_binary_embed_all_dyn_libs
else
{
# Found only embedded dynamic lib
- myprint( "Found nothing\n" ) ;
+ myprint( "Found only embedded dynamic lib. Good!\n" ) ;
return 1 ;
}
}
@@ -13819,22 +15976,22 @@ sub search_dyn_lib_locale
sub search_dyn_lib_locale_darwin
{
- my $command = qq{ lsof -p $PID | grep ' REG ' | grep .dylib | grep -v '/par-' } ;
+ my $command = qq{ lsof -p $PROCESS_ID | grep ' REG ' | grep .dylib | grep -v '/par-' } ;
myprint( "Search non embeded dynamic libs with the command: $command\n" ) ;
return backtick( $command ) ;
}
sub search_dyn_lib_locale_linux
{
- my $command = qq{ lsof -p $PID | grep ' REG ' | grep -v '/tmp/par-' | grep '\.so' } ;
+ my $command = qq{ lsof -p $PROCESS_ID | grep ' REG ' | grep -v '/tmp/par-' | grep '\.so' } ;
myprint( "Search non embeded dynamic libs with the command: $command\n" ) ;
return backtick( $command ) ;
}
sub search_dyn_lib_locale_MSWin32
{
- my $command = qq{ Listdlls.exe $PID|findstr Strawberry } ;
- # $command = qq{ Listdlls.exe $PID|findstr Strawberry } ;
+ my $command = qq{ Listdlls.exe $PROCESS_ID|findstr Strawberry } ;
+ # $command = qq{ Listdlls.exe $PROCESS_ID|findstr Strawberry } ;
myprint( "Search non embeded dynamic libs with the command: $command\n" ) ;
return qx( $command ) ;
}
@@ -14229,6 +16386,8 @@ sub comment_on_final_diff_in_1_not_in_2
{
myprint( "The sync is not finished, there are ",
$mysync->{ nb_messages_in_1_not_in_2 },
+ " among ",
+ $nb_identified_h1_messages,
" identified messages in host1 that are not on host2.\n" ) ;
}
@@ -14242,7 +16401,7 @@ sub comment_on_final_diff_in_1_not_in_2
}
else
{
- myprint( "There is no unidentified message\n" ) ;
+ myprint( "There is no unidentified message on host1.\n" ) ;
}
return ;
@@ -14277,9 +16436,11 @@ sub comment_on_final_diff_in_2_not_in_1
{
myprint( "The sync is not strict, there are ",
$mysync->{ nb_messages_in_2_not_in_1 },
- " messages in host2 that are not on host1.",
- " Use --delete2 to delete them and have a strict sync.",
- " ($nb_identified_h2_messages identified messages in host2)\n" ) ;
+ " among ",
+ $nb_identified_h2_messages,
+ " identified messages in host2 that are not on host1.",
+ " Use --delete2 and sync again to delete them and have a strict sync.\n"
+ ) ;
}
return ;
}
@@ -15053,24 +17214,25 @@ sub setlogfile
my( $mysync ) = shift ;
# When aborting another process the log file name finishes with "_abort.txt"
- my $abort_suffix = ( $mysync->{abort} ) ? '_abort' : q{} ;
+ my $abort_suffix = ( $mysync->{ abort } ) ? '_abort' : q{} ;
+
# When acting as a proxy the log file name finishes with "_remote.txt"
- # proxy mode is not done yet
- my $remote_suffix = ( $mysync->{remote} ) ? '_remote' : q{} ;
+ # proxy mode is not done in imapsync, it is done by proximapsync
+ my $remote_suffix = ( $mysync->{ remote } ) ? '_remote' : q{} ;
my $suffix = (
- filter_forbidden_characters( slash_to_underscore( $mysync->{user1} ) ) || q{} )
+ filter_forbidden_characters( slash_to_underscore( $mysync->{ user1 } ) ) || q{} )
. '_'
- . ( filter_forbidden_characters( slash_to_underscore( $mysync->{user2} ) ) || q{} )
+ . ( filter_forbidden_characters( slash_to_underscore( $mysync->{ user2 } ) ) || q{} )
. $remote_suffix . $abort_suffix ;
- $mysync->{logdir} = defined $mysync->{logdir} ? $mysync->{logdir} : $DEFAULT_LOGDIR ;
+ $mysync->{ logdir } = defined $mysync->{ logdir } ? $mysync->{ logdir } : $DEFAULT_LOGDIR ;
- $mysync->{logfile} = defined $mysync->{logfile}
- ? "$mysync->{logdir}/$mysync->{logfile}"
- : logfile( $mysync->{timestart}, $suffix, $mysync->{logdir} ) ;
+ $mysync->{ logfile } = defined $mysync->{ logfile }
+ ? "$mysync->{ logdir }/$mysync->{ logfile }"
+ : logfile( $mysync->{ timestart }, $suffix, $mysync->{ logdir } ) ;
- return( $mysync->{logfile} ) ;
+ return( $mysync->{ logfile } ) ;
}
sub tests_logfile
@@ -15082,7 +17244,8 @@ sub tests_logfile
skip( 'Too hard to have a well known timezone on Windows', 10 ) if ( 'MSWin32' eq $OSNAME ) ;
local $ENV{TZ} = 'GMT' ;
- { POSIX::tzset unless ('MSWin32' eq $OSNAME) ;
+ {
+ POSIX::tzset unless ('MSWin32' eq $OSNAME) ;
is( '1970_01_01_00_00_00_000.txt', logfile( ), 'logfile: no args => 1970_01_01_00_00_00.txt' ) ;
is( '1970_01_01_00_00_00_000.txt', logfile( 0 ), 'logfile: 0 => 1970_01_01_00_00_00.txt' ) ;
is( '1970_01_01_00_01_01_000.txt', logfile( 61 ), 'logfile: 0 => 1970_01_01_00_01_01.txt' ) ;
@@ -15129,6 +17292,38 @@ sub logfile
}
+sub tests_localtimez
+{
+ note( 'Entering tests_localtimez()' ) ;
+
+ SKIP: {
+ # Too hard to have a well known timezone on Windows
+ skip( 'Too hard to have a well known timezone on Windows', 1 ) if ( 'MSWin32' eq $OSNAME ) ;
+ local $ENV{TZ} = 'GMT' ;
+ like( localtimez( 0 ), qr'1970-01-01 00:00:00 \+0000 (GMT|UTC)', 'localtimez: 0 => match 1970-01-01 00:00:00 +0000 GMT' ) ;
+ }
+
+ is( localtimez( ), localtimez( time ), 'localtimez: undef => equals currrent' ) ;
+ note( 'Leaving tests_localtimez()' ) ;
+ return ;
+}
+
+
+
+sub localtimez
+{
+ my $time = shift ;
+
+ $time = defined( $time ) ? $time : time ;
+
+ my $datetimestr = POSIX::strftime( '%A %e %B %Y-%m-%d %H:%M:%S %z %Z', localtime( $time ) ) ;
+
+ #myprint( "$datetimestr\n" ) ;
+ return $datetimestr ;
+}
+
+
+
sub tests_slash_to_underscore
{
@@ -15227,6 +17422,8 @@ sub tests_teelaunch
is( undef, teelaunch( $mysync ), 'teelaunch: arg empty {} => undef' ) ;
$mysync->{logfile} = q{} ;
is( undef, teelaunch( $mysync ), 'teelaunch: logfile empty string => undef' ) ;
+
+ # First time, learning IO::Tee intrasics
$mysync->{logfile} = 'W/tmp/tests/tests_teelaunch.txt' ;
isa_ok( my $tee = teelaunch( $mysync ), 'IO::Tee' , 'teelaunch: logfile W/tmp/tests/tests_teelaunch.txt' ) ;
is( 1, print( $tee "Hi!\n" ), 'teelaunch: write Hi!') ;
@@ -15234,6 +17431,38 @@ sub tests_teelaunch
is( 1, print( $tee "Hoo\n" ), 'teelaunch: write Hoo') ;
is( "Hi!\nHoo\n", file_to_string( 'W/tmp/tests/tests_teelaunch.txt' ), 'teelaunch: reading W/tmp/tests/tests_teelaunch.txt is Hi!\nHoo\n' ) ;
+ # closing so tee won't be happy
+ close $mysync->{logfile_handle} ;
+ is( undef, print( $tee "Argh1\n" ), 'teelaunch: write Argh1') ;
+ is( undef, print( $tee "Argh2\n" ), 'teelaunch: write Argh2') ;
+ # write not done
+ is( "Hi!\nHoo\n", file_to_string( 'W/tmp/tests/tests_teelaunch.txt' ), 'teelaunch: reading W/tmp/tests/tests_teelaunch.txt is still Hi!\nHoo\n' ) ;
+ print join( ' ', $tee->handles ), "\n";
+ is( 2, scalar $tee->handles, 'teelaunch: 2 handles') ;
+ shift @{*{$tee}};
+ print join(' ', $tee->handles), "\n" ;
+ is( 1, scalar $tee->handles, 'teelaunch: 1 handle') ;
+ is( 1, print( $tee "Argh3\n" ), 'teelaunch: write Argh3 yeah') ;
+
+ shift @{*{$tee}};
+ # will not print anything now
+ is( 0, scalar $tee->handles, 'teelaunch: 0 handle') ;
+ is( 1, print( $tee "Argh 4\n" ), 'teelaunch: write Argh4 no') ;
+
+ # Second time, lesson learnt IO::Tee
+ $mysync->{logfile} = 'W/tmp/tests/tests_teelaunch2.txt' ;
+ isa_ok( $tee = teelaunch( $mysync ), 'IO::Tee' , 'teelaunch: logfile W/tmp/tests/tests_teelaunch2.txt' ) ;
+ is( 1, print( $tee "Hi!\n" ), 'teelaunch: write Hi!') ;
+ is( "Hi!\n", file_to_string( 'W/tmp/tests/tests_teelaunch2.txt' ), 'teelaunch: reading W/tmp/tests/tests_teelaunch2.txt is Hi!\n' ) ;
+ is( 1, print( $tee "Hoo\n" ), 'teelaunch: write Hoo') ;
+ is( "Hi!\nHoo\n", file_to_string( 'W/tmp/tests/tests_teelaunch2.txt' ), 'teelaunch: reading W/tmp/tests/tests_teelaunch2.txt is Hi!\nHoo\n' ) ;
+
+ is( 1, teefinish( $mysync ), 'teefinish: return 1') ;
+ is( 1, print( $tee "Argh1\n" ), 'teelaunch: write Argh1') ;
+ is( 1, print( $tee "Argh2\n" ), 'teelaunch: write Argh2') ;
+ is( "Hi!\nHoo\n", file_to_string( 'W/tmp/tests/tests_teelaunch2.txt' ), 'teelaunch: reading W/tmp/tests/tests_teelaunch2.txt is still Hi!\nHoo\n' ) ;
+ is( 1, teefinish( $mysync ), 'teefinish: still return 1') ;
+
note( 'Leaving tests_teelaunch()' ) ;
return ;
}
@@ -15268,6 +17497,28 @@ sub teelaunch
return $tee ;
}
+sub teefinish
+{
+ my $mysync = shift ;
+
+ if ( ! defined( $mysync ) ) { return ; }
+
+ my $tee = $mysync->{tee} ;
+
+ if ( ! defined( $tee ) ) { return ; }
+
+ if ( 2 == scalar $tee->handles )
+ {
+ shift @{*{$tee}};
+ }
+ else
+ {
+ # nothing
+ }
+ return scalar $tee->handles ;
+}
+
+
sub getpwuid_any_os
{
my $uid = shift ;
@@ -15278,15 +17529,44 @@ sub getpwuid_any_os
}
+
+
+sub abortifneeded
+{
+ my $mysync = shift ;
+ if ( -e $mysync->{ abortfile } )
+ {
+ myprint( "Asked to terminate by file $mysync->{ abortfile }\n" ) ;
+ do_and_print_stats( $mysync ) ;
+ myprint( "You should resynchronize those accounts by running a sync again,\n",
+ "since some messages and entire folders might still be missing on host2.\n"
+ ) ;
+ exit_clean( $mysync, $EXIT_BY_FILE ) ;
+ return ;
+ }
+ else
+ {
+ return ;
+ }
+}
+
sub simulong
{
- my $max_seconds = shift ;
+ my $mysync = shift ;
+
+ my $max_seconds = $mysync->{ simulong } ;
+
+ if ( ! $max_seconds ) { return ; }
+
my $division = 5 ;
- my $last_count = $division * $max_seconds ;
+ my $last_count = int( $division * $max_seconds ) ;
+ $mysync->{ debug } and myprint "last_count $last_count = int( division $division * max_seconds $max_seconds)\n" ;
foreach my $i ( 1 .. ( $last_count ) ) {
- myprint( "Are you still here ETA: " . ($last_count - $i) . "/$last_count msgs left\n" ) ;
+ myprint( "Are you still here ETA: " . ( $last_count - $i ) . "/$last_count msgs left\n" ) ;
+ #this one is for testing huge page behavior
#myprint( "Are you still here ETA: " . ($last_count - $i) . "/$last_count msgs left\n" . ( "Ah" x 40 . "\n") x 4000 ) ;
sleep( 1 / $division ) ;
+ abortifneeded( $mysync ) ;
}
return ;
@@ -15302,12 +17582,14 @@ sub printenv
return ;
}
-sub testsexit
+
+sub unittestssuite
{
my $mysync = shift ;
if ( ! ( $mysync->{ tests } or $mysync->{ testsdebug } or $mysync->{ testsunit } ) ) {
return ;
}
+
my $test_builder = Test::More->builder ;
tests( $mysync ) ;
testsdebug( $mysync ) ;
@@ -15323,16 +17605,12 @@ sub testsexit
#$test_builder->reset( ) ;
myprint( "Summary of tests: failed $nb_tests_failed tests, run $nb_tests_run tests, expected to run $nb_tests_expected tests.\n",
"List of failed tests:\n", $tests_failed ) ;
- exit $EXIT_TESTS_FAILED ;
+ return $EXIT_TESTS_FAILED ;
}
cleanup_mess_from_tests( ) ;
- # Cover is larger with --tests --testslive
- if ( ! $mysync->{ testslive } )
- {
- exit ;
- }
- return ;
+
+ return 0 ;
}
sub cleanup_mess_from_tests
@@ -15476,8 +17754,8 @@ sub gmail12
$mysync->{ssl1} = ( defined $mysync->{ssl1} ) ? $mysync->{ssl1} : 1 ;
$mysync->{host2} ||= 'imap.gmail.com' ;
$mysync->{ssl2} = ( defined $mysync->{ssl2} ) ? $mysync->{ssl2} : 1 ;
- $mysync->{maxbytespersecond} ||= 20_000 ; # should be 10_000 when computed from Gmail documentation
- $mysync->{maxbytesafter} ||= 1_000_000_000 ;
+ $mysync->{maxbytespersecond} ||= 20_000 ; # should be less than 10_000 when computed from Gmail documentation
+ $mysync->{maxbytesafter} ||= 1_000_000_000 ; # In fact it is documented as half: 500_000_000
$mysync->{automap} = ( defined $mysync->{automap} ) ? $mysync->{automap} : 1 ;
$mysync->{maxsleep} = ( defined $mysync->{maxsleep} ) ? $mysync->{maxsleep} : $MAX_SLEEP ; ;
$skipcrossduplicates = ( defined $skipcrossduplicates ) ? $skipcrossduplicates : 0 ;
@@ -15495,8 +17773,8 @@ sub gmail1
# Gmail at host2
$mysync->{host1} ||= 'imap.gmail.com' ;
$mysync->{ssl1} = ( defined $mysync->{ssl1} ) ? $mysync->{ssl1} : 1 ;
- $mysync->{maxbytespersecond} ||= 40_000 ; # should be 20_000 computed from by Gmail documentation
- $mysync->{maxbytesafter} ||= 2_500_000_000 ;
+ $mysync->{maxbytespersecond} ||= 40_000 ; # should be 30_000 computed from by Gmail documentation
+ $mysync->{maxbytesafter} ||= 3_000_000_000 ; #
$mysync->{automap} = ( defined $mysync->{automap} ) ? $mysync->{automap} : 1 ;
$mysync->{maxsleep} = ( defined $mysync->{maxsleep} ) ? $mysync->{maxsleep} : $MAX_SLEEP ; ;
$skipcrossduplicates = ( defined $skipcrossduplicates ) ? $skipcrossduplicates : 1 ;
@@ -15507,24 +17785,24 @@ sub gmail1
return ;
}
-sub gmail2
+sub gmail2
{
my $mysync = shift ;
# Gmail at host2
- $mysync->{host2} ||= 'imap.gmail.com' ;
- $mysync->{ssl2} = ( defined $mysync->{ssl2} ) ? $mysync->{ssl2} : 1 ;
- $mysync->{maxbytespersecond} ||= 20_000 ; # should be 10_000 computed from by Gmail documentation
- $mysync->{maxbytesafter} ||= 1_000_000_000 ; # In fact it is documented as half: 500_000_000
+ $mysync->{ host2 } ||= 'imap.gmail.com' ;
+ $mysync->{ ssl2 } = ( defined $mysync->{ ssl2 } ) ? $mysync->{ ssl2 } : 1 ;
+ $mysync->{ maxbytespersecond } ||= 20_000 ; # should be less than 10_000 computed from by Gmail documentation
+ $mysync->{ maxbytesafter } ||= 1_000_000_000 ; # In fact it is documented as half: 500_000_000
- $mysync->{automap} = ( defined $mysync->{automap} ) ? $mysync->{automap} : 1 ;
+ $mysync->{ automap } = ( defined $mysync->{ automap } ) ? $mysync->{ automap } : 1 ;
#$skipcrossduplicates = ( defined $skipcrossduplicates ) ? $skipcrossduplicates : 1 ;
$mysync->{ expunge1 } = ( defined $mysync->{ expunge1 } ) ? $mysync->{ expunge1 } : 1 ;
- $mysync->{addheader} = ( defined $mysync->{addheader} ) ? $mysync->{addheader} : 1 ;
- $mysync->{maxsleep} = ( defined $mysync->{maxsleep} ) ? $mysync->{maxsleep} : $MAX_SLEEP ; ;
+ $mysync->{ addheader } = ( defined $mysync->{ addheader } ) ? $mysync->{ addheader } : 1 ;
+ $mysync->{ maxsleep } = ( defined $mysync->{ maxsleep } ) ? $mysync->{ maxsleep } : $MAX_SLEEP ; ;
- $mysync->{maxsize} = ( defined $mysync->{maxsize} ) ? $mysync->{maxsize} : $GMAIL_MAXSIZE ;
+ #$mysync->{ maxsize } = ( defined $mysync->{ maxsize } ) ? $mysync->{ maxsize } : $GMAIL_MAXSIZE ;
- if ( ! $mysync->{noexclude} ) {
+ if ( ! $mysync->{ noexclude } ) {
push @exclude, '\[Gmail\]$' ;
}
push @useheader, 'Message-Id' ;
@@ -15571,7 +17849,7 @@ sub office2
$mysync->{ssl2} = ( defined $mysync->{ssl2} ) ? $mysync->{ssl2} : 1 ;
$mysync->{ maxsize } ||= 45_000_000 ;
$mysync->{maxmessagespersecond} ||= 4 ;
- #push @regexflag, 's/\\\\Flagged//g' ; # No problem without! tested 2018_09_10
+ #push @{ $mysync->{ regexflag } }, 's/\\\\Flagged//g' ; # No problem without! tested 2018_09_10
$disarmreadreceipts = ( defined $disarmreadreceipts ) ? $disarmreadreceipts : 1 ;
# I dislike double negation but here is one
if ( ! $mysync->{noregexmess} )
@@ -15607,7 +17885,7 @@ sub exchange2
$disarmreadreceipts = ( defined $disarmreadreceipts ) ? $disarmreadreceipts : 1 ;
# I dislike double negation but here are two
if ( ! $mysync->{noregexflag} ) {
- push @regexflag, 's/\\\\Flagged//g' ;
+ push @{ $mysync->{ regexflag } }, 's/\\\\Flagged//g' ;
}
if ( ! $mysync->{noregexmess} ) {
push @regexmess, 's,(.{10239}),$1\r\n,g' ;
@@ -15649,14 +17927,17 @@ sub tests_resolv
is( undef, resolv( 'hostnotexist' ), 'resolv: hostnotexist => undef' ) ;
is( '127.0.0.1', resolv( '127.0.0.1' ), 'resolv: 127.0.0.1 => 127.0.0.1' ) ;
is( '127.0.0.1', resolv( 'localhost' ), 'resolv: localhost => 127.0.0.1' ) ;
- is( '5.135.158.182', resolv( 'imapsync.lamiral.info' ), 'resolv: imapsync.lamiral.info => 5.135.158.182' ) ;
+ is( '2001:41d0:2:84e0::1', resolv( 'imapsync.lamiral.info' ), 'resolv: imapsync.lamiral.info => 2001:41d0:2:84e0::1' ) ;
# ip6-localhost ( in /etc/hosts )
is( '::1', resolv( 'ip6-localhost' ), 'resolv: ip6-localhost => ::1' ) ;
is( '::1', resolv( '::1' ), 'resolv: ::1 => ::1' ) ;
- # ks2
+ # ks2ipv6 now has CNAME ks6ipv6
is( '2001:41d0:8:d8b6::1', resolv( '2001:41d0:8:d8b6::1' ), 'resolv: 2001:41d0:8:d8b6::1 => 2001:41d0:8:d8b6::1' ) ;
- is( '2001:41d0:8:d8b6::1', resolv( 'ks2ipv6.lamiral.info' ), 'resolv: ks2ipv6.lamiral.info => 2001:41d0:8:d8b6::1' ) ;
+ is( '2001:41d0:8:9951::1', resolv( 'ks6ipv6.lamiral.info' ), 'resolv: ks6ipv6.lamiral.info => 2001:41d0:8:9951::1' ) ;
+ # ks6
+ is( '2001:41d0:8:9951::1', resolv( '2001:41d0:8:9951::1' ), 'resolv: 2001:41d0:8:9951::1 => 2001:41d0:8:9951::1' ) ;
+ is( '2001:41d0:8:9951::1', resolv( 'ks6ipv6.lamiral.info' ), 'resolv: ks6ipv6.lamiral.info => 2001:41d0:8:9951::1' ) ;
# ks3
is( '2001:41d0:8:bebd::1', resolv( '2001:41d0:8:bebd::1' ), 'resolv: 2001:41d0:8:bebd::1 => 2001:41d0:8:bebd::1' ) ;
is( '2001:41d0:8:bebd::1', resolv( 'ks3ipv6.lamiral.info' ), 'resolv: ks3ipv6.lamiral.info => 2001:41d0:8:bebd::1' ) ;
@@ -15692,6 +17973,7 @@ sub resolv_with_getaddrinfo
{
my $host = shift @ARG ;
+ $sync->{ debug } and myprint( "Entering resolv_with_getaddrinfo( $host )\n" ) ;
if ( ! $host ) { return ; }
my ( $err_getaddrinfo, @res ) = Socket::getaddrinfo( $host, "", { socktype => Socket::SOCK_RAW } ) ;
@@ -15706,14 +17988,17 @@ sub resolv_with_getaddrinfo
if ( $err_getnameinfo ) {
myprint( "Cannot getnameinfo of $host: $err_getnameinfo\n" ) ;
return ;
- }
- $sync->{ debug } and myprint( "$host => $ipaddr\n" ) ;
- push @addr, $ipaddr ;
- my $reverse ;
- ( $err_getnameinfo, $reverse ) = Socket::getnameinfo( $ai->{addr}, 0, Socket::NIx_NOSERV() ) ;
- $sync->{ debug } and myprint( "$host => $ipaddr => $reverse\n" ) ;
- }
+ }else{
+ $sync->{ debug } and myprint( "$host => $ipaddr\n" ) ;
+ push @addr, $ipaddr ;
+ my $reverse ;
+ ( $err_getnameinfo, $reverse ) = Socket::getnameinfo( $ai->{addr}, 0, Socket::NIx_NOSERV() ) ;
+ $sync->{ debug } and myprint( "$host => $ipaddr => $reverse\n" ) ;
+ }
+ $sync->{ debug } and myprint( "$host => $ipaddr\n" ) ;
+ }
+ $sync->{ debug } and myprint( "Leaving resolv_with_getaddrinfo( $host => $addr[0])\n" ) ;
return $addr[0] ;
}
@@ -15733,8 +18018,8 @@ sub tests_resolvrev
is( 'ip6-localhost', resolvrev( 'ip6-localhost' ), 'resolvrev: ip6-localhost => ip6-localhost' ) ;
is( 'ip6-localhost', resolvrev( '::1' ), 'resolvrev: ::1 => ip6-localhost' ) ;
# ks2
- is( 'ks2ipv6.lamiral.info', resolvrev( '2001:41d0:8:d8b6::1' ), 'resolvrev: 2001:41d0:8:d8b6::1 => ks2ipv6.lamiral.info' ) ;
- is( 'ks2ipv6.lamiral.info', resolvrev( 'ks2ipv6.lamiral.info' ), 'resolvrev: ks2ipv6.lamiral.info => ks2ipv6.lamiral.info' ) ;
+ is( 'ks6ipv6.lamiral.info', resolvrev( '2001:41d0:8:d8b6::1' ), 'resolvrev: 2001:41d0:8:d8b6::1 => ks6ipv6.lamiral.info' ) ;
+ is( 'ks6ipv6.lamiral.info', resolvrev( 'ks6ipv6.lamiral.info' ), 'resolvrev: ks6ipv6.lamiral.info => ks6ipv6.lamiral.info' ) ;
# ks3
is( 'ks3ipv6.lamiral.info', resolvrev( '2001:41d0:8:bebd::1' ), 'resolvrev: 2001:41d0:8:bebd::1 => ks3ipv6.lamiral.info' ) ;
is( 'ks3ipv6.lamiral.info', resolvrev( 'ks3ipv6.lamiral.info' ), 'resolvrev: ks3ipv6.lamiral.info => ks3ipv6.lamiral.info' ) ;
@@ -15793,7 +18078,7 @@ sub tests_imapsping
is( undef, imapsping( ), 'imapsping: no args => undef' ) ;
is( undef, imapsping( 'hostnotexist' ), 'imapsping: hostnotexist => undef' ) ;
is( 1, imapsping( 'imapsync.lamiral.info' ), 'imapsping: imapsync.lamiral.info => 1' ) ;
- is( 1, imapsping( 'ks2ipv6.lamiral.info' ), 'imapsping: ks2ipv6.lamiral.info => 1' ) ;
+ is( 1, imapsping( 'ks6ipv6.lamiral.info' ), 'imapsping: ks6ipv6.lamiral.info => 1' ) ;
note( 'Leaving tests_imapsping()' ) ;
return ;
}
@@ -15873,7 +18158,7 @@ sub tests_sslcheck
$mysync = {
sslcheck => 1,
- host1 => 'imapsync.lamiral.info',
+ host1 => 'test1.lamiral.info',
tls1 => 1,
} ;
@@ -15881,32 +18166,32 @@ sub tests_sslcheck
$mysync = {
sslcheck => 1,
- host1 => 'imapsync.lamiral.info',
+ host1 => 'test1.lamiral.info',
} ;
- is( 1, sslcheck( $mysync ), 'sslcheck: imapsync.lamiral.info => 1' ) ;
- is( 1, $mysync->{ssl1}, 'sslcheck: imapsync.lamiral.info => ssl1 1' ) ;
+ is( 1, sslcheck( $mysync ), 'sslcheck: test1.lamiral.info => 1' ) ;
+ is( 1, $mysync->{ssl1}, 'sslcheck: test1.lamiral.info => ssl1 1' ) ;
$mysync->{sslcheck} = 0 ;
is( undef, sslcheck( $mysync ), 'sslcheck: sslcheck off => undef' ) ;
$mysync = {
sslcheck => 1,
- host1 => 'imapsync.lamiral.info',
+ host1 => 'test1.lamiral.info',
host2 => 'test2.lamiral.info',
} ;
- is( 2, sslcheck( $mysync ), 'sslcheck: imapsync.lamiral.info + test2.lamiral.info => 2' ) ;
+ is( 2, sslcheck( $mysync ), 'sslcheck: test1.lamiral.info + test2.lamiral.info => 2' ) ;
$mysync = {
sslcheck => 1,
- host1 => 'imapsync.lamiral.info',
+ host1 => 'test1.lamiral.info',
host2 => 'test2.lamiral.info',
tls1 => 1,
} ;
- is( 1, sslcheck( $mysync ), 'sslcheck: imapsync.lamiral.info + test2.lamiral.info + tls1 => 1' ) ;
+ is( 1, sslcheck( $mysync ), 'sslcheck: test1.lamiral.info + test2.lamiral.info + tls1 => 1' ) ;
note( 'Leaving tests_sslcheck()' ) ;
return ;
@@ -15977,10 +18262,10 @@ sub testslive_init
sub testslive6_init
{
my $mysync = shift ;
- $mysync->{host1} ||= 'ks2ipv6.lamiral.info' ;
+ $mysync->{host1} ||= 'ks6ipv6.lamiral.info' ;
$mysync->{user1} ||= 'test1' ;
$mysync->{password1} ||= 'secret1' ;
- $mysync->{host2} ||= 'ks2ipv6.lamiral.info' ;
+ $mysync->{host2} ||= 'ks6ipv6.lamiral.info' ;
$mysync->{user2} ||= 'test2' ;
$mysync->{password2} ||= 'secret2' ;
return ;
@@ -16143,7 +18428,6 @@ sub mygetppid
}
-
sub tests_toggle_sleep
{
note( 'Entering tests_toggle_sleep()' ) ;
@@ -16318,6 +18602,147 @@ EOF
return( $usage ) ;
}
+
+
+
+sub setvalfromcgikey
+{
+ my ( $mysync, $mycgi, $key, $val ) = @ARG ;
+
+ my $badthings = 0 ;
+
+
+ my ( $name, $type, $struct ) ;
+ if ( $key !~ m/^([\w\d\|]+)([=:][isf])?([\+!\@\%])?$/mxs )
+ {
+ $badthings++ ;
+ next ; # Unknown item
+ }
+ else
+ {
+ $name = [ split '|', $1, 1 ]->[0] ; # option name ab|cd|ef => keep only ab
+ $type = $2 ; # = or : followed by i or s or f
+ $struct = $3 ; # + or ! or @ or %
+ }
+
+ if ( ( $struct || q{} ) eq '+' )
+ {
+ ${$val} = $mycgi->param( $name ) ; # "Incremental" integer
+ }
+ elsif ( $type )
+ {
+ my @values = $mycgi->multi_param( $name ) ;
+
+ #myprint( "type[$type]values[@values]\$struct[", $struct || q{}, "]val[$val]ref(val)[", ref($val), "]\n" ) ;
+ if ( ( $struct || q{} ) eq '%' or ref( $val ) eq 'HASH' )
+ {
+ setvalfromhash( $val, $type, @values ) ;
+ }
+ else
+ {
+ setvalfromlist( $mysync, $val, $name, $type, $struct, @values ) ;
+ }
+ }
+ else
+ {
+ setvalfromcheckbox( $mysync, $mycgi, $key, $name, $val ) ;
+ }
+
+ return $badthings ;
+}
+
+sub setvalfromlist
+{
+ my ( $mysync, $val, $name, $type, $struct, @values ) = @ARG ;
+ if ( $type =~ m/i$/mxs )
+ {
+ @values = map { q{} ne $_ ? int $_ : undef } @values ;
+ }
+ elsif ( $type =~ m/f$/mxs )
+ {
+ @values = map { 0 + $_ } @values ;
+ }
+
+ if ( ( $struct || q{} ) eq '@' )
+ {
+ @{ ${$val} } = @values ;
+ my @option = map { +( "--$name", "$_" ) } @values ;
+ push @{ $mysync->{ cmdcgi } }, @option ;
+ }
+ elsif ( ref( $val ) eq 'ARRAY' )
+ {
+ @{$val} = @values ;
+ }
+ elsif ( my $value = $values[0] )
+ {
+ ${$val} = $value ;
+ push @{ $mysync->{ cmdcgi } }, "--$name", $value ;
+ }
+ else
+ {
+ }
+
+ return ;
+}
+sub setvalfromhash
+{
+ my ( $val, $type, @values ) = @ARG ;
+
+ my %values = map { split /=/mxs, $_ } @values ;
+
+ if ( $type =~ m/i$/mxs )
+ {
+ foreach my $k ( keys %values )
+ {
+ $values{$k} = int $values{$k} ;
+ }
+ }
+ elsif ( $type =~ m/f$/mxs )
+ {
+ foreach my $k ( keys %values ) {
+ $values{$k} = 0 + $values{$k};
+ }
+ }
+
+ if ( 'REF' eq ref $val )
+ {
+ %{ ${$val} } = %values ;
+ }
+ else
+ {
+ %{$val} = %values ;
+ }
+
+ return ;
+}
+
+
+sub setvalfromcheckbox
+{
+ my ( $mysync, $mycgi, $key, $name, $val ) = @ARG ;
+
+ # Checkbox
+ # --noname is set by name=0 or name=
+ my $value = $mycgi->param( $name ) ;
+ if ( defined $value )
+ {
+ ${$val} = $value ;
+ if ( $value )
+ {
+ push @{ $mysync->{ cmdcgi } }, "--$name" ;
+ }
+ else
+ {
+ push @{ $mysync->{ cmdcgi } }, "--no$name" ;
+ }
+ }
+ else
+ {
+ ${$val} = undef ;
+ }
+ return ;
+}
+
sub myGetOptions
{
@@ -16325,6 +18750,7 @@ sub myGetOptions
# https://metacpan.org/release/Getopt-Long-CGI
# So this sub function 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.
+ # It also applies for the sub functions called from this one.
my $mysync = shift @ARG ;
my $arguments_ref = shift @ARG ;
@@ -16345,84 +18771,10 @@ sub myGetOptions
foreach my $key ( sort keys %options ) {
my $val = $options{$key} ;
- if ( $key !~ m/^([\w\d\|]+)([=:][isf])?([\+!\@\%])?$/mxs ) {
- $badthings++ ;
- next ; # Unknown item
- }
+ $badthings += setvalfromcgikey( $mysync, $mycgi, $key, $val ) ;
- my $name = [ split '|', $1, 1 ]->[0] ;
-
- if ( ( $3 || q{} ) eq '+' ) {
- ${$val} = $mycgi->param( $name ) ; # "Incremental" integer
- }
- elsif ( $2 ) {
- my @values = $mycgi->multi_param( $name ) ;
- my $type = $2 ;
-
- #myprint( "type[$type]values[@values]\$3[", $3 || q{}, "]val[$val]ref(val)[", ref($val), "]\n" ) ;
- if ( ( $3 || q{} ) eq '%' or ref( $val ) eq 'HASH' ) {
- my %values = map { split /=/mxs, $_ } @values ;
-
- if ( $type =~ m/i$/mxs ) {
- foreach my $k ( keys %values ) {
- $values{$k} = int $values{$k} ;
- }
- }
- elsif ( $type =~ m/f$/mxs ) {
- foreach my $k ( keys %values ) {
- $values{$k} = 0 + $values{$k};
- }
- }
- if ( 'REF' eq ref $val ) {
- %{ ${$val} } = %values ;
- }
- else {
- %{$val} = %values ;
- }
- }
- else {
- if ( $type =~ m/i$/mxs ) {
- @values = map { q{} ne $_ ? int $_ : undef } @values ;
- }
- elsif ( $type =~ m/f$/mxs ) {
- @values = map { 0 + $_ } @values ;
- }
- if ( ( $3 || q{} ) eq '@' ) {
- @{ ${$val} } = @values ;
- my @option = map { +( "--$name", "$_" ) } @values ;
- push @{ $mysync->{ cmdcgi } }, @option ;
- }
- elsif ( ref( $val ) eq 'ARRAY' ) {
- @{$val} = @values ;
- }
- elsif ( my $value = $values[0] )
- {
- ${$val} = $value ;
- push @{ $mysync->{ cmdcgi } }, "--$name", $value ;
- }
- else
- {
-
- }
- }
- }
- else
- {
- # Checkbox
- # Considers only --name
- # Should consider also --no-name and --noname
- my $value = $mycgi->param( $name ) ;
- if ( $value )
- {
- ${$val} = 1 ;
- push @{ $mysync->{ cmdcgi } }, "--$name" ;
- }
- else
- {
- ${$val} = undef ;
- }
- }
}
+
if ( $badthings ) {
return ; # undef or ()
}
@@ -16432,11 +18784,12 @@ sub myGetOptions
}
-my @blabla ; # just used to check get_options_cgi() with an array
+
sub tests_get_options_cgi_context
{
- note( 'Entering tests_get_options_cgi()' ) ;
+ note( 'Entering tests_get_options_cgi_context()' ) ;
+
# Temporary, have to think harder about testing CGI context in command line --tests
# API:
@@ -16454,15 +18807,21 @@ sub tests_get_options_cgi_context
my $mysync ;
is( undef, get_options( $mysync ), 'get_options cgi context: no CGI module => undef' ) ;
- require CGI ;
- CGI->import( qw( -no_debug -utf8 ) ) ;
+ # skip all next tests if the CGI module is not available
+
+ SKIP: {
+ if ( ! eval { require CGI ; } ) {
+ skip( "CGI Perl module is not installed", 19 ) ;
+ }
+
+ CGI->import( qw( -no_debug -utf8 ) ) ;
is( undef, get_options( $mysync ), 'get_options cgi context: no CGI param => undef' ) ;
# Testing boolean
$mysync->{cgi} = CGI->new( 'version=on&debugenv=on' ) ;
local $ENV{'QUERY_STRING'} = 'version=on&debugenv=on' ;
is( 22, get_options( $mysync ), 'get_options cgi context: QUERY_STRING => 22' ) ;
- is( 1, $mysync->{ version }, 'get_options cgi context: --version => 1' ) ;
+ is( 'on', $mysync->{ version }, 'get_options cgi context: --version => on' ) ;
# debugenv is not allowed in cgi context
is( undef, $mysync->{debugenv}, 'get_options cgi context: $mysync->{debugenv} => undef' ) ;
@@ -16474,14 +18833,6 @@ sub tests_get_options_cgi_context
is( 'test1', $mysync->{user1}, 'get_options cgi context: $mysync->{user1} => test1' ) ;
#local $ENV{'QUERY_STRING'} = undef ;
- # Testing @
- $mysync->{cgi} = CGI->new( 'blabla=fd1' ) ;
- get_options( $mysync ) ;
- is_deeply( [ 'fd1' ], [ @blabla ], 'get_options cgi context: @blabla => fd1' ) ;
- $mysync->{cgi} = CGI->new( 'blabla=fd1&blabla=fd2' ) ;
- get_options( $mysync ) ;
- is_deeply( [ 'fd1', 'fd2' ], [ @blabla ], 'get_options cgi context: @blabla => fd1, fd2' ) ;
-
# Testing s@ as ref
$mysync->{cgi} = CGI->new( 'folder=fd1' ) ;
get_options( $mysync ) ;
@@ -16522,10 +18873,21 @@ sub tests_get_options_cgi_context
#myprint( Data::Dumper->Dump( [ $mysync ] ) ) ;
$mysync ={} ;
- $mysync->{cgi} = CGI->new( 'justfoldersizes=on' ) ;
+ $mysync->{cgi} = CGI->new( 'testslive=on' ) ;
get_options( $mysync ) ;
- is( 1, $mysync->{ justfoldersizes }, 'get_options cgi context: --justfoldersizes=1 => justfoldersizes => 1' ) ;
- myprint( Data::Dumper->Dump( [ $mysync ] ) ) ;
+ is( 'on', $mysync->{ testslive }, 'get_options cgi context: --testslive=on => testslive => on' ) ;
+ #myprint( Data::Dumper->Dump( [ $mysync ] ) ) ;
+
+ $mysync ={} ;
+ $mysync->{cgi} = CGI->new( 'log=0' ) ;
+ get_options( $mysync ) ;
+ is( 0, $mysync->{ log }, 'get_options cgi context: --log=0 => log => 0' ) ;
+ #myprint( Data::Dumper->Dump( [ $mysync ] ) ) ;
+
+
+ # What is this fucked up indentation?
+ }
+
note( 'Leaving tests_get_options_cgi_context()' ) ;
return ;
@@ -16545,41 +18907,43 @@ sub get_options_cgi
my $opt_ret = myGetOptions(
$mysync,
\@arguments,
- 'abort' => \$mysync->{abort},
+ 'abort' => \$mysync->{ abort },
+ 'abortbyfile' => \$mysync->{ abortbyfile },
'host1=s' => \$mysync->{ host1 },
'host2=s' => \$mysync->{ host2 },
'user1=s' => \$mysync->{ user1 },
'user2=s' => \$mysync->{ user2 },
- 'password1=s' => \$mysync->{password1},
- 'password2=s' => \$mysync->{password2},
- 'dry!' => \$mysync->{dry},
- 'version' => \$mysync->{version},
- 'ssl1!' => \$mysync->{ssl1},
- 'ssl2!' => \$mysync->{ssl2},
- 'tls1!' => \$mysync->{tls1},
- 'tls2!' => \$mysync->{tls2},
- 'justlogin!' => \$mysync->{justlogin},
- 'justconnect!' => \$mysync->{justconnect},
- 'addheader!' => \$mysync->{addheader},
- 'automap!' => \$mysync->{automap},
- 'justautomap!' => \$mysync->{justautomap},
- 'gmail1' => \$mysync->{gmail1},
- 'gmail2' => \$mysync->{gmail2},
- 'office1' => \$mysync->{office1},
- 'office2' => \$mysync->{office2},
- 'exchange1' => \$mysync->{exchange1},
- 'exchange2' => \$mysync->{exchange2},
- 'domino1' => \$mysync->{domino1},
- 'domino2' => \$mysync->{domino2},
- 'f1f2=s@' => \$mysync->{f1f2},
- 'f1f2h=s%' => \$mysync->{f1f2h},
- 'folder=s@' => \$mysync->{ folder },
- 'blabla=s' => \@blabla,
- 'testslive!' => \$mysync->{testslive},
- 'testslive6!' => \$mysync->{testslive6},
- 'releasecheck!' => \$mysync->{releasecheck},
- 'simulong=i' => \$mysync->{simulong},
- 'debugsleep=f' => \$mysync->{debugsleep},
+ 'password1=s' => \$mysync->{ password1 },
+ 'password2=s' => \$mysync->{ password2 },
+ 'dry!' => \$mysync->{ dry },
+ 'dry1!' => \$mysync->{ dry1 },
+ 'version' => \$mysync->{ version },
+ 'ssl1!' => \$mysync->{ ssl1 },
+ 'ssl2!' => \$mysync->{ ssl2 },
+ 'tls1!' => \$mysync->{ tls1 },
+ 'tls2!' => \$mysync->{ tls2 },
+ 'justbanner!' => \$mysync->{ justbanner },
+ 'justlogin!' => \$mysync->{ justlogin },
+ 'justconnect!' => \$mysync->{ justconnect },
+ 'addheader!' => \$mysync->{ addheader },
+ 'automap!' => \$mysync->{ automap },
+ 'justautomap!' => \$mysync->{ justautomap },
+ 'gmail1' => \$mysync->{ gmail1 },
+ 'gmail2' => \$mysync->{ gmail2 },
+ 'office1' => \$mysync->{ office1 },
+ 'office2' => \$mysync->{ office2 },
+ 'exchange1' => \$mysync->{ exchange1 },
+ 'exchange2' => \$mysync->{ exchange2 },
+ 'domino1' => \$mysync->{ domino1 },
+ 'domino2' => \$mysync->{ domino2 },
+ 'f1f2=s@' => \$mysync->{ f1f2 },
+ 'f1f2h=s%' => \$mysync->{ f1f2h },
+ 'folder=s@' => \$mysync->{ folder },
+ 'testslive!' => \$mysync->{ testslive },
+ 'testslive6!' => \$mysync->{ testslive6 },
+ 'releasecheck!' => \$mysync->{ releasecheck },
+ 'simulong=f' => \$mysync->{ simulong },
+ 'debugsleep=f' => \$mysync->{ debugsleep },
'subfolder1=s' => \$mysync->{ subfolder1 },
'subfolder2=s' => \$mysync->{ subfolder2 },
'justfolders!' => \$mysync->{ justfolders },
@@ -16587,9 +18951,15 @@ sub get_options_cgi
'delete1!' => \$mysync->{ delete1 },
'delete2!' => \$mysync->{ delete2 },
'delete2duplicates!' => \$mysync->{ delete2duplicates },
- 'tail!' => \$mysync->{tail},
+ 'tail!' => \$mysync->{ tail },
+ 'tmphash=s' => \$mysync->{ tmphash },
+ 'exitwhenover=i' => \$mysync->{ exitwhenover },
+ 'syncduplicates!' => \$mysync->{ syncduplicates },
+ 'log!' => \$mysync->{ log },
+ 'loglogfile!' => \$mysync->{ loglogfile },
+
-# blabla and f1f2h=s% could be removed but
+# f1f2h=s% could be removed but
# tests_get_options_cgi() should be split before
# with a sub tests_myGetOptions()
) ;
@@ -16624,9 +18994,9 @@ sub get_options_cmd
'debugcontent!' => \$debugcontent,
'debugsleep=f' => \$mysync->{debugsleep},
'debugflags!' => \$debugflags,
- 'debugimap!' => \$debugimap,
- 'debugimap1!' => \$debugimap1,
- 'debugimap2!' => \$debugimap2,
+ 'debugimap!' => \$mysync->{ debugimap },
+ 'debugimap1!' => \$mysync->{ acc1 }->{ debugimap },
+ 'debugimap2!' => \$mysync->{ acc2 }->{ debugimap },
'debugdev!' => \$debugdev,
'debugmemory!' => \$mysync->{debugmemory},
'debugfolders!' => \$mysync->{debugfolders},
@@ -16635,8 +19005,11 @@ sub get_options_cmd
'debugenv!' => \$mysync->{debugenv},
'debugsig!' => \$mysync->{debugsig},
'debuglabels!' => \$mysync->{debuglabels},
- 'simulong=i' => \$mysync->{simulong},
+
+ 'simulong=f' => \$mysync->{simulong},
'abort' => \$mysync->{abort},
+ 'abortbyfile' => \$mysync->{abortbyfile},
+
'host1=s' => \$mysync->{ host1 },
'host2=s' => \$mysync->{ host2 },
'port1=i' => \$mysync->{port1},
@@ -16653,8 +19026,8 @@ sub get_options_cmd
'exchange2' => \$mysync->{exchange2},
'domino1' => \$mysync->{domino1},
'domino2' => \$mysync->{domino2},
- 'domain1=s' => \$domain1,
- 'domain2=s' => \$domain2,
+ 'domain1=s' => \$mysync->{ acc1 }->{ domain },
+ 'domain2=s' => \$mysync->{ acc2 }->{ domain },
'password1=s' => \$mysync->{password1},
'password2=s' => \$mysync->{password2},
'passfile1=s' => \$mysync->{ passfile1 },
@@ -16662,6 +19035,15 @@ sub get_options_cmd
'authmd5!' => \$authmd5,
'authmd51!' => \$authmd51,
'authmd52!' => \$authmd52,
+
+ 'trylogin!' => \$mysync->{ trylogin },
+
+ 'oauthdirect1=s' => \$mysync->{ acc1 }->{ oauthdirect },
+ 'oauthdirect2=s' => \$mysync->{ acc2 }->{ oauthdirect },
+
+ 'oauthaccesstoken1=s' => \$mysync->{ acc1 }->{ oauthaccesstoken },
+ 'oauthaccesstoken2=s' => \$mysync->{ acc2 }->{ oauthaccesstoken },
+
'sep1=s' => \$mysync->{ sep1 },
'sep2=s' => \$mysync->{ sep2 },
'sanitize!' => \$mysync->{ sanitize },
@@ -16687,10 +19069,11 @@ sub get_options_cmd
'pipemess=s' => \@pipemess,
'pipemesscheck!' => \$pipemesscheck,
'disarmreadreceipts!' => \$disarmreadreceipts,
- 'regexflag=s' => \@regexflag,
- 'noregexflag' => \$mysync->{noregexflag},
- 'filterflags!' => \$filterflags,
- 'flagscase!' => \$flagscase,
+ 'regexflag=s@' => \$mysync->{ regexflag },
+ 'noregexflag' => \$mysync->{ noregexflag },
+ 'filterflags!' => \$mysync->{ filterflags },
+ 'filterbuggyflags!' => \$mysync->{ filterbuggyflags },
+ 'flagscase!' => \$mysync->{ flagscase },
'syncflagsaftercopy!' => \$syncflagsaftercopy,
'resyncflags!' => \$mysync->{ resyncflags },
'synclabels!' => \$mysync->{ synclabels },
@@ -16703,7 +19086,7 @@ sub get_options_cmd
'delete2foldersbutnot=s' => \$delete2foldersbutnot,
'syncinternaldates!' => \$syncinternaldates,
'idatefromheader!' => \$idatefromheader,
- 'syncacls!' => \$syncacls,
+ 'syncacls!' => \$mysync->{ syncacls },
'maxsize=i' => \$mysync->{ maxsize },
'appendlimit=i' => \$mysync->{ appendlimit },
'truncmess=i' => \$mysync->{ truncmess },
@@ -16716,51 +19099,52 @@ sub get_options_cmd
'foldersizes!' => \$mysync->{ foldersizes },
'foldersizesatend!' => \$mysync->{ foldersizesatend },
'dry!' => \$mysync->{dry},
+ 'dry1!' => \$mysync->{dry1},
'expunge1|expunge!' => \$mysync->{ expunge1 },
'expunge2!' => \$mysync->{ expunge2 },
'uidexpunge2!' => \$mysync->{ uidexpunge2 },
'subscribed' => \$subscribed,
'subscribe!' => \$subscribe,
'subscribeall|subscribe_all!' => \$subscribeall,
- 'justbanner!' => \$justbanner,
+ 'justbanner!' => \$mysync->{ justbanner },
'justfolders!'=> \$mysync->{ justfolders },
'justfoldersizes!' => \$mysync->{ justfoldersizes },
'fast!' => \$fast,
'version' => \$mysync->{version},
'help' => \$help,
- 'timeout=i' => \$timeout,
- 'timeout1=i' => \$mysync->{h1}->{timeout},
- 'timeout2=i' => \$mysync->{h2}->{timeout},
- 'skipheader=s' => \$skipheader,
+ 'timeout=f' => \$mysync->{timeout},
+ 'timeout1=f' => \$mysync->{ acc1 }->{timeout},
+ 'timeout2=f' => \$mysync->{ acc2 }->{timeout},
+ 'skipheader=s' => \$mysync->{ skipheader },
'useheader=s' => \@useheader,
'wholeheaderifneeded!' => \$wholeheaderifneeded,
'messageidnodomain!' => \$messageidnodomain,
'skipsize!' => \$skipsize,
'allowsizemismatch!' => \$allowsizemismatch,
- 'fastio1!' => \$fastio1,
- 'fastio2!' => \$fastio2,
+ 'fastio1!' => \$mysync->{ acc1 }->{ fastio },
+ 'fastio2!' => \$mysync->{ acc2 }->{ fastio },
'sslcheck!' => \$mysync->{sslcheck},
'ssl1!' => \$mysync->{ssl1},
'ssl2!' => \$mysync->{ssl2},
- 'ssl1_ssl_version=s' => \$mysync->{h1}->{sslargs}->{SSL_version},
- 'ssl2_ssl_version=s' => \$mysync->{h2}->{sslargs}->{SSL_version},
- 'sslargs1=s%' => \$mysync->{h1}->{sslargs},
- 'sslargs2=s%' => \$mysync->{h2}->{sslargs},
+ 'ssl1_ssl_version=s' => \$mysync->{ acc1 }->{sslargs}->{SSL_version},
+ 'ssl2_ssl_version=s' => \$mysync->{ acc2 }->{sslargs}->{SSL_version},
+ 'sslargs1=s%' => \$mysync->{ acc1 }->{sslargs},
+ 'sslargs2=s%' => \$mysync->{ acc2 }->{sslargs},
'tls1!' => \$mysync->{tls1},
'tls2!' => \$mysync->{tls2},
'uid1!' => \$uid1,
'uid2!' => \$uid2,
- 'authmech1=s' => \$authmech1,
- 'authmech2=s' => \$authmech2,
- 'authuser1=s' => \$authuser1,
- 'authuser2=s' => \$authuser2,
- 'proxyauth1' => \$proxyauth1,
- 'proxyauth2' => \$proxyauth2,
+ 'authmech1=s' => \$mysync->{ acc1 }->{ authmech },
+ 'authmech2=s' => \$mysync->{ acc2 }->{ authmech },
+ 'authuser1=s' => \$mysync->{ acc1 }->{ authuser },
+ 'authuser2=s' => \$mysync->{ acc2 }->{ authuser },
+ 'proxyauth1' => \$mysync->{ acc1 }->{ proxyauth },
+ 'proxyauth2' => \$mysync->{ acc2 }->{ proxyauth },
'split1=i' => \$split1,
'split2=i' => \$split2,
'buffersize=i' => \$buffersize,
- 'reconnectretry1=i' => \$reconnectretry1,
- 'reconnectretry2=i' => \$reconnectretry2,
+ 'reconnectretry1=i' => \$mysync->{ acc1 }->{ reconnectretry },
+ 'reconnectretry2=i' => \$mysync->{ acc2 }->{ reconnectretry },
'tests!' => \$mysync->{ tests },
'testsdebug|tests_debug!' => \$mysync->{ testsdebug },
'testsunit=s@' => \$mysync->{testsunit},
@@ -16816,15 +19200,23 @@ sub get_options_cmd
'nof1f2' => \$mysync->{nof1f2},
'f1f2h=s%' => \$mysync->{f1f2h},
'justfolderlists!' => \$mysync->{justfolderlists},
- 'delete1emptyfolders' => \$mysync->{delete1emptyfolders},
+ 'delete1emptyfolders' => \$mysync->{delete1emptyfolders},
+ 'checknoabletosearch!' => \$mysync->{checknoabletosearch},
+ 'syncduplicates!' => \$mysync->{ syncduplicates },
+ 'dockercontext!' => \$mysync->{ dockercontext },
+
+
) ;
#myprint( Data::Dumper->Dump( [ $mysync ] ) ) ;
$mysync->{ debug } and output( $mysync, "get options: [$opt_ret][$numopt]\n" ) ;
my $numopt_after = scalar @arguments ;
#myprint( "get options: [$opt_ret][$numopt][$numopt_after]\n" ) ;
- if ( $numopt_after ) {
+
+ # The $arguments[0] test is just because parallel adds "" when it is
+ # used with {=7=} in sync_parallel_unix.sh
+ if ( $numopt_after and $arguments[0] ) {
myprint(
- "Extra arguments found: @arguments\n",
+ "Found ", scalar( @arguments ), " extra arguments : [@arguments]\n",
"It usually means a quoting issue in the command line ",
"or some misspelling options.\n",
) ;
@@ -16915,6 +19307,25 @@ sub get_options
return $ret ;
}
+
+sub condition_to_leave_after_tests
+{
+ my $mysync = shift ;
+ if ( $mysync->{ testslive } or $mysync->{ testslive6 } )
+ {
+ return 0 ;
+ }
+
+ if ( $mysync->{ tests }
+ or $mysync->{ testsdebug }
+ or $mysync->{ testsunit }
+ )
+ {
+ return 1 ;
+ }
+}
+
+
sub testunitsession
{
my $mysync = shift ;
@@ -17055,7 +19466,9 @@ sub testsdebug
#tests_killpid_by_brother( ) ;
#tests_kill_zero( ) ;
#tests_connect_socket( ) ;
- tests_probe_imapssl( ) ;
+ #tests_probe_imapssl( ) ;
+ tests_cpu_number( ) ;
+ tests_mailimapclient_connect( ) ;
#tests_always_fail( ) ;
note( 'Leaving testsdebug()' ) ;
@@ -17077,7 +19490,7 @@ sub tests
tests_compare_lists( ) ;
tests_regexmess( ) ;
tests_skipmess( ) ;
- tests_flags_regex();
+ tests_regexflags( );
tests_ucsecond( ) ;
tests_permanentflags();
tests_flags_filter( ) ;
@@ -17186,7 +19599,7 @@ sub tests
tests_remove_pidfile_not_running( ) ;
tests_match_a_pid_number( ) ;
tests_prefix_seperator_invertion( ) ;
- tests_is_an_integer( ) ;
+ tests_is_integer( ) ;
tests_integer_or_1( ) ;
tests_is_number( ) ;
tests_sig_install( ) ;
@@ -17232,16 +19645,37 @@ sub tests
tests_abort( ) ;
tests_probe_imapssl( ) ;
tests_mailimapclient_connect( ) ;
+ tests_checknoabletosearch( ) ;
+ tests_errorsdump( ) ;
+ tests_errorsanalyse( ) ;
+ tests_most_common_error( ) ;
+ tests_errorclassify( ) ;
+ tests_error_type( ) ;
+ tests_sanitize_host( ) ;
+ tests_hmac_sha1_hex( ) ;
+ tests_total_bytes_max_reached( ) ;
+ tests_header_construct( ) ;
+ tests_remove_doublequotes_if_any( ) ;
+ tests_login_imap( ) ;
+ tests_login_imap_oauth( ) ;
+ tests_skipmess_neg( ) ;
+ tests_localtimez( ) ;
+ tests_file_to_array( ) ;
+ tests_cpu_time( ) ;
+ tests_cpu_percent( ) ;
+ tests_cpu_percent_global( ) ;
+ tests_flags_for_host2( ) ;
+ tests_under_docker_context( ) ;
#tests_resolv( ) ;
-
- # Those three are for later use, when webserver will be inside imapsync
+
+ # Those three are for later use, when webserver will be inside imapsync
# or will be deleted them if I abandon the project.
#tests_killpid_by_parent( ) ;
#tests_killpid_by_brother( ) ;
#tests_kill_zero( ) ;
-
+
#tests_always_fail( ) ;
- done_testing( 1496 ) ;
+ done_testing( 1742 ) ;
note( 'Leaving tests()' ) ;
}
return ;
@@ -17251,11 +19685,19 @@ sub tests_template
{
note( 'Entering tests_template()' ) ;
- is( undef, undef, 'template: no args => undef' ) ;
+ is( undef, template( ), 'template: no args => undef' ) ;
+ my $mysync = { } ;
+ is( undef, template( $mysync ), 'template: undef => undef' ) ;
is_deeply( {}, {}, 'template: a hash is a hash' ) ;
is_deeply( [], [], 'template: an array is an array' ) ;
+
note( 'Leaving tests_template()' ) ;
return ;
}
-
+sub template
+{
+ my $mysync = shift @ARG ;
+
+ return ;
+}
diff --git a/data/Dockerfiles/dovecot/postlogin.sh b/data/Dockerfiles/dovecot/postlogin.sh
deleted file mode 100755
index 01a45f31..00000000
--- a/data/Dockerfiles/dovecot/postlogin.sh
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/bin/sh
-export MASTER_USER=$USER
-exec "$@"
diff --git a/data/Dockerfiles/dovecot/quota_notify.py b/data/Dockerfiles/dovecot/quota_notify.py
index fdfda304..2d7361b8 100755
--- a/data/Dockerfiles/dovecot/quota_notify.py
+++ b/data/Dockerfiles/dovecot/quota_notify.py
@@ -9,6 +9,7 @@ import jinja2
from jinja2 import Template
import redis
import time
+import json
import sys
import html2text
from subprocess import Popen, PIPE, STDOUT
@@ -57,6 +58,27 @@ try:
p = Popen(['/usr/lib/dovecot/dovecot-lda', '-d', username, '-o', '"plugin/quota=maildir:User quota:noenforcing"'], stdout=PIPE, stdin=PIPE, stderr=STDOUT)
p.communicate(input=bytes(msg.as_string(), 'utf-8'))
+ domain = username.split("@")[-1]
+ if domain and r.hget('QW_BCC', domain):
+ bcc_data = json.loads(r.hget('QW_BCC', domain))
+ bcc_rcpts = bcc_data['bcc_rcpts']
+ if bcc_data['active'] == 1:
+ for rcpt in bcc_rcpts:
+ msg = MIMEMultipart('alternative')
+ msg['From'] = username
+ subject = r.get('QW_SUBJ') or "Quota warning"
+ msg['Subject'] = subject + ' (' + username + ')'
+ msg['Date'] = formatdate(localtime = True)
+ text_part = MIMEText(text, 'plain', 'utf-8')
+ html_part = MIMEText(html, 'html', 'utf-8')
+ msg.attach(text_part)
+ msg.attach(html_part)
+ msg['To'] = rcpt
+ server = smtplib.SMTP('postfix', 588, 'quotanotification')
+ server.ehlo()
+ server.sendmail(msg['From'], str(rcpt), msg.as_string())
+ server.quit()
+
except Exception as ex:
print('Failed to send quota notification: %s' % (ex))
sys.exit(1)
diff --git a/data/Dockerfiles/phpfpm/Dockerfile b/data/Dockerfiles/phpfpm/Dockerfile
index 5f613b57..ca851e65 100644
--- a/data/Dockerfiles/phpfpm/Dockerfile
+++ b/data/Dockerfiles/phpfpm/Dockerfile
@@ -1,12 +1,12 @@
-FROM php:7.4-fpm-alpine3.13
+FROM php:8.0-fpm-alpine3.14
LABEL maintainer "Andre Peters "
-ENV APCU_PECL 5.1.19
-ENV IMAGICK_PECL 3.4.4
+ENV APCU_PECL 5.1.20
+ENV IMAGICK_PECL 3.5.1
# Mailparse is pulled from master branch
#ENV MAILPARSE_PECL 3.0.2
ENV MEMCACHED_PECL 3.1.5
-ENV REDIS_PECL 5.3.3
+ENV REDIS_PECL 5.3.4
RUN apk add -U --no-cache autoconf \
aspell-dev \
@@ -61,7 +61,7 @@ RUN apk add -U --no-cache autoconf \
&& docker-php-ext-configure exif \
&& docker-php-ext-configure gd --with-freetype=/usr/include/ \
--with-jpeg=/usr/include/ \
- && docker-php-ext-install -j 4 exif gd gettext intl ldap opcache pcntl pdo pdo_mysql pspell soap sockets xmlrpc zip bcmath gmp \
+ && docker-php-ext-install -j 4 exif gd gettext intl ldap opcache pcntl pdo pdo_mysql pspell soap sockets zip bcmath gmp \
&& docker-php-ext-configure imap --with-imap --with-imap-ssl \
&& docker-php-ext-install -j 4 imap \
&& curl --silent --show-error https://getcomposer.org/installer | php \
diff --git a/data/Dockerfiles/postfix/Dockerfile b/data/Dockerfiles/postfix/Dockerfile
index 8b913af4..56b274aa 100644
--- a/data/Dockerfiles/postfix/Dockerfile
+++ b/data/Dockerfiles/postfix/Dockerfile
@@ -45,7 +45,6 @@ COPY postfix.sh /opt/postfix.sh
COPY rspamd-pipe-ham /usr/local/bin/rspamd-pipe-ham
COPY rspamd-pipe-spam /usr/local/bin/rspamd-pipe-spam
COPY whitelist_forwardinghosts.sh /usr/local/bin/whitelist_forwardinghosts.sh
-COPY smtpd_last_login.sh /usr/local/bin/smtpd_last_login.sh
COPY stop-supervisor.sh /usr/local/sbin/stop-supervisor.sh
COPY docker-entrypoint.sh /docker-entrypoint.sh
@@ -53,7 +52,6 @@ RUN chmod +x /opt/postfix.sh \
/usr/local/bin/rspamd-pipe-ham \
/usr/local/bin/rspamd-pipe-spam \
/usr/local/bin/whitelist_forwardinghosts.sh \
- /usr/local/bin/smtpd_last_login.sh \
/usr/local/sbin/stop-supervisor.sh
RUN rm -rf /tmp/* /var/tmp/*
diff --git a/data/Dockerfiles/postfix/postfix.sh b/data/Dockerfiles/postfix/postfix.sh
index 753f446e..e734a9ab 100755
--- a/data/Dockerfiles/postfix/postfix.sh
+++ b/data/Dockerfiles/postfix/postfix.sh
@@ -125,16 +125,31 @@ query = SELECT GROUP_CONCAT(transport SEPARATOR '') AS transport_maps
AND mailbox.active = '1'
), 'smtp_enforced_tls:', 'smtp:') AS 'transport'
UNION ALL
- SELECT hostname AS transport FROM relayhosts
+ SELECT COALESCE(
+ (SELECT hostname FROM relayhosts
+ LEFT OUTER JOIN mailbox ON JSON_UNQUOTE(JSON_VALUE(mailbox.attributes, '$.relayhost')) = relayhosts.id
+ WHERE relayhosts.active = '1'
+ AND (
+ mailbox.username IN (SELECT alias.goto from alias
+ JOIN mailbox ON mailbox.username = alias.goto
+ WHERE alias.active = '1'
+ AND alias.address = '%s'
+ AND alias.address NOT LIKE '@%%'
+ )
+ )
+ ),
+ (SELECT hostname FROM relayhosts
LEFT OUTER JOIN domain ON domain.relayhost = relayhosts.id
WHERE relayhosts.active = '1'
- AND domain = '%d'
- OR domain IN (
- SELECT target_domain FROM alias_domain
- WHERE alias_domain = '%d'
+ AND (domain.domain = '%d'
+ OR domain.domain IN (
+ SELECT target_domain FROM alias_domain
+ WHERE alias_domain = '%d'
+ )
)
- )
- AS transport_view;
+ )
+ )
+ ) AS transport_view;
EOF
cat < /opt/postfix/conf/sql/mysql_transport_maps.cf
@@ -166,11 +181,31 @@ hosts = unix:/var/run/mysqld/mysqld.sock
dbname = ${DBNAME}
query = SELECT CONCAT_WS(':', username, password) AS auth_data FROM relayhosts
WHERE id IN (
- SELECT relayhost FROM domain
- WHERE CONCAT('@', domain) = '%s'
- OR domain IN (
- SELECT target_domain FROM alias_domain WHERE CONCAT('@', alias_domain) = '%s'
+ SELECT COALESCE(
+ (SELECT id FROM relayhosts
+ LEFT OUTER JOIN domain ON domain.relayhost = relayhosts.id
+ WHERE relayhosts.active = '1'
+ AND (domain.domain = '%d'
+ OR domain.domain IN (
+ SELECT target_domain FROM alias_domain
+ WHERE alias_domain = '%d'
+ )
+ )
+ ),
+ (SELECT id FROM relayhosts
+ LEFT OUTER JOIN mailbox ON JSON_UNQUOTE(JSON_VALUE(mailbox.attributes, '$.relayhost')) = relayhosts.id
+ WHERE relayhosts.active = '1'
+ AND (
+ mailbox.username IN (
+ SELECT alias.goto from alias
+ JOIN mailbox ON mailbox.username = alias.goto
+ WHERE alias.active = '1'
+ AND alias.address = '%s'
+ AND alias.address NOT LIKE '@%%'
+ )
+ )
)
+ )
)
AND active = '1'
AND username != '';
@@ -322,14 +357,17 @@ query = SELECT goto FROM alias
AND alias_domain.active='1'
EOF
-# Reject sasl usernames with smtp disabled
-cat < /opt/postfix/conf/sql/mysql_sasl_access_maps.cf
+# MX based routing
+cat < /opt/postfix/conf/sql/mysql_mbr_access_maps.cf
# Autogenerated by mailcow
user = ${DBUSER}
password = ${DBPASS}
hosts = unix:/var/run/mysqld/mysqld.sock
dbname = ${DBNAME}
-query = SELECT 'REJECT' FROM mailbox WHERE username = '%u' AND JSON_UNQUOTE(JSON_VALUE(attributes, '$.smtp_access')) = '0';
+query = SELECT CONCAT('FILTER smtp_via_transport_maps:', nexthop) as transport FROM transports
+ WHERE '%s' REGEXP destination
+ AND active='1'
+ AND is_mx_based='1';
EOF
cat < /opt/postfix/conf/sql/mysql_virtual_spamalias_maps.cf
@@ -367,9 +405,6 @@ if [[ ! -f /opt/postfix/conf/custom_postscreen_whitelist.cidr ]]; then
EOF
fi
-# Fix SMTP last login on slaves
-sed -i "s/__REDIS_SLAVEOF_IP__/${REDIS_SLAVEOF_IP}/g" /usr/local/bin/smtpd_last_login.sh
-
# Fix Postfix permissions
chown -R root:postfix /opt/postfix/conf/sql/ /opt/postfix/conf/custom_transport.pcre
chmod 640 /opt/postfix/conf/sql/*.cf /opt/postfix/conf/custom_transport.pcre
diff --git a/data/Dockerfiles/postfix/smtpd_last_login.sh b/data/Dockerfiles/postfix/smtpd_last_login.sh
deleted file mode 100755
index 9d249af9..00000000
--- a/data/Dockerfiles/postfix/smtpd_last_login.sh
+++ /dev/null
@@ -1,23 +0,0 @@
-#!/bin/bash
-
-REDIS_SLAVEOF_IP=__REDIS_SLAVEOF_IP__
-
-# Do not attempt to write to slave
-if [[ ! -z ${REDIS_SLAVEOF_IP} ]]; then
- REDIS_CMDLINE="redis-cli -h ${REDIS_SLAVEOF_IP} -p ${REDIS_SLAVEOF_PORT}"
-else
- REDIS_CMDLINE="redis-cli -h redis -p 6379"
-fi
-
-while read QUERY; do
- QUERY=($QUERY)
- # If nothing matched, end here - Postfix last line will be empty
- if [[ -z "$(echo ${QUERY[0]} | tr -d '\040\011\012\015')" ]]; then
- echo -ne "action=dunno\n\n"
- # We found a username, log and return
- elif [[ "${QUERY[0]}" =~ sasl_username ]]; then
- MUSER=$(printf "%q" ${QUERY[0]#sasl_username=})
- ${REDIS_CMDLINE} SET "last-login/smtp/$MUSER" "$(date +%s)"
- echo -ne "action=dunno\n\n"
- fi
-done
diff --git a/data/Dockerfiles/rspamd/docker-entrypoint.sh b/data/Dockerfiles/rspamd/docker-entrypoint.sh
index 203a1963..e6d329b5 100755
--- a/data/Dockerfiles/rspamd/docker-entrypoint.sh
+++ b/data/Dockerfiles/rspamd/docker-entrypoint.sh
@@ -104,7 +104,8 @@ touch /etc/rspamd/custom/global_mime_from_blacklist.map \
/etc/rspamd/custom/bad_words.map \
/etc/rspamd/custom/bad_asn.map \
/etc/rspamd/custom/bad_words_de.map \
- /etc/rspamd/custom/bulk_header.map
+ /etc/rspamd/custom/bulk_header.map \
+ /etc/rspamd/custom/bad_header.map
# www-data (82) group needs to write to these files
chown _rspamd:_rspamd /etc/rspamd/custom/
diff --git a/data/Dockerfiles/sogo/bootstrap-sogo.sh b/data/Dockerfiles/sogo/bootstrap-sogo.sh
index d908bb3e..04472dfd 100755
--- a/data/Dockerfiles/sogo/bootstrap-sogo.sh
+++ b/data/Dockerfiles/sogo/bootstrap-sogo.sh
@@ -128,11 +128,6 @@ EOF
done
fi
-if [[ "${ALLOW_ADMIN_EMAIL_LOGIN}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
- TRUST_PROXY="YES"
-else
- TRUST_PROXY="NO"
-fi
# cat /dev/urandom seems to hang here occasionally and is not recommended anyway, better use openssl
RAND_PASS=$(openssl rand -base64 16 | tr -dc _A-Z-a-z-0-9)
@@ -148,7 +143,7 @@ cat < /var/lib/sogo/GNUstep/Defaults/sogod.plist
SOGoIMAPServer
imap://${IPV4_NETWORK}.250:143/?TLS=YES&tlsVerifyMode=none
SOGoTrustProxyAuthentication
- ${TRUST_PROXY}
+ YES
SOGoEncryptionKey
${RAND_PASS}
OCSCacheFolderURL
diff --git a/data/Dockerfiles/sogo/docker-entrypoint.sh b/data/Dockerfiles/sogo/docker-entrypoint.sh
index ce28c34f..2ff602a3 100755
--- a/data/Dockerfiles/sogo/docker-entrypoint.sh
+++ b/data/Dockerfiles/sogo/docker-entrypoint.sh
@@ -10,4 +10,12 @@ if [[ ! -z ${REDIS_SLAVEOF_IP} ]]; then
cp /etc/syslog-ng/syslog-ng-redis_slave.conf /etc/syslog-ng/syslog-ng.conf
fi
+# Run hooks
+for file in /hooks/*; do
+ if [ -x "${file}" ]; then
+ echo "Running hook ${file}"
+ "${file}"
+ fi
+done
+
exec "$@"
diff --git a/data/Dockerfiles/watchdog/watchdog.sh b/data/Dockerfiles/watchdog/watchdog.sh
index 19c0d2ed..ade99f2a 100755
--- a/data/Dockerfiles/watchdog/watchdog.sh
+++ b/data/Dockerfiles/watchdog/watchdog.sh
@@ -660,39 +660,6 @@ acme_checks() {
return 1
}
-ipv6nat_checks() {
- err_count=0
- diff_c=0
- THRESHOLD=${IPV6NAT_THRESHOLD}
- # Reduce error count by 2 after restarting an unhealthy container
- trap "[ ${err_count} -gt 1 ] && err_count=$(( ${err_count} - 2 ))" USR1
- while [ ${err_count} -lt ${THRESHOLD} ]; do
- err_c_cur=${err_count}
- CONTAINERS=$(curl --silent --insecure https://dockerapi/containers/json)
- IPV6NAT_CONTAINER_ID=$(echo ${CONTAINERS} | jq -r ".[] | {name: .Config.Labels[\"com.docker.compose.service\"], project: .Config.Labels[\"com.docker.compose.project\"], id: .Id}" | jq -rc "select( .name | tostring | contains(\"ipv6nat-mailcow\")) | select( .project | tostring | contains(\"${COMPOSE_PROJECT_NAME,,}\")) | .id")
- if [[ ! -z ${IPV6NAT_CONTAINER_ID} ]]; then
- LATEST_STARTED="$(echo ${CONTAINERS} | jq -r ".[] | {name: .Config.Labels[\"com.docker.compose.service\"], project: .Config.Labels[\"com.docker.compose.project\"], StartedAt: .State.StartedAt}" | jq -rc "select( .project | tostring | contains(\"${COMPOSE_PROJECT_NAME,,}\")) | select( .name | tostring | contains(\"ipv6nat-mailcow\") | not)" | jq -rc .StartedAt | xargs -n1 date +%s -d | sort | tail -n1)"
- LATEST_IPV6NAT="$(echo ${CONTAINERS} | jq -r ".[] | {name: .Config.Labels[\"com.docker.compose.service\"], project: .Config.Labels[\"com.docker.compose.project\"], StartedAt: .State.StartedAt}" | jq -rc "select( .project | tostring | contains(\"${COMPOSE_PROJECT_NAME,,}\")) | select( .name | tostring | contains(\"ipv6nat-mailcow\"))" | jq -rc .StartedAt | xargs -n1 date +%s -d | sort | tail -n1)"
- DIFFERENCE_START_TIME=$(expr ${LATEST_IPV6NAT} - ${LATEST_STARTED} 2>/dev/null)
- if [[ "${DIFFERENCE_START_TIME}" -lt 30 ]]; then
- err_count=$(( ${err_count} + 1 ))
- fi
- fi
- [ ${err_c_cur} -eq ${err_count} ] && [ ! $((${err_count} - 1)) -lt 0 ] && err_count=$((${err_count} - 1)) diff_c=1
- [ ${err_c_cur} -ne ${err_count} ] && diff_c=$(( ${err_c_cur} - ${err_count} ))
- progress "IPv6 NAT" ${THRESHOLD} $(( ${THRESHOLD} - ${err_count} )) ${diff_c}
- if [[ $? == 10 ]]; then
- diff_c=0
- sleep 30
- else
- diff_c=0
- sleep 300
- fi
- done
- return 1
-}
-
-
rspamd_checks() {
err_count=0
diff_c=0
@@ -1005,18 +972,6 @@ PID=$!
echo "Spawned acme_checks with PID ${PID}"
BACKGROUND_TASKS+=(${PID})
-(
-while true; do
- if ! ipv6nat_checks; then
- log_msg "IPv6 NAT warning: ipv6nat-mailcow container was not started at least 30s after siblings (not an error)"
- echo ipv6nat-mailcow > /tmp/com_pipe
- fi
-done
-) &
-PID=$!
-echo "Spawned ipv6nat_checks with PID ${PID}"
-BACKGROUND_TASKS+=(${PID})
-
# Monitor watchdog agents, stop script when agents fails and wait for respawn by Docker (restart:always:n)
(
while true; do
@@ -1112,9 +1067,7 @@ while true; do
else
log_msg "Sending restart command to ${CONTAINER_ID}..."
curl --silent --insecure -XPOST https://dockerapi/containers/${CONTAINER_ID}/restart
- if [[ ${com_pipe_answer} != "ipv6nat-mailcow" ]]; then
- [[ ! -z ${WATCHDOG_NOTIFY_EMAIL} ]] && mail_error "${com_pipe_answer}"
- fi
+ [[ ! -z ${WATCHDOG_NOTIFY_EMAIL} ]] && mail_error "${com_pipe_answer}"
log_msg "Wait for restarted container to settle and continue watching..."
sleep 35
fi
diff --git a/data/assets/ejabberd/sqlite/sqlite_template.db b/data/assets/ejabberd/sqlite/sqlite_template.db
deleted file mode 100644
index 981ba6b9..00000000
Binary files a/data/assets/ejabberd/sqlite/sqlite_template.db and /dev/null differ
diff --git a/data/conf/dovecot/dovecot.conf b/data/conf/dovecot/dovecot.conf
index 1076c31c..b7aca757 100644
--- a/data/conf/dovecot/dovecot.conf
+++ b/data/conf/dovecot/dovecot.conf
@@ -45,36 +45,25 @@ recipient_delimiter = +
auth_master_user_separator = *
mail_shared_explicit_inbox = yes
mail_prefetch_count = 30
+passdb {
+ driver = lua
+ args = file=/etc/dovecot/lua/passwd-verify.lua blocking=yes
+ result_success = return-ok
+ result_failure = continue
+ result_internalfail = continue
+}
# try a master passwd
passdb {
driver = passwd-file
args = /etc/dovecot/dovecot-master.passwd
master = yes
- pass = yes
- result_failure = continue
- result_internalfail = continue
-}
-# try an app passwd
-passdb {
- driver = lua
- args = file=/etc/dovecot/lua/app-passdb.lua blocking=yes
- pass = yes
- result_failure = continue
- result_internalfail = continue
+ skip = authenticated
}
# check for regular password - if empty (e.g. force-passwd-reset), previous pass=yes passdbs also fail
# a return of the following passdb is mandatory
passdb {
- args = /etc/dovecot/sql/dovecot-dict-sql-passdb.conf
- driver = sql
- result_success = return-ok
- result_failure = continue
- result_internalfail = continue
-}
-passdb {
- driver = passwd-file
- args = /etc/dovecot/dovecot-master.passwd
- skip = authenticated
+ driver = lua
+ args = file=/etc/dovecot/lua/passwd-verify.lua blocking=yes
}
# Set doveadm_password=your-secret-password in data/conf/dovecot/extra.conf (create if missing)
service doveadm {
@@ -83,299 +72,7 @@ service doveadm {
}
vsz_limit=2048 MB
}
-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 "Rubbish" {
- special_use = \Trash
- }
- mailbox "Gelöschte Objekte" {
- special_use = \Trash
- }
- mailbox "Gelöschte Elemente" {
- 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 "Odstránené položky" {
- special_use = \Trash
- }
- mailbox "Koš" {
- special_use = \Trash
- }
- mailbox "Verwijderde items" {
- special_use = \Trash
- }
- mailbox "Удаленные" {
- special_use = \Trash
- }
- mailbox "Удаленные элементы" {
- special_use = \Trash
- }
- mailbox "Корзина" {
- special_use = \Trash
- }
- mailbox "Видалені" {
- special_use = \Trash
- }
- mailbox "Видалені елементи" {
- special_use = \Trash
- }
- mailbox "Кошик" {
- special_use = \Trash
- }
- mailbox "废件箱" {
- special_use = \Trash
- }
- mailbox "已删除消息" {
- special_use = \Trash
- }
- mailbox "已删除邮件" {
- 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 "Archív" {
- special_use = \Archive
- }
- mailbox "Archivovať" {
- special_use = \Archive
- }
- mailbox "归档" {
- special_use = \Archive
- }
- mailbox "Архив" {
- special_use = \Archive
- }
- mailbox "Архів" {
- special_use = \Archive
- }
- mailbox "Sent" {
- auto = subscribe
- special_use = \Sent
- }
- mailbox "Sent Messages" {
- special_use = \Sent
- }
- mailbox "Sent Items" {
- special_use = \Sent
- }
- mailbox "已发送" {
- special_use = \Sent
- }
- mailbox "已发送消息" {
- special_use = \Sent
- }
- mailbox "已发送邮件" {
- special_use = \Sent
- }
- mailbox "Отправленные" {
- special_use = \Sent
- }
- mailbox "Отправленные элементы" {
- special_use = \Sent
- }
- mailbox "Надіслані" {
- special_use = \Sent
- }
- mailbox "Надіслані елементи" {
- special_use = \Sent
- }
- mailbox "Gesendet" {
- special_use = \Sent
- }
- mailbox "Gesendete Objekte" {
- special_use = \Sent
- }
- mailbox "Gesendete Elemente" {
- 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 "Odoslaná pošta" {
- special_use = \Sent
- }
- mailbox "Odoslané" {
- 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 "Koncepty" {
- special_use = \Drafts
- }
- mailbox "草稿" {
- special_use = \Drafts
- }
- mailbox "草稿箱" {
- special_use = \Drafts
- }
- mailbox "Черновики" {
- special_use = \Drafts
- }
- mailbox "Чернетки" {
- special_use = \Drafts
- }
- mailbox "Junk" {
- auto = subscribe
- special_use = \Junk
- }
- mailbox "Junk-E-Mail" {
- special_use = \Junk
- }
- mailbox "Junk E-Mail" {
- special_use = \Junk
- }
- mailbox "Spam" {
- special_use = \Junk
- }
- mailbox "Lixo Eletrônico" {
- special_use = \Junk
- }
- mailbox "Nevyžiadaná pošta" {
- special_use = \Junk
- }
- mailbox "Infikované položky" {
- special_use = \Junk
- }
- mailbox "Ongewenste e-mail" {
- special_use = \Junk
- }
- mailbox "垃圾" {
- special_use = \Junk
- }
- mailbox "垃圾箱" {
- special_use = \Junk
- }
- mailbox "Нежелательная почта" {
- special_use = \Junk
- }
- mailbox "Спам" {
- special_use = \Junk
- }
- mailbox "Небажана пошта" {
- special_use = \Junk
- }
- mailbox "Koncepty" {
- special_use = \Drafts
- }
- mailbox "Nevyžádaná pošta" {
- special_use = \Junk
- }
- mailbox "Odstraněná pošta" {
- special_use = \Trash
- }
- mailbox "Odeslaná pošta" {
- special_use = \Sent
- }
- mailbox "Skräp" {
- special_use = \Trash
- }
- mailbox "Borttagna Meddelanden" {
- special_use = \Trash
- }
- mailbox "Arkiv" {
- special_use = \Archive
- }
- mailbox "Arkeverat" {
- special_use = \Archive
- }
- mailbox "Skickat" {
- special_use = \Sent
- }
- mailbox "Skickade Meddelanden" {
- special_use = \Sent
- }
- mailbox "Utkast" {
- special_use = \Drafts
- }
- mailbox "Skraldespand" {
- special_use = \Trash
- }
- mailbox "Slettet mails" {
- special_use = \Trash
- }
- mailbox "Arkiv" {
- special_use = \Archive
- }
- mailbox "Arkiveret mails" {
- special_use = \Archive
- }
- mailbox "Sendt" {
- special_use = \Sent
- }
- mailbox "Sendte mails" {
- special_use = \Sent
- }
- mailbox "Udkast" {
- special_use = \Drafts
- }
- mailbox "Kladde" {
- special_use = \Drafts
- }
- prefix =
-}
+!include /etc/dovecot/dovecot.folders.conf
protocols = imap sieve lmtp pop3
service dict {
unix_listener dict {
@@ -449,7 +146,7 @@ service pop3-login {
}
}
service imap {
- executable = imap imap-postlogin
+ executable = imap
user = vmail
vsz_limit = 1G
}
@@ -465,8 +162,6 @@ service lmtp {
listen = *,[::]
ssl_cert =
+!include_try /etc/dovecot/sni.conf
+!include_try /etc/dovecot/sogo_trusted_ip.conf
!include_try /etc/dovecot/extra.conf
!include_try /etc/dovecot/sogo-sso.conf
!include_try /etc/dovecot/shared_namespace.conf
+#
default_client_limit = 10400
default_vsz_limit = 1024 M
diff --git a/data/conf/dovecot/dovecot.folders.conf b/data/conf/dovecot/dovecot.folders.conf
new file mode 100644
index 00000000..99c9670f
--- /dev/null
+++ b/data/conf/dovecot/dovecot.folders.conf
@@ -0,0 +1,293 @@
+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 "Rubbish" {
+ special_use = \Trash
+ }
+ mailbox "Gelöschte Objekte" {
+ special_use = \Trash
+ }
+ mailbox "Gelöschte Elemente" {
+ 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 "Odstránené položky" {
+ special_use = \Trash
+ }
+ mailbox "Koš" {
+ special_use = \Trash
+ }
+ mailbox "Verwijderde items" {
+ special_use = \Trash
+ }
+ mailbox "Удаленные" {
+ special_use = \Trash
+ }
+ mailbox "Удаленные элементы" {
+ special_use = \Trash
+ }
+ mailbox "Корзина" {
+ special_use = \Trash
+ }
+ mailbox "Видалені" {
+ special_use = \Trash
+ }
+ mailbox "Видалені елементи" {
+ special_use = \Trash
+ }
+ mailbox "Кошик" {
+ special_use = \Trash
+ }
+ mailbox "废件箱" {
+ special_use = \Trash
+ }
+ mailbox "已删除消息" {
+ special_use = \Trash
+ }
+ mailbox "已删除邮件" {
+ 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 "Archív" {
+ special_use = \Archive
+ }
+ mailbox "Archivovať" {
+ special_use = \Archive
+ }
+ mailbox "归档" {
+ special_use = \Archive
+ }
+ mailbox "Архив" {
+ special_use = \Archive
+ }
+ mailbox "Архів" {
+ special_use = \Archive
+ }
+ mailbox "Sent" {
+ auto = subscribe
+ special_use = \Sent
+ }
+ mailbox "Sent Messages" {
+ special_use = \Sent
+ }
+ mailbox "Sent Items" {
+ special_use = \Sent
+ }
+ mailbox "已发送" {
+ special_use = \Sent
+ }
+ mailbox "已发送消息" {
+ special_use = \Sent
+ }
+ mailbox "已发送邮件" {
+ special_use = \Sent
+ }
+ mailbox "Отправленные" {
+ special_use = \Sent
+ }
+ mailbox "Отправленные элементы" {
+ special_use = \Sent
+ }
+ mailbox "Надіслані" {
+ special_use = \Sent
+ }
+ mailbox "Надіслані елементи" {
+ special_use = \Sent
+ }
+ mailbox "Gesendet" {
+ special_use = \Sent
+ }
+ mailbox "Gesendete Objekte" {
+ special_use = \Sent
+ }
+ mailbox "Gesendete Elemente" {
+ 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 "Odoslaná pošta" {
+ special_use = \Sent
+ }
+ mailbox "Odoslané" {
+ 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 "Koncepty" {
+ special_use = \Drafts
+ }
+ mailbox "草稿" {
+ special_use = \Drafts
+ }
+ mailbox "草稿箱" {
+ special_use = \Drafts
+ }
+ mailbox "Черновики" {
+ special_use = \Drafts
+ }
+ mailbox "Чернетки" {
+ special_use = \Drafts
+ }
+ mailbox "Junk" {
+ auto = subscribe
+ special_use = \Junk
+ }
+ mailbox "Junk-E-Mail" {
+ special_use = \Junk
+ }
+ mailbox "Junk E-Mail" {
+ special_use = \Junk
+ }
+ mailbox "Spam" {
+ special_use = \Junk
+ }
+ mailbox "Lixo Eletrônico" {
+ special_use = \Junk
+ }
+ mailbox "Nevyžiadaná pošta" {
+ special_use = \Junk
+ }
+ mailbox "Infikované položky" {
+ special_use = \Junk
+ }
+ mailbox "Ongewenste e-mail" {
+ special_use = \Junk
+ }
+ mailbox "垃圾" {
+ special_use = \Junk
+ }
+ mailbox "垃圾箱" {
+ special_use = \Junk
+ }
+ mailbox "Нежелательная почта" {
+ special_use = \Junk
+ }
+ mailbox "Спам" {
+ special_use = \Junk
+ }
+ mailbox "Небажана пошта" {
+ special_use = \Junk
+ }
+ mailbox "Koncepty" {
+ special_use = \Drafts
+ }
+ mailbox "Nevyžádaná pošta" {
+ special_use = \Junk
+ }
+ mailbox "Odstraněná pošta" {
+ special_use = \Trash
+ }
+ mailbox "Odeslaná pošta" {
+ special_use = \Sent
+ }
+ mailbox "Skräp" {
+ special_use = \Trash
+ }
+ mailbox "Borttagna Meddelanden" {
+ special_use = \Trash
+ }
+ mailbox "Arkiv" {
+ special_use = \Archive
+ }
+ mailbox "Arkeverat" {
+ special_use = \Archive
+ }
+ mailbox "Skickat" {
+ special_use = \Sent
+ }
+ mailbox "Skickade Meddelanden" {
+ special_use = \Sent
+ }
+ mailbox "Utkast" {
+ special_use = \Drafts
+ }
+ mailbox "Skraldespand" {
+ special_use = \Trash
+ }
+ mailbox "Slettet mails" {
+ special_use = \Trash
+ }
+ mailbox "Arkiv" {
+ special_use = \Archive
+ }
+ mailbox "Arkiveret mails" {
+ special_use = \Archive
+ }
+ mailbox "Sendt" {
+ special_use = \Sent
+ }
+ mailbox "Sendte mails" {
+ special_use = \Sent
+ }
+ mailbox "Udkast" {
+ special_use = \Drafts
+ }
+ mailbox "Kladde" {
+ special_use = \Drafts
+ }
+ prefix =
+}
\ No newline at end of file
diff --git a/data/conf/dovecot/global_sieve_before b/data/conf/dovecot/global_sieve_before
index a71419d5..3e79ca1d 100644
--- a/data/conf/dovecot/global_sieve_before
+++ b/data/conf/dovecot/global_sieve_before
@@ -1,8 +1,7 @@
# global_sieve_before script
# global_sieve_before -> user sieve_before (mailcow UI) -> user sieve_after (mailcow UI) -> global_sieve_after
-require "fileinto";
-require "mailbox";
+require ["mailbox", "fileinto"];
if header :contains ["Chat-Version"] [""] {
if mailboxexists "DeltaChat" {
diff --git a/data/conf/ejabberd/ejabberd.yml b/data/conf/ejabberd/ejabberd.yml
deleted file mode 100644
index 11a74a6d..00000000
--- a/data/conf/ejabberd/ejabberd.yml
+++ /dev/null
@@ -1,239 +0,0 @@
-loglevel: info
-
-auth_method: [external]
-auth_use_cache: false
-extauth_program: /var/www/authentication/authenticator
-
-include_config_file:
- /ejabberd/ejabberd_api.yml
-
-include_config_file:
- /ejabberd/ejabberd_acl.yml
-
-include_config_file:
- /ejabberd/ejabberd_hosts.yml:
- allow_only:
- - hosts
-
-include_config_file:
- /ejabberd/ejabberd_macros.yml:
- allow_only:
- - define_macro
-
-define_macro:
- 'TLS_CIPHERS': "ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256"
- 'TLS_OPTIONS':
- - "no_sslv3"
- - "no_tlsv1"
- - "no_tlsv1_1"
- - "cipher_server_preference"
- - "no_compression"
-
-c2s_ciphers: 'TLS_CIPHERS'
-s2s_ciphers: 'TLS_CIPHERS'
-c2s_protocol_options: 'TLS_OPTIONS'
-s2s_protocol_options: 'TLS_OPTIONS'
-s2s_use_starttls: required
-
-new_sql_schema: true
-sql_type: sqlite
-sql_database: /sqlite/sqlite.db
-default_db: sql
-
-certfiles:
- - /ejabberd_ssl/cert.pem
- - /ejabberd_ssl/key.pem
-
-listen:
- -
- port: 5222
- ip: "::"
- module: ejabberd_c2s
- max_stanza_size: 262144
- shaper: c2s_shaper
- access: c2s
- starttls_required: true
- -
- port: 5269
- ip: "::"
- module: ejabberd_s2s_in
- max_stanza_size: 524288
- -
- port: EJABBERD_HTTPS
- ip: "::"
- module: ejabberd_http
- tls: true
- request_handlers:
- /captcha: ejabberd_captcha
- /upload: mod_http_upload
- /ws: ejabberd_http_ws
- -
- port: 5280
- ip: "::"
- module: ejabberd_http
- request_handlers:
- /api: mod_http_api
- -
- port: 5282
- ip: "::"
- module: ejabberd_http
- request_handlers:
- /xmpp: ejabberd_web_admin
- -
- module: ejabberd_http
- port: 5281
- ip: "::"
- request_handlers:
- /.well-known/acme-challenge: ejabberd_acme
- -
- port: 1883
- ip: "::"
- module: mod_mqtt
- backlog: 1000
-
-acme:
- auto: true
-
-acl:
- admin:
- user:
- - "admin": "localhost"
- local:
- user_regexp: ""
- loopback:
- ip:
- - 127.0.0.0/8
- - ::1/128
-
-access_rules:
- local:
- allow: local
- c2s:
- deny: blocked
- allow: all
- announce:
- allow: admin
- configure:
- allow: admin
- muc_create:
- allow: local
- pubsub_createnode:
- allow: local
- trusted_network:
- allow: loopback
-
-api_permissions:
- "console commands":
- from:
- - ejabberd_ctl
- who: all
- what: "*"
- "admin access":
- who:
- access:
- allow:
- - acl: loopback
- - acl: admin
- what:
- - "*"
- - "!stop"
- - "!start"
-
-shaper:
- normal:
- rate: 3000
- burst_size: 20000
- fast: 100000
-
-shaper_rules:
- max_user_sessions: 10
- max_user_offline_messages:
- 1000: admin
- 1000: all
- c2s_shaper:
- none: admin
- normal: all
- s2s_shaper: fast
-
-modules:
- mod_adhoc: {}
- mod_admin_extra: {}
- mod_announce:
- access: announce
- mod_avatar: {}
- mod_blocking: {}
- mod_caps: {}
- mod_carboncopy: {}
- mod_client_state: {}
- mod_configure: {}
- mod_disco: {}
- mod_fail2ban: {}
- mod_http_api: {}
- mod_http_upload_quota:
- max_days: 30
- mod_http_upload:
- docroot: /var/www/upload
- custom_headers:
- "Access-Control-Allow-Origin": "https://@HOST@"
- "Access-Control-Allow-Methods": "GET,HEAD,PUT,OPTIONS"
- "Access-Control-Allow-Headers": "Content-Type"
- thumbnail: true
- jid_in_url: node
- file_mode: "0600"
- dir_mode: "0700"
- mod_last: {}
- mod_mam:
- clear_archive_on_room_destroy: true
- default: never
- compress_xml: true
- request_activates_archiving: true
- mod_mqtt: {}
- mod_muc:
- access:
- - allow
- access_admin:
- - allow: admin
- access_create: muc_create
- access_persistent: muc_create
- access_mam:
- - allow
- default_room_options:
- mam: false
- persistent: false
- mod_muc_admin: {}
- mod_offline:
- access_max_user_messages: max_user_offline_messages
- mod_ping: {}
- mod_privacy: {}
- mod_private: {}
- mod_proxy65:
- access: local
- max_connections: 5
- mod_pubsub:
- access_createnode: pubsub_createnode
- plugins:
- - flat
- - pep
- force_node_config:
- ## Avoid buggy clients to make their bookmarks public
- storage:bookmarks:
- access_model: whitelist
- mod_push: {}
- mod_push_keepalive: {}
- mod_register:
- ## Only accept registration requests from the "trusted"
- ## network (see access_rules section above).
- ## Think twice before enabling registration from any
- ## address. See the Jabber SPAM Manifesto for details:
- ## https://github.com/ge0rg/jabber-spam-fighting-manifesto
- ip_access: trusted_network
- mod_roster:
- versioning: true
- mod_s2s_dialback: {}
- mod_stream_mgmt:
- resend_on_timeout: if_offline
- mod_stun_disco: {}
- mod_vcard: {}
- mod_vcard_xupdate: {}
- mod_version:
- show_os: false
diff --git a/data/conf/nginx/includes/site-defaults.conf b/data/conf/nginx/includes/site-defaults.conf
index ae4de7b8..b38f4b2d 100644
--- a/data/conf/nginx/includes/site-defaults.conf
+++ b/data/conf/nginx/includes/site-defaults.conf
@@ -88,6 +88,11 @@
return 301 /SOGo/dav;
}
+ location ^~ /inc/lib/ {
+ deny all;
+ return 403;
+ }
+
location ~ \.php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
@@ -118,14 +123,6 @@
proxy_redirect off;
}
- location /xmpp/ {
- proxy_pass http://ejabberd:5282/xmpp/;
- proxy_set_header Host $http_host;
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- proxy_set_header X-Real-IP $remote_addr;
- proxy_redirect off;
- }
-
location ~* ^/Autodiscover/Autodiscover.xml {
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass phpfpm:9002;
@@ -150,7 +147,6 @@
try_files /autoconfig.php =404;
}
- # auth_request endpoint if ALLOW_ADMIN_EMAIL_LOGIN is set
location /sogo-auth-verify {
internal;
proxy_set_header X-Original-URI $request_uri;
@@ -162,7 +158,7 @@
}
location ^~ /Microsoft-Server-ActiveSync {
- include /etc/nginx/conf.d/sogo_proxy_auth.active;
+ include /etc/nginx/conf.d/includes/sogo_proxy_auth.conf;
include /etc/nginx/conf.d/sogo_eas.active;
proxy_connect_timeout 75;
proxy_send_timeout 3600;
@@ -176,7 +172,22 @@
}
location ^~ /SOGo {
- include /etc/nginx/conf.d/sogo_proxy_auth.active;
+ location ~* ^/SOGo/so/.*\.(xml|js|html|xhtml)$ {
+ include /etc/nginx/conf.d/includes/sogo_proxy_auth.conf;
+ include /etc/nginx/conf.d/sogo.active;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header Host $http_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 $client_req_scheme://$http_host;
+ proxy_set_header x-webobjects-server-port $server_port;
+ proxy_hide_header Content-Type;
+ add_header Content-Type text/plain;
+ break;
+ }
+ include /etc/nginx/conf.d/includes/sogo_proxy_auth.conf;
include /etc/nginx/conf.d/sogo.active;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
diff --git a/data/conf/nginx/templates/sogo.auth_request.template.sh b/data/conf/nginx/includes/sogo_proxy_auth.conf
similarity index 66%
rename from data/conf/nginx/templates/sogo.auth_request.template.sh
rename to data/conf/nginx/includes/sogo_proxy_auth.conf
index f6d2d98e..045b98ad 100644
--- a/data/conf/nginx/templates/sogo.auth_request.template.sh
+++ b/data/conf/nginx/includes/sogo_proxy_auth.conf
@@ -1,10 +1,8 @@
-if printf "%s\n" "${ALLOW_ADMIN_EMAIL_LOGIN}" | grep -E '^([yY][eE][sS]|[yY])+$' >/dev/null; then
- echo 'auth_request /sogo-auth-verify;
+auth_request /sogo-auth-verify;
auth_request_set $user $upstream_http_x_user;
auth_request_set $auth $upstream_http_x_auth;
auth_request_set $auth_type $upstream_http_x_auth_type;
proxy_set_header x-webobjects-remote-user "$user";
proxy_set_header Authorization "$auth";
proxy_set_header x-webobjects-auth-type "$auth_type";
-'
-fi
+
diff --git a/data/conf/postfix/main.cf b/data/conf/postfix/main.cf
index 1005b284..00eb8a93 100644
--- a/data/conf/postfix/main.cf
+++ b/data/conf/postfix/main.cf
@@ -77,7 +77,7 @@ postscreen_greet_wait = 3s
postscreen_non_smtp_command_enable = no
postscreen_pipelining_enable = no
proxy_read_maps = proxy:mysql:/opt/postfix/conf/sql/mysql_sasl_passwd_maps_transport_maps.cf,
- proxy:mysql:/opt/postfix/conf/sql/mysql_sasl_access_maps.cf,
+ proxy:mysql:/opt/postfix/conf/sql/mysql_mbr_access_maps.cf,
proxy:mysql:/opt/postfix/conf/sql/mysql_tls_enforce_in_policy.cf,
$sender_dependent_default_transport_maps,
$smtp_tls_policy_maps,
@@ -115,7 +115,7 @@ smtpd_error_sleep_time = 10s
smtpd_hard_error_limit = ${stress?1}${stress:5}
smtpd_helo_required = yes
smtpd_proxy_timeout = 600s
-smtpd_recipient_restrictions = check_sasl_access proxy:mysql:/opt/postfix/conf/sql/mysql_sasl_access_maps.cf,
+smtpd_recipient_restrictions = check_recipient_mx_access proxy:mysql:/opt/postfix/conf/sql/mysql_mbr_access_maps.cf,
permit_sasl_authenticated,
permit_mynetworks,
check_recipient_access proxy:mysql:/opt/postfix/conf/sql/mysql_tls_enforce_in_policy.cf,
@@ -159,8 +159,9 @@ virtual_alias_maps = proxy:mysql:/opt/postfix/conf/sql/mysql_virtual_alias_maps.
virtual_gid_maps = static:5000
virtual_mailbox_base = /var/vmail/
virtual_mailbox_domains = proxy:mysql:/opt/postfix/conf/sql/mysql_virtual_domains_maps.cf
-recipient_bcc_maps = proxy:mysql:/opt/postfix/conf/sql/mysql_recipient_bcc_maps.cf
-sender_bcc_maps = proxy:mysql:/opt/postfix/conf/sql/mysql_sender_bcc_maps.cf
+# -- moved to rspamd on 2021-06-01
+#recipient_bcc_maps = proxy:mysql:/opt/postfix/conf/sql/mysql_recipient_bcc_maps.cf
+#sender_bcc_maps = proxy:mysql:/opt/postfix/conf/sql/mysql_sender_bcc_maps.cf
recipient_canonical_maps = proxy:mysql:/opt/postfix/conf/sql/mysql_recipient_canonical_maps.cf
recipient_canonical_classes = envelope_recipient
virtual_mailbox_maps = proxy:mysql:/opt/postfix/conf/sql/mysql_virtual_mailbox_maps.cf
@@ -190,7 +191,6 @@ smtp_sasl_auth_soft_bounce = no
postscreen_discard_ehlo_keywords = silent-discard, dsn
compatibility_level = 2
smtputf8_enable = no
-smtpd_last_auth = check_policy_service inet:127.0.0.1:10028
# Define protocols for SMTPS and submission service
submission_smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
smtps_smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
diff --git a/data/conf/postfix/master.cf b/data/conf/postfix/master.cf
index ffd1ac47..63ce875d 100644
--- a/data/conf/postfix/master.cf
+++ b/data/conf/postfix/master.cf
@@ -17,7 +17,6 @@ smtps inet n - n - - smtpd
-o tls_preempt_cipherlist=yes
-o cleanup_service_name=smtp_sender_cleanup
-o syslog_name=postfix/smtps
- -o smtpd_end_of_data_restrictions=$smtpd_last_auth
10465 inet n - n - - smtpd
-o smtpd_upstream_proxy_protocol=haproxy
-o smtpd_tls_wrappermode=yes
@@ -26,7 +25,6 @@ smtps inet n - n - - smtpd
-o tls_preempt_cipherlist=yes
-o cleanup_service_name=smtp_sender_cleanup
-o syslog_name=postfix/smtps-haproxy
- -o smtpd_end_of_data_restrictions=$smtpd_last_auth
# smtpd with starttls on 587/tcp
# TLS protocol can be modified by setting submission_smtpd_tls_mandatory_protocols in extra.cf
@@ -38,7 +36,6 @@ submission inet n - n - - smtpd
-o tls_preempt_cipherlist=yes
-o cleanup_service_name=smtp_sender_cleanup
-o syslog_name=postfix/submission
- -o smtpd_end_of_data_restrictions=$smtpd_last_auth
10587 inet n - n - - smtpd
-o smtpd_upstream_proxy_protocol=haproxy
-o smtpd_client_restrictions=permit_mynetworks,permit_sasl_authenticated,reject
@@ -48,7 +45,6 @@ submission inet n - n - - smtpd
-o tls_preempt_cipherlist=yes
-o cleanup_service_name=smtp_sender_cleanup
-o syslog_name=postfix/submission-haproxy
- -o smtpd_end_of_data_restrictions=$smtpd_last_auth
# used by SOGo
# smtpd_sender_restrictions should match main.cf, but with check_sasl_access prepended for login-as-mailbox-user function
@@ -58,7 +54,6 @@ submission inet n - n - - smtpd
-o smtpd_sender_restrictions=check_sasl_access,regexp:/opt/postfix/conf/allow_mailcow_local.regexp,reject_authenticated_sender_login_mismatch,permit_mynetworks,permit_sasl_authenticated,reject_unlisted_sender,reject_unknown_sender_domain
-o cleanup_service_name=smtp_sender_cleanup
-o syslog_name=postfix/sogo
- -o smtpd_end_of_data_restrictions=$smtpd_last_auth
# used to reinject quarantine mails
590 inet n - n - - smtpd
@@ -68,7 +63,15 @@ submission inet n - n - - smtpd
-o smtpd_milters=
-o non_smtpd_milters=
-o syslog_name=postfix/quarantine
- -o smtpd_end_of_data_restrictions=$smtpd_last_auth
+
+# used to send bcc mails
+591 inet n - n - - smtpd
+ -o smtpd_helo_restrictions=
+ -o smtpd_client_restrictions=permit_mynetworks,reject
+ -o smtpd_tls_auth_only=no
+ -o smtpd_milters=
+ -o non_smtpd_milters=
+ -o syslog_name=postfix/bcc
# enforced smtp connector
smtp_enforced_tls unix - - n - - smtp
@@ -115,7 +118,6 @@ smtp_sender_cleanup unix n - y - 0 cleanup
# start whitelist_fwd
127.0.0.1:10027 inet n n n - 0 spawn user=nobody argv=/usr/local/bin/whitelist_forwardinghosts.sh
-127.0.0.1:10028 inet n n n - 0 spawn user=nobody argv=/usr/local/bin/smtpd_last_login.sh
# end whitelist_fwd
# start watchdog-specific
diff --git a/data/conf/rspamd/custom/bad_header.map b/data/conf/rspamd/custom/bad_header.map
new file mode 100644
index 00000000..839c3c37
--- /dev/null
+++ b/data/conf/rspamd/custom/bad_header.map
@@ -0,0 +1,2 @@
+/Thread-Topic:\s[a-zA-Z]{3}\s[a-zA-Z]{2}[\s\r\n]{0,1}[^a-zA-Z0-9][\r\n]/i
+/Thread-Topic:\s[a-zA-Z]{3}\s[a-zA-Z]{2}\s[a-zA-Z]{1}\s[a-zA-Z]{5}[\s\r\n]{0,1}[^a-zA-Z0-9][\r\n]/i
diff --git a/data/conf/rspamd/dynmaps/bcc.php b/data/conf/rspamd/dynmaps/bcc.php
new file mode 100644
index 00000000..3145feeb
--- /dev/null
+++ b/data/conf/rspamd/dynmaps/bcc.php
@@ -0,0 +1,88 @@
+ 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) {
+ error_log("BCC MAP SQL ERROR: " . $e . PHP_EOL);
+ http_response_code(501);
+ exit;
+}
+
+function parse_email($email) {
+ if(!filter_var($email, FILTER_VALIDATE_EMAIL)) return false;
+ $a = strrpos($email, '@');
+ return array('local' => substr($email, 0, $a), 'domain' => substr(substr($email, $a), 1));
+}
+if (!function_exists('getallheaders')) {
+ function getallheaders() {
+ if (!is_array($_SERVER)) {
+ return array();
+ }
+ $headers = array();
+ foreach ($_SERVER as $name => $value) {
+ if (substr($name, 0, 5) == 'HTTP_') {
+ $headers[str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))))] = $value;
+ }
+ }
+ return $headers;
+ }
+}
+
+// Read headers
+$headers = getallheaders();
+// Get rcpt
+$rcpt = $headers['Rcpt'];
+// Get from
+$from = $headers['From'];
+// Remove tags
+$rcpt = preg_replace('/^(.*?)\+.*(@.*)$/', '$1$2', $rcpt);
+$from = preg_replace('/^(.*?)\+.*(@.*)$/', '$1$2', $from);
+
+try {
+ if (!empty($rcpt)) {
+ $stmt = $pdo->prepare("SELECT `bcc_dest` FROM `bcc_maps` WHERE `type` = 'rcpt' AND `local_dest` = :local_dest AND `active` = '1'");
+ $stmt->execute(array(
+ ':local_dest' => $rcpt
+ ));
+ $bcc_dest = $stmt->fetch(PDO::FETCH_ASSOC)['bcc_dest'];
+ if (!empty($bcc_dest) && filter_var($bcc_dest, FILTER_VALIDATE_EMAIL)) {
+ error_log("BCC MAP: returning ". $bcc_dest . " for " . $rcpt . PHP_EOL);
+ http_response_code(201);
+ echo trim($bcc_dest);
+ exit;
+ }
+ }
+ if (!empty($from)) {
+ $stmt = $pdo->prepare("SELECT `bcc_dest` FROM `bcc_maps` WHERE `type` = 'sender' AND `local_dest` = :local_dest AND `active` = '1'");
+ $stmt->execute(array(
+ ':local_dest' => $from
+ ));
+ $bcc_dest = $stmt->fetch(PDO::FETCH_ASSOC)['bcc_dest'];
+ if (!empty($bcc_dest) && filter_var($bcc_dest, FILTER_VALIDATE_EMAIL)) {
+ error_log("BCC MAP: returning ". $bcc_dest . " for " . $from . PHP_EOL);
+ http_response_code(201);
+ echo trim($bcc_dest);
+ exit;
+ }
+ }
+}
+catch (PDOException $e) {
+ error_log("BCC MAP SQL ERROR: " . $e->getMessage() . PHP_EOL);
+ http_response_code(502);
+ exit;
+}
+
diff --git a/data/conf/rspamd/dynmaps/sasl_logs.php b/data/conf/rspamd/dynmaps/sasl_logs.php
new file mode 100644
index 00000000..2d4cbe65
--- /dev/null
+++ b/data/conf/rspamd/dynmaps/sasl_logs.php
@@ -0,0 +1,2 @@
+
Preparing
-
-
- 🐄 - we are on our way.
- 502
- Please do not stop the stack, while we are initializing the database or do other preparations.
- What is happening? - Nginx cannot connect to an upstream server.
- This is fine, if mailcow was just
+
+ What is happening?
+ Please do not stop the stack while we are initializing the database or do other preparations.
+ What is happening? - Nginx cannot connect to an upstream server or other services are not ready yet.
+ This is fine if mailcow was just
installed or updated and can take a few minutes to complete.
- Please check the logs or contact support, if the error persists.
- docker-compose logs --tail=200 php-fpm-mailcow
- Need support?
+ Please check the logs or contact support if the error persists.
+ Quick debugging
+ Check Nginx and PHP logs:
+ docker-compose logs --tail=200 php-fpm-mailcow nginx-mailcow
+ Make sure your SQL credentials in mailcow.conf (a link to .env) do fit your initialized SQL volume. If you see an access denied, you might have the wrong mailcow.conf:
+ source mailcow.conf ; docker-compose exec mysql-mailcow mysql -u${DBUSER} -p${DBPASS} ${DBNAME}
+ In case of a previous failed installation, remove all volumes and start over (NEVER do this with a production system, it will remove ALL data):
+ docker-compose down -v ; docker-compose up -d
+ Make sure your timezone is correct. Use "America/New_York" for example, do not use spaces. Check here for a list.
+ Click to learn more about getting support.
diff --git a/data/web/admin.php b/data/web/admin.php
index ab1719eb..1a28776c 100644
--- a/data/web/admin.php
+++ b/data/web/admin.php
@@ -12,13 +12,13 @@ if (!isset($_SESSION['gal']) && $license_cache = $redis->Get('LICENSE_STATUS_CAC
?>
-
+
=$lang['admin']['access'];?>
@@ -52,15 +52,16 @@ if (!isset($_SESSION['gal']) && $license_cache = $redis->Get('LICENSE_STATUS_CAC
-
=$lang['admin']['admin_details'];?>
+
=$lang['admin']['admin_details'];?>
@@ -80,7 +81,7 @@ if (!isset($_SESSION['gal']) && $license_cache = $redis->Get('LICENSE_STATUS_CAC
=$lang['tfa']['tfa'];?>:
-
=$tfa_data['pretty'];?>
+
=(isset($tfa_data['pretty'])) ? $tfa_data['pretty'] : '';?>
Get('LICENSE_STATUS_CAC
// FIDO2 ?>
-
-
-
+
=$lang['fido2']['fido2_auth'];?>
-
=$lang['fido2']['known_ids'];?>:
-
+
=$lang['fido2']['known_ids'];?>:
+
@@ -134,14 +133,14 @@ if (!isset($_SESSION['gal']) && $license_cache = $redis->Get('LICENSE_STATUS_CAC
?>
- =($_SESSION['fido2_cid'] == $key_info['cid']) ? '→ ' : NULL; ?>=(!empty($key_info['fn']))?$key_info['fn']:$key_info['subject'];?>
+ =(isset($_SESSION['fido2_cid']) && $_SESSION['fido2_cid'] == $key_info['cid']) ? ' ' : NULL; ?>=(!empty($key_info['fn']))?$key_info['fn']:$key_info['subject'];?>
@@ -157,7 +156,7 @@ if (!isset($_SESSION['gal']) && $license_cache = $redis->Get('LICENSE_STATUS_CAC
- =$lang['fido2']['set_fido2'];?>
+ =$lang['fido2']['set_fido2'];?>
@@ -169,8 +168,8 @@ if (!isset($_SESSION['gal']) && $license_cache = $redis->Get('LICENSE_STATUS_CAC
-
- =$lang['admin']['guid_and_license'];?>
+
+ =$lang['admin']['guid_and_license'];?>
-
- API
+
+ API
@@ -216,20 +215,20 @@ if (!isset($_SESSION['gal']) && $license_cache = $redis->Get('LICENSE_STATUS_CAC
-
⇇ Read-Only Access
+ =$lang['admin']['api_read_only'];?>
@@ -729,22 +737,23 @@ if (!isset($_SESSION['gal']) && $license_cache = $redis->Get('LICENSE_STATUS_CAC
=$lang['admin']['add_forwarding_host'];?>
=$lang['admin']['forwarding_hosts_add_hint'];?>
- =$lang['admin']['host'];?>
-
+ =$lang['admin']['host'];?>
+
@@ -752,7 +761,7 @@ if (!isset($_SESSION['gal']) && $license_cache = $redis->Get('LICENSE_STATUS_CAC
=$lang['admin']['inactive'];?>
- =$lang['admin']['add'];?>
+ =$lang['admin']['add'];?>
@@ -767,48 +776,51 @@ if (!isset($_SESSION['gal']) && $license_cache = $redis->Get('LICENSE_STATUS_CAC
?>
- =$lang['admin']['f2b_ban_time'];?>:
-
+ =$lang['admin']['f2b_ban_time'];?>:
+
- =$lang['admin']['f2b_max_attempts'];?>:
-
+ =$lang['admin']['f2b_max_attempts'];?>:
+
- =$lang['admin']['f2b_retry_window'];?>:
-
+ =$lang['admin']['f2b_retry_window'];?>:
+
=$lang['admin']['f2b_list_info'];?>
- =$lang['admin']['f2b_whitelist'];?>:
- =$f2b_data['whitelist'];?>
+ =$lang['admin']['f2b_whitelist'];?>:
+ =$f2b_data['whitelist'];?>
- =$lang['admin']['f2b_blacklist'];?>:
- =$f2b_data['blacklist'];?>
+ =$lang['admin']['f2b_blacklist'];?>:
+ =$f2b_data['blacklist'];?>
-
- =$lang['admin']['f2b_filter'];?>
+
+ =$lang['admin']['f2b_filter'];?>
+
+
=$lang['admin']['f2b_regex_info'];?>
- =$lang['admin']['save'];?>
- =$lang['admin']['reset_default'];?>
- =$lang['admin']['add_row'];?>
+ =$lang['admin']['save'];?>
+ =$lang['admin']['reset_default'];?>
+ =$lang['admin']['add_row'];?>
-
+
+
=$lang['admin']['ban_list_info'];?>
Get('LICENSE_STATUS_CAC
if (!empty($f2b_data['active_bans'])):
foreach ($f2b_data['active_bans'] as $active_bans):
?>
- =$active_bans['network'];?> (=$active_bans['banned_until'];?>) -
+ =$active_bans['network'];?> (=$active_bans['banned_until'];?>) -
@@ -872,7 +885,7 @@ if (!isset($_SESSION['gal']) && $license_cache = $redis->Get('LICENSE_STATUS_CAC
if (!empty($f2b_data['perm_bans'])):
foreach ($f2b_data['perm_bans'] as $perm_bans):
?>
- =$perm_bans?>
+ =$perm_bans['network'];?>
Get('LICENSE_STATUS_CAC
- =$lang['quarantine']['disabled_by_config'];?>
+ =$lang['quarantine']['disabled_by_config'];?>
@@ -990,21 +1005,21 @@ if (!isset($_SESSION['gal']) && $license_cache = $redis->Get('LICENSE_STATUS_CAC
-
- =$lang['admin']['quarantine_notification_html'];?>
+
+ =$lang['admin']['quarantine_notification_html'];?>
@@ -1016,11 +1031,11 @@ if (!isset($_SESSION['gal']) && $license_cache = $redis->Get('LICENSE_STATUS_CAC
@@ -1033,8 +1048,8 @@ if (!isset($_SESSION['gal']) && $license_cache = $redis->Get('LICENSE_STATUS_CAC
=$lang['admin']['rspamd_settings_map'];?>
-
- =$lang['admin']['active_rspamd_settings_map'];?>
+
+ =$lang['admin']['active_rspamd_settings_map'];?>
@@ -1121,12 +1136,12 @@ if (!isset($_SESSION['gal']) && $license_cache = $redis->Get('LICENSE_STATUS_CAC
=$lang['admin']['customize'];?>
- =$lang['admin']['reset_default'];?>
+ =$lang['admin']['reset_default'];?>
@@ -1161,29 +1176,32 @@ if (!isset($_SESSION['gal']) && $license_cache = $redis->Get('LICENSE_STATUS_CAC
$val) {
- ?>
+ if ($app_links) {
+ foreach ($app_links as $row) {
+ foreach ($row as $key => $val) {
+ ?>
-
-
- =$lang['admin']['remove_row'];?>
+
+
+ =$lang['admin']['remove_row'];?>
-
-
+
+
-
=$lang['admin']['save'];?>
-
=$lang['admin']['add_row'];?>
+
=$lang['admin']['save'];?>
+
=$lang['admin']['add_row'];?>
+
=$lang['admin']['ui_texts'];?>
@@ -1191,16 +1209,16 @@ if (!isset($_SESSION['gal']) && $license_cache = $redis->Get('LICENSE_STATUS_CAC
- =$lang['admin']['title_name'];?>:
-
+ =$lang['admin']['title_name'];?>:
+
- =$lang['admin']['main_name'];?>:
-
+ =$lang['admin']['main_name'];?>:
+
- =$lang['admin']['apps_name'];?>:
-
+ =$lang['admin']['apps_name'];?>:
+
@@ -1268,7 +1286,7 @@ if (!isset($_SESSION['gal']) && $license_cache = $redis->Get('LICENSE_STATUS_CAC
@@ -1283,15 +1301,15 @@ if (!isset($_SESSION['gal']) && $license_cache = $redis->Get('LICENSE_STATUS_CAC
Get('LICENSE_STATUS_CAC
=$lang['admin']['html'];?> (=$lang['admin']['optional'];?>):
@@ -1359,7 +1377,7 @@ if (!isset($_SESSION['gal']) && $license_cache = $redis->Get('LICENSE_STATUS_CAC
@@ -1381,30 +1399,31 @@ if (!isset($_SESSION['gal']) && $license_cache = $redis->Get('LICENSE_STATUS_CAC
@@ -1418,7 +1437,7 @@ if (!isset($_SESSION['gal']) && $license_cache = $redis->Get('LICENSE_STATUS_CAC
=$lang['admin']['rspamd_global_filters_info'];?>
-
+
-
+
=$lang['admin']['regex_maps'];?>
@@ -1455,8 +1474,8 @@ if (!isset($_SESSION['gal']) && $license_cache = $redis->Get('LICENSE_STATUS_CAC
@@ -1491,7 +1510,7 @@ $js_minifier->add('/web/js/presets/rspamd.js');
$js_minifier->add('/web/js/site/pwgen.js');
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/footer.inc.php';
} else {
- header('Location: /');
- exit();
+ header('Location: /');
+ exit();
}
?>
diff --git a/data/web/css/build/001-bootstrap.min.css b/data/web/css/build/001-bootstrap.min.css
index 769bf30c..232dfb9d 100644
--- a/data/web/css/build/001-bootstrap.min.css
+++ b/data/web/css/build/001-bootstrap.min.css
@@ -8,4 +8,4 @@
* Bootstrap v3.4.1 (https://getbootstrap.com/)
* Copyright 2011-2019 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
- *//*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bold}dfn{font-style:italic}h1{font-size:2em;margin:0.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace, monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0}input[type="number"]::-webkit-inner-spin-button,input[type="number"]::-webkit-outer-spin-button{height:auto}input[type="search"]{-webkit-appearance:textfield;box-sizing:content-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:0.35em 0.625em 0.75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:bold}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,*:before,*:after{color:#000 !important;text-shadow:none !important;background:transparent !important;box-shadow:none !important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="#"]:after,a[href^="javascript:"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100% !important}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000 !important}.label{border:1px solid #000}.table{border-collapse:collapse !important}.table td,.table th{background-color:#fff !important}.table-bordered th,.table-bordered td{border:1px solid #ddd !important}}@font-face{font-family:"Glyphicons Halflings";src:url("/fonts/glyphicons-halflings-regular.eot");src:url("/fonts/glyphicons-halflings-regular.eot?#iefix") format("embedded-opentype"),url("/fonts/glyphicons-halflings-regular.woff2") format("woff2"),url("/fonts/glyphicons-halflings-regular.woff") format("woff"),url("/fonts/glyphicons-halflings-regular.ttf") format("truetype"),url("/fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular") format("svg")}.glyphicon{position:relative;top:1px;display:inline-block;font-family:"Glyphicons Halflings";font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\002a"}.glyphicon-plus:before{content:"\002b"}.glyphicon-euro:before,.glyphicon-eur:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-lock:before{content:"\e033"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-bookmark:before{content:"\e044"}.glyphicon-print:before{content:"\e045"}.glyphicon-camera:before{content:"\e046"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before{content:"\e072"}.glyphicon-pause:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-fire:before{content:"\e104"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-calendar:before{content:"\e109"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-bell:before{content:"\e123"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-wrench:before{content:"\e136"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-briefcase:before{content:"\e139"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-paperclip:before{content:"\e142"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-pushpin:before{content:"\e146"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}.glyphicon-cd:before{content:"\e201"}.glyphicon-save-file:before{content:"\e202"}.glyphicon-open-file:before{content:"\e203"}.glyphicon-level-up:before{content:"\e204"}.glyphicon-copy:before{content:"\e205"}.glyphicon-paste:before{content:"\e206"}.glyphicon-alert:before{content:"\e209"}.glyphicon-equalizer:before{content:"\e210"}.glyphicon-king:before{content:"\e211"}.glyphicon-queen:before{content:"\e212"}.glyphicon-pawn:before{content:"\e213"}.glyphicon-bishop:before{content:"\e214"}.glyphicon-knight:before{content:"\e215"}.glyphicon-baby-formula:before{content:"\e216"}.glyphicon-tent:before{content:"\26fa"}.glyphicon-blackboard:before{content:"\e218"}.glyphicon-bed:before{content:"\e219"}.glyphicon-apple:before{content:"\f8ff"}.glyphicon-erase:before{content:"\e221"}.glyphicon-hourglass:before{content:"\231b"}.glyphicon-lamp:before{content:"\e223"}.glyphicon-duplicate:before{content:"\e224"}.glyphicon-piggy-bank:before{content:"\e225"}.glyphicon-scissors:before{content:"\e226"}.glyphicon-bitcoin:before{content:"\e227"}.glyphicon-btc:before{content:"\e227"}.glyphicon-xbt:before{content:"\e227"}.glyphicon-yen:before{content:"\00a5"}.glyphicon-jpy:before{content:"\00a5"}.glyphicon-ruble:before{content:"\20bd"}.glyphicon-rub:before{content:"\20bd"}.glyphicon-scale:before{content:"\e230"}.glyphicon-ice-lolly:before{content:"\e231"}.glyphicon-ice-lolly-tasted:before{content:"\e232"}.glyphicon-education:before{content:"\e233"}.glyphicon-option-horizontal:before{content:"\e234"}.glyphicon-option-vertical:before{content:"\e235"}.glyphicon-menu-hamburger:before{content:"\e236"}.glyphicon-modal-window:before{content:"\e237"}.glyphicon-oil:before{content:"\e238"}.glyphicon-grain:before{content:"\e239"}.glyphicon-sunglasses:before{content:"\e240"}.glyphicon-text-size:before{content:"\e241"}.glyphicon-text-color:before{content:"\e242"}.glyphicon-text-background:before{content:"\e243"}.glyphicon-object-align-top:before{content:"\e244"}.glyphicon-object-align-bottom:before{content:"\e245"}.glyphicon-object-align-horizontal:before{content:"\e246"}.glyphicon-object-align-left:before{content:"\e247"}.glyphicon-object-align-vertical:before{content:"\e248"}.glyphicon-object-align-right:before{content:"\e249"}.glyphicon-triangle-right:before{content:"\e250"}.glyphicon-triangle-left:before{content:"\e251"}.glyphicon-triangle-bottom:before{content:"\e252"}.glyphicon-triangle-top:before{content:"\e253"}.glyphicon-console:before{content:"\e254"}.glyphicon-superscript:before{content:"\e255"}.glyphicon-subscript:before{content:"\e256"}.glyphicon-menu-left:before{content:"\e257"}.glyphicon-menu-right:before{content:"\e258"}.glyphicon-menu-down:before{content:"\e259"}.glyphicon-menu-up:before{content:"\e260"}*{box-sizing:border-box}*:before,*:after{box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Source Sans Pro","Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#555555;background-color:#ffffff}input,button,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#158cba;text-decoration:none}a:hover,a:focus{color:#158cba;text-decoration:underline}a:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.img-responsive,.thumbnail>img,.thumbnail a>img,.carousel-inner>.item>img,.carousel-inner>.item>a>img{display:block;max-width:100%;height:auto}.img-rounded{border-radius:5px}.img-thumbnail{padding:4px;line-height:1.42857143;background-color:#ffffff;border:1px solid #eeeeee;border-radius:4px;transition:all .2s ease-in-out;display:inline-block;max-width:100%;height:auto}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eeeeee}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}[role="button"]{cursor:pointer}h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6{font-family:inherit;font-weight:400;line-height:1.1;color:#333333}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small,.h1 small,.h2 small,.h3 small,.h4 small,.h5 small,.h6 small,h1 .small,h2 .small,h3 .small,h4 .small,h5 .small,h6 .small,.h1 .small,.h2 .small,.h3 .small,.h4 .small,.h5 .small,.h6 .small{font-weight:400;line-height:1;color:#999999}h1,.h1,h2,.h2,h3,.h3{margin-top:20px;margin-bottom:10px}h1 small,.h1 small,h2 small,.h2 small,h3 small,.h3 small,h1 .small,.h1 .small,h2 .small,.h2 .small,h3 .small,.h3 .small{font-size:65%}h4,.h4,h5,.h5,h6,.h6{margin-top:10px;margin-bottom:10px}h4 small,.h4 small,h5 small,.h5 small,h6 small,.h6 small,h4 .small,.h4 .small,h5 .small,.h5 .small,h6 .small,.h6 .small{font-size:75%}h1,.h1{font-size:36px}h2,.h2{font-size:30px}h3,.h3{font-size:24px}h4,.h4{font-size:18px}h5,.h5{font-size:14px}h6,.h6{font-size:12px}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16px;font-weight:300;line-height:1.4}@media (min-width:768px){.lead{font-size:21px}}small,.small{font-size:85%}mark,.mark{padding:.2em;background-color:#ff851b}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#999999}.text-primary{color:#158cba}a.text-primary:hover,a.text-primary:focus{color:#106a8c}.text-success{color:#ffffff}a.text-success:hover,a.text-success:focus{color:#e6e6e6}.text-info{color:#ffffff}a.text-info:hover,a.text-info:focus{color:#e6e6e6}.text-warning{color:#ffffff}a.text-warning:hover,a.text-warning:focus{color:#e6e6e6}.text-danger{color:#ffffff}a.text-danger:hover,a.text-danger:focus{color:#e6e6e6}.bg-primary{color:#fff;background-color:#158cba}a.bg-primary:hover,a.bg-primary:focus{background-color:#106a8c}.bg-success{background-color:#28b62c}a.bg-success:hover,a.bg-success:focus{background-color:#1f8c22}.bg-info{background-color:#75caeb}a.bg-info:hover,a.bg-info:focus{background-color:#48b9e5}.bg-warning{background-color:#ff851b}a.bg-warning:hover,a.bg-warning:focus{background-color:#e76b00}.bg-danger{background-color:#ff4136}a.bg-danger:hover,a.bg-danger:focus{background-color:#ff1103}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eeeeee}ul,ol{margin-top:0;margin-bottom:10px}ul ul,ol ul,ul ol,ol ol{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none;margin-left:-5px}.list-inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-top:0;margin-bottom:20px}dt,dd{line-height:1.42857143}dt{font-weight:700}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;clear:left;text-align:right;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[title],abbr[data-original-title]{cursor:help}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #eeeeee}blockquote p:last-child,blockquote ul:last-child,blockquote ol:last-child{margin-bottom:0}blockquote footer,blockquote small,blockquote .small{display:block;font-size:80%;line-height:1.42857143;color:#999999}blockquote footer:before,blockquote small:before,blockquote .small:before{content:"\2014 \00A0"}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;text-align:right;border-right:5px solid #eeeeee;border-left:0}.blockquote-reverse footer:before,blockquote.pull-right footer:before,.blockquote-reverse small:before,blockquote.pull-right small:before,.blockquote-reverse .small:before,blockquote.pull-right .small:before{content:""}.blockquote-reverse footer:after,blockquote.pull-right footer:after,.blockquote-reverse small:after,blockquote.pull-right small:after,.blockquote-reverse .small:after,blockquote.pull-right .small:after{content:"\00A0 \2014"}address{margin-bottom:20px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#ffffff;background-color:#333333;border-radius:2px;box-shadow:inset 0 -1px 0 rgba(0,0,0,0.25)}kbd kbd{padding:0;font-size:100%;font-weight:700;box-shadow:none}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.42857143;color:#333333;word-break:break-all;word-wrap:break-word;background-color:#f5f5f5;border:1px solid #cccccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{margin-right:-15px;margin-left:-15px}.row-no-gutters{margin-right:0;margin-left:0}.row-no-gutters [class*="col-"]{padding-right:0;padding-left:0}.col-xs-1,.col-sm-1,.col-md-1,.col-lg-1,.col-xs-2,.col-sm-2,.col-md-2,.col-lg-2,.col-xs-3,.col-sm-3,.col-md-3,.col-lg-3,.col-xs-4,.col-sm-4,.col-md-4,.col-lg-4,.col-xs-5,.col-sm-5,.col-md-5,.col-lg-5,.col-xs-6,.col-sm-6,.col-md-6,.col-lg-6,.col-xs-7,.col-sm-7,.col-md-7,.col-lg-7,.col-xs-8,.col-sm-8,.col-md-8,.col-lg-8,.col-xs-9,.col-sm-9,.col-md-9,.col-lg-9,.col-xs-10,.col-sm-10,.col-md-10,.col-lg-10,.col-xs-11,.col-sm-11,.col-md-11,.col-lg-11,.col-xs-12,.col-sm-12,.col-md-12,.col-lg-12{position:relative;min-height:1px;padding-right:15px;padding-left:15px}.col-xs-1,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9,.col-xs-10,.col-xs-11,.col-xs-12{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0%}@media (min-width:768px){.col-sm-1,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-10,.col-sm-11,.col-sm-12{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0%}}@media (min-width:992px){.col-md-1,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-10,.col-md-11,.col-md-12{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0%}}@media (min-width:1200px){.col-lg-1,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-10,.col-lg-11,.col-lg-12{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0%}}table{background-color:transparent}table col[class*="col-"]{position:static;display:table-column;float:none}table td[class*="col-"],table th[class*="col-"]{position:static;display:table-cell;float:none}caption{padding-top:8px;padding-bottom:8px;color:#999999;text-align:left}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:20px}.table>thead>tr>th,.table>tbody>tr>th,.table>tfoot>tr>th,.table>thead>tr>td,.table>tbody>tr>td,.table>tfoot>tr>td{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #eeeeee}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #eeeeee}.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>th,.table>caption+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>td,.table>thead:first-child>tr:first-child>td{border-top:0}.table>tbody+tbody{border-top:2px solid #eeeeee}.table .table{background-color:#ffffff}.table-condensed>thead>tr>th,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>tbody>tr>td,.table-condensed>tfoot>tr>td{padding:5px}.table-bordered{border:1px solid #eeeeee}.table-bordered>thead>tr>th,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>tbody>tr>td,.table-bordered>tfoot>tr>td{border:1px solid #eeeeee}.table-bordered>thead>tr>th,.table-bordered>thead>tr>td{border-bottom-width:2px}.table-striped>tbody>tr:nth-of-type(odd){background-color:#f9f9f9}.table-hover>tbody>tr:hover{background-color:#f5f5f5}.table>thead>tr>td.active,.table>tbody>tr>td.active,.table>tfoot>tr>td.active,.table>thead>tr>th.active,.table>tbody>tr>th.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>tbody>tr.active>td,.table>tfoot>tr.active>td,.table>thead>tr.active>th,.table>tbody>tr.active>th,.table>tfoot>tr.active>th{background-color:#f5f5f5}.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover,.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr.active:hover>th{background-color:#e8e8e8}.table>thead>tr>td.success,.table>tbody>tr>td.success,.table>tfoot>tr>td.success,.table>thead>tr>th.success,.table>tbody>tr>th.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>tbody>tr.success>td,.table>tfoot>tr.success>td,.table>thead>tr.success>th,.table>tbody>tr.success>th,.table>tfoot>tr.success>th{background-color:#28b62c}.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover,.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr.success:hover>th{background-color:#23a127}.table>thead>tr>td.info,.table>tbody>tr>td.info,.table>tfoot>tr>td.info,.table>thead>tr>th.info,.table>tbody>tr>th.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>tbody>tr.info>td,.table>tfoot>tr.info>td,.table>thead>tr.info>th,.table>tbody>tr.info>th,.table>tfoot>tr.info>th{background-color:#75caeb}.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover,.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr.info:hover>th{background-color:#5fc1e8}.table>thead>tr>td.warning,.table>tbody>tr>td.warning,.table>tfoot>tr>td.warning,.table>thead>tr>th.warning,.table>tbody>tr>th.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>tbody>tr.warning>td,.table>tfoot>tr.warning>td,.table>thead>tr.warning>th,.table>tbody>tr.warning>th,.table>tfoot>tr.warning>th{background-color:#ff851b}.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover,.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr.warning:hover>th{background-color:#ff7701}.table>thead>tr>td.danger,.table>tbody>tr>td.danger,.table>tfoot>tr>td.danger,.table>thead>tr>th.danger,.table>tbody>tr>th.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>tbody>tr.danger>td,.table>tfoot>tr.danger>td,.table>thead>tr.danger>th,.table>tbody>tr.danger>th,.table>tfoot>tr.danger>th{background-color:#ff4136}.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover,.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr.danger:hover>th{background-color:#ff291c}.table-responsive{min-height:.01%;overflow-x:auto}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #eeeeee}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>thead>tr>th,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tfoot>tr>td{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>thead>tr>th:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.table-responsive>.table-bordered>thead>tr>th:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>th,.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>td{border-bottom:0}}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:700}input[type="search"]{box-sizing:border-box;-webkit-appearance:none;appearance:none}input[type="radio"],input[type="checkbox"]{margin:4px 0 0;margin-top:1px \9;line-height:normal}input[type="radio"][disabled],input[type="checkbox"][disabled],input[type="radio"].disabled,input[type="checkbox"].disabled,fieldset[disabled] input[type="radio"],fieldset[disabled] input[type="checkbox"]{cursor:not-allowed}input[type="file"]{display:block}input[type="range"]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:8px;font-size:14px;line-height:1.42857143;color:#555555}.form-control{display:block;width:100%;height:38px;padding:7px 12px;font-size:14px;line-height:1.42857143;color:#555555;background-color:#ffffff;background-image:none;border:1px solid #e7e7e7;border-radius:4px;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(102,175,233,0.6)}.form-control::-moz-placeholder{color:#999999;opacity:1}.form-control:-ms-input-placeholder{color:#999999}.form-control::-webkit-input-placeholder{color:#999999}.form-control::-ms-expand{background-color:transparent;border:0}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{background-color:#eeeeee;opacity:1}.form-control[disabled],fieldset[disabled] .form-control{cursor:not-allowed}textarea.form-control{height:auto}@media screen and (-webkit-min-device-pixel-ratio:0){input[type="date"].form-control,input[type="time"].form-control,input[type="datetime-local"].form-control,input[type="month"].form-control{line-height:38px}input[type="date"].input-sm,input[type="time"].input-sm,input[type="datetime-local"].input-sm,input[type="month"].input-sm,.input-group-sm input[type="date"],.input-group-sm input[type="time"],.input-group-sm input[type="datetime-local"],.input-group-sm input[type="month"]{line-height:28px}input[type="date"].input-lg,input[type="time"].input-lg,input[type="datetime-local"].input-lg,input[type="month"].input-lg,.input-group-lg input[type="date"],.input-group-lg input[type="time"],.input-group-lg input[type="datetime-local"],.input-group-lg input[type="month"]{line-height:52px}}.form-group{margin-bottom:15px}.radio,.checkbox{position:relative;display:block;margin-top:10px;margin-bottom:10px}.radio.disabled label,.checkbox.disabled label,fieldset[disabled] .radio label,fieldset[disabled] .checkbox label{cursor:not-allowed}.radio label,.checkbox label{min-height:20px;padding-left:20px;margin-bottom:0;font-weight:400;cursor:pointer}.radio input[type="radio"],.radio-inline input[type="radio"],.checkbox input[type="checkbox"],.checkbox-inline input[type="checkbox"]{position:absolute;margin-top:4px \9;margin-left:-20px}.radio+.radio,.checkbox+.checkbox{margin-top:-5px}.radio-inline,.checkbox-inline{position:relative;display:inline-block;padding-left:20px;margin-bottom:0;font-weight:400;vertical-align:middle;cursor:pointer}.radio-inline.disabled,.checkbox-inline.disabled,fieldset[disabled] .radio-inline,fieldset[disabled] .checkbox-inline{cursor:not-allowed}.radio-inline+.radio-inline,.checkbox-inline+.checkbox-inline{margin-top:0;margin-left:10px}.form-control-static{min-height:34px;padding-top:8px;padding-bottom:8px;margin-bottom:0}.form-control-static.input-lg,.form-control-static.input-sm{padding-right:0;padding-left:0}.input-sm{height:28px;padding:4px 10px;font-size:12px;line-height:1.5;border-radius:2px}select.input-sm{height:28px;line-height:28px}textarea.input-sm,select[multiple].input-sm{height:auto}.form-group-sm .form-control{height:28px;padding:4px 10px;font-size:12px;line-height:1.5;border-radius:2px}.form-group-sm select.form-control{height:28px;line-height:28px}.form-group-sm textarea.form-control,.form-group-sm select[multiple].form-control{height:auto}.form-group-sm .form-control-static{height:28px;min-height:32px;padding:5px 10px;font-size:12px;line-height:1.5}.input-lg{height:52px;padding:13px 16px;font-size:18px;line-height:1.3333333;border-radius:5px}select.input-lg{height:52px;line-height:52px}textarea.input-lg,select[multiple].input-lg{height:auto}.form-group-lg .form-control{height:52px;padding:13px 16px;font-size:18px;line-height:1.3333333;border-radius:5px}.form-group-lg select.form-control{height:52px;line-height:52px}.form-group-lg textarea.form-control,.form-group-lg select[multiple].form-control{height:auto}.form-group-lg .form-control-static{height:52px;min-height:38px;padding:14px 16px;font-size:18px;line-height:1.3333333}.has-feedback{position:relative}.has-feedback .form-control{padding-right:47.5px}.form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:38px;height:38px;line-height:38px;text-align:center;pointer-events:none}.input-lg+.form-control-feedback,.input-group-lg+.form-control-feedback,.form-group-lg .form-control+.form-control-feedback{width:52px;height:52px;line-height:52px}.input-sm+.form-control-feedback,.input-group-sm+.form-control-feedback,.form-group-sm .form-control+.form-control-feedback{width:28px;height:28px;line-height:28px}.has-success .help-block,.has-success .control-label,.has-success .radio,.has-success .checkbox,.has-success .radio-inline,.has-success .checkbox-inline,.has-success.radio label,.has-success.checkbox label,.has-success.radio-inline label,.has-success.checkbox-inline label{color:#ffffff}.has-success .form-control{border-color:#ffffff;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-success .form-control:focus{border-color:#e6e6e6;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #fff}.has-success .input-group-addon{color:#ffffff;background-color:#28b62c;border-color:#ffffff}.has-success .form-control-feedback{color:#ffffff}.has-warning .help-block,.has-warning .control-label,.has-warning .radio,.has-warning .checkbox,.has-warning .radio-inline,.has-warning .checkbox-inline,.has-warning.radio label,.has-warning.checkbox label,.has-warning.radio-inline label,.has-warning.checkbox-inline label{color:#ffffff}.has-warning .form-control{border-color:#ffffff;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-warning .form-control:focus{border-color:#e6e6e6;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #fff}.has-warning .input-group-addon{color:#ffffff;background-color:#ff851b;border-color:#ffffff}.has-warning .form-control-feedback{color:#ffffff}.has-error .help-block,.has-error .control-label,.has-error .radio,.has-error .checkbox,.has-error .radio-inline,.has-error .checkbox-inline,.has-error.radio label,.has-error.checkbox label,.has-error.radio-inline label,.has-error.checkbox-inline label{color:#ffffff}.has-error .form-control{border-color:#ffffff;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-error .form-control:focus{border-color:#e6e6e6;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #fff}.has-error .input-group-addon{color:#ffffff;background-color:#ff4136;border-color:#ffffff}.has-error .form-control-feedback{color:#ffffff}.has-feedback label~.form-control-feedback{top:25px}.has-feedback label.sr-only~.form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#959595}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-static{display:inline-block}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn,.form-inline .input-group .form-control{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .radio,.form-inline .checkbox{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .radio label,.form-inline .checkbox label{padding-left:0}.form-inline .radio input[type="radio"],.form-inline .checkbox input[type="checkbox"]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .radio,.form-horizontal .checkbox,.form-horizontal .radio-inline,.form-horizontal .checkbox-inline{padding-top:8px;margin-top:0;margin-bottom:0}.form-horizontal .radio,.form-horizontal .checkbox{min-height:28px}.form-horizontal .form-group{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.form-horizontal .control-label{padding-top:8px;margin-bottom:0;text-align:right}}.form-horizontal .has-feedback .form-control-feedback{right:15px}@media (min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:14px;font-size:18px}}@media (min-width:768px){.form-horizontal .form-group-sm .control-label{padding-top:5px;font-size:12px}}.btn{display:inline-block;margin-bottom:0;font-weight:normal;text-align:center;white-space:nowrap;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;background-image:none;border:1px solid transparent;padding:7px 12px;font-size:14px;line-height:1.42857143;border-radius:4px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.btn:focus,.btn:active:focus,.btn.active:focus,.btn.focus,.btn:active.focus,.btn.active.focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn:hover,.btn:focus,.btn.focus{color:#555555;text-decoration:none}.btn:active,.btn.active{background-image:none;outline:0;box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;filter:alpha(opacity=65);opacity:0.65;box-shadow:none}a.btn.disabled,fieldset[disabled] a.btn{pointer-events:none}.btn-default{color:#555555;background-color:#eeeeee;border-color:#e2e2e2}.btn-default:focus,.btn-default.focus{color:#555555;background-color:#d5d5d5;border-color:#a2a2a2}.btn-default:hover{color:#555555;background-color:#d5d5d5;border-color:#c3c3c3}.btn-default:active,.btn-default.active,.open>.dropdown-toggle.btn-default{color:#555555;background-color:#d5d5d5;background-image:none;border-color:#c3c3c3}.btn-default:active:hover,.btn-default.active:hover,.open>.dropdown-toggle.btn-default:hover,.btn-default:active:focus,.btn-default.active:focus,.open>.dropdown-toggle.btn-default:focus,.btn-default:active.focus,.btn-default.active.focus,.open>.dropdown-toggle.btn-default.focus{color:#555555;background-color:#c3c3c3;border-color:#a2a2a2}.btn-default.disabled:hover,.btn-default[disabled]:hover,fieldset[disabled] .btn-default:hover,.btn-default.disabled:focus,.btn-default[disabled]:focus,fieldset[disabled] .btn-default:focus,.btn-default.disabled.focus,.btn-default[disabled].focus,fieldset[disabled] .btn-default.focus{background-color:#eeeeee;border-color:#e2e2e2}.btn-default .badge{color:#eeeeee;background-color:#555555}.btn-primary{color:#ffffff;background-color:#158cba;border-color:#127ba3}.btn-primary:focus,.btn-primary.focus{color:#ffffff;background-color:#106a8c;border-color:#052531}.btn-primary:hover{color:#ffffff;background-color:#106a8c;border-color:#0c516c}.btn-primary:active,.btn-primary.active,.open>.dropdown-toggle.btn-primary{color:#ffffff;background-color:#106a8c;background-image:none;border-color:#0c516c}.btn-primary:active:hover,.btn-primary.active:hover,.open>.dropdown-toggle.btn-primary:hover,.btn-primary:active:focus,.btn-primary.active:focus,.open>.dropdown-toggle.btn-primary:focus,.btn-primary:active.focus,.btn-primary.active.focus,.open>.dropdown-toggle.btn-primary.focus{color:#ffffff;background-color:#0c516c;border-color:#052531}.btn-primary.disabled:hover,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary:hover,.btn-primary.disabled:focus,.btn-primary[disabled]:focus,fieldset[disabled] .btn-primary:focus,.btn-primary.disabled.focus,.btn-primary[disabled].focus,fieldset[disabled] .btn-primary.focus{background-color:#158cba;border-color:#127ba3}.btn-primary .badge{color:#158cba;background-color:#ffffff}.btn-success{color:#ffffff;background-color:#28b62c;border-color:#23a127}.btn-success:focus,.btn-success.focus{color:#ffffff;background-color:#1f8c22;border-color:#0c390e}.btn-success:hover{color:#ffffff;background-color:#1f8c22;border-color:#186f1b}.btn-success:active,.btn-success.active,.open>.dropdown-toggle.btn-success{color:#ffffff;background-color:#1f8c22;background-image:none;border-color:#186f1b}.btn-success:active:hover,.btn-success.active:hover,.open>.dropdown-toggle.btn-success:hover,.btn-success:active:focus,.btn-success.active:focus,.open>.dropdown-toggle.btn-success:focus,.btn-success:active.focus,.btn-success.active.focus,.open>.dropdown-toggle.btn-success.focus{color:#ffffff;background-color:#186f1b;border-color:#0c390e}.btn-success.disabled:hover,.btn-success[disabled]:hover,fieldset[disabled] .btn-success:hover,.btn-success.disabled:focus,.btn-success[disabled]:focus,fieldset[disabled] .btn-success:focus,.btn-success.disabled.focus,.btn-success[disabled].focus,fieldset[disabled] .btn-success.focus{background-color:#28b62c;border-color:#23a127}.btn-success .badge{color:#28b62c;background-color:#ffffff}.btn-info{color:#ffffff;background-color:#75caeb;border-color:#5fc1e8}.btn-info:focus,.btn-info.focus{color:#ffffff;background-color:#48b9e5;border-color:#1984ae}.btn-info:hover{color:#ffffff;background-color:#48b9e5;border-color:#29ade0}.btn-info:active,.btn-info.active,.open>.dropdown-toggle.btn-info{color:#ffffff;background-color:#48b9e5;background-image:none;border-color:#29ade0}.btn-info:active:hover,.btn-info.active:hover,.open>.dropdown-toggle.btn-info:hover,.btn-info:active:focus,.btn-info.active:focus,.open>.dropdown-toggle.btn-info:focus,.btn-info:active.focus,.btn-info.active.focus,.open>.dropdown-toggle.btn-info.focus{color:#ffffff;background-color:#29ade0;border-color:#1984ae}.btn-info.disabled:hover,.btn-info[disabled]:hover,fieldset[disabled] .btn-info:hover,.btn-info.disabled:focus,.btn-info[disabled]:focus,fieldset[disabled] .btn-info:focus,.btn-info.disabled.focus,.btn-info[disabled].focus,fieldset[disabled] .btn-info.focus{background-color:#75caeb;border-color:#5fc1e8}.btn-info .badge{color:#75caeb;background-color:#ffffff}.btn-warning{color:#ffffff;background-color:#ff851b;border-color:#ff7701}.btn-warning:focus,.btn-warning.focus{color:#ffffff;background-color:#e76b00;border-color:#813c00}.btn-warning:hover{color:#ffffff;background-color:#e76b00;border-color:#c35b00}.btn-warning:active,.btn-warning.active,.open>.dropdown-toggle.btn-warning{color:#ffffff;background-color:#e76b00;background-image:none;border-color:#c35b00}.btn-warning:active:hover,.btn-warning.active:hover,.open>.dropdown-toggle.btn-warning:hover,.btn-warning:active:focus,.btn-warning.active:focus,.open>.dropdown-toggle.btn-warning:focus,.btn-warning:active.focus,.btn-warning.active.focus,.open>.dropdown-toggle.btn-warning.focus{color:#ffffff;background-color:#c35b00;border-color:#813c00}.btn-warning.disabled:hover,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning:hover,.btn-warning.disabled:focus,.btn-warning[disabled]:focus,fieldset[disabled] .btn-warning:focus,.btn-warning.disabled.focus,.btn-warning[disabled].focus,fieldset[disabled] .btn-warning.focus{background-color:#ff851b;border-color:#ff7701}.btn-warning .badge{color:#ff851b;background-color:#ffffff}.btn-danger{color:#ffffff;background-color:#ff4136;border-color:#ff291c}.btn-danger:focus,.btn-danger.focus{color:#ffffff;background-color:#ff1103;border-color:#9c0900}.btn-danger:hover{color:#ffffff;background-color:#ff1103;border-color:#de0c00}.btn-danger:active,.btn-danger.active,.open>.dropdown-toggle.btn-danger{color:#ffffff;background-color:#ff1103;background-image:none;border-color:#de0c00}.btn-danger:active:hover,.btn-danger.active:hover,.open>.dropdown-toggle.btn-danger:hover,.btn-danger:active:focus,.btn-danger.active:focus,.open>.dropdown-toggle.btn-danger:focus,.btn-danger:active.focus,.btn-danger.active.focus,.open>.dropdown-toggle.btn-danger.focus{color:#ffffff;background-color:#de0c00;border-color:#9c0900}.btn-danger.disabled:hover,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger:hover,.btn-danger.disabled:focus,.btn-danger[disabled]:focus,fieldset[disabled] .btn-danger:focus,.btn-danger.disabled.focus,.btn-danger[disabled].focus,fieldset[disabled] .btn-danger.focus{background-color:#ff4136;border-color:#ff291c}.btn-danger .badge{color:#ff4136;background-color:#ffffff}.btn-link{font-weight:400;color:#158cba;border-radius:0}.btn-link,.btn-link:active,.btn-link.active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;box-shadow:none}.btn-link,.btn-link:hover,.btn-link:focus,.btn-link:active{border-color:transparent}.btn-link:hover,.btn-link:focus{color:#158cba;text-decoration:underline;background-color:transparent}.btn-link[disabled]:hover,fieldset[disabled] .btn-link:hover,.btn-link[disabled]:focus,fieldset[disabled] .btn-link:focus{color:#999999;text-decoration:none}.btn-lg,.btn-group-lg>.btn{padding:13px 16px;font-size:18px;line-height:1.3333333;border-radius:5px}.btn-sm,.btn-group-sm>.btn{padding:4px 10px;font-size:12px;line-height:1.5;border-radius:2px}.btn-xs,.btn-group-xs>.btn{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:2px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type="submit"].btn-block,input[type="reset"].btn-block,input[type="button"].btn-block{width:100%}.fade{opacity:0;transition:opacity 0.15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;transition-property:height, visibility;transition-duration:0.35s;transition-timing-function:ease}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px dashed;border-top:4px solid \9;border-right:4px solid transparent;border-left:4px solid transparent}.dropup,.dropdown{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;font-size:14px;text-align:left;list-style:none;background-color:#ffffff;background-clip:padding-box;border:1px solid #cccccc;border:1px solid #e7e7e7;border-radius:4px;box-shadow:0 6px 12px rgba(0,0,0,0.175)}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:#eeeeee}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.42857143;color:#999999;white-space:nowrap}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus{color:#333333;text-decoration:none;background-color:transparent}.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{color:#ffffff;text-decoration:none;background-color:#158cba;outline:0}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{color:#eeeeee}.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{text-decoration:none;cursor:not-allowed;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{right:0;left:auto}.dropdown-menu-left{right:auto;left:0}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857143;color:#999999;white-space:nowrap}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{content:"";border-top:0;border-bottom:4px dashed;border-bottom:4px solid \9}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}@media (min-width:768px){.navbar-right .dropdown-menu{right:0;left:auto}.navbar-right .dropdown-menu-left{right:auto;left:0}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group>.btn,.btn-group-vertical>.btn{position:relative;float:left}.btn-group>.btn:hover,.btn-group-vertical>.btn:hover,.btn-group>.btn:focus,.btn-group-vertical>.btn:focus,.btn-group>.btn:active,.btn-group-vertical>.btn:active,.btn-group>.btn.active,.btn-group-vertical>.btn.active{z-index:2}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar .btn,.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-bottom-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-right:8px;padding-left:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-right:12px;padding-left:12px}.btn-group.open .dropdown-toggle{box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn-group.open .dropdown-toggle.btn-link{box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-left-radius:0;border-top-right-radius:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-top-right-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{display:table-cell;float:none;width:1%}.btn-group-justified>.btn-group .btn{width:100%}.btn-group-justified>.btn-group .dropdown-menu{left:auto}[data-toggle="buttons"]>.btn input[type="radio"],[data-toggle="buttons"]>.btn-group>.btn input[type="radio"],[data-toggle="buttons"]>.btn input[type="checkbox"],[data-toggle="buttons"]>.btn-group>.btn input[type="checkbox"]{position:absolute;clip:rect(0, 0, 0, 0);pointer-events:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*="col-"]{float:none;padding-right:0;padding-left:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group .form-control:focus{z-index:3}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:52px;padding:13px 16px;font-size:18px;line-height:1.3333333;border-radius:5px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:52px;line-height:52px}textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn,select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:28px;padding:4px 10px;font-size:12px;line-height:1.5;border-radius:2px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:28px;line-height:28px}textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn,select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn{height:auto}.input-group-addon,.input-group-btn,.input-group .form-control{display:table-cell}.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child),.input-group .form-control:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:7px 12px;font-size:14px;font-weight:400;line-height:1;color:#555555;text-align:center;background-color:#eeeeee;border:1px solid #e7e7e7;border-radius:4px}.input-group-addon.input-sm{padding:4px 10px;font-size:12px;border-radius:2px}.input-group-addon.input-lg{padding:13px 16px;font-size:18px;border-radius:5px}.input-group-addon input[type="radio"],.input-group-addon input[type="checkbox"]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group-btn:last-child>.btn-group:not(:last-child)>.btn{border-top-right-radius:0;border-bottom-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:first-child>.btn-group:not(:first-child)>.btn{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:hover,.input-group-btn>.btn:focus,.input-group-btn>.btn:active{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{z-index:2;margin-left:-1px}.nav{padding-left:0;margin-bottom:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:hover,.nav>li>a:focus{text-decoration:none;background-color:#ffffff}.nav>li.disabled>a{color:#999999}.nav>li.disabled>a:hover,.nav>li.disabled>a:focus{color:#999999;text-decoration:none;cursor:not-allowed;background-color:transparent}.nav .open>a,.nav .open>a:hover,.nav .open>a:focus{background-color:#ffffff;border-color:#158cba}.nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #e7e7e7}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eeeeee #eeeeee #e7e7e7}.nav-tabs>li.active>a,.nav-tabs>li.active>a:hover,.nav-tabs>li.active>a:focus{color:#555555;cursor:default;background-color:#ffffff;border:1px solid #e7e7e7;border-bottom-color:transparent}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border:1px solid #e7e7e7}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #e7e7e7;border-radius:4px 4px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border-bottom-color:#ffffff}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:hover,.nav-pills>li.active>a:focus{color:#ffffff;background-color:#158cba}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus{border:1px solid #e7e7e7}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #e7e7e7;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus{border-bottom-color:#ffffff}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.navbar{position:relative;min-height:50px;margin-bottom:20px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:4px}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{padding-right:15px;padding-left:15px;overflow-x:visible;border-top:1px solid transparent;box-shadow:inset 0 1px 0 rgba(255,255,255,0.1);-webkit-overflow-scrolling:touch}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;box-shadow:none}.navbar-collapse.collapse{display:block !important;height:auto !important;padding-bottom:0;overflow:visible !important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{padding-right:0;padding-left:0}}.navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030}.navbar-fixed-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{max-height:340px}@media (max-device-width:480px) and (orientation:landscape){.navbar-fixed-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{max-height:200px}}@media (min-width:768px){.navbar-fixed-top,.navbar-fixed-bottom{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.container>.navbar-header,.container-fluid>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-collapse{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.container>.navbar-header,.container-fluid>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-collapse{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width:768px){.navbar-static-top{border-radius:0}}.navbar-brand{float:left;height:50px;padding:15px 15px;font-size:18px;line-height:20px}.navbar-brand:hover,.navbar-brand:focus{text-decoration:none}.navbar-brand>img{display:block}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;padding:9px 10px;margin-right:15px;margin-top:8px;margin-bottom:8px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:7.5px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:20px}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;box-shadow:none}.navbar-nav .open .dropdown-menu>li>a,.navbar-nav .open .dropdown-menu .dropdown-header{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:20px}.navbar-nav .open .dropdown-menu>li>a:hover,.navbar-nav .open .dropdown-menu>li>a:focus{background-image:none}}@media (min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:15px;padding-bottom:15px}}.navbar-form{padding:10px 15px;margin-right:-15px;margin-left:-15px;border-top:1px solid transparent;border-bottom:1px solid transparent;box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);margin-top:6px;margin-bottom:6px}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .form-control-static{display:inline-block}.navbar-form .input-group{display:inline-table;vertical-align:middle}.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn,.navbar-form .input-group .form-control{width:auto}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .radio,.navbar-form .checkbox{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.navbar-form .radio label,.navbar-form .checkbox label{padding-left:0}.navbar-form .radio input[type="radio"],.navbar-form .checkbox input[type="checkbox"]{position:relative;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}.navbar-form .form-group:last-child{margin-bottom:0}}@media (min-width:768px){.navbar-form{width:auto;padding-top:0;padding-bottom:0;margin-right:0;margin-left:0;border:0;box-shadow:none}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-left-radius:0;border-top-right-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{margin-bottom:0;border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:6px;margin-bottom:6px}.navbar-btn.btn-sm{margin-top:11px;margin-bottom:11px}.navbar-btn.btn-xs{margin-top:14px;margin-bottom:14px}.navbar-text{margin-top:15px;margin-bottom:15px}@media (min-width:768px){.navbar-text{float:left;margin-right:15px;margin-left:15px}}@media (min-width:768px){.navbar-left{float:left !important}.navbar-right{float:right !important;margin-right:-15px}.navbar-right~.navbar-right{margin-right:0}}.navbar-default{background-color:#f8f8f8;border-color:#e7e7e7}.navbar-default .navbar-brand{color:#333333}.navbar-default .navbar-brand:hover,.navbar-default .navbar-brand:focus{color:#333333;background-color:transparent}.navbar-default .navbar-text{color:#555555}.navbar-default .navbar-nav>li>a{color:#999999}.navbar-default .navbar-nav>li>a:hover,.navbar-default .navbar-nav>li>a:focus{color:#333333;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:hover,.navbar-default .navbar-nav>.active>a:focus{color:#333333;background-color:transparent}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:hover,.navbar-default .navbar-nav>.disabled>a:focus{color:#eeeeee;background-color:transparent}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:hover,.navbar-default .navbar-nav>.open>a:focus{color:#333333;background-color:transparent}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#999999}.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus{color:#333333;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus{color:#333333;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#eeeeee;background-color:transparent}}.navbar-default .navbar-toggle{border-color:#eeeeee}.navbar-default .navbar-toggle:hover,.navbar-default .navbar-toggle:focus{background-color:#ffffff}.navbar-default .navbar-toggle .icon-bar{background-color:#999999}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#e7e7e7}.navbar-default .navbar-link{color:#999999}.navbar-default .navbar-link:hover{color:#333333}.navbar-default .btn-link{color:#999999}.navbar-default .btn-link:hover,.navbar-default .btn-link:focus{color:#333333}.navbar-default .btn-link[disabled]:hover,fieldset[disabled] .navbar-default .btn-link:hover,.navbar-default .btn-link[disabled]:focus,fieldset[disabled] .navbar-default .btn-link:focus{color:#eeeeee}.navbar-inverse{background-color:#ffffff;border-color:#e6e6e6}.navbar-inverse .navbar-brand{color:#999999}.navbar-inverse .navbar-brand:hover,.navbar-inverse .navbar-brand:focus{color:#333333;background-color:transparent}.navbar-inverse .navbar-text{color:#999999}.navbar-inverse .navbar-nav>li>a{color:#999999}.navbar-inverse .navbar-nav>li>a:hover,.navbar-inverse .navbar-nav>li>a:focus{color:#333333;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:hover,.navbar-inverse .navbar-nav>.active>a:focus{color:#333333;background-color:transparent}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:hover,.navbar-inverse .navbar-nav>.disabled>a:focus{color:#eeeeee;background-color:transparent}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:hover,.navbar-inverse .navbar-nav>.open>a:focus{color:#333333;background-color:transparent}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#e6e6e6}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#e6e6e6}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#999999}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus{color:#333333;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus{color:#333333;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#eeeeee;background-color:transparent}}.navbar-inverse .navbar-toggle{border-color:#eeeeee}.navbar-inverse .navbar-toggle:hover,.navbar-inverse .navbar-toggle:focus{background-color:#eeeeee}.navbar-inverse .navbar-toggle .icon-bar{background-color:#999999}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#ededed}.navbar-inverse .navbar-link{color:#999999}.navbar-inverse .navbar-link:hover{color:#333333}.navbar-inverse .btn-link{color:#999999}.navbar-inverse .btn-link:hover,.navbar-inverse .btn-link:focus{color:#333333}.navbar-inverse .btn-link[disabled]:hover,fieldset[disabled] .navbar-inverse .btn-link:hover,.navbar-inverse .btn-link[disabled]:focus,fieldset[disabled] .navbar-inverse .btn-link:focus{color:#eeeeee}.breadcrumb{padding:8px 15px;margin-bottom:20px;list-style:none;background-color:#fafafa;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{padding:0 5px;color:#999999;content:">\00a0"}.breadcrumb>.active{color:#999999}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:7px 12px;margin-left:-1px;line-height:1.42857143;color:#555555;text-decoration:none;background-color:#eeeeee;border:1px solid #e2e2e2}.pagination>li>a:hover,.pagination>li>span:hover,.pagination>li>a:focus,.pagination>li>span:focus{z-index:2;color:#555555;background-color:#eeeeee;border-color:#e2e2e2}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-top-left-radius:4px;border-bottom-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-top-right-radius:4px;border-bottom-right-radius:4px}.pagination>.active>a,.pagination>.active>span,.pagination>.active>a:hover,.pagination>.active>span:hover,.pagination>.active>a:focus,.pagination>.active>span:focus{z-index:3;color:#ffffff;cursor:default;background-color:#158cba;border-color:#127ba3}.pagination>.disabled>span,.pagination>.disabled>span:hover,.pagination>.disabled>span:focus,.pagination>.disabled>a,.pagination>.disabled>a:hover,.pagination>.disabled>a:focus{color:#999999;cursor:not-allowed;background-color:#eeeeee;border-color:#e2e2e2}.pagination-lg>li>a,.pagination-lg>li>span{padding:13px 16px;font-size:18px;line-height:1.3333333}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-top-left-radius:5px;border-bottom-left-radius:5px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-top-right-radius:5px;border-bottom-right-radius:5px}.pagination-sm>li>a,.pagination-sm>li>span{padding:4px 10px;font-size:12px;line-height:1.5}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-top-left-radius:2px;border-bottom-left-radius:2px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-top-right-radius:2px;border-bottom-right-radius:2px}.pager{padding-left:0;margin:20px 0;text-align:center;list-style:none}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#eeeeee;border:1px solid #e2e2e2;border-radius:15px}.pager li>a:hover,.pager li>a:focus{text-decoration:none;background-color:#eeeeee}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:hover,.pager .disabled>a:focus,.pager .disabled>span{color:#999999;cursor:not-allowed;background-color:#eeeeee}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#ffffff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}a.label:hover,a.label:focus{color:#ffffff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#999999}.label-default[href]:hover,.label-default[href]:focus{background-color:#808080}.label-primary{background-color:#158cba}.label-primary[href]:hover,.label-primary[href]:focus{background-color:#106a8c}.label-success{background-color:#28b62c}.label-success[href]:hover,.label-success[href]:focus{background-color:#1f8c22}.label-info{background-color:#75caeb}.label-info[href]:hover,.label-info[href]:focus{background-color:#48b9e5}.label-warning{background-color:#ff851b}.label-warning[href]:hover,.label-warning[href]:focus{background-color:#e76b00}.label-danger{background-color:#ff4136}.label-danger[href]:hover,.label-danger[href]:focus{background-color:#ff1103}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:normal;line-height:1;color:#ffffff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#158cba;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-xs .badge,.btn-group-xs>.btn .badge{top:0;padding:1px 5px}a.badge:hover,a.badge:focus{color:#ffffff;text-decoration:none;cursor:pointer}.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#158cba;background-color:#ffffff}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding-top:30px;padding-bottom:30px;margin-bottom:30px;color:inherit;background-color:#fafafa}.jumbotron h1,.jumbotron .h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:21px;font-weight:200}.jumbotron>hr{border-top-color:#e1e1e1}.container .jumbotron,.container-fluid .jumbotron{padding-right:15px;padding-left:15px;border-radius:5px}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding-top:48px;padding-bottom:48px}.container .jumbotron,.container-fluid .jumbotron{padding-right:60px;padding-left:60px}.jumbotron h1,.jumbotron .h1{font-size:63px}}.thumbnail{display:block;padding:4px;margin-bottom:20px;line-height:1.42857143;background-color:#ffffff;border:1px solid #eeeeee;border-radius:4px;transition:border .2s ease-in-out}.thumbnail>img,.thumbnail a>img{margin-right:auto;margin-left:auto}a.thumbnail:hover,a.thumbnail:focus,a.thumbnail.active{border-color:#158cba}.thumbnail .caption{padding:9px;color:#555555}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:bold}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{color:#ffffff;background-color:#28b62c;border-color:#24a528}.alert-success hr{border-top-color:#209023}.alert-success .alert-link{color:#e6e6e6}.alert-info{color:#ffffff;background-color:#75caeb;border-color:#40b5e3}.alert-info hr{border-top-color:#29ade0}.alert-info .alert-link{color:#e6e6e6}.alert-warning{color:#ffffff;background-color:#ff851b;border-color:#ff7701}.alert-warning hr{border-top-color:#e76b00}.alert-warning .alert-link{color:#e6e6e6}.alert-danger{color:#ffffff;background-color:#ff4136;border-color:#ff1103}.alert-danger hr{border-top-color:#e90d00}.alert-danger .alert-link{color:#e6e6e6}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#fafafa;border-radius:4px;box-shadow:inset 0 1px 2px rgba(0,0,0,0.1)}.progress-bar{float:left;width:0%;height:100%;font-size:12px;line-height:20px;color:#ffffff;text-align:center;background-color:#158cba;box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);transition:width 0.6s ease}.progress-striped .progress-bar,.progress-bar-striped{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-size:40px 40px}.progress.active .progress-bar,.progress-bar.active{-webkit-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#28b62c}.progress-striped .progress-bar-success{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-info{background-color:#75caeb}.progress-striped .progress-bar-info{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-warning{background-color:#ff851b}.progress-striped .progress-bar-warning{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-danger{background-color:#ff4136}.progress-striped .progress-bar-danger{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.media{margin-top:15px}.media:first-child{margin-top:0}.media,.media-body{overflow:hidden;zoom:1}.media-body{width:10000px}.media-object{display:block}.media-object.img-thumbnail{max-width:none}.media-right,.media>.pull-right{padding-left:10px}.media-left,.media>.pull-left{padding-right:10px}.media-left,.media-right,.media-body{display:table-cell;vertical-align:top}.media-middle{vertical-align:middle}.media-bottom{vertical-align:bottom}.media-heading{margin-top:0;margin-bottom:5px}.media-list{padding-left:0;list-style:none}.list-group{padding-left:0;margin-bottom:20px}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#ffffff;border:1px solid #eeeeee}.list-group-item:first-child{border-top-left-radius:4px;border-top-right-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.list-group-item.disabled,.list-group-item.disabled:hover,.list-group-item.disabled:focus{color:#999999;cursor:not-allowed;background-color:#eeeeee}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text{color:#999999}.list-group-item.active,.list-group-item.active:hover,.list-group-item.active:focus{z-index:2;color:#ffffff;background-color:#158cba;border-color:#158cba}.list-group-item.active .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading>.small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:hover .list-group-item-text,.list-group-item.active:focus .list-group-item-text{color:#a6dff5}a.list-group-item,button.list-group-item{color:#555555}a.list-group-item .list-group-item-heading,button.list-group-item .list-group-item-heading{color:#333333}a.list-group-item:hover,button.list-group-item:hover,a.list-group-item:focus,button.list-group-item:focus{color:#555555;text-decoration:none;background-color:#f5f5f5}button.list-group-item{width:100%;text-align:left}.list-group-item-success{color:#ffffff;background-color:#28b62c}a.list-group-item-success,button.list-group-item-success{color:#ffffff}a.list-group-item-success .list-group-item-heading,button.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:hover,button.list-group-item-success:hover,a.list-group-item-success:focus,button.list-group-item-success:focus{color:#ffffff;background-color:#23a127}a.list-group-item-success.active,button.list-group-item-success.active,a.list-group-item-success.active:hover,button.list-group-item-success.active:hover,a.list-group-item-success.active:focus,button.list-group-item-success.active:focus{color:#fff;background-color:#ffffff;border-color:#ffffff}.list-group-item-info{color:#ffffff;background-color:#75caeb}a.list-group-item-info,button.list-group-item-info{color:#ffffff}a.list-group-item-info .list-group-item-heading,button.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:hover,button.list-group-item-info:hover,a.list-group-item-info:focus,button.list-group-item-info:focus{color:#ffffff;background-color:#5fc1e8}a.list-group-item-info.active,button.list-group-item-info.active,a.list-group-item-info.active:hover,button.list-group-item-info.active:hover,a.list-group-item-info.active:focus,button.list-group-item-info.active:focus{color:#fff;background-color:#ffffff;border-color:#ffffff}.list-group-item-warning{color:#ffffff;background-color:#ff851b}a.list-group-item-warning,button.list-group-item-warning{color:#ffffff}a.list-group-item-warning .list-group-item-heading,button.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:hover,button.list-group-item-warning:hover,a.list-group-item-warning:focus,button.list-group-item-warning:focus{color:#ffffff;background-color:#ff7701}a.list-group-item-warning.active,button.list-group-item-warning.active,a.list-group-item-warning.active:hover,button.list-group-item-warning.active:hover,a.list-group-item-warning.active:focus,button.list-group-item-warning.active:focus{color:#fff;background-color:#ffffff;border-color:#ffffff}.list-group-item-danger{color:#ffffff;background-color:#ff4136}a.list-group-item-danger,button.list-group-item-danger{color:#ffffff}a.list-group-item-danger .list-group-item-heading,button.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:hover,button.list-group-item-danger:hover,a.list-group-item-danger:focus,button.list-group-item-danger:focus{color:#ffffff;background-color:#ff291c}a.list-group-item-danger.active,button.list-group-item-danger.active,a.list-group-item-danger.active:hover,button.list-group-item-danger.active:hover,a.list-group-item-danger.active:focus,button.list-group-item-danger.active:focus{color:#fff;background-color:#ffffff;border-color:#ffffff}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:20px;background-color:#ffffff;border:1px solid transparent;border-radius:4px;box-shadow:0 1px 1px rgba(0,0,0,0.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-left-radius:3px;border-top-right-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:16px;color:inherit}.panel-title>a,.panel-title>small,.panel-title>.small,.panel-title>small>a,.panel-title>.small>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid transparent;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group,.panel>.panel-collapse>.list-group{margin-bottom:0}.panel>.list-group .list-group-item,.panel>.panel-collapse>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child,.panel>.panel-collapse>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-left-radius:3px;border-top-right-radius:3px}.panel>.list-group:last-child .list-group-item:last-child,.panel>.panel-collapse>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.panel-heading+.panel-collapse>.list-group .list-group-item:first-child{border-top-left-radius:0;border-top-right-radius:0}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.list-group+.panel-footer{border-top-width:0}.panel>.table,.panel>.table-responsive>.table,.panel>.panel-collapse>.table{margin-bottom:0}.panel>.table caption,.panel>.table-responsive>.table caption,.panel>.panel-collapse>.table caption{padding-right:15px;padding-left:15px}.panel>.table:first-child,.panel>.table-responsive:first-child>.table:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table:last-child,.panel>.table-responsive:last-child>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive,.panel>.table+.panel-body,.panel>.table-responsive+.panel-body{border-top:1px solid #eeeeee}.panel>.table>tbody:first-child>tr:first-child th,.panel>.table>tbody:first-child>tr:first-child td{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{margin-bottom:0;border:0}.panel-group{margin-bottom:20px}.panel-group .panel{margin-bottom:0;border-radius:4px}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse>.panel-body,.panel-group .panel-heading+.panel-collapse>.list-group{border-top:1px solid transparent}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid transparent}.panel-default{border-color:transparent}.panel-default>.panel-heading{color:#333333;background-color:#f5f5f5;border-color:transparent}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:transparent}.panel-default>.panel-heading .badge{color:#f5f5f5;background-color:#333333}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:transparent}.panel-primary{border-color:transparent}.panel-primary>.panel-heading{color:#ffffff;background-color:#158cba;border-color:transparent}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:transparent}.panel-primary>.panel-heading .badge{color:#158cba;background-color:#ffffff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:transparent}.panel-success{border-color:transparent}.panel-success>.panel-heading{color:#ffffff;background-color:#28b62c;border-color:transparent}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:transparent}.panel-success>.panel-heading .badge{color:#28b62c;background-color:#ffffff}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:transparent}.panel-info{border-color:transparent}.panel-info>.panel-heading{color:#ffffff;background-color:#75caeb;border-color:transparent}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:transparent}.panel-info>.panel-heading .badge{color:#75caeb;background-color:#ffffff}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:transparent}.panel-warning{border-color:transparent}.panel-warning>.panel-heading{color:#ffffff;background-color:#ff851b;border-color:transparent}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:transparent}.panel-warning>.panel-heading .badge{color:#ff851b;background-color:#ffffff}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:transparent}.panel-danger{border-color:transparent}.panel-danger>.panel-heading{color:#ffffff;background-color:#ff4136;border-color:transparent}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:transparent}.panel-danger>.panel-heading .badge{color:#ff4136;background-color:#ffffff}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:transparent}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.embed-responsive .embed-responsive-item,.embed-responsive iframe,.embed-responsive embed,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-16by9{padding-bottom:56.25%}.embed-responsive-4by3{padding-bottom:75%}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#fafafa;border:1px solid #e8e8e8;border-radius:4px;box-shadow:inset 0 1px 1px rgba(0,0,0,0.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,0.15)}.well-lg{padding:24px;border-radius:5px}.well-sm{padding:9px;border-radius:2px}.close{float:right;font-size:21px;font-weight:bold;line-height:1;color:#ffffff;text-shadow:0 1px 0 #ffffff;filter:alpha(opacity=20);opacity:0.2}.close:hover,.close:focus{color:#ffffff;text-decoration:none;cursor:pointer;filter:alpha(opacity=50);opacity:0.5}button.close{padding:0;cursor:pointer;background:transparent;border:0;-webkit-appearance:none;appearance:none}.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;overflow:hidden;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transform:translate(0, -25%);transform:translate(0, -25%);transition:-webkit-transform .3s ease-out;transition:transform .3s ease-out}.modal.in .modal-dialog{-webkit-transform:translate(0, 0);transform:translate(0, 0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#ffffff;background-clip:padding-box;border:1px solid #eeeeee;border:1px solid rgba(0,0,0,0.05);border-radius:5px;box-shadow:0 3px 9px rgba(0,0,0,0.5);outline:0}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000000}.modal-backdrop.fade{filter:alpha(opacity=0);opacity:0}.modal-backdrop.in{filter:alpha(opacity=50);opacity:0.5}.modal-header{padding:15px;border-bottom:1px solid #e5e5e5}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:20px}.modal-footer{padding:20px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{box-shadow:0 5px 15px rgba(0,0,0,0.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-family:"Source Sans Pro","Helvetica Neue",Helvetica,Arial,sans-serif;font-style:normal;font-weight:400;line-height:1.42857143;line-break:auto;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;font-size:12px;filter:alpha(opacity=0);opacity:0}.tooltip.in{filter:alpha(opacity=90);opacity:0.9}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000000}.tooltip.top-left .tooltip-arrow{right:5px;bottom:0;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000000}.tooltip.top-right .tooltip-arrow{bottom:0;left:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000000}.tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000000}.tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000000}.tooltip-inner{max-width:200px;padding:3px 8px;color:#ffffff;text-align:center;background-color:#000000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;font-family:"Source Sans Pro","Helvetica Neue",Helvetica,Arial,sans-serif;font-style:normal;font-weight:400;line-height:1.42857143;line-break:auto;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;font-size:14px;background-color:#ffffff;background-clip:padding-box;border:1px solid #cccccc;border:1px solid rgba(0,0,0,0.2);border-radius:5px;box-shadow:0 5px 10px rgba(0,0,0,0.2)}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover>.arrow{border-width:11px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow:after{content:"";border-width:10px}.popover.top>.arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999999;border-top-color:rgba(0,0,0,0.25);border-bottom-width:0}.popover.top>.arrow:after{bottom:1px;margin-left:-10px;content:" ";border-top-color:#ffffff;border-bottom-width:0}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999999;border-right-color:rgba(0,0,0,0.25);border-left-width:0}.popover.right>.arrow:after{bottom:-10px;left:1px;content:" ";border-right-color:#ffffff;border-left-width:0}.popover.bottom>.arrow{top:-11px;left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999999;border-bottom-color:rgba(0,0,0,0.25)}.popover.bottom>.arrow:after{top:1px;margin-left:-10px;content:" ";border-top-width:0;border-bottom-color:#ffffff}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999999;border-left-color:rgba(0,0,0,0.25)}.popover.left>.arrow:after{right:1px;bottom:-10px;content:" ";border-right-width:0;border-left-color:#ffffff}.popover-title{padding:8px 14px;margin:0;font-size:14px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:4px 4px 0 0}.popover-content{padding:9px 14px}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;transition:.6s ease-in-out left}.carousel-inner>.item>img,.carousel-inner>.item>a>img{line-height:1}@media all and (transform-3d),(-webkit-transform-3d){.carousel-inner>.item{transition:-webkit-transform .6s ease-in-out;transition:transform .6s ease-in-out;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000px;perspective:1000px}.carousel-inner>.item.next,.carousel-inner>.item.active.right{-webkit-transform:translate3d(100%, 0, 0);transform:translate3d(100%, 0, 0);left:0}.carousel-inner>.item.prev,.carousel-inner>.item.active.left{-webkit-transform:translate3d(-100%, 0, 0);transform:translate3d(-100%, 0, 0);left:0}.carousel-inner>.item.next.left,.carousel-inner>.item.prev.right,.carousel-inner>.item.active{-webkit-transform:translate3d(0, 0, 0);transform:translate3d(0, 0, 0);left:0}}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;bottom:0;left:0;width:15%;font-size:20px;color:#ffffff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,0.6);background-color:rgba(0,0,0,0);filter:alpha(opacity=50);opacity:0.5}.carousel-control.left{background-image:linear-gradient(to right, rgba(0,0,0,0.5) 0, rgba(0,0,0,0.0001) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);background-repeat:repeat-x}.carousel-control.right{right:0;left:auto;background-image:linear-gradient(to right, rgba(0,0,0,0.0001) 0, rgba(0,0,0,0.5) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);background-repeat:repeat-x}.carousel-control:hover,.carousel-control:focus{color:#ffffff;text-decoration:none;outline:0;filter:alpha(opacity=90);opacity:0.9}.carousel-control .icon-prev,.carousel-control .icon-next,.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right{position:absolute;top:50%;z-index:5;display:inline-block;margin-top:-10px}.carousel-control .icon-prev,.carousel-control .glyphicon-chevron-left{left:50%;margin-left:-10px}.carousel-control .icon-next,.carousel-control .glyphicon-chevron-right{right:50%;margin-right:-10px}.carousel-control .icon-prev,.carousel-control .icon-next{width:20px;height:20px;font-family:serif;line-height:1}.carousel-control .icon-prev:before{content:"\2039"}.carousel-control .icon-next:before{content:"\203a"}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;padding-left:0;margin-left:-30%;text-align:center;list-style:none}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;cursor:pointer;background-color:#000 \9;background-color:rgba(0,0,0,0);border:1px solid #ffffff;border-radius:10px}.carousel-indicators .active{width:12px;height:12px;margin:0;background-color:#ffffff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#ffffff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,0.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-prev,.carousel-control .icon-next{width:30px;height:30px;margin-top:-10px;font-size:30px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{margin-right:-10px}.carousel-caption{right:20%;left:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.clearfix:before,.clearfix:after,.dl-horizontal dd:before,.dl-horizontal dd:after,.container:before,.container:after,.container-fluid:before,.container-fluid:after,.row:before,.row:after,.form-horizontal .form-group:before,.form-horizontal .form-group:after,.btn-toolbar:before,.btn-toolbar:after,.btn-group-vertical>.btn-group:before,.btn-group-vertical>.btn-group:after,.nav:before,.nav:after,.navbar:before,.navbar:after,.navbar-header:before,.navbar-header:after,.navbar-collapse:before,.navbar-collapse:after,.pager:before,.pager:after,.panel-body:before,.panel-body:after,.modal-header:before,.modal-header:after,.modal-footer:before,.modal-footer:after{display:table;content:" "}.clearfix:after,.dl-horizontal dd:after,.container:after,.container-fluid:after,.row:after,.form-horizontal .form-group:after,.btn-toolbar:after,.btn-group-vertical>.btn-group:after,.nav:after,.navbar:after,.navbar-header:after,.navbar-collapse:after,.pager:after,.panel-body:after,.modal-header:after,.modal-footer:after{clear:both}.center-block{display:block;margin-right:auto;margin-left:auto}.pull-right{float:right !important}.pull-left{float:left !important}.hide{display:none !important}.show{display:block !important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none !important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-xs,.visible-sm,.visible-md,.visible-lg{display:none !important}.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block{display:none !important}@media (max-width:767px){.visible-xs{display:block !important}table.visible-xs{display:table !important}tr.visible-xs{display:table-row !important}th.visible-xs,td.visible-xs{display:table-cell !important}}@media (max-width:767px){.visible-xs-block{display:block !important}}@media (max-width:767px){.visible-xs-inline{display:inline !important}}@media (max-width:767px){.visible-xs-inline-block{display:inline-block !important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block !important}table.visible-sm{display:table !important}tr.visible-sm{display:table-row !important}th.visible-sm,td.visible-sm{display:table-cell !important}}@media (min-width:768px) and (max-width:991px){.visible-sm-block{display:block !important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline{display:inline !important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline-block{display:inline-block !important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block !important}table.visible-md{display:table !important}tr.visible-md{display:table-row !important}th.visible-md,td.visible-md{display:table-cell !important}}@media (min-width:992px) and (max-width:1199px){.visible-md-block{display:block !important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline{display:inline !important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline-block{display:inline-block !important}}@media (min-width:1200px){.visible-lg{display:block !important}table.visible-lg{display:table !important}tr.visible-lg{display:table-row !important}th.visible-lg,td.visible-lg{display:table-cell !important}}@media (min-width:1200px){.visible-lg-block{display:block !important}}@media (min-width:1200px){.visible-lg-inline{display:inline !important}}@media (min-width:1200px){.visible-lg-inline-block{display:inline-block !important}}@media (max-width:767px){.hidden-xs{display:none !important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none !important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none !important}}@media (min-width:1200px){.hidden-lg{display:none !important}}.visible-print{display:none !important}@media print{.visible-print{display:block !important}table.visible-print{display:table !important}tr.visible-print{display:table-row !important}th.visible-print,td.visible-print{display:table-cell !important}}.visible-print-block{display:none !important}@media print{.visible-print-block{display:block !important}}.visible-print-inline{display:none !important}@media print{.visible-print-inline{display:inline !important}}.visible-print-inline-block{display:none !important}@media print{.visible-print-inline-block{display:inline-block !important}}@media print{.hidden-print{display:none !important}}.navbar{border-width:0 1px 4px 1px}.btn{padding:9px 12px 7px;border-width:0 1px 4px 1px;font-size:12px;font-weight:bold;text-transform:uppercase}.btn:hover{margin-top:1px;border-bottom-width:3px}.btn:active{margin-top:2px;border-bottom-width:2px;box-shadow:none}.btn-lg,.btn-group-lg>.btn{padding:15px 16px 13px;line-height:15px}.btn-sm,.btn-group-sm>.btn{padding:6px 10px 4px}.btn-xs,.btn-group-xs>.btn{padding:3px 5px 1px}.btn-default:hover,.btn-default:focus,.btn-group.open .dropdown-toggle.btn-default{background-color:#eeeeee;border-color:#e2e2e2}.btn-primary:hover,.btn-primary:focus,.btn-group.open .dropdown-toggle.btn-primary{background-color:#158cba;border-color:#127ba3}.btn-success:hover,.btn-success:focus,.btn-group.open .dropdown-toggle.btn-success{background-color:#28b62c;border-color:#23a127}.btn-info:hover,.btn-info:focus,.btn-group.open .dropdown-toggle.btn-info{background-color:#75caeb;border-color:#5fc1e8}.btn-warning:hover,.btn-warning:focus,.btn-group.open .dropdown-toggle.btn-warning{background-color:#ff851b;border-color:#ff7701}.btn-danger:hover,.btn-danger:focus,.btn-group.open .dropdown-toggle.btn-danger{background-color:#ff4136;border-color:#ff291c}.btn-group.open .dropdown-toggle{box-shadow:none}.navbar-btn:hover{margin-top:8px}.navbar-btn:active{margin-top:9px}.navbar-btn.btn-sm:hover{margin-top:11px}.navbar-btn.btn-sm:active{margin-top:12px}.navbar-btn.btn-xs:hover{margin-top:15px}.navbar-btn.btn-xs:active{margin-top:16px}.btn-group-vertical .btn+.btn:hover{border-top-width:1px}.btn-group-vertical .btn+.btn:active{border-top-width:2px}.text-primary,.text-primary:hover{color:#158cba}.text-success,.text-success:hover{color:#28b62c}.text-danger,.text-danger:hover{color:#ff4136}.text-warning,.text-warning:hover{color:#ff851b}.text-info,.text-info:hover{color:#75caeb}table a:not(.btn),.table a:not(.btn){text-decoration:underline}table .dropdown-menu a,.table .dropdown-menu a{text-decoration:none}table .success,.table .success,table .warning,.table .warning,table .danger,.table .danger,table .info,.table .info{color:#fff}table .success a:not(.btn),.table .success a:not(.btn),table .warning a:not(.btn),.table .warning a:not(.btn),table .danger a:not(.btn),.table .danger a:not(.btn),table .info a:not(.btn),.table .info a:not(.btn){color:#fff}table:not(.table-bordered)>thead>tr>th,.table:not(.table-bordered)>thead>tr>th,table:not(.table-bordered)>tbody>tr>th,.table:not(.table-bordered)>tbody>tr>th,table:not(.table-bordered)>tfoot>tr>th,.table:not(.table-bordered)>tfoot>tr>th,table:not(.table-bordered)>thead>tr>td,.table:not(.table-bordered)>thead>tr>td,table:not(.table-bordered)>tbody>tr>td,.table:not(.table-bordered)>tbody>tr>td,table:not(.table-bordered)>tfoot>tr>td,.table:not(.table-bordered)>tfoot>tr>td{border-color:transparent}.form-control{box-shadow:inset 0 2px 0 rgba(0,0,0,0.075)}label{font-weight:normal}.has-warning .help-block,.has-warning .control-label,.has-warning .radio,.has-warning .checkbox,.has-warning .radio-inline,.has-warning .checkbox-inline,.has-warning.radio label,.has-warning.checkbox label,.has-warning.radio-inline label,.has-warning.checkbox-inline label,.has-warning .form-control-feedback{color:#ff851b}.has-warning .form-control,.has-warning .form-control:focus{border:1px solid #ff851b;box-shadow:inset 0 2px 0 rgba(0,0,0,0.075)}.has-warning .input-group-addon{border:1px solid #ff851b}.has-error .help-block,.has-error .control-label,.has-error .radio,.has-error .checkbox,.has-error .radio-inline,.has-error .checkbox-inline,.has-error.radio label,.has-error.checkbox label,.has-error.radio-inline label,.has-error.checkbox-inline label,.has-error .form-control-feedback{color:#ff4136}.has-error .form-control,.has-error .form-control:focus{border:1px solid #ff4136;box-shadow:inset 0 2px 0 rgba(0,0,0,0.075)}.has-error .input-group-addon{border:1px solid #ff4136}.has-success .help-block,.has-success .control-label,.has-success .radio,.has-success .checkbox,.has-success .radio-inline,.has-success .checkbox-inline,.has-success.radio label,.has-success.checkbox label,.has-success.radio-inline label,.has-success.checkbox-inline label,.has-success .form-control-feedback{color:#28b62c}.has-success .form-control,.has-success .form-control:focus{border:1px solid #28b62c;box-shadow:inset 0 2px 0 rgba(0,0,0,0.075)}.has-success .input-group-addon{border:1px solid #28b62c}.nav .open>a,.nav .open>a:hover,.nav .open>a:focus{border-color:transparent}.nav-tabs>li>a{margin-top:6px;border-color:#e7e7e7;color:#333333;transition:all .2s ease-in-out}.nav-tabs>li>a:hover,.nav-tabs>li>a:focus,.nav-tabs>li.active>a,.nav-tabs>li.active>a:hover,.nav-tabs>li.active>a:focus,.nav-tabs .open>a,.nav-tabs .open>a:hover,.nav-tabs .open>a:focus{padding-bottom:16px;margin-top:0}.nav-tabs .open>a,.nav-tabs .open>a:hover,.nav-tabs .open>a:focus{border-color:#e7e7e7}.nav-tabs>li.disabled>a:hover,.nav-tabs>li.disabled>a:focus{padding-top:10px;padding-bottom:10px;margin-top:6px}.nav-tabs.nav-justified>li{vertical-align:bottom}.dropdown-menu{margin-top:0;border-width:0 1px 4px 1px;border-top-width:1px;box-shadow:none}.breadcrumb{border-color:#ededed;border-style:solid;border-width:0 1px 4px 1px}.pagination>li>a,.pager>li>a,.pagination>li>span,.pager>li>span{position:relative;top:0;border-width:0 1px 4px 1px;color:#555555;font-size:12px;font-weight:bold;text-transform:uppercase}.pagination>li>a:hover,.pager>li>a:hover,.pagination>li>span:hover,.pager>li>span:hover{top:1px;border-bottom-width:3px}.pagination>li>a:active,.pager>li>a:active,.pagination>li>span:active,.pager>li>span:active{top:2px;border-bottom-width:2px}.pagination>.disabled>a:hover,.pager>.disabled>a:hover,.pagination>.disabled>span:hover,.pager>.disabled>span:hover{top:0;border-width:0 1px 4px 1px}.pagination>.disabled>a:active,.pager>.disabled>a:active,.pagination>.disabled>span:active,.pager>.disabled>span:active{top:0;border-width:0 1px 4px 1px}.pager>li>a,.pager>li>span,.pager>.disabled>a,.pager>.disabled>span,.pager>li>a:hover,.pager>li>span:hover,.pager>.disabled>a:hover,.pager>.disabled>span:hover,.pager>li>a:active,.pager>li>span:active,.pager>.disabled>a:active,.pager>.disabled>span:active{border-left-width:2px;border-right-width:2px}.close{color:#fff;text-decoration:none;opacity:0.4}.close:hover,.close:focus{color:#fff;opacity:1}.alert{border-width:0 1px 4px 1px}.alert .alert-link{font-weight:normal;color:#fff;text-decoration:underline}.label{font-weight:normal}.progress{border:1px solid #e7e7e7;box-shadow:inset 0 2px 0 rgba(0,0,0,0.1)}.progress-bar{box-shadow:inset 0 -4px 0 rgba(0,0,0,0.15)}.well{border:1px solid #e7e7e7;box-shadow:inset 0 2px 0 rgba(0,0,0,0.05)}a.list-group-item.active,a.list-group-item.active:hover,a.list-group-item.active:focus{border-color:#eeeeee}a.list-group-item-success.active{background-color:#28b62c}a.list-group-item-success.active:hover,a.list-group-item-success.active:focus{background-color:#23a127}a.list-group-item-warning.active{background-color:#ff851b}a.list-group-item-warning.active:hover,a.list-group-item-warning.active:focus{background-color:#ff7701}a.list-group-item-danger.active{background-color:#ff4136}a.list-group-item-danger.active:hover,a.list-group-item-danger.active:focus{background-color:#ff291c}.jumbotron{border:1px solid #e7e7e7;box-shadow:inset 0 2px 0 rgba(0,0,0,0.05)}.panel{border:1px solid #e7e7e7;border-width:0 1px 4px 1px}.panel-default .close{color:#555555}.modal .close{color:#555555}.popover{color:#555555}
\ No newline at end of file
+ *//*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{font-size:2em;margin:.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-appearance:textfield;box-sizing:content-box}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,:after,:before{color:#000!important;text-shadow:none!important;background:0 0!important;box-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="#"]:after,a[href^="javascript:"]:after{content:""}blockquote,pre{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}img{max-width:100%!important}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000!important}.label{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #ddd!important}}*{box-sizing:border-box}:after,:before{box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:transparent}body{font-family:"Source Sans Pro","Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#555;background-color:#fff}button,input,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#158cba;text-decoration:none}a:focus,a:hover{color:#158cba;text-decoration:underline}a:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.carousel-inner>.item>a>img,.carousel-inner>.item>img,.img-responsive,.thumbnail a>img,.thumbnail>img{display:block;max-width:100%;height:auto}.img-rounded{border-radius:5px}.img-thumbnail{padding:4px;line-height:1.42857143;background-color:#fff;border:1px solid #eee;border-radius:4px;transition:all .2s ease-in-out;display:inline-block;max-width:100%;height:auto}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}[role=button]{cursor:pointer}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{font-family:inherit;font-weight:400;line-height:1.1;color:#333}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-weight:400;line-height:1;color:#999}.h1,.h2,.h3,h1,h2,h3{margin-top:20px;margin-bottom:10px}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small{font-size:65%}.h4,.h5,.h6,h4,h5,h6{margin-top:10px;margin-bottom:10px}.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-size:75%}.h1,h1{font-size:36px}.h2,h2{font-size:30px}.h3,h3{font-size:24px}.h4,h4{font-size:18px}.h5,h5{font-size:14px}.h6,h6{font-size:12px}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16px;font-weight:300;line-height:1.4}@media (min-width:768px){.lead{font-size:21px}}.small,small{font-size:85%}.mark,mark{padding:.2em;background-color:#ff851b}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#999}.text-primary{color:#158cba}a.text-primary:focus,a.text-primary:hover{color:#106a8c}.text-success{color:#fff}a.text-success:focus,a.text-success:hover{color:#e6e6e6}.text-info{color:#fff}a.text-info:focus,a.text-info:hover{color:#e6e6e6}.text-warning{color:#fff}a.text-warning:focus,a.text-warning:hover{color:#e6e6e6}.text-danger{color:#fff}a.text-danger:focus,a.text-danger:hover{color:#e6e6e6}.bg-primary{color:#fff;background-color:#158cba}a.bg-primary:focus,a.bg-primary:hover{background-color:#106a8c}.bg-success{background-color:#28b62c}a.bg-success:focus,a.bg-success:hover{background-color:#1f8c22}.bg-info{background-color:#75caeb}a.bg-info:focus,a.bg-info:hover{background-color:#48b9e5}.bg-warning{background-color:#ff851b}a.bg-warning:focus,a.bg-warning:hover{background-color:#e76b00}.bg-danger{background-color:#ff4136}a.bg-danger:focus,a.bg-danger:hover{background-color:#ff1103}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eee}ol,ul{margin-top:0;margin-bottom:10px}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none;margin-left:-5px}.list-inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-top:0;margin-bottom:20px}dd,dt{line-height:1.42857143}dt{font-weight:700}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;clear:left;text-align:right;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[data-original-title],abbr[title]{cursor:help}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #eee}blockquote ol:last-child,blockquote p:last-child,blockquote ul:last-child{margin-bottom:0}blockquote .small,blockquote footer,blockquote small{display:block;font-size:80%;line-height:1.42857143;color:#999}blockquote .small:before,blockquote footer:before,blockquote small:before{content:"\2014 \00A0"}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;text-align:right;border-right:5px solid #eee;border-left:0}.blockquote-reverse .small:before,.blockquote-reverse footer:before,.blockquote-reverse small:before,blockquote.pull-right .small:before,blockquote.pull-right footer:before,blockquote.pull-right small:before{content:""}.blockquote-reverse .small:after,.blockquote-reverse footer:after,.blockquote-reverse small:after,blockquote.pull-right .small:after,blockquote.pull-right footer:after,blockquote.pull-right small:after{content:"\00A0 \2014"}address{margin-bottom:20px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#fff;background-color:#333;border-radius:2px;box-shadow:inset 0 -1px 0 rgba(0,0,0,.25)}kbd kbd{padding:0;font-size:100%;font-weight:700;box-shadow:none}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.42857143;color:#333;word-break:break-all;word-wrap:break-word;background-color:#f5f5f5;border:1px solid #ccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{margin-right:-15px;margin-left:-15px}.row-no-gutters{margin-right:0;margin-left:0}.row-no-gutters [class*=col-]{padding-right:0;padding-left:0}.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{position:relative;min-height:1px;padding-right:15px;padding-left:15px}.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0}@media (min-width:768px){.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0}}@media (min-width:992px){.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0}}@media (min-width:1200px){.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0}}table{background-color:transparent}table col[class*=col-]{position:static;display:table-column;float:none}table td[class*=col-],table th[class*=col-]{position:static;display:table-cell;float:none}caption{padding-top:8px;padding-bottom:8px;color:#999;text-align:left}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:20px}.table>tbody>tr>td,.table>tbody>tr>th,.table>tfoot>tr>td,.table>tfoot>tr>th,.table>thead>tr>td,.table>thead>tr>th{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #eee}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #eee}.table>caption+thead>tr:first-child>td,.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>td,.table>thead:first-child>tr:first-child>th{border-top:0}.table>tbody+tbody{border-top:2px solid #eee}.table .table{background-color:#fff}.table-condensed>tbody>tr>td,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>td,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>thead>tr>th{padding:5px}.table-bordered{border:1px solid #eee}.table-bordered>tbody>tr>td,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>td,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border:1px solid #eee}.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border-bottom-width:2px}.table-striped>tbody>tr:nth-of-type(odd){background-color:#f9f9f9}.table-hover>tbody>tr:hover{background-color:#f5f5f5}.table>tbody>tr.active>td,.table>tbody>tr.active>th,.table>tbody>tr>td.active,.table>tbody>tr>th.active,.table>tfoot>tr.active>td,.table>tfoot>tr.active>th,.table>tfoot>tr>td.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>thead>tr.active>th,.table>thead>tr>td.active,.table>thead>tr>th.active{background-color:#f5f5f5}.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr.active:hover>th,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover{background-color:#e8e8e8}.table>tbody>tr.success>td,.table>tbody>tr.success>th,.table>tbody>tr>td.success,.table>tbody>tr>th.success,.table>tfoot>tr.success>td,.table>tfoot>tr.success>th,.table>tfoot>tr>td.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>thead>tr.success>th,.table>thead>tr>td.success,.table>thead>tr>th.success{background-color:#28b62c}.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr.success:hover>th,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover{background-color:#23a127}.table>tbody>tr.info>td,.table>tbody>tr.info>th,.table>tbody>tr>td.info,.table>tbody>tr>th.info,.table>tfoot>tr.info>td,.table>tfoot>tr.info>th,.table>tfoot>tr>td.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>thead>tr.info>th,.table>thead>tr>td.info,.table>thead>tr>th.info{background-color:#75caeb}.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr.info:hover>th,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover{background-color:#5fc1e8}.table>tbody>tr.warning>td,.table>tbody>tr.warning>th,.table>tbody>tr>td.warning,.table>tbody>tr>th.warning,.table>tfoot>tr.warning>td,.table>tfoot>tr.warning>th,.table>tfoot>tr>td.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>thead>tr.warning>th,.table>thead>tr>td.warning,.table>thead>tr>th.warning{background-color:#ff851b}.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr.warning:hover>th,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover{background-color:#ff7701}.table>tbody>tr.danger>td,.table>tbody>tr.danger>th,.table>tbody>tr>td.danger,.table>tbody>tr>th.danger,.table>tfoot>tr.danger>td,.table>tfoot>tr.danger>th,.table>tfoot>tr>td.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>thead>tr.danger>th,.table>thead>tr>td.danger,.table>thead>tr>th.danger{background-color:#ff4136}.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr.danger:hover>th,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover{background-color:#ff291c}.table-responsive{min-height:.01%;overflow-x:auto}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #eee}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>td,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>thead>tr>th{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:700}input[type=search]{box-sizing:border-box;-webkit-appearance:none;appearance:none}input[type=checkbox],input[type=radio]{margin:4px 0 0;line-height:normal}fieldset[disabled] input[type=checkbox],fieldset[disabled] input[type=radio],input[type=checkbox].disabled,input[type=checkbox][disabled],input[type=radio].disabled,input[type=radio][disabled]{cursor:not-allowed}input[type=file]{display:block}input[type=range]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type=checkbox]:focus,input[type=file]:focus,input[type=radio]:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:8px;font-size:14px;line-height:1.42857143;color:#555}.form-control{display:block;width:100%;height:38px;padding:7px 12px;font-size:14px;line-height:1.42857143;color:#555;background-color:#fff;background-image:none;border:1px solid #e7e7e7;border-radius:4px;box-shadow:inset 0 1px 1px rgba(0,0,0,.075);transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)}.form-control::-moz-placeholder{color:#999;opacity:1}.form-control:-ms-input-placeholder{color:#999}.form-control::-webkit-input-placeholder{color:#999}.form-control::-ms-expand{background-color:transparent;border:0}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{background-color:#eee;opacity:1}.form-control[disabled],fieldset[disabled] .form-control{cursor:not-allowed}textarea.form-control{height:auto}@media screen and (-webkit-min-device-pixel-ratio:0){input[type=date].form-control,input[type=datetime-local].form-control,input[type=month].form-control,input[type=time].form-control{line-height:38px}.input-group-sm input[type=date],.input-group-sm input[type=datetime-local],.input-group-sm input[type=month],.input-group-sm input[type=time],input[type=date].input-sm,input[type=datetime-local].input-sm,input[type=month].input-sm,input[type=time].input-sm{line-height:28px}.input-group-lg input[type=date],.input-group-lg input[type=datetime-local],.input-group-lg input[type=month],.input-group-lg input[type=time],input[type=date].input-lg,input[type=datetime-local].input-lg,input[type=month].input-lg,input[type=time].input-lg{line-height:52px}}.form-group{margin-bottom:15px}.checkbox,.radio{position:relative;display:block;margin-top:10px;margin-bottom:10px}.checkbox.disabled label,.radio.disabled label,fieldset[disabled] .checkbox label,fieldset[disabled] .radio label{cursor:not-allowed}.checkbox label,.radio label{min-height:20px;padding-left:20px;margin-bottom:0;font-weight:400;cursor:pointer}.checkbox input[type=checkbox],.checkbox-inline input[type=checkbox],.radio input[type=radio],.radio-inline input[type=radio]{position:absolute;margin-left:-20px}.checkbox+.checkbox,.radio+.radio{margin-top:-5px}.checkbox-inline,.radio-inline{position:relative;display:inline-block;padding-left:20px;margin-bottom:0;font-weight:400;vertical-align:middle;cursor:pointer}.checkbox-inline.disabled,.radio-inline.disabled,fieldset[disabled] .checkbox-inline,fieldset[disabled] .radio-inline{cursor:not-allowed}.checkbox-inline+.checkbox-inline,.radio-inline+.radio-inline{margin-top:0;margin-left:10px}.form-control-static{min-height:34px;padding-top:8px;padding-bottom:8px;margin-bottom:0}.form-control-static.input-lg,.form-control-static.input-sm{padding-right:0;padding-left:0}.input-sm{height:28px;padding:4px 10px;font-size:12px;line-height:1.5;border-radius:2px}select.input-sm{height:28px;line-height:28px}select[multiple].input-sm,textarea.input-sm{height:auto}.form-group-sm .form-control{height:28px;padding:4px 10px;font-size:12px;line-height:1.5;border-radius:2px}.form-group-sm select.form-control{height:28px;line-height:28px}.form-group-sm select[multiple].form-control,.form-group-sm textarea.form-control{height:auto}.form-group-sm .form-control-static{height:28px;min-height:32px;padding:5px 10px;font-size:12px;line-height:1.5}.input-lg{height:52px;padding:13px 16px;font-size:18px;line-height:1.3333333;border-radius:5px}select.input-lg{height:52px;line-height:52px}select[multiple].input-lg,textarea.input-lg{height:auto}.form-group-lg .form-control{height:52px;padding:13px 16px;font-size:18px;line-height:1.3333333;border-radius:5px}.form-group-lg select.form-control{height:52px;line-height:52px}.form-group-lg select[multiple].form-control,.form-group-lg textarea.form-control{height:auto}.form-group-lg .form-control-static{height:52px;min-height:38px;padding:14px 16px;font-size:18px;line-height:1.3333333}.has-feedback{position:relative}.has-feedback .form-control{padding-right:47.5px}.form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:38px;height:38px;line-height:38px;text-align:center;pointer-events:none}.form-group-lg .form-control+.form-control-feedback,.input-group-lg+.form-control-feedback,.input-lg+.form-control-feedback{width:52px;height:52px;line-height:52px}.form-group-sm .form-control+.form-control-feedback,.input-group-sm+.form-control-feedback,.input-sm+.form-control-feedback{width:28px;height:28px;line-height:28px}.has-success .checkbox,.has-success .checkbox-inline,.has-success .control-label,.has-success .help-block,.has-success .radio,.has-success .radio-inline,.has-success.checkbox label,.has-success.checkbox-inline label,.has-success.radio label,.has-success.radio-inline label{color:#fff}.has-success .form-control{border-color:#fff;box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-success .form-control:focus{border-color:#e6e6e6;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #fff}.has-success .input-group-addon{color:#fff;background-color:#28b62c;border-color:#fff}.has-success .form-control-feedback{color:#fff}.has-warning .checkbox,.has-warning .checkbox-inline,.has-warning .control-label,.has-warning .help-block,.has-warning .radio,.has-warning .radio-inline,.has-warning.checkbox label,.has-warning.checkbox-inline label,.has-warning.radio label,.has-warning.radio-inline label{color:#fff}.has-warning .form-control{border-color:#fff;box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-warning .form-control:focus{border-color:#e6e6e6;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #fff}.has-warning .input-group-addon{color:#fff;background-color:#ff851b;border-color:#fff}.has-warning .form-control-feedback{color:#fff}.has-error .checkbox,.has-error .checkbox-inline,.has-error .control-label,.has-error .help-block,.has-error .radio,.has-error .radio-inline,.has-error.checkbox label,.has-error.checkbox-inline label,.has-error.radio label,.has-error.radio-inline label{color:#fff}.has-error .form-control{border-color:#fff;box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-error .form-control:focus{border-color:#e6e6e6;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #fff}.has-error .input-group-addon{color:#fff;background-color:#ff4136;border-color:#fff}.has-error .form-control-feedback{color:#fff}.has-feedback label~.form-control-feedback{top:25px}.has-feedback label.sr-only~.form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#959595}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-static{display:inline-block}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .form-control,.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .checkbox,.form-inline .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .checkbox label,.form-inline .radio label{padding-left:0}.form-inline .checkbox input[type=checkbox],.form-inline .radio input[type=radio]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .checkbox,.form-horizontal .checkbox-inline,.form-horizontal .radio,.form-horizontal .radio-inline{padding-top:8px;margin-top:0;margin-bottom:0}.form-horizontal .checkbox,.form-horizontal .radio{min-height:28px}.form-horizontal .form-group{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.form-horizontal .control-label{padding-top:8px;margin-bottom:0;text-align:right}}.form-horizontal .has-feedback .form-control-feedback{right:15px}@media (min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:14px;font-size:18px}}@media (min-width:768px){.form-horizontal .form-group-sm .control-label{padding-top:5px;font-size:12px}}.btn{display:inline-block;margin-bottom:0;font-weight:400;text-align:center;white-space:nowrap;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;background-image:none;border:1px solid transparent;padding:7px 12px;font-size:14px;line-height:1.42857143;border-radius:4px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.btn.active.focus,.btn.active:focus,.btn.focus,.btn:active.focus,.btn:active:focus,.btn:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn.focus,.btn:focus,.btn:hover{color:#555;text-decoration:none}.btn.active,.btn:active{background-image:none;outline:0;box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;opacity:.65;box-shadow:none}a.btn.disabled,fieldset[disabled] a.btn{pointer-events:none}.btn-default{color:#555;background-color:#eee;border-color:#e2e2e2}.btn-default.focus,.btn-default:focus{color:#555;background-color:#d5d5d5;border-color:#a2a2a2}.btn-default:hover{color:#555;background-color:#d5d5d5;border-color:#c3c3c3}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{color:#555;background-color:#d5d5d5;background-image:none;border-color:#c3c3c3}.btn-default.active.focus,.btn-default.active:focus,.btn-default.active:hover,.btn-default:active.focus,.btn-default:active:focus,.btn-default:active:hover,.open>.dropdown-toggle.btn-default.focus,.open>.dropdown-toggle.btn-default:focus,.open>.dropdown-toggle.btn-default:hover{color:#555;background-color:#c3c3c3;border-color:#a2a2a2}.btn-default.disabled.focus,.btn-default.disabled:focus,.btn-default.disabled:hover,.btn-default[disabled].focus,.btn-default[disabled]:focus,.btn-default[disabled]:hover,fieldset[disabled] .btn-default.focus,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default:hover{background-color:#eee;border-color:#e2e2e2}.btn-default .badge{color:#eee;background-color:#555}.btn-primary{color:#fff;background-color:#158cba;border-color:#127ba3}.btn-primary.focus,.btn-primary:focus{color:#fff;background-color:#106a8c;border-color:#052531}.btn-primary:hover{color:#fff;background-color:#106a8c;border-color:#0c516c}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{color:#fff;background-color:#106a8c;background-image:none;border-color:#0c516c}.btn-primary.active.focus,.btn-primary.active:focus,.btn-primary.active:hover,.btn-primary:active.focus,.btn-primary:active:focus,.btn-primary:active:hover,.open>.dropdown-toggle.btn-primary.focus,.open>.dropdown-toggle.btn-primary:focus,.open>.dropdown-toggle.btn-primary:hover{color:#fff;background-color:#0c516c;border-color:#052531}.btn-primary.disabled.focus,.btn-primary.disabled:focus,.btn-primary.disabled:hover,.btn-primary[disabled].focus,.btn-primary[disabled]:focus,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary.focus,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary:hover{background-color:#158cba;border-color:#127ba3}.btn-primary .badge{color:#158cba;background-color:#fff}.btn-success{color:#fff;background-color:#28b62c;border-color:#23a127}.btn-success.focus,.btn-success:focus{color:#fff;background-color:#1f8c22;border-color:#0c390e}.btn-success:hover{color:#fff;background-color:#1f8c22;border-color:#186f1b}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{color:#fff;background-color:#1f8c22;background-image:none;border-color:#186f1b}.btn-success.active.focus,.btn-success.active:focus,.btn-success.active:hover,.btn-success:active.focus,.btn-success:active:focus,.btn-success:active:hover,.open>.dropdown-toggle.btn-success.focus,.open>.dropdown-toggle.btn-success:focus,.open>.dropdown-toggle.btn-success:hover{color:#fff;background-color:#186f1b;border-color:#0c390e}.btn-success.disabled.focus,.btn-success.disabled:focus,.btn-success.disabled:hover,.btn-success[disabled].focus,.btn-success[disabled]:focus,.btn-success[disabled]:hover,fieldset[disabled] .btn-success.focus,fieldset[disabled] .btn-success:focus,fieldset[disabled] .btn-success:hover{background-color:#28b62c;border-color:#23a127}.btn-success .badge{color:#28b62c;background-color:#fff}.btn-info{color:#fff;background-color:#75caeb;border-color:#5fc1e8}.btn-info.focus,.btn-info:focus{color:#fff;background-color:#48b9e5;border-color:#1984ae}.btn-info:hover{color:#fff;background-color:#48b9e5;border-color:#29ade0}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{color:#fff;background-color:#48b9e5;background-image:none;border-color:#29ade0}.btn-info.active.focus,.btn-info.active:focus,.btn-info.active:hover,.btn-info:active.focus,.btn-info:active:focus,.btn-info:active:hover,.open>.dropdown-toggle.btn-info.focus,.open>.dropdown-toggle.btn-info:focus,.open>.dropdown-toggle.btn-info:hover{color:#fff;background-color:#29ade0;border-color:#1984ae}.btn-info.disabled.focus,.btn-info.disabled:focus,.btn-info.disabled:hover,.btn-info[disabled].focus,.btn-info[disabled]:focus,.btn-info[disabled]:hover,fieldset[disabled] .btn-info.focus,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info:hover{background-color:#75caeb;border-color:#5fc1e8}.btn-info .badge{color:#75caeb;background-color:#fff}.btn-warning{color:#fff;background-color:#ff851b;border-color:#ff7701}.btn-warning.focus,.btn-warning:focus{color:#fff;background-color:#e76b00;border-color:#813c00}.btn-warning:hover{color:#fff;background-color:#e76b00;border-color:#c35b00}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{color:#fff;background-color:#e76b00;background-image:none;border-color:#c35b00}.btn-warning.active.focus,.btn-warning.active:focus,.btn-warning.active:hover,.btn-warning:active.focus,.btn-warning:active:focus,.btn-warning:active:hover,.open>.dropdown-toggle.btn-warning.focus,.open>.dropdown-toggle.btn-warning:focus,.open>.dropdown-toggle.btn-warning:hover{color:#fff;background-color:#c35b00;border-color:#813c00}.btn-warning.disabled.focus,.btn-warning.disabled:focus,.btn-warning.disabled:hover,.btn-warning[disabled].focus,.btn-warning[disabled]:focus,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning.focus,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning:hover{background-color:#ff851b;border-color:#ff7701}.btn-warning .badge{color:#ff851b;background-color:#fff}.btn-danger{color:#fff;background-color:#ff4136;border-color:#ff291c}.btn-danger.focus,.btn-danger:focus{color:#fff;background-color:#ff1103;border-color:#9c0900}.btn-danger:hover{color:#fff;background-color:#ff1103;border-color:#de0c00}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{color:#fff;background-color:#ff1103;background-image:none;border-color:#de0c00}.btn-danger.active.focus,.btn-danger.active:focus,.btn-danger.active:hover,.btn-danger:active.focus,.btn-danger:active:focus,.btn-danger:active:hover,.open>.dropdown-toggle.btn-danger.focus,.open>.dropdown-toggle.btn-danger:focus,.open>.dropdown-toggle.btn-danger:hover{color:#fff;background-color:#de0c00;border-color:#9c0900}.btn-danger.disabled.focus,.btn-danger.disabled:focus,.btn-danger.disabled:hover,.btn-danger[disabled].focus,.btn-danger[disabled]:focus,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger.focus,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger:hover{background-color:#ff4136;border-color:#ff291c}.btn-danger .badge{color:#ff4136;background-color:#fff}.btn-link{font-weight:400;color:#158cba;border-radius:0}.btn-link,.btn-link.active,.btn-link:active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;box-shadow:none}.btn-link,.btn-link:active,.btn-link:focus,.btn-link:hover{border-color:transparent}.btn-link:focus,.btn-link:hover{color:#158cba;text-decoration:underline;background-color:transparent}.btn-link[disabled]:focus,.btn-link[disabled]:hover,fieldset[disabled] .btn-link:focus,fieldset[disabled] .btn-link:hover{color:#999;text-decoration:none}.btn-group-lg>.btn,.btn-lg{padding:13px 16px;font-size:18px;line-height:1.3333333;border-radius:5px}.btn-group-sm>.btn,.btn-sm{padding:4px 10px;font-size:12px;line-height:1.5;border-radius:2px}.btn-group-xs>.btn,.btn-xs{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:2px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{opacity:0;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;transition-property:height,visibility;transition-duration:.35s;transition-timing-function:ease}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px dashed;border-right:4px solid transparent;border-left:4px solid transparent}.dropdown,.dropup{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;font-size:14px;text-align:left;list-style:none;background-color:#fff;background-clip:padding-box;border:1px solid #ccc;border:1px solid #e7e7e7;border-radius:4px;box-shadow:0 6px 12px rgba(0,0,0,.175)}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:#eee}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.42857143;color:#999;white-space:nowrap}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{color:#333;text-decoration:none;background-color:transparent}.dropdown-menu>.active>a,.dropdown-menu>.active>a:focus,.dropdown-menu>.active>a:hover{color:#fff;text-decoration:none;background-color:#158cba;outline:0}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{color:#eee}.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{text-decoration:none;cursor:not-allowed;background-color:transparent;background-image:none}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{right:0;left:auto}.dropdown-menu-left{right:auto;left:0}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857143;color:#999;white-space:nowrap}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{content:"";border-top:0;border-bottom:4px dashed}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}@media (min-width:768px){.navbar-right .dropdown-menu{right:0;left:auto}.navbar-right .dropdown-menu-left{right:auto;left:0}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;float:left}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:hover,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus,.btn-group>.btn:hover{z-index:2}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar .btn,.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-bottom-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-right:8px;padding-left:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-right:12px;padding-left:12px}.btn-group.open .dropdown-toggle{box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-group.open .dropdown-toggle.btn-link{box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-left-radius:0;border-top-right-radius:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-top-right-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{display:table-cell;float:none;width:1%}.btn-group-justified>.btn-group .btn{width:100%}.btn-group-justified>.btn-group .dropdown-menu{left:auto}[data-toggle=buttons]>.btn input[type=checkbox],[data-toggle=buttons]>.btn input[type=radio],[data-toggle=buttons]>.btn-group>.btn input[type=checkbox],[data-toggle=buttons]>.btn-group>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*=col-]{float:none;padding-right:0;padding-left:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group .form-control:focus{z-index:3}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:52px;padding:13px 16px;font-size:18px;line-height:1.3333333;border-radius:5px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:52px;line-height:52px}select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn,textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:28px;padding:4px 10px;font-size:12px;line-height:1.5;border-radius:2px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:28px;line-height:28px}select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn,textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn{height:auto}.input-group .form-control,.input-group-addon,.input-group-btn{display:table-cell}.input-group .form-control:not(:first-child):not(:last-child),.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:7px 12px;font-size:14px;font-weight:400;line-height:1;color:#555;text-align:center;background-color:#eee;border:1px solid #e7e7e7;border-radius:4px}.input-group-addon.input-sm{padding:4px 10px;font-size:12px;border-radius:2px}.input-group-addon.input-lg{padding:13px 16px;font-size:18px;border-radius:5px}.input-group-addon input[type=checkbox],.input-group-addon input[type=radio]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn-group:not(:last-child)>.btn,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:first-child>.btn-group:not(:first-child)>.btn,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:active,.input-group-btn>.btn:focus,.input-group-btn>.btn:hover{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{z-index:2;margin-left:-1px}.nav{padding-left:0;margin-bottom:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:focus,.nav>li>a:hover{text-decoration:none;background-color:#fff}.nav>li.disabled>a{color:#999}.nav>li.disabled>a:focus,.nav>li.disabled>a:hover{color:#999;text-decoration:none;cursor:not-allowed;background-color:transparent}.nav .open>a,.nav .open>a:focus,.nav .open>a:hover{background-color:#fff;border-color:#158cba}.nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #e7e7e7}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #e7e7e7}.nav-tabs>li.active>a,.nav-tabs>li.active>a:focus,.nav-tabs>li.active>a:hover{color:#555;cursor:default;background-color:#fff;border:1px solid #e7e7e7;border-bottom-color:transparent}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border:1px solid #e7e7e7}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #e7e7e7;border-radius:4px 4px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border-bottom-color:#fff}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:focus,.nav-pills>li.active>a:hover{color:#fff;background-color:#158cba}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border:1px solid #e7e7e7}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #e7e7e7;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border-bottom-color:#fff}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.navbar{position:relative;min-height:50px;margin-bottom:20px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:4px}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{padding-right:15px;padding-left:15px;overflow-x:visible;border-top:1px solid transparent;box-shadow:inset 0 1px 0 rgba(255,255,255,.1);-webkit-overflow-scrolling:touch}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;box-shadow:none}.navbar-collapse.collapse{display:block!important;height:auto!important;padding-bottom:0;overflow:visible!important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse{padding-right:0;padding-left:0}}.navbar-fixed-bottom,.navbar-fixed-top{position:fixed;right:0;left:0;z-index:1030}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:340px}@media (max-device-width:480px) and (orientation:landscape){.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:200px}}@media (min-width:768px){.navbar-fixed-bottom,.navbar-fixed-top{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width:768px){.navbar-static-top{border-radius:0}}.navbar-brand{float:left;height:50px;padding:15px 15px;font-size:18px;line-height:20px}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-brand>img{display:block}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;padding:9px 10px;margin-right:15px;margin-top:8px;margin-bottom:8px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:7.5px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:20px}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;box-shadow:none}.navbar-nav .open .dropdown-menu .dropdown-header,.navbar-nav .open .dropdown-menu>li>a{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:20px}.navbar-nav .open .dropdown-menu>li>a:focus,.navbar-nav .open .dropdown-menu>li>a:hover{background-image:none}}@media (min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:15px;padding-bottom:15px}}.navbar-form{padding:10px 15px;margin-right:-15px;margin-left:-15px;border-top:1px solid transparent;border-bottom:1px solid transparent;box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1);margin-top:6px;margin-bottom:6px}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .form-control-static{display:inline-block}.navbar-form .input-group{display:inline-table;vertical-align:middle}.navbar-form .input-group .form-control,.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn{width:auto}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .checkbox,.navbar-form .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.navbar-form .checkbox label,.navbar-form .radio label{padding-left:0}.navbar-form .checkbox input[type=checkbox],.navbar-form .radio input[type=radio]{position:relative;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}.navbar-form .form-group:last-child{margin-bottom:0}}@media (min-width:768px){.navbar-form{width:auto;padding-top:0;padding-bottom:0;margin-right:0;margin-left:0;border:0;box-shadow:none}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-left-radius:0;border-top-right-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{margin-bottom:0;border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:6px;margin-bottom:6px}.navbar-btn.btn-sm{margin-top:11px;margin-bottom:11px}.navbar-btn.btn-xs{margin-top:14px;margin-bottom:14px}.navbar-text{margin-top:15px;margin-bottom:15px}@media (min-width:768px){.navbar-text{float:left;margin-right:15px;margin-left:15px}}@media (min-width:768px){.navbar-left{float:left!important}.navbar-right{float:right!important;margin-right:-15px}.navbar-right~.navbar-right{margin-right:0}}.navbar-default{background-color:#f8f8f8;border-color:#e7e7e7}.navbar-default .navbar-brand{color:#333}.navbar-default .navbar-brand:focus,.navbar-default .navbar-brand:hover{color:#333;background-color:transparent}.navbar-default .navbar-text{color:#555}.navbar-default .navbar-nav>li>a{color:#999}.navbar-default .navbar-nav>li>a:focus,.navbar-default .navbar-nav>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:focus,.navbar-default .navbar-nav>.active>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:focus,.navbar-default .navbar-nav>.disabled>a:hover{color:#eee;background-color:transparent}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:focus,.navbar-default .navbar-nav>.open>a:hover{color:#333;background-color:transparent}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#999}.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#eee;background-color:transparent}}.navbar-default .navbar-toggle{border-color:#eee}.navbar-default .navbar-toggle:focus,.navbar-default .navbar-toggle:hover{background-color:#fff}.navbar-default .navbar-toggle .icon-bar{background-color:#999}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#e7e7e7}.navbar-default .navbar-link{color:#999}.navbar-default .navbar-link:hover{color:#333}.navbar-default .btn-link{color:#999}.navbar-default .btn-link:focus,.navbar-default .btn-link:hover{color:#333}.navbar-default .btn-link[disabled]:focus,.navbar-default .btn-link[disabled]:hover,fieldset[disabled] .navbar-default .btn-link:focus,fieldset[disabled] .navbar-default .btn-link:hover{color:#eee}.navbar-inverse{background-color:#fff;border-color:#e6e6e6}.navbar-inverse .navbar-brand{color:#999}.navbar-inverse .navbar-brand:focus,.navbar-inverse .navbar-brand:hover{color:#333;background-color:transparent}.navbar-inverse .navbar-text{color:#999}.navbar-inverse .navbar-nav>li>a{color:#999}.navbar-inverse .navbar-nav>li>a:focus,.navbar-inverse .navbar-nav>li>a:hover{color:#333;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:focus,.navbar-inverse .navbar-nav>.active>a:hover{color:#333;background-color:transparent}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:focus,.navbar-inverse .navbar-nav>.disabled>a:hover{color:#eee;background-color:transparent}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:focus,.navbar-inverse .navbar-nav>.open>a:hover{color:#333;background-color:transparent}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#e6e6e6}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#e6e6e6}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#999}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover{color:#333;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover{color:#333;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#eee;background-color:transparent}}.navbar-inverse .navbar-toggle{border-color:#eee}.navbar-inverse .navbar-toggle:focus,.navbar-inverse .navbar-toggle:hover{background-color:#eee}.navbar-inverse .navbar-toggle .icon-bar{background-color:#999}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#ededed}.navbar-inverse .navbar-link{color:#999}.navbar-inverse .navbar-link:hover{color:#333}.navbar-inverse .btn-link{color:#999}.navbar-inverse .btn-link:focus,.navbar-inverse .btn-link:hover{color:#333}.navbar-inverse .btn-link[disabled]:focus,.navbar-inverse .btn-link[disabled]:hover,fieldset[disabled] .navbar-inverse .btn-link:focus,fieldset[disabled] .navbar-inverse .btn-link:hover{color:#eee}.breadcrumb{padding:8px 15px;margin-bottom:20px;list-style:none;background-color:#fafafa;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{padding:0 5px;color:#999;content:">\00a0"}.breadcrumb>.active{color:#999}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:7px 12px;margin-left:-1px;line-height:1.42857143;color:#555;text-decoration:none;background-color:#eee;border:1px solid #e2e2e2}.pagination>li>a:focus,.pagination>li>a:hover,.pagination>li>span:focus,.pagination>li>span:hover{z-index:2;color:#555;background-color:#eee;border-color:#e2e2e2}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-top-left-radius:4px;border-bottom-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-top-right-radius:4px;border-bottom-right-radius:4px}.pagination>.active>a,.pagination>.active>a:focus,.pagination>.active>a:hover,.pagination>.active>span,.pagination>.active>span:focus,.pagination>.active>span:hover{z-index:3;color:#fff;cursor:default;background-color:#158cba;border-color:#127ba3}.pagination>.disabled>a,.pagination>.disabled>a:focus,.pagination>.disabled>a:hover,.pagination>.disabled>span,.pagination>.disabled>span:focus,.pagination>.disabled>span:hover{color:#999;cursor:not-allowed;background-color:#eee;border-color:#e2e2e2}.pagination-lg>li>a,.pagination-lg>li>span{padding:13px 16px;font-size:18px;line-height:1.3333333}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-top-left-radius:5px;border-bottom-left-radius:5px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-top-right-radius:5px;border-bottom-right-radius:5px}.pagination-sm>li>a,.pagination-sm>li>span{padding:4px 10px;font-size:12px;line-height:1.5}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-top-left-radius:2px;border-bottom-left-radius:2px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-top-right-radius:2px;border-bottom-right-radius:2px}.pager{padding-left:0;margin:20px 0;text-align:center;list-style:none}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#eee;border:1px solid #e2e2e2;border-radius:15px}.pager li>a:focus,.pager li>a:hover{text-decoration:none;background-color:#eee}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:focus,.pager .disabled>a:hover,.pager .disabled>span{color:#999;cursor:not-allowed;background-color:#eee}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}a.label:focus,a.label:hover{color:#fff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#999}.label-default[href]:focus,.label-default[href]:hover{background-color:grey}.label-primary{background-color:#158cba}.label-primary[href]:focus,.label-primary[href]:hover{background-color:#106a8c}.label-success{background-color:#28b62c}.label-success[href]:focus,.label-success[href]:hover{background-color:#1f8c22}.label-info{background-color:#75caeb}.label-info[href]:focus,.label-info[href]:hover{background-color:#48b9e5}.label-warning{background-color:#ff851b}.label-warning[href]:focus,.label-warning[href]:hover{background-color:#e76b00}.label-danger{background-color:#ff4136}.label-danger[href]:focus,.label-danger[href]:hover{background-color:#ff1103}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:400;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#158cba;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-group-xs>.btn .badge,.btn-xs .badge{top:0;padding:1px 5px}a.badge:focus,a.badge:hover{color:#fff;text-decoration:none;cursor:pointer}.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#158cba;background-color:#fff}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding-top:30px;padding-bottom:30px;margin-bottom:30px;color:inherit;background-color:#fafafa}.jumbotron .h1,.jumbotron h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:21px;font-weight:200}.jumbotron>hr{border-top-color:#e1e1e1}.container .jumbotron,.container-fluid .jumbotron{padding-right:15px;padding-left:15px;border-radius:5px}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding-top:48px;padding-bottom:48px}.container .jumbotron,.container-fluid .jumbotron{padding-right:60px;padding-left:60px}.jumbotron .h1,.jumbotron h1{font-size:63px}}.thumbnail{display:block;padding:4px;margin-bottom:20px;line-height:1.42857143;background-color:#fff;border:1px solid #eee;border-radius:4px;transition:border .2s ease-in-out}.thumbnail a>img,.thumbnail>img{margin-right:auto;margin-left:auto}a.thumbnail.active,a.thumbnail:focus,a.thumbnail:hover{border-color:#158cba}.thumbnail .caption{padding:9px;color:#555}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:700}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{color:#fff;background-color:#28b62c;border-color:#24a528}.alert-success hr{border-top-color:#209023}.alert-success .alert-link{color:#e6e6e6}.alert-info{color:#fff;background-color:#75caeb;border-color:#40b5e3}.alert-info hr{border-top-color:#29ade0}.alert-info .alert-link{color:#e6e6e6}.alert-warning{color:#fff;background-color:#ff851b;border-color:#ff7701}.alert-warning hr{border-top-color:#e76b00}.alert-warning .alert-link{color:#e6e6e6}.alert-danger{color:#fff;background-color:#ff4136;border-color:#ff1103}.alert-danger hr{border-top-color:#e90d00}.alert-danger .alert-link{color:#e6e6e6}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#fafafa;border-radius:4px;box-shadow:inset 0 1px 2px rgba(0,0,0,.1)}.progress-bar{float:left;width:0%;height:100%;font-size:12px;line-height:20px;color:#fff;text-align:center;background-color:#158cba;box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);transition:width .6s ease}.progress-bar-striped,.progress-striped .progress-bar{background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-size:40px 40px}.progress-bar.active,.progress.active .progress-bar{-webkit-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#28b62c}.progress-striped .progress-bar-success{background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-info{background-color:#75caeb}.progress-striped .progress-bar-info{background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-warning{background-color:#ff851b}.progress-striped .progress-bar-warning{background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-danger{background-color:#ff4136}.progress-striped .progress-bar-danger{background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.media{margin-top:15px}.media:first-child{margin-top:0}.media,.media-body{overflow:hidden;zoom:1}.media-body{width:10000px}.media-object{display:block}.media-object.img-thumbnail{max-width:none}.media-right,.media>.pull-right{padding-left:10px}.media-left,.media>.pull-left{padding-right:10px}.media-body,.media-left,.media-right{display:table-cell;vertical-align:top}.media-middle{vertical-align:middle}.media-bottom{vertical-align:bottom}.media-heading{margin-top:0;margin-bottom:5px}.media-list{padding-left:0;list-style:none}.list-group{padding-left:0;margin-bottom:20px}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#fff;border:1px solid #eee}.list-group-item:first-child{border-top-left-radius:4px;border-top-right-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.list-group-item.disabled,.list-group-item.disabled:focus,.list-group-item.disabled:hover{color:#999;cursor:not-allowed;background-color:#eee}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text{color:#999}.list-group-item.active,.list-group-item.active:focus,.list-group-item.active:hover{z-index:2;color:#fff;background-color:#158cba;border-color:#158cba}.list-group-item.active .list-group-item-heading,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading>small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:focus .list-group-item-text,.list-group-item.active:hover .list-group-item-text{color:#a6dff5}a.list-group-item,button.list-group-item{color:#555}a.list-group-item .list-group-item-heading,button.list-group-item .list-group-item-heading{color:#333}a.list-group-item:focus,a.list-group-item:hover,button.list-group-item:focus,button.list-group-item:hover{color:#555;text-decoration:none;background-color:#f5f5f5}button.list-group-item{width:100%;text-align:left}.list-group-item-success{color:#fff;background-color:#28b62c}a.list-group-item-success,button.list-group-item-success{color:#fff}a.list-group-item-success .list-group-item-heading,button.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:focus,a.list-group-item-success:hover,button.list-group-item-success:focus,button.list-group-item-success:hover{color:#fff;background-color:#23a127}a.list-group-item-success.active,a.list-group-item-success.active:focus,a.list-group-item-success.active:hover,button.list-group-item-success.active,button.list-group-item-success.active:focus,button.list-group-item-success.active:hover{color:#fff;background-color:#fff;border-color:#fff}.list-group-item-info{color:#fff;background-color:#75caeb}a.list-group-item-info,button.list-group-item-info{color:#fff}a.list-group-item-info .list-group-item-heading,button.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:focus,a.list-group-item-info:hover,button.list-group-item-info:focus,button.list-group-item-info:hover{color:#fff;background-color:#5fc1e8}a.list-group-item-info.active,a.list-group-item-info.active:focus,a.list-group-item-info.active:hover,button.list-group-item-info.active,button.list-group-item-info.active:focus,button.list-group-item-info.active:hover{color:#fff;background-color:#fff;border-color:#fff}.list-group-item-warning{color:#fff;background-color:#ff851b}a.list-group-item-warning,button.list-group-item-warning{color:#fff}a.list-group-item-warning .list-group-item-heading,button.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:focus,a.list-group-item-warning:hover,button.list-group-item-warning:focus,button.list-group-item-warning:hover{color:#fff;background-color:#ff7701}a.list-group-item-warning.active,a.list-group-item-warning.active:focus,a.list-group-item-warning.active:hover,button.list-group-item-warning.active,button.list-group-item-warning.active:focus,button.list-group-item-warning.active:hover{color:#fff;background-color:#fff;border-color:#fff}.list-group-item-danger{color:#fff;background-color:#ff4136}a.list-group-item-danger,button.list-group-item-danger{color:#fff}a.list-group-item-danger .list-group-item-heading,button.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:focus,a.list-group-item-danger:hover,button.list-group-item-danger:focus,button.list-group-item-danger:hover{color:#fff;background-color:#ff291c}a.list-group-item-danger.active,a.list-group-item-danger.active:focus,a.list-group-item-danger.active:hover,button.list-group-item-danger.active,button.list-group-item-danger.active:focus,button.list-group-item-danger.active:hover{color:#fff;background-color:#fff;border-color:#fff}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:20px;background-color:#fff;border:1px solid transparent;border-radius:4px;box-shadow:0 1px 1px rgba(0,0,0,.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-left-radius:3px;border-top-right-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:16px;color:inherit}.panel-title>.small,.panel-title>.small>a,.panel-title>a,.panel-title>small,.panel-title>small>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid transparent;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group,.panel>.panel-collapse>.list-group{margin-bottom:0}.panel>.list-group .list-group-item,.panel>.panel-collapse>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child,.panel>.panel-collapse>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-left-radius:3px;border-top-right-radius:3px}.panel>.list-group:last-child .list-group-item:last-child,.panel>.panel-collapse>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.panel-heading+.panel-collapse>.list-group .list-group-item:first-child{border-top-left-radius:0;border-top-right-radius:0}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.list-group+.panel-footer{border-top-width:0}.panel>.panel-collapse>.table,.panel>.table,.panel>.table-responsive>.table{margin-bottom:0}.panel>.panel-collapse>.table caption,.panel>.table caption,.panel>.table-responsive>.table caption{padding-right:15px;padding-left:15px}.panel>.table-responsive:first-child>.table:first-child,.panel>.table:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child,.panel>.table:first-child>thead:first-child>tr:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table-responsive:last-child>.table:last-child,.panel>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive,.panel>.table+.panel-body,.panel>.table-responsive+.panel-body{border-top:1px solid #eee}.panel>.table>tbody:first-child>tr:first-child td,.panel>.table>tbody:first-child>tr:first-child th{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{margin-bottom:0;border:0}.panel-group{margin-bottom:20px}.panel-group .panel{margin-bottom:0;border-radius:4px}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse>.list-group,.panel-group .panel-heading+.panel-collapse>.panel-body{border-top:1px solid transparent}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid transparent}.panel-default{border-color:transparent}.panel-default>.panel-heading{color:#333;background-color:#f5f5f5;border-color:transparent}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:transparent}.panel-default>.panel-heading .badge{color:#f5f5f5;background-color:#333}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:transparent}.panel-primary{border-color:transparent}.panel-primary>.panel-heading{color:#fff;background-color:#158cba;border-color:transparent}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:transparent}.panel-primary>.panel-heading .badge{color:#158cba;background-color:#fff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:transparent}.panel-success{border-color:transparent}.panel-success>.panel-heading{color:#fff;background-color:#28b62c;border-color:transparent}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:transparent}.panel-success>.panel-heading .badge{color:#28b62c;background-color:#fff}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:transparent}.panel-info{border-color:transparent}.panel-info>.panel-heading{color:#fff;background-color:#75caeb;border-color:transparent}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:transparent}.panel-info>.panel-heading .badge{color:#75caeb;background-color:#fff}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:transparent}.panel-warning{border-color:transparent}.panel-warning>.panel-heading{color:#fff;background-color:#ff851b;border-color:transparent}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:transparent}.panel-warning>.panel-heading .badge{color:#ff851b;background-color:#fff}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:transparent}.panel-danger{border-color:transparent}.panel-danger>.panel-heading{color:#fff;background-color:#ff4136;border-color:transparent}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:transparent}.panel-danger>.panel-heading .badge{color:#ff4136;background-color:#fff}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:transparent}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-16by9{padding-bottom:56.25%}.embed-responsive-4by3{padding-bottom:75%}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#fafafa;border:1px solid #e8e8e8;border-radius:4px;box-shadow:inset 0 1px 1px rgba(0,0,0,.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,.15)}.well-lg{padding:24px;border-radius:5px}.well-sm{padding:9px;border-radius:2px}.close{float:right;font-size:21px;font-weight:700;line-height:1;color:#fff;text-shadow:0 1px 0 #fff;opacity:.2}.close:focus,.close:hover{color:#fff;text-decoration:none;cursor:pointer;opacity:.5}button.close{padding:0;cursor:pointer;background:0 0;border:0;-webkit-appearance:none;appearance:none}.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;overflow:hidden;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transform:translate(0,-25%);transform:translate(0,-25%);transition:-webkit-transform .3s ease-out;transition:transform .3s ease-out}.modal.in .modal-dialog{-webkit-transform:translate(0,0);transform:translate(0,0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#fff;background-clip:padding-box;border:1px solid #eee;border:1px solid rgba(0,0,0,.05);border-radius:5px;box-shadow:0 3px 9px rgba(0,0,0,.5);outline:0}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{opacity:0}.modal-backdrop.in{opacity:.5}.modal-header{padding:15px;border-bottom:1px solid #e5e5e5}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:20px}.modal-footer{padding:20px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{box-shadow:0 5px 15px rgba(0,0,0,.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-family:"Source Sans Pro","Helvetica Neue",Helvetica,Arial,sans-serif;font-style:normal;font-weight:400;line-height:1.42857143;line-break:auto;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;font-size:12px;opacity:0}.tooltip.in{opacity:.9}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-left .tooltip-arrow{right:5px;bottom:0;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-right .tooltip-arrow{bottom:0;left:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;background-color:#000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;font-family:"Source Sans Pro","Helvetica Neue",Helvetica,Arial,sans-serif;font-style:normal;font-weight:400;line-height:1.42857143;line-break:auto;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;font-size:14px;background-color:#fff;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.2);border-radius:5px;box-shadow:0 5px 10px rgba(0,0,0,.2)}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover>.arrow{border-width:11px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow:after{content:"";border-width:10px}.popover.top>.arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999;border-top-color:rgba(0,0,0,.25);border-bottom-width:0}.popover.top>.arrow:after{bottom:1px;margin-left:-10px;content:" ";border-top-color:#fff;border-bottom-width:0}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999;border-right-color:rgba(0,0,0,.25);border-left-width:0}.popover.right>.arrow:after{bottom:-10px;left:1px;content:" ";border-right-color:#fff;border-left-width:0}.popover.bottom>.arrow{top:-11px;left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,.25)}.popover.bottom>.arrow:after{top:1px;margin-left:-10px;content:" ";border-top-width:0;border-bottom-color:#fff}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999;border-left-color:rgba(0,0,0,.25)}.popover.left>.arrow:after{right:1px;bottom:-10px;content:" ";border-right-width:0;border-left-color:#fff}.popover-title{padding:8px 14px;margin:0;font-size:14px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:4px 4px 0 0}.popover-content{padding:9px 14px}.btn-group-vertical>.btn-group:after,.btn-group-vertical>.btn-group:before,.btn-toolbar:after,.btn-toolbar:before,.clearfix:after,.clearfix:before,.container-fluid:after,.container-fluid:before,.container:after,.container:before,.dl-horizontal dd:after,.dl-horizontal dd:before,.form-horizontal .form-group:after,.form-horizontal .form-group:before,.modal-footer:after,.modal-footer:before,.modal-header:after,.modal-header:before,.nav:after,.nav:before,.navbar-collapse:after,.navbar-collapse:before,.navbar-header:after,.navbar-header:before,.navbar:after,.navbar:before,.pager:after,.pager:before,.panel-body:after,.panel-body:before,.row:after,.row:before{display:table;content:" "}.btn-group-vertical>.btn-group:after,.btn-toolbar:after,.clearfix:after,.container-fluid:after,.container:after,.dl-horizontal dd:after,.form-horizontal .form-group:after,.modal-footer:after,.modal-header:after,.nav:after,.navbar-collapse:after,.navbar-header:after,.navbar:after,.pager:after,.panel-body:after,.row:after{clear:both}.center-block{display:block;margin-right:auto;margin-left:auto}.pull-right{float:right!important}.pull-left{float:left!important}.hide{display:none!important}.show{display:block!important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none!important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-lg,.visible-md,.visible-sm,.visible-xs{display:none!important}.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block{display:none!important}@media (max-width:767px){.visible-xs{display:block!important}table.visible-xs{display:table!important}tr.visible-xs{display:table-row!important}td.visible-xs,th.visible-xs{display:table-cell!important}}@media (max-width:767px){.visible-xs-block{display:block!important}}@media (max-width:767px){.visible-xs-inline{display:inline!important}}@media (max-width:767px){.visible-xs-inline-block{display:inline-block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block!important}table.visible-sm{display:table!important}tr.visible-sm{display:table-row!important}td.visible-sm,th.visible-sm{display:table-cell!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-block{display:block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline{display:inline!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline-block{display:inline-block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block!important}table.visible-md{display:table!important}tr.visible-md{display:table-row!important}td.visible-md,th.visible-md{display:table-cell!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-block{display:block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline{display:inline!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline-block{display:inline-block!important}}@media (min-width:1200px){.visible-lg{display:block!important}table.visible-lg{display:table!important}tr.visible-lg{display:table-row!important}td.visible-lg,th.visible-lg{display:table-cell!important}}@media (min-width:1200px){.visible-lg-block{display:block!important}}@media (min-width:1200px){.visible-lg-inline{display:inline!important}}@media (min-width:1200px){.visible-lg-inline-block{display:inline-block!important}}@media (max-width:767px){.hidden-xs{display:none!important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none!important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none!important}}@media (min-width:1200px){.hidden-lg{display:none!important}}.visible-print{display:none!important}@media print{.visible-print{display:block!important}table.visible-print{display:table!important}tr.visible-print{display:table-row!important}td.visible-print,th.visible-print{display:table-cell!important}}.visible-print-block{display:none!important}@media print{.visible-print-block{display:block!important}}.visible-print-inline{display:none!important}@media print{.visible-print-inline{display:inline!important}}.visible-print-inline-block{display:none!important}@media print{.visible-print-inline-block{display:inline-block!important}}@media print{.hidden-print{display:none!important}}.navbar{border-width:0 1px 4px 1px}.btn{padding:9px 12px 7px;border-width:0 1px 4px 1px;font-size:12px;font-weight:700;text-transform:uppercase}.btn:hover{margin-top:1px;border-bottom-width:3px}.btn:active{margin-top:2px;border-bottom-width:2px;box-shadow:none}.btn-group-lg>.btn,.btn-lg{padding:15px 16px 13px;line-height:15px}.btn-group-sm>.btn,.btn-sm{padding:6px 10px 4px}.btn-group-xs>.btn,.btn-xs{padding:3px 5px 1px}.btn-default:focus,.btn-default:hover,.btn-group.open .dropdown-toggle.btn-default{background-color:#eee;border-color:#e2e2e2}.btn-group.open .dropdown-toggle.btn-primary,.btn-primary:focus,.btn-primary:hover{background-color:#158cba;border-color:#127ba3}.btn-group.open .dropdown-toggle.btn-success,.btn-success:focus,.btn-success:hover{background-color:#28b62c;border-color:#23a127}.btn-group.open .dropdown-toggle.btn-info,.btn-info:focus,.btn-info:hover{background-color:#75caeb;border-color:#5fc1e8}.btn-group.open .dropdown-toggle.btn-warning,.btn-warning:focus,.btn-warning:hover{background-color:#ff851b;border-color:#ff7701}.btn-danger:focus,.btn-danger:hover,.btn-group.open .dropdown-toggle.btn-danger{background-color:#ff4136;border-color:#ff291c}.btn-group.open .dropdown-toggle{box-shadow:none}.navbar-btn:hover{margin-top:8px}.navbar-btn:active{margin-top:9px}.navbar-btn.btn-sm:hover{margin-top:11px}.navbar-btn.btn-sm:active{margin-top:12px}.navbar-btn.btn-xs:hover{margin-top:15px}.navbar-btn.btn-xs:active{margin-top:16px}.btn-group-vertical .btn+.btn:hover{border-top-width:1px}.btn-group-vertical .btn+.btn:active{border-top-width:2px}.text-primary,.text-primary:hover{color:#158cba}.text-success,.text-success:hover{color:#28b62c}.text-danger,.text-danger:hover{color:#ff4136}.text-warning,.text-warning:hover{color:#ff851b}.text-info,.text-info:hover{color:#75caeb}.table a:not(.btn),table a:not(.btn){text-decoration:underline}.table .dropdown-menu a,table .dropdown-menu a{text-decoration:none}.table .danger,.table .info,.table .success,.table .warning,table .danger,table .info,table .success,table .warning{color:#fff}.table .danger a:not(.btn),.table .info a:not(.btn),.table .success a:not(.btn),.table .warning a:not(.btn),table .danger a:not(.btn),table .info a:not(.btn),table .success a:not(.btn),table .warning a:not(.btn){color:#fff}.table:not(.table-bordered)>tbody>tr>td,.table:not(.table-bordered)>tbody>tr>th,.table:not(.table-bordered)>tfoot>tr>td,.table:not(.table-bordered)>tfoot>tr>th,.table:not(.table-bordered)>thead>tr>td,.table:not(.table-bordered)>thead>tr>th,table:not(.table-bordered)>tbody>tr>td,table:not(.table-bordered)>tbody>tr>th,table:not(.table-bordered)>tfoot>tr>td,table:not(.table-bordered)>tfoot>tr>th,table:not(.table-bordered)>thead>tr>td,table:not(.table-bordered)>thead>tr>th{border-color:transparent}.form-control{box-shadow:inset 0 2px 0 rgba(0,0,0,.075)}label{font-weight:400}.has-warning .checkbox,.has-warning .checkbox-inline,.has-warning .control-label,.has-warning .form-control-feedback,.has-warning .help-block,.has-warning .radio,.has-warning .radio-inline,.has-warning.checkbox label,.has-warning.checkbox-inline label,.has-warning.radio label,.has-warning.radio-inline label{color:#ff851b}.has-warning .form-control,.has-warning .form-control:focus{border:1px solid #ff851b;box-shadow:inset 0 2px 0 rgba(0,0,0,.075)}.has-warning .input-group-addon{border:1px solid #ff851b}.has-error .checkbox,.has-error .checkbox-inline,.has-error .control-label,.has-error .form-control-feedback,.has-error .help-block,.has-error .radio,.has-error .radio-inline,.has-error.checkbox label,.has-error.checkbox-inline label,.has-error.radio label,.has-error.radio-inline label{color:#ff4136}.has-error .form-control,.has-error .form-control:focus{border:1px solid #ff4136;box-shadow:inset 0 2px 0 rgba(0,0,0,.075)}.has-error .input-group-addon{border:1px solid #ff4136}.has-success .checkbox,.has-success .checkbox-inline,.has-success .control-label,.has-success .form-control-feedback,.has-success .help-block,.has-success .radio,.has-success .radio-inline,.has-success.checkbox label,.has-success.checkbox-inline label,.has-success.radio label,.has-success.radio-inline label{color:#28b62c}.has-success .form-control,.has-success .form-control:focus{border:1px solid #28b62c;box-shadow:inset 0 2px 0 rgba(0,0,0,.075)}.has-success .input-group-addon{border:1px solid #28b62c}.nav .open>a,.nav .open>a:focus,.nav .open>a:hover{border-color:transparent}.nav-tabs>li>a{margin-top:6px;border-color:#e7e7e7;color:#333;transition:all .2s ease-in-out}.nav-tabs .open>a,.nav-tabs .open>a:focus,.nav-tabs .open>a:hover,.nav-tabs>li.active>a,.nav-tabs>li.active>a:focus,.nav-tabs>li.active>a:hover,.nav-tabs>li>a:focus,.nav-tabs>li>a:hover{padding-bottom:16px;margin-top:0}.nav-tabs .open>a,.nav-tabs .open>a:focus,.nav-tabs .open>a:hover{border-color:#e7e7e7}.nav-tabs>li.disabled>a:focus,.nav-tabs>li.disabled>a:hover{padding-top:10px;padding-bottom:10px;margin-top:6px}.nav-tabs.nav-justified>li{vertical-align:bottom}.dropdown-menu{margin-top:0;border-width:0 1px 4px 1px;border-top-width:1px;box-shadow:none}.breadcrumb{border-color:#ededed;border-style:solid;border-width:0 1px 4px 1px}.pager>li>a,.pager>li>span,.pagination>li>a,.pagination>li>span{position:relative;top:0;border-width:0 1px 4px 1px;color:#555;font-size:12px;font-weight:700;text-transform:uppercase}.pager>li>a:hover,.pager>li>span:hover,.pagination>li>a:hover,.pagination>li>span:hover{top:1px;border-bottom-width:3px}.pager>li>a:active,.pager>li>span:active,.pagination>li>a:active,.pagination>li>span:active{top:2px;border-bottom-width:2px}.pager>.disabled>a:hover,.pager>.disabled>span:hover,.pagination>.disabled>a:hover,.pagination>.disabled>span:hover{top:0;border-width:0 1px 4px 1px}.pager>.disabled>a:active,.pager>.disabled>span:active,.pagination>.disabled>a:active,.pagination>.disabled>span:active{top:0;border-width:0 1px 4px 1px}.pager>.disabled>a,.pager>.disabled>a:active,.pager>.disabled>a:hover,.pager>.disabled>span,.pager>.disabled>span:active,.pager>.disabled>span:hover,.pager>li>a,.pager>li>a:active,.pager>li>a:hover,.pager>li>span,.pager>li>span:active,.pager>li>span:hover{border-left-width:2px;border-right-width:2px}.close{color:#fff;text-decoration:none;opacity:.4}.close:focus,.close:hover{color:#fff;opacity:1}.alert{border-width:0 1px 4px 1px}.alert .alert-link{font-weight:400;color:#fff;text-decoration:underline}.label{font-weight:400}.progress{border:1px solid #e7e7e7;box-shadow:inset 0 2px 0 rgba(0,0,0,.1)}.progress-bar{box-shadow:inset 0 -4px 0 rgba(0,0,0,.15)}.well{border:1px solid #e7e7e7;box-shadow:inset 0 2px 0 rgba(0,0,0,.05)}a.list-group-item.active,a.list-group-item.active:focus,a.list-group-item.active:hover{border-color:#eee}a.list-group-item-success.active{background-color:#28b62c}a.list-group-item-success.active:focus,a.list-group-item-success.active:hover{background-color:#23a127}a.list-group-item-warning.active{background-color:#ff851b}a.list-group-item-warning.active:focus,a.list-group-item-warning.active:hover{background-color:#ff7701}a.list-group-item-danger.active{background-color:#ff4136}a.list-group-item-danger.active:focus,a.list-group-item-danger.active:hover{background-color:#ff291c}.jumbotron{border:1px solid #e7e7e7;box-shadow:inset 0 2px 0 rgba(0,0,0,.05)}.panel{border:1px solid #e7e7e7;border-width:0 1px 4px 1px}.panel-default .close{color:#555}.modal .close{color:#555}.popover{color:#555}
\ No newline at end of file
diff --git a/data/web/css/build/004-bootstrap-slider.min.css b/data/web/css/build/004-bootstrap-slider.min.css
deleted file mode 100644
index f8e395be..00000000
--- a/data/web/css/build/004-bootstrap-slider.min.css
+++ /dev/null
@@ -1,41 +0,0 @@
-/*! =======================================================
- VERSION 10.6.1
-========================================================= */
-/*! =========================================================
- * 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) 2019 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%;-ms-transform:translateY(-50%);transform:translateY(-50%);border-width:0 10px 10px 10px;width:0;height:0;border-bottom-color:#2e6da4;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 .tooltip{-ms-transform:translateX(-50%);transform:translateX(-50%)}.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-horizontal.slider-rtl .tooltip{-ms-transform:translateX(50%);transform:translateX(50%)}.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:#2e6da4;border-right-color:#2e6da4;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 .tooltip{-ms-transform:translateY(-50%);transform:translateY(-50%)}.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{pointer-events: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,#8ac1ef 0,#82b3de 100%);background-image:-o-linear-gradient(top,#8ac1ef 0,#82b3de 100%);background-image:linear-gradient(to bottom,#8ac1ef 0,#82b3de 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff8ac1ef',endColorstr='#ff82b3de',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,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7',endColorstr='#ff2e6da4',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:hover{cursor:pointer}.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;cursor:pointer;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,#8ac1ef 0,#82b3de 100%);background-image:-o-linear-gradient(top,#8ac1ef 0,#82b3de 100%);background-image:linear-gradient(to bottom,#8ac1ef 0,#82b3de 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff8ac1ef',endColorstr='#ff82b3de',GradientType=0);opacity:1}
\ No newline at end of file
diff --git a/data/web/css/build/004-slider.min.css b/data/web/css/build/004-slider.min.css
new file mode 100644
index 00000000..6cc5621d
--- /dev/null
+++ b/data/web/css/build/004-slider.min.css
@@ -0,0 +1 @@
+.noUi-target,.noUi-target *{-webkit-touch-callout:none;-webkit-tap-highlight-color:transparent;-webkit-user-select:none;-ms-touch-action:none;touch-action:none;-ms-user-select:none;-moz-user-select:none;user-select:none;-moz-box-sizing:border-box;box-sizing:border-box}.noUi-target{position:relative}.noUi-base,.noUi-connects{width:100%;height:100%;position:relative;z-index:1}.noUi-connects{overflow:hidden;z-index:0}.noUi-connect,.noUi-origin{will-change:transform;position:absolute;z-index:1;top:0;right:0;-ms-transform-origin:0 0;-webkit-transform-origin:0 0;-webkit-transform-style:preserve-3d;transform-origin:0 0;transform-style:flat}.noUi-connect{height:100%;width:100%}.noUi-origin{height:10%;width:10%}.noUi-txt-dir-rtl.noUi-horizontal .noUi-origin{left:0;right:auto}.noUi-vertical .noUi-origin{width:0}.noUi-horizontal .noUi-origin{height:0}.noUi-handle{-webkit-backface-visibility:hidden;backface-visibility:hidden;position:absolute}.noUi-touch-area{height:100%;width:100%}.noUi-state-tap .noUi-connect,.noUi-state-tap .noUi-origin{-webkit-transition:transform .3s;transition:transform .3s}.noUi-state-drag *{cursor:inherit!important}.noUi-horizontal{height:18px}.noUi-horizontal .noUi-handle{width:34px;height:28px;right:-17px;top:-6px}.noUi-vertical{width:18px}.noUi-vertical .noUi-handle{width:28px;height:34px;right:-6px;top:-17px}.noUi-txt-dir-rtl.noUi-horizontal .noUi-handle{left:-17px;right:auto}.noUi-target{background:#FAFAFA;border-radius:4px;border:1px solid #D3D3D3;box-shadow:inset 0 1px 1px #F0F0F0,0 3px 6px -5px #BBB}.noUi-connects{border-radius:3px}.noUi-connect{background:#3FB8AF}.noUi-draggable{cursor:ew-resize}.noUi-vertical .noUi-draggable{cursor:ns-resize}.noUi-handle{border:1px solid #D9D9D9;border-radius:3px;background:#FFF;cursor:default;box-shadow:inset 0 0 1px #FFF,inset 0 1px 7px #EBEBEB,0 3px 6px -3px #BBB}.noUi-active{box-shadow:inset 0 0 1px #FFF,inset 0 1px 7px #DDD,0 3px 6px -3px #BBB}.noUi-handle:after,.noUi-handle:before{content:"";display:block;position:absolute;height:14px;width:1px;background:#E8E7E6;left:14px;top:6px}.noUi-handle:after{left:17px}.noUi-vertical .noUi-handle:after,.noUi-vertical .noUi-handle:before{width:14px;height:1px;left:6px;top:14px}.noUi-vertical .noUi-handle:after{top:17px}[disabled] .noUi-connect{background:#B8B8B8}[disabled] .noUi-handle,[disabled].noUi-handle,[disabled].noUi-target{cursor:not-allowed}.noUi-pips,.noUi-pips *{-moz-box-sizing:border-box;box-sizing:border-box}.noUi-pips{position:absolute;color:#999}.noUi-value{position:absolute;white-space:nowrap;text-align:center}.noUi-value-sub{color:#ccc;font-size:10px}.noUi-marker{position:absolute;background:#CCC}.noUi-marker-sub{background:#AAA}.noUi-marker-large{background:#AAA}.noUi-pips-horizontal{padding:10px 0;height:80px;top:100%;left:0;width:100%}.noUi-value-horizontal{-webkit-transform:translate(-50%,50%);transform:translate(-50%,50%)}.noUi-rtl .noUi-value-horizontal{-webkit-transform:translate(50%,50%);transform:translate(50%,50%)}.noUi-marker-horizontal.noUi-marker{margin-left:-1px;width:2px;height:5px}.noUi-marker-horizontal.noUi-marker-sub{height:10px}.noUi-marker-horizontal.noUi-marker-large{height:15px}.noUi-pips-vertical{padding:0 10px;height:100%;top:0;left:100%}.noUi-value-vertical{-webkit-transform:translate(0,-50%);transform:translate(0,-50%);padding-left:25px}.noUi-rtl .noUi-value-vertical{-webkit-transform:translate(0,50%);transform:translate(0,50%)}.noUi-marker-vertical.noUi-marker{width:5px;height:2px;margin-top:-1px}.noUi-marker-vertical.noUi-marker-sub{width:10px}.noUi-marker-vertical.noUi-marker-large{width:15px}.noUi-tooltip{display:block;position:absolute;border:1px solid #D9D9D9;border-radius:3px;background:#fff;color:#000;padding:5px;text-align:center;white-space:nowrap}.noUi-horizontal .noUi-tooltip{-webkit-transform:translate(-50%,0);transform:translate(-50%,0);left:50%;bottom:120%}.noUi-vertical .noUi-tooltip{-webkit-transform:translate(0,-50%);transform:translate(0,-50%);top:50%;right:120%}.noUi-horizontal .noUi-origin>.noUi-tooltip{-webkit-transform:translate(50%,0);transform:translate(50%,0);left:auto;bottom:10px}.noUi-vertical .noUi-origin>.noUi-tooltip{-webkit-transform:translate(0,-18px);transform:translate(0,-18px);top:auto;right:28px}
\ No newline at end of file
diff --git a/data/web/css/build/005-bootstrap-switch.min.css b/data/web/css/build/005-bootstrap-switch.min.css
deleted file mode 100644
index cbfa013b..00000000
--- a/data/web/css/build/005-bootstrap-switch.min.css
+++ /dev/null
@@ -1,10 +0,0 @@
-/**
- * bootstrap-switch - Turn checkboxes and radio buttons into toggle switches.
- *
- * @version v3.3.3
- * @homepage http://www.bootstrap-switch.org
- * @author Mattia Larentis
(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/build/006-footable.bootstrap.min.css b/data/web/css/build/006-footable.bootstrap.min.css
index 87841b27..a04d825e 100644
--- a/data/web/css/build/006-footable.bootstrap.min.css
+++ b/data/web/css/build/006-footable.bootstrap.min.css
@@ -1 +1,323 @@
-table.footable-details,table.footable>thead>tr.footable-filtering>th div.form-group{margin-bottom:0}table.footable,table.footable-details{position:relative;width:100%;border-spacing:0;border-collapse:collapse}table.footable-hide-fouc{display:none}table>tbody>tr>td>span.footable-toggle{margin-right:8px;opacity:.3}table>tbody>tr>td>span.footable-toggle.last-column{margin-left:8px;float:right}table.table-condensed>tbody>tr>td>span.footable-toggle{margin-right:5px}table.footable-details>tbody>tr>th:nth-child(1){min-width:40px;width:120px}table.footable-details>tbody>tr>td:nth-child(2){word-break:break-all}table.footable-details>tbody>tr:first-child>td,table.footable-details>tbody>tr:first-child>th,table.footable-details>tfoot>tr:first-child>td,table.footable-details>tfoot>tr:first-child>th,table.footable-details>thead>tr:first-child>td,table.footable-details>thead>tr:first-child>th{border-top-width:0}table.footable-details.table-bordered>tbody>tr:first-child>td,table.footable-details.table-bordered>tbody>tr:first-child>th,table.footable-details.table-bordered>tfoot>tr:first-child>td,table.footable-details.table-bordered>tfoot>tr:first-child>th,table.footable-details.table-bordered>thead>tr:first-child>td,table.footable-details.table-bordered>thead>tr:first-child>th{border-top-width:1px}div.footable-loader{vertical-align:middle;text-align:center;height:300px;position:relative}div.footable-loader>span.fooicon{display:inline-block;opacity:.3;font-size:30px;line-height:32px;width:32px;height:32px;margin-top:-16px;margin-left:-16px;position:absolute;top:50%;left:50%;-webkit-animation:fooicon-spin-r 2s infinite linear;animation:fooicon-spin-r 2s infinite linear}table.footable>tbody>tr.footable-empty>td{vertical-align:middle;text-align:center;font-size:30px}table.footable>tbody>tr>td,table.footable>tbody>tr>th{display:none}table.footable>tbody>tr.footable-detail-row>td,table.footable>tbody>tr.footable-detail-row>th,table.footable>tbody>tr.footable-empty>td,table.footable>tbody>tr.footable-empty>th{display:table-cell}@-webkit-keyframes fooicon-spin-r{0%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fooicon-spin-r{0%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fooicon{position:relative;top:1px;display:inline-block;font-family:'Glyphicons Halflings'!important;font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fooicon:after,.fooicon:before{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.fooicon-loader:before{content:"\e030"}.fooicon-plus:before{content:"\2b"}.fooicon-minus:before{content:"\2212"}.fooicon-search:before{content:"\e003"}.fooicon-remove:before{content:"\e014"}.fooicon-sort:before{content:"\e150"}.fooicon-sort-asc:before{content:"\e155"}.fooicon-sort-desc:before{content:"\e156"}.fooicon-pencil:before{content:"\270f"}.fooicon-trash:before{content:"\e020"}.fooicon-eye-close:before{content:"\e106"}.fooicon-flash:before{content:"\e162"}.fooicon-cog:before{content:"\e019"}.fooicon-stats:before{content:"\e185"}table.footable>thead>tr.footable-filtering>th{border-bottom-width:1px;font-weight:400}.footable-filtering-external.footable-filtering-right,table.footable.footable-filtering-right>thead>tr.footable-filtering>th,table.footable>thead>tr.footable-filtering>th{text-align:right}.footable-filtering-external.footable-filtering-left,table.footable.footable-filtering-left>thead>tr.footable-filtering>th{text-align:left}.footable-filtering-external.footable-filtering-center,.footable-paging-external.footable-paging-center,table.footable-paging-center>tfoot>tr.footable-paging>td,table.footable.footable-filtering-center>thead>tr.footable-filtering>th,table.footable>tfoot>tr.footable-paging>td{text-align:center}table.footable>thead>tr.footable-filtering>th div.form-group+div.form-group{margin-top:5px}table.footable>thead>tr.footable-filtering>th div.input-group{width:100%}.footable-filtering-external ul.dropdown-menu>li>a.checkbox,table.footable>thead>tr.footable-filtering>th ul.dropdown-menu>li>a.checkbox{margin:0;display:block;position:relative}.footable-filtering-external ul.dropdown-menu>li>a.checkbox>label,table.footable>thead>tr.footable-filtering>th ul.dropdown-menu>li>a.checkbox>label{display:block;padding-left:20px}.footable-filtering-external ul.dropdown-menu>li>a.checkbox input[type=checkbox],table.footable>thead>tr.footable-filtering>th ul.dropdown-menu>li>a.checkbox input[type=checkbox]{position:absolute;margin-left:-20px}@media (min-width:768px){table.footable>thead>tr.footable-filtering>th div.input-group{width:auto}table.footable>thead>tr.footable-filtering>th div.form-group{margin-left:2px;margin-right:2px}table.footable>thead>tr.footable-filtering>th div.form-group+div.form-group{margin-top:0}}table.footable>tbody>tr>td.footable-sortable,table.footable>tbody>tr>th.footable-sortable,table.footable>tfoot>tr>td.footable-sortable,table.footable>tfoot>tr>th.footable-sortable,table.footable>thead>tr>td.footable-sortable,table.footable>thead>tr>th.footable-sortable{position:relative;padding-right:30px;cursor:pointer}td.footable-sortable>span.fooicon,th.footable-sortable>span.fooicon{position:absolute;right:6px;top:50%;margin-top:-7px;opacity:0;transition:opacity .3s ease-in}td.footable-sortable.footable-asc>span.fooicon,td.footable-sortable.footable-desc>span.fooicon,td.footable-sortable:hover>span.fooicon,th.footable-sortable.footable-asc>span.fooicon,th.footable-sortable.footable-desc>span.fooicon,th.footable-sortable:hover>span.fooicon{opacity:1}table.footable-sorting-disabled td.footable-sortable.footable-asc>span.fooicon,table.footable-sorting-disabled td.footable-sortable.footable-desc>span.fooicon,table.footable-sorting-disabled td.footable-sortable:hover>span.fooicon,table.footable-sorting-disabled th.footable-sortable.footable-asc>span.fooicon,table.footable-sorting-disabled th.footable-sortable.footable-desc>span.fooicon,table.footable-sorting-disabled th.footable-sortable:hover>span.fooicon{opacity:0;visibility:hidden}.footable-paging-external ul.pagination,table.footable>tfoot>tr.footable-paging>td>ul.pagination{margin:10px 0 0}.footable-paging-external span.label,table.footable>tfoot>tr.footable-paging>td>span.label{display:inline-block;margin:0 0 10px;padding:4px 10px}.footable-paging-external.footable-paging-left,table.footable-paging-left>tfoot>tr.footable-paging>td{text-align:left}.footable-paging-external.footable-paging-right,table.footable-editing-right td.footable-editing,table.footable-editing-right tr.footable-editing,table.footable-paging-right>tfoot>tr.footable-paging>td{text-align:right}ul.pagination>li.footable-page{display:none}ul.pagination>li.footable-page.visible{display:inline}td.footable-editing{width:90px;max-width:90px}table.footable-editing-no-delete td.footable-editing,table.footable-editing-no-edit td.footable-editing,table.footable-editing-no-view td.footable-editing{width:70px;max-width:70px}table.footable-editing-no-delete.footable-editing-no-view td.footable-editing,table.footable-editing-no-edit.footable-editing-no-delete td.footable-editing,table.footable-editing-no-edit.footable-editing-no-view td.footable-editing{width:50px;max-width:50px}table.footable-editing-no-edit.footable-editing-no-delete.footable-editing-no-view td.footable-editing,table.footable-editing-no-edit.footable-editing-no-delete.footable-editing-no-view th.footable-editing{width:0;max-width:0;display:none!important}table.footable-editing-left td.footable-editing,table.footable-editing-left tr.footable-editing{text-align:left}table.footable-editing button.footable-add,table.footable-editing button.footable-hide,table.footable-editing-show button.footable-show,table.footable-editing.footable-editing-always-show button.footable-hide,table.footable-editing.footable-editing-always-show button.footable-show,table.footable-editing.footable-editing-always-show.footable-editing-no-add tr.footable-editing{display:none}table.footable-editing.footable-editing-always-show button.footable-add,table.footable-editing.footable-editing-show button.footable-add,table.footable-editing.footable-editing-show button.footable-hide{display:inline-block}
\ No newline at end of file
+table.footable-details,
+table.footable > thead > tr.footable-filtering > th div.form-group {
+ margin-bottom: 0;
+}
+table.footable,
+table.footable-details {
+ position: relative;
+ width: 100%;
+ border-spacing: 0;
+ border-collapse: collapse;
+}
+table.footable-hide-fouc {
+ display: none;
+}
+table > tbody > tr > td > span.footable-toggle {
+ margin-right: 8px;
+ opacity: 0.3;
+}
+table > tbody > tr > td > span.footable-toggle.last-column {
+ margin-left: 8px;
+ float: right;
+}
+table.table-condensed > tbody > tr > td > span.footable-toggle {
+ margin-right: 5px;
+}
+table.footable-details > tbody > tr > th:nth-child(1) {
+ min-width: 40px;
+ width: 120px;
+}
+table.footable-details > tbody > tr > td:nth-child(2) {
+ word-break: break-all;
+}
+table.footable-details > tbody > tr:first-child > td,
+table.footable-details > tbody > tr:first-child > th,
+table.footable-details > tfoot > tr:first-child > td,
+table.footable-details > tfoot > tr:first-child > th,
+table.footable-details > thead > tr:first-child > td,
+table.footable-details > thead > tr:first-child > th {
+ border-top-width: 0;
+}
+table.footable-details.table-bordered > tbody > tr:first-child > td,
+table.footable-details.table-bordered > tbody > tr:first-child > th,
+table.footable-details.table-bordered > tfoot > tr:first-child > td,
+table.footable-details.table-bordered > tfoot > tr:first-child > th,
+table.footable-details.table-bordered > thead > tr:first-child > td,
+table.footable-details.table-bordered > thead > tr:first-child > th {
+ border-top-width: 1px;
+}
+div.footable-loader {
+ vertical-align: middle;
+ text-align: center;
+ height: 300px;
+ position: relative;
+}
+div.footable-loader > span.fooicon {
+ display: inline-block;
+ opacity: 0.3;
+ font-size: 30px;
+ line-height: 32px;
+ width: 32px;
+ height: 32px;
+ margin-top: -16px;
+ margin-left: -16px;
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ -webkit-animation: fooicon-spin-r 2s infinite linear;
+ animation: fooicon-spin-r 2s infinite linear;
+}
+table.footable > tbody > tr.footable-empty > td {
+ vertical-align: middle;
+ text-align: center;
+ font-size: 30px;
+}
+table.footable > tbody > tr > td,
+table.footable > tbody > tr > th {
+ display: none;
+}
+table.footable > tbody > tr.footable-detail-row > td,
+table.footable > tbody > tr.footable-detail-row > th,
+table.footable > tbody > tr.footable-empty > td,
+table.footable > tbody > tr.footable-empty > th {
+ display: table-cell;
+}
+@-webkit-keyframes fooicon-spin-r {
+ 0% {
+ -webkit-transform: rotate(0);
+ transform: rotate(0);
+ }
+ 100% {
+ -webkit-transform: rotate(359deg);
+ transform: rotate(359deg);
+ }
+}
+@keyframes fooicon-spin-r {
+ 0% {
+ -webkit-transform: rotate(0);
+ transform: rotate(0);
+ }
+ 100% {
+ -webkit-transform: rotate(359deg);
+ transform: rotate(359deg);
+ }
+}
+.fooicon {
+ position: relative;
+ top: 0px;
+ display: inline-block;
+ font-family: "bootstrap-icons" !important;
+ font-style: normal;
+ font-weight: 400;
+ line-height: 1;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+@-moz-document url-prefix() {
+ .fooicon {
+ top: 2px;
+ }
+}
+.fooicon:after,
+.fooicon:before {
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+}
+.fooicon-loader:before {
+ content: "\f130";
+}
+.fooicon-plus:before {
+ content: "\f4fc";
+}
+.fooicon-minus:before {
+ content: "\f2e8";
+}
+.fooicon-search:before {
+ content: "\f52a";
+}
+.fooicon-remove:before {
+ content: "\f62a";
+}
+.fooicon-sort:before {
+ content: "\f3c6";
+}
+.fooicon-sort-asc:before {
+ content: "\f575";
+}
+.fooicon-sort-desc:before {
+ content: "\f57b";
+}
+.fooicon-pencil:before {
+ content: "\f4c9";
+}
+.fooicon-trash:before {
+ content: "\f62a";
+}
+.fooicon-eye-close:before {
+ content: "\f33f";
+}
+.fooicon-flash:before {
+ content: "\f46e";
+}
+.fooicon-cog:before {
+ content: "\f3e2";
+}
+.fooicon-stats:before {
+ content: "\f359";
+}
+table.footable > thead > tr.footable-filtering > th {
+ border-bottom-width: 1px;
+ font-weight: 400;
+}
+.footable-filtering-external.footable-filtering-right,
+table.footable.footable-filtering-right > thead > tr.footable-filtering > th,
+table.footable > thead > tr.footable-filtering > th {
+ text-align: right;
+}
+.footable-filtering-external.footable-filtering-left,
+table.footable.footable-filtering-left > thead > tr.footable-filtering > th {
+ text-align: left;
+}
+.footable-filtering-external.footable-filtering-center,
+.footable-paging-external.footable-paging-center,
+table.footable-paging-center > tfoot > tr.footable-paging > td,
+table.footable.footable-filtering-center > thead > tr.footable-filtering > th,
+table.footable > tfoot > tr.footable-paging > td {
+ text-align: center;
+}
+table.footable > thead > tr.footable-filtering > th div.form-group + div.form-group {
+ margin-top: 5px;
+}
+table.footable > thead > tr.footable-filtering > th div.input-group {
+ width: 100%;
+}
+.footable-filtering-external ul.dropdown-menu > li > a.checkbox,
+table.footable > thead > tr.footable-filtering > th ul.dropdown-menu > li > a.checkbox {
+ margin: 0;
+ display: block;
+ position: relative;
+}
+.footable-filtering-external ul.dropdown-menu > li > a.checkbox > label,
+table.footable > thead > tr.footable-filtering > th ul.dropdown-menu > li > a.checkbox > label {
+ display: block;
+ padding-left: 20px;
+}
+.footable-filtering-external ul.dropdown-menu > li > a.checkbox input[type="checkbox"],
+table.footable > thead > tr.footable-filtering > th ul.dropdown-menu > li > a.checkbox input[type="checkbox"] {
+ position: absolute;
+ margin-left: -20px;
+}
+@media (min-width: 768px) {
+ table.footable > thead > tr.footable-filtering > th div.input-group {
+ width: auto;
+ }
+ table.footable > thead > tr.footable-filtering > th div.form-group {
+ margin-left: 2px;
+ margin-right: 2px;
+ }
+ table.footable > thead > tr.footable-filtering > th div.form-group + div.form-group {
+ margin-top: 0;
+ }
+}
+table.footable > tbody > tr > td.footable-sortable,
+table.footable > tbody > tr > th.footable-sortable,
+table.footable > tfoot > tr > td.footable-sortable,
+table.footable > tfoot > tr > th.footable-sortable,
+table.footable > thead > tr > td.footable-sortable,
+table.footable > thead > tr > th.footable-sortable {
+ position: relative;
+ padding-right: 30px;
+ cursor: pointer;
+}
+td.footable-sortable > span.fooicon,
+th.footable-sortable > span.fooicon {
+ position: absolute;
+ right: 6px;
+ top: 50%;
+ margin-top: -7px;
+ opacity: 0;
+ transition: opacity 0.3s ease-in;
+}
+td.footable-sortable.footable-asc > span.fooicon,
+td.footable-sortable.footable-desc > span.fooicon,
+td.footable-sortable:hover > span.fooicon,
+th.footable-sortable.footable-asc > span.fooicon,
+th.footable-sortable.footable-desc > span.fooicon,
+th.footable-sortable:hover > span.fooicon {
+ opacity: 1;
+}
+table.footable-sorting-disabled td.footable-sortable.footable-asc > span.fooicon,
+table.footable-sorting-disabled td.footable-sortable.footable-desc > span.fooicon,
+table.footable-sorting-disabled td.footable-sortable:hover > span.fooicon,
+table.footable-sorting-disabled th.footable-sortable.footable-asc > span.fooicon,
+table.footable-sorting-disabled th.footable-sortable.footable-desc > span.fooicon,
+table.footable-sorting-disabled th.footable-sortable:hover > span.fooicon {
+ opacity: 0;
+ visibility: hidden;
+}
+.footable-paging-external ul.pagination,
+table.footable > tfoot > tr.footable-paging > td > ul.pagination {
+ margin: 10px 0 0;
+}
+.footable-paging-external span.label,
+table.footable > tfoot > tr.footable-paging > td > span.label {
+ display: inline-block;
+ margin: 0 0 10px;
+ padding: 4px 10px;
+}
+.footable-paging-external.footable-paging-left,
+table.footable-paging-left > tfoot > tr.footable-paging > td {
+ text-align: left;
+}
+.footable-paging-external.footable-paging-right,
+table.footable-editing-right td.footable-editing,
+table.footable-editing-right tr.footable-editing,
+table.footable-paging-right > tfoot > tr.footable-paging > td {
+ text-align: right;
+}
+ul.pagination > li.footable-page {
+ display: none;
+}
+ul.pagination > li.footable-page.visible {
+ display: inline;
+}
+td.footable-editing {
+ width: 90px;
+ max-width: 90px;
+}
+table.footable-editing-no-delete td.footable-editing,
+table.footable-editing-no-edit td.footable-editing,
+table.footable-editing-no-view td.footable-editing {
+ width: 70px;
+ max-width: 70px;
+}
+table.footable-editing-no-delete.footable-editing-no-view td.footable-editing,
+table.footable-editing-no-edit.footable-editing-no-delete td.footable-editing,
+table.footable-editing-no-edit.footable-editing-no-view td.footable-editing {
+ width: 50px;
+ max-width: 50px;
+}
+table.footable-editing-no-edit.footable-editing-no-delete.footable-editing-no-view td.footable-editing,
+table.footable-editing-no-edit.footable-editing-no-delete.footable-editing-no-view th.footable-editing {
+ width: 0;
+ max-width: 0;
+ display: none !important;
+}
+table.footable-editing-left td.footable-editing,
+table.footable-editing-left tr.footable-editing {
+ text-align: left;
+}
+table.footable-editing button.footable-add,
+table.footable-editing button.footable-hide,
+table.footable-editing-show button.footable-show,
+table.footable-editing.footable-editing-always-show button.footable-hide,
+table.footable-editing.footable-editing-always-show button.footable-show,
+table.footable-editing.footable-editing-always-show.footable-editing-no-add tr.footable-editing {
+ display: none;
+}
+table.footable-editing.footable-editing-always-show button.footable-add,
+table.footable-editing.footable-editing-show button.footable-add,
+table.footable-editing.footable-editing-show button.footable-hide {
+ display: inline-block;
+}
diff --git a/data/web/css/build/007-languages.min.css b/data/web/css/build/007-languages.min.css
index 2c6a9262..a081cac8 100644
--- a/data/web/css/build/007-languages.min.css
+++ b/data/web/css/build/007-languages.min.css
@@ -1 +1 @@
-.lang-lg,.lang-sm,.lang-xs{background-repeat:no-repeat;display:inline-block;background-image:url(/img/languages.png)}.lang-sm,.lang-sm:after,.lang-xs,.lang-xs:after{position:relative}.lang-xs{background-position:0 -484px;min-width:14px;height:11px;min-height:11px;max-height:11px}.lang-sm{background-position:0 -1199px;min-width:22px;height:16px;min-height:16px;max-height:16px}.lang-lg{background-position:0 -2134px;min-width:30px;height:22px;min-height:22px;max-height:22px}.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-xs[lang=ca]{background-position:0 -473px}.lang-sm[lang=ar]{background-position:0 -495px}.lang-sm[lang=be]{background-position:0 -511px}.lang-sm[lang=bg]{background-position:0 -527px}.lang-sm[lang=cs]{background-position:0 -543px}.lang-sm[lang=da]{background-position:0 -559px}.lang-sm[lang=de]{background-position:0 -575px}.lang-sm[lang=el]{background-position:0 -591px}.lang-sm[lang=en]{background-position:0 -607px}.lang-sm[lang=es]{background-position:0 -623px}.lang-sm[lang=et]{background-position:0 -639px}.lang-sm[lang=fi]{background-position:0 -655px}.lang-sm[lang=fr]{background-position:0 -671px}.lang-sm[lang=ga]{background-position:0 -687px}.lang-sm[lang=hi]{background-position:0 -703px}.lang-sm[lang=hr]{background-position:0 -719px}.lang-sm[lang=hu]{background-position:0 -735px}.lang-sm[lang=in]{background-position:0 -751px}.lang-sm[lang=is]{background-position:0 -767px}.lang-sm[lang=it]{background-position:0 -783px}.lang-sm[lang=iw]{background-position:0 -799px}.lang-sm[lang=ja]{background-position:0 -815px}.lang-sm[lang=ko]{background-position:0 -831px}.lang-sm[lang=lt]{background-position:0 -847px}.lang-sm[lang=lv]{background-position:0 -863px}.lang-sm[lang=mk]{background-position:0 -879px}.lang-sm[lang=ms]{background-position:0 -895px}.lang-sm[lang=mt]{background-position:0 -911px}.lang-sm[lang=nl]{background-position:0 -927px}.lang-sm[lang=no]{background-position:0 -943px}.lang-sm[lang=pl]{background-position:0 -959px}.lang-sm[lang=pt]{background-position:0 -975px}.lang-sm[lang=ro]{background-position:0 -991px}.lang-sm[lang=ru]{background-position:0 -1007px}.lang-sm[lang=sk]{background-position:0 -1023px}.lang-sm[lang=sl]{background-position:0 -1039px}.lang-sm[lang=sq]{background-position:0 -1055px}.lang-sm[lang=sr]{background-position:0 -1071px}.lang-sm[lang=sv]{background-position:0 -1087px}.lang-sm[lang=th]{background-position:0 -1103px}.lang-sm[lang=tr]{background-position:0 -1119px}.lang-sm[lang=uk]{background-position:0 -1135px}.lang-sm[lang=vi]{background-position:0 -1151px}.lang-sm[lang=zh]{background-position:0 -1167px}.lang-sm[lang=ca]{background-position:0 -1183px}.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=ca]:after{content:"Catal\0000E0"}.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=ca]:after{content:"Catalan"}.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=ca]:after{content:"Catal\0000E0\0000A0/\0000A0Catalan"}.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}.lang-sm:after{top:-1px}.lang-xs{top:4px}.lang-xs:after{top:-4px}.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
+.flag-icon-background{background-size:contain;background-position:50%;background-repeat:no-repeat}.flag-icon{background-size:contain;background-position:50%;background-repeat:no-repeat;position:relative;display:inline-block;width:1.33333333em;line-height:1em}.flag-icon:before{content:'\00a0'}.flag-icon.flag-icon-squared{width:1em}.flag-icon-ad{background-image:url(../flags/4x3/ad.svg)}.flag-icon-ad.flag-icon-squared{background-image:url(../flags/1x1/ad.svg)}.flag-icon-ae{background-image:url(../flags/4x3/ae.svg)}.flag-icon-ae.flag-icon-squared{background-image:url(../flags/1x1/ae.svg)}.flag-icon-af{background-image:url(../flags/4x3/af.svg)}.flag-icon-af.flag-icon-squared{background-image:url(../flags/1x1/af.svg)}.flag-icon-ag{background-image:url(../flags/4x3/ag.svg)}.flag-icon-ag.flag-icon-squared{background-image:url(../flags/1x1/ag.svg)}.flag-icon-ai{background-image:url(../flags/4x3/ai.svg)}.flag-icon-ai.flag-icon-squared{background-image:url(../flags/1x1/ai.svg)}.flag-icon-al{background-image:url(../flags/4x3/al.svg)}.flag-icon-al.flag-icon-squared{background-image:url(../flags/1x1/al.svg)}.flag-icon-am{background-image:url(../flags/4x3/am.svg)}.flag-icon-am.flag-icon-squared{background-image:url(../flags/1x1/am.svg)}.flag-icon-ao{background-image:url(../flags/4x3/ao.svg)}.flag-icon-ao.flag-icon-squared{background-image:url(../flags/1x1/ao.svg)}.flag-icon-aq{background-image:url(../flags/4x3/aq.svg)}.flag-icon-aq.flag-icon-squared{background-image:url(../flags/1x1/aq.svg)}.flag-icon-ar{background-image:url(../flags/4x3/ar.svg)}.flag-icon-ar.flag-icon-squared{background-image:url(../flags/1x1/ar.svg)}.flag-icon-as{background-image:url(../flags/4x3/as.svg)}.flag-icon-as.flag-icon-squared{background-image:url(../flags/1x1/as.svg)}.flag-icon-at{background-image:url(../flags/4x3/at.svg)}.flag-icon-at.flag-icon-squared{background-image:url(../flags/1x1/at.svg)}.flag-icon-au{background-image:url(../flags/4x3/au.svg)}.flag-icon-au.flag-icon-squared{background-image:url(../flags/1x1/au.svg)}.flag-icon-aw{background-image:url(../flags/4x3/aw.svg)}.flag-icon-aw.flag-icon-squared{background-image:url(../flags/1x1/aw.svg)}.flag-icon-ax{background-image:url(../flags/4x3/ax.svg)}.flag-icon-ax.flag-icon-squared{background-image:url(../flags/1x1/ax.svg)}.flag-icon-az{background-image:url(../flags/4x3/az.svg)}.flag-icon-az.flag-icon-squared{background-image:url(../flags/1x1/az.svg)}.flag-icon-ba{background-image:url(../flags/4x3/ba.svg)}.flag-icon-ba.flag-icon-squared{background-image:url(../flags/1x1/ba.svg)}.flag-icon-bb{background-image:url(../flags/4x3/bb.svg)}.flag-icon-bb.flag-icon-squared{background-image:url(../flags/1x1/bb.svg)}.flag-icon-bd{background-image:url(../flags/4x3/bd.svg)}.flag-icon-bd.flag-icon-squared{background-image:url(../flags/1x1/bd.svg)}.flag-icon-be{background-image:url(../flags/4x3/be.svg)}.flag-icon-be.flag-icon-squared{background-image:url(../flags/1x1/be.svg)}.flag-icon-bf{background-image:url(../flags/4x3/bf.svg)}.flag-icon-bf.flag-icon-squared{background-image:url(../flags/1x1/bf.svg)}.flag-icon-bg{background-image:url(../flags/4x3/bg.svg)}.flag-icon-bg.flag-icon-squared{background-image:url(../flags/1x1/bg.svg)}.flag-icon-bh{background-image:url(../flags/4x3/bh.svg)}.flag-icon-bh.flag-icon-squared{background-image:url(../flags/1x1/bh.svg)}.flag-icon-bi{background-image:url(../flags/4x3/bi.svg)}.flag-icon-bi.flag-icon-squared{background-image:url(../flags/1x1/bi.svg)}.flag-icon-bj{background-image:url(../flags/4x3/bj.svg)}.flag-icon-bj.flag-icon-squared{background-image:url(../flags/1x1/bj.svg)}.flag-icon-bl{background-image:url(../flags/4x3/bl.svg)}.flag-icon-bl.flag-icon-squared{background-image:url(../flags/1x1/bl.svg)}.flag-icon-bm{background-image:url(../flags/4x3/bm.svg)}.flag-icon-bm.flag-icon-squared{background-image:url(../flags/1x1/bm.svg)}.flag-icon-bn{background-image:url(../flags/4x3/bn.svg)}.flag-icon-bn.flag-icon-squared{background-image:url(../flags/1x1/bn.svg)}.flag-icon-bo{background-image:url(../flags/4x3/bo.svg)}.flag-icon-bo.flag-icon-squared{background-image:url(../flags/1x1/bo.svg)}.flag-icon-bq{background-image:url(../flags/4x3/bq.svg)}.flag-icon-bq.flag-icon-squared{background-image:url(../flags/1x1/bq.svg)}.flag-icon-br{background-image:url(../flags/4x3/br.svg)}.flag-icon-br.flag-icon-squared{background-image:url(../flags/1x1/br.svg)}.flag-icon-bs{background-image:url(../flags/4x3/bs.svg)}.flag-icon-bs.flag-icon-squared{background-image:url(../flags/1x1/bs.svg)}.flag-icon-bt{background-image:url(../flags/4x3/bt.svg)}.flag-icon-bt.flag-icon-squared{background-image:url(../flags/1x1/bt.svg)}.flag-icon-bv{background-image:url(../flags/4x3/bv.svg)}.flag-icon-bv.flag-icon-squared{background-image:url(../flags/1x1/bv.svg)}.flag-icon-bw{background-image:url(../flags/4x3/bw.svg)}.flag-icon-bw.flag-icon-squared{background-image:url(../flags/1x1/bw.svg)}.flag-icon-by{background-image:url(../flags/4x3/by.svg)}.flag-icon-by.flag-icon-squared{background-image:url(../flags/1x1/by.svg)}.flag-icon-bz{background-image:url(../flags/4x3/bz.svg)}.flag-icon-bz.flag-icon-squared{background-image:url(../flags/1x1/bz.svg)}.flag-icon-ca{background-image:url(../flags/4x3/ca.svg)}.flag-icon-ca.flag-icon-squared{background-image:url(../flags/1x1/ca.svg)}.flag-icon-cc{background-image:url(../flags/4x3/cc.svg)}.flag-icon-cc.flag-icon-squared{background-image:url(../flags/1x1/cc.svg)}.flag-icon-cd{background-image:url(../flags/4x3/cd.svg)}.flag-icon-cd.flag-icon-squared{background-image:url(../flags/1x1/cd.svg)}.flag-icon-cf{background-image:url(../flags/4x3/cf.svg)}.flag-icon-cf.flag-icon-squared{background-image:url(../flags/1x1/cf.svg)}.flag-icon-cg{background-image:url(../flags/4x3/cg.svg)}.flag-icon-cg.flag-icon-squared{background-image:url(../flags/1x1/cg.svg)}.flag-icon-ch{background-image:url(../flags/4x3/ch.svg)}.flag-icon-ch.flag-icon-squared{background-image:url(../flags/1x1/ch.svg)}.flag-icon-ci{background-image:url(../flags/4x3/ci.svg)}.flag-icon-ci.flag-icon-squared{background-image:url(../flags/1x1/ci.svg)}.flag-icon-ck{background-image:url(../flags/4x3/ck.svg)}.flag-icon-ck.flag-icon-squared{background-image:url(../flags/1x1/ck.svg)}.flag-icon-cl{background-image:url(../flags/4x3/cl.svg)}.flag-icon-cl.flag-icon-squared{background-image:url(../flags/1x1/cl.svg)}.flag-icon-cm{background-image:url(../flags/4x3/cm.svg)}.flag-icon-cm.flag-icon-squared{background-image:url(../flags/1x1/cm.svg)}.flag-icon-zh{background-image:url(../flags/4x3/zh.svg)}.flag-iconzh.flag-icon-squared{background-image:url(../flags/1x1/zh.svg)}.flag-icon-cn{background-image:url(../flags/4x3/cn.svg)}.flag-icon-cn.flag-icon-squared{background-image:url(../flags/1x1/cn.svg)}.flag-icon-co{background-image:url(../flags/4x3/co.svg)}.flag-icon-co.flag-icon-squared{background-image:url(../flags/1x1/co.svg)}.flag-icon-cr{background-image:url(../flags/4x3/cr.svg)}.flag-icon-cr.flag-icon-squared{background-image:url(../flags/1x1/cr.svg)}.flag-icon-cu{background-image:url(../flags/4x3/cu.svg)}.flag-icon-cu.flag-icon-squared{background-image:url(../flags/1x1/cu.svg)}.flag-icon-cv{background-image:url(../flags/4x3/cv.svg)}.flag-icon-cv.flag-icon-squared{background-image:url(../flags/1x1/cv.svg)}.flag-icon-cw{background-image:url(../flags/4x3/cw.svg)}.flag-icon-cw.flag-icon-squared{background-image:url(../flags/1x1/cw.svg)}.flag-icon-cx{background-image:url(../flags/4x3/cx.svg)}.flag-icon-cx.flag-icon-squared{background-image:url(../flags/1x1/cx.svg)}.flag-icon-cy{background-image:url(../flags/4x3/cy.svg)}.flag-icon-cy.flag-icon-squared{background-image:url(../flags/1x1/cy.svg)}.flag-icon-cz{background-image:url(../flags/4x3/cz.svg)}.flag-icon-cz.flag-icon-squared{background-image:url(../flags/1x1/cz.svg)}.flag-icon-cs{background-image:url(../flags/4x3/cs.svg)}.flag-icon-cs.flag-icon-squared{background-image:url(../flags/1x1/cs.svg)}.flag-icon-de{background-image:url(../flags/4x3/de.svg)}.flag-icon-de.flag-icon-squared{background-image:url(../flags/1x1/de.svg)}.flag-icon-dj{background-image:url(../flags/4x3/dj.svg)}.flag-icon-dj.flag-icon-squared{background-image:url(../flags/1x1/dj.svg)}.flag-icon-da{background-image:url(../flags/4x3/da.svg)}.flag-icon-da.flag-icon-squared{background-image:url(../flags/1x1/d.svg)}.flag-icon-dk{background-image:url(../flags/4x3/dk.svg)}.flag-icon-dk.flag-icon-squared{background-image:url(../flags/1x1/dk.svg)}.flag-icon-dm{background-image:url(../flags/4x3/dm.svg)}.flag-icon-dm.flag-icon-squared{background-image:url(../flags/1x1/dm.svg)}.flag-icon-do{background-image:url(../flags/4x3/do.svg)}.flag-icon-do.flag-icon-squared{background-image:url(../flags/1x1/do.svg)}.flag-icon-dz{background-image:url(../flags/4x3/dz.svg)}.flag-icon-dz.flag-icon-squared{background-image:url(../flags/1x1/dz.svg)}.flag-icon-ec{background-image:url(../flags/4x3/ec.svg)}.flag-icon-ec.flag-icon-squared{background-image:url(../flags/1x1/ec.svg)}.flag-icon-ee{background-image:url(../flags/4x3/ee.svg)}.flag-icon-ee.flag-icon-squared{background-image:url(../flags/1x1/ee.svg)}.flag-icon-eg{background-image:url(../flags/4x3/eg.svg)}.flag-icon-eg.flag-icon-squared{background-image:url(../flags/1x1/eg.svg)}.flag-icon-eh{background-image:url(../flags/4x3/eh.svg)}.flag-icon-eh.flag-icon-squared{background-image:url(../flags/1x1/eh.svg)}.flag-icon-er{background-image:url(../flags/4x3/er.svg)}.flag-icon-er.flag-icon-squared{background-image:url(../flags/1x1/er.svg)}.flag-icon-es{background-image:url(../flags/4x3/es.svg)}.flag-icon-es.flag-icon-squared{background-image:url(../flags/1x1/es.svg)}.flag-icon-et{background-image:url(../flags/4x3/et.svg)}.flag-icon-et.flag-icon-squared{background-image:url(../flags/1x1/et.svg)}.flag-icon-fi{background-image:url(../flags/4x3/fi.svg)}.flag-icon-fi.flag-icon-squared{background-image:url(../flags/1x1/fi.svg)}.flag-icon-fj{background-image:url(../flags/4x3/fj.svg)}.flag-icon-fj.flag-icon-squared{background-image:url(../flags/1x1/fj.svg)}.flag-icon-fk{background-image:url(../flags/4x3/fk.svg)}.flag-icon-fk.flag-icon-squared{background-image:url(../flags/1x1/fk.svg)}.flag-icon-fm{background-image:url(../flags/4x3/fm.svg)}.flag-icon-fm.flag-icon-squared{background-image:url(../flags/1x1/fm.svg)}.flag-icon-fo{background-image:url(../flags/4x3/fo.svg)}.flag-icon-fo.flag-icon-squared{background-image:url(../flags/1x1/fo.svg)}.flag-icon-fr{background-image:url(../flags/4x3/fr.svg)}.flag-icon-fr.flag-icon-squared{background-image:url(../flags/1x1/fr.svg)}.flag-icon-ga{background-image:url(../flags/4x3/ga.svg)}.flag-icon-ga.flag-icon-squared{background-image:url(../flags/1x1/ga.svg)}.flag-icon-en{background-image:url(../flags/4x3/en.svg)}.flag-icon-en.flag-icon-squared{background-image:url(../flags/1x1/en.svg)}.flag-icon-gd{background-image:url(../flags/4x3/gd.svg)}.flag-icon-gd.flag-icon-squared{background-image:url(../flags/1x1/gd.svg)}.flag-icon-ge{background-image:url(../flags/4x3/ge.svg)}.flag-icon-ge.flag-icon-squared{background-image:url(../flags/1x1/ge.svg)}.flag-icon-gf{background-image:url(../flags/4x3/gf.svg)}.flag-icon-gf.flag-icon-squared{background-image:url(../flags/1x1/gf.svg)}.flag-icon-gg{background-image:url(../flags/4x3/gg.svg)}.flag-icon-gg.flag-icon-squared{background-image:url(../flags/1x1/gg.svg)}.flag-icon-gh{background-image:url(../flags/4x3/gh.svg)}.flag-icon-gh.flag-icon-squared{background-image:url(../flags/1x1/gh.svg)}.flag-icon-gi{background-image:url(../flags/4x3/gi.svg)}.flag-icon-gi.flag-icon-squared{background-image:url(../flags/1x1/gi.svg)}.flag-icon-gl{background-image:url(../flags/4x3/gl.svg)}.flag-icon-gl.flag-icon-squared{background-image:url(../flags/1x1/gl.svg)}.flag-icon-gm{background-image:url(../flags/4x3/gm.svg)}.flag-icon-gm.flag-icon-squared{background-image:url(../flags/1x1/gm.svg)}.flag-icon-gn{background-image:url(../flags/4x3/gn.svg)}.flag-icon-gn.flag-icon-squared{background-image:url(../flags/1x1/gn.svg)}.flag-icon-gp{background-image:url(../flags/4x3/gp.svg)}.flag-icon-gp.flag-icon-squared{background-image:url(../flags/1x1/gp.svg)}.flag-icon-gq{background-image:url(../flags/4x3/gq.svg)}.flag-icon-gq.flag-icon-squared{background-image:url(../flags/1x1/gq.svg)}.flag-icon-gr{background-image:url(../flags/4x3/gr.svg)}.flag-icon-gr.flag-icon-squared{background-image:url(../flags/1x1/gr.svg)}.flag-icon-gs{background-image:url(../flags/4x3/gs.svg)}.flag-icon-gs.flag-icon-squared{background-image:url(../flags/1x1/gs.svg)}.flag-icon-gt{background-image:url(../flags/4x3/gt.svg)}.flag-icon-gt.flag-icon-squared{background-image:url(../flags/1x1/gt.svg)}.flag-icon-gu{background-image:url(../flags/4x3/gu.svg)}.flag-icon-gu.flag-icon-squared{background-image:url(../flags/1x1/gu.svg)}.flag-icon-gw{background-image:url(../flags/4x3/gw.svg)}.flag-icon-gw.flag-icon-squared{background-image:url(../flags/1x1/gw.svg)}.flag-icon-gy{background-image:url(../flags/4x3/gy.svg)}.flag-icon-gy.flag-icon-squared{background-image:url(../flags/1x1/gy.svg)}.flag-icon-hk{background-image:url(../flags/4x3/hk.svg)}.flag-icon-hk.flag-icon-squared{background-image:url(../flags/1x1/hk.svg)}.flag-icon-hm{background-image:url(../flags/4x3/hm.svg)}.flag-icon-hm.flag-icon-squared{background-image:url(../flags/1x1/hm.svg)}.flag-icon-hn{background-image:url(../flags/4x3/hn.svg)}.flag-icon-hn.flag-icon-squared{background-image:url(../flags/1x1/hn.svg)}.flag-icon-hr{background-image:url(../flags/4x3/hr.svg)}.flag-icon-hr.flag-icon-squared{background-image:url(../flags/1x1/hr.svg)}.flag-icon-ht{background-image:url(../flags/4x3/ht.svg)}.flag-icon-ht.flag-icon-squared{background-image:url(../flags/1x1/ht.svg)}.flag-icon-hu{background-image:url(../flags/4x3/hu.svg)}.flag-icon-hu.flag-icon-squared{background-image:url(../flags/1x1/hu.svg)}.flag-icon-id{background-image:url(../flags/4x3/id.svg)}.flag-icon-id.flag-icon-squared{background-image:url(../flags/1x1/id.svg)}.flag-icon-ie{background-image:url(../flags/4x3/ie.svg)}.flag-icon-ie.flag-icon-squared{background-image:url(../flags/1x1/ie.svg)}.flag-icon-il{background-image:url(../flags/4x3/il.svg)}.flag-icon-il.flag-icon-squared{background-image:url(../flags/1x1/il.svg)}.flag-icon-im{background-image:url(../flags/4x3/im.svg)}.flag-icon-im.flag-icon-squared{background-image:url(../flags/1x1/im.svg)}.flag-icon-in{background-image:url(../flags/4x3/in.svg)}.flag-icon-in.flag-icon-squared{background-image:url(../flags/1x1/in.svg)}.flag-icon-io{background-image:url(../flags/4x3/io.svg)}.flag-icon-io.flag-icon-squared{background-image:url(../flags/1x1/io.svg)}.flag-icon-iq{background-image:url(../flags/4x3/iq.svg)}.flag-icon-iq.flag-icon-squared{background-image:url(../flags/1x1/iq.svg)}.flag-icon-ir{background-image:url(../flags/4x3/ir.svg)}.flag-icon-ir.flag-icon-squared{background-image:url(../flags/1x1/ir.svg)}.flag-icon-is{background-image:url(../flags/4x3/is.svg)}.flag-icon-is.flag-icon-squared{background-image:url(../flags/1x1/is.svg)}.flag-icon-it{background-image:url(../flags/4x3/it.svg)}.flag-icon-it.flag-icon-squared{background-image:url(../flags/1x1/it.svg)}.flag-icon-je{background-image:url(../flags/4x3/je.svg)}.flag-icon-je.flag-icon-squared{background-image:url(../flags/1x1/je.svg)}.flag-icon-jm{background-image:url(../flags/4x3/jm.svg)}.flag-icon-jm.flag-icon-squared{background-image:url(../flags/1x1/jm.svg)}.flag-icon-jo{background-image:url(../flags/4x3/jo.svg)}.flag-icon-jo.flag-icon-squared{background-image:url(../flags/1x1/jo.svg)}.flag-icon-jp{background-image:url(../flags/4x3/jp.svg)}.flag-icon-jp.flag-icon-squared{background-image:url(../flags/1x1/jp.svg)}.flag-icon-ke{background-image:url(../flags/4x3/ke.svg)}.flag-icon-ke.flag-icon-squared{background-image:url(../flags/1x1/ke.svg)}.flag-icon-kg{background-image:url(../flags/4x3/kg.svg)}.flag-icon-kg.flag-icon-squared{background-image:url(../flags/1x1/kg.svg)}.flag-icon-kh{background-image:url(../flags/4x3/kh.svg)}.flag-icon-kh.flag-icon-squared{background-image:url(../flags/1x1/kh.svg)}.flag-icon-ki{background-image:url(../flags/4x3/ki.svg)}.flag-icon-ki.flag-icon-squared{background-image:url(../flags/1x1/ki.svg)}.flag-icon-km{background-image:url(../flags/4x3/km.svg)}.flag-icon-km.flag-icon-squared{background-image:url(../flags/1x1/km.svg)}.flag-icon-kn{background-image:url(../flags/4x3/kn.svg)}.flag-icon-kn.flag-icon-squared{background-image:url(../flags/1x1/kn.svg)}.flag-icon-kp{background-image:url(../flags/4x3/kp.svg)}.flag-icon-kp.flag-icon-squared{background-image:url(../flags/1x1/kp.svg)}.flag-icon-ko{background-image:url(../flags/4x3/ko.svg)}.flag-icon-ko.flag-icon-squared{background-image:url(../flags/1x1/ko.svg)}.flag-icon-kr{background-image:url(../flags/4x3/kr.svg)}.flag-icon-kr.flag-icon-squared{background-image:url(../flags/1x1/kr.svg)}.flag-icon-kw{background-image:url(../flags/4x3/kw.svg)}.flag-icon-kw.flag-icon-squared{background-image:url(../flags/1x1/kw.svg)}.flag-icon-ky{background-image:url(../flags/4x3/ky.svg)}.flag-icon-ky.flag-icon-squared{background-image:url(../flags/1x1/ky.svg)}.flag-icon-kz{background-image:url(../flags/4x3/kz.svg)}.flag-icon-kz.flag-icon-squared{background-image:url(../flags/1x1/kz.svg)}.flag-icon-la{background-image:url(../flags/4x3/la.svg)}.flag-icon-la.flag-icon-squared{background-image:url(../flags/1x1/la.svg)}.flag-icon-lb{background-image:url(../flags/4x3/lb.svg)}.flag-icon-lb.flag-icon-squared{background-image:url(../flags/1x1/lb.svg)}.flag-icon-lc{background-image:url(../flags/4x3/lc.svg)}.flag-icon-lc.flag-icon-squared{background-image:url(../flags/1x1/lc.svg)}.flag-icon-li{background-image:url(../flags/4x3/li.svg)}.flag-icon-li.flag-icon-squared{background-image:url(../flags/1x1/li.svg)}.flag-icon-lk{background-image:url(../flags/4x3/lk.svg)}.flag-icon-lk.flag-icon-squared{background-image:url(../flags/1x1/lk.svg)}.flag-icon-lr{background-image:url(../flags/4x3/lr.svg)}.flag-icon-lr.flag-icon-squared{background-image:url(../flags/1x1/lr.svg)}.flag-icon-ls{background-image:url(../flags/4x3/ls.svg)}.flag-icon-ls.flag-icon-squared{background-image:url(../flags/1x1/ls.svg)}.flag-icon-lt{background-image:url(../flags/4x3/lt.svg)}.flag-icon-lt.flag-icon-squared{background-image:url(../flags/1x1/lt.svg)}.flag-icon-lu{background-image:url(../flags/4x3/lu.svg)}.flag-icon-lu.flag-icon-squared{background-image:url(../flags/1x1/lu.svg)}.flag-icon-lv{background-image:url(../flags/4x3/lv.svg)}.flag-icon-lv.flag-icon-squared{background-image:url(../flags/1x1/lv.svg)}.flag-icon-ly{background-image:url(../flags/4x3/ly.svg)}.flag-icon-ly.flag-icon-squared{background-image:url(../flags/1x1/ly.svg)}.flag-icon-ma{background-image:url(../flags/4x3/ma.svg)}.flag-icon-ma.flag-icon-squared{background-image:url(../flags/1x1/ma.svg)}.flag-icon-mc{background-image:url(../flags/4x3/mc.svg)}.flag-icon-mc.flag-icon-squared{background-image:url(../flags/1x1/mc.svg)}.flag-icon-md{background-image:url(../flags/4x3/md.svg)}.flag-icon-md.flag-icon-squared{background-image:url(../flags/1x1/md.svg)}.flag-icon-me{background-image:url(../flags/4x3/me.svg)}.flag-icon-me.flag-icon-squared{background-image:url(../flags/1x1/me.svg)}.flag-icon-mf{background-image:url(../flags/4x3/mf.svg)}.flag-icon-mf.flag-icon-squared{background-image:url(../flags/1x1/mf.svg)}.flag-icon-mg{background-image:url(../flags/4x3/mg.svg)}.flag-icon-mg.flag-icon-squared{background-image:url(../flags/1x1/mg.svg)}.flag-icon-mh{background-image:url(../flags/4x3/mh.svg)}.flag-icon-mh.flag-icon-squared{background-image:url(../flags/1x1/mh.svg)}.flag-icon-mk{background-image:url(../flags/4x3/mk.svg)}.flag-icon-mk.flag-icon-squared{background-image:url(../flags/1x1/mk.svg)}.flag-icon-ml{background-image:url(../flags/4x3/ml.svg)}.flag-icon-ml.flag-icon-squared{background-image:url(../flags/1x1/ml.svg)}.flag-icon-mm{background-image:url(../flags/4x3/mm.svg)}.flag-icon-mm.flag-icon-squared{background-image:url(../flags/1x1/mm.svg)}.flag-icon-mn{background-image:url(../flags/4x3/mn.svg)}.flag-icon-mn.flag-icon-squared{background-image:url(../flags/1x1/mn.svg)}.flag-icon-mo{background-image:url(../flags/4x3/mo.svg)}.flag-icon-mo.flag-icon-squared{background-image:url(../flags/1x1/mo.svg)}.flag-icon-mp{background-image:url(../flags/4x3/mp.svg)}.flag-icon-mp.flag-icon-squared{background-image:url(../flags/1x1/mp.svg)}.flag-icon-mq{background-image:url(../flags/4x3/mq.svg)}.flag-icon-mq.flag-icon-squared{background-image:url(../flags/1x1/mq.svg)}.flag-icon-mr{background-image:url(../flags/4x3/mr.svg)}.flag-icon-mr.flag-icon-squared{background-image:url(../flags/1x1/mr.svg)}.flag-icon-ms{background-image:url(../flags/4x3/ms.svg)}.flag-icon-ms.flag-icon-squared{background-image:url(../flags/1x1/ms.svg)}.flag-icon-mt{background-image:url(../flags/4x3/mt.svg)}.flag-icon-mt.flag-icon-squared{background-image:url(../flags/1x1/mt.svg)}.flag-icon-mu{background-image:url(../flags/4x3/mu.svg)}.flag-icon-mu.flag-icon-squared{background-image:url(../flags/1x1/mu.svg)}.flag-icon-mv{background-image:url(../flags/4x3/mv.svg)}.flag-icon-mv.flag-icon-squared{background-image:url(../flags/1x1/mv.svg)}.flag-icon-mw{background-image:url(../flags/4x3/mw.svg)}.flag-icon-mw.flag-icon-squared{background-image:url(../flags/1x1/mw.svg)}.flag-icon-mx{background-image:url(../flags/4x3/mx.svg)}.flag-icon-mx.flag-icon-squared{background-image:url(../flags/1x1/mx.svg)}.flag-icon-my{background-image:url(../flags/4x3/my.svg)}.flag-icon-my.flag-icon-squared{background-image:url(../flags/1x1/my.svg)}.flag-icon-mz{background-image:url(../flags/4x3/mz.svg)}.flag-icon-mz.flag-icon-squared{background-image:url(../flags/1x1/mz.svg)}.flag-icon-na{background-image:url(../flags/4x3/na.svg)}.flag-icon-na.flag-icon-squared{background-image:url(../flags/1x1/na.svg)}.flag-icon-nc{background-image:url(../flags/4x3/nc.svg)}.flag-icon-nc.flag-icon-squared{background-image:url(../flags/1x1/nc.svg)}.flag-icon-ne{background-image:url(../flags/4x3/ne.svg)}.flag-icon-ne.flag-icon-squared{background-image:url(../flags/1x1/ne.svg)}.flag-icon-nf{background-image:url(../flags/4x3/nf.svg)}.flag-icon-nf.flag-icon-squared{background-image:url(../flags/1x1/nf.svg)}.flag-icon-ng{background-image:url(../flags/4x3/ng.svg)}.flag-icon-ng.flag-icon-squared{background-image:url(../flags/1x1/ng.svg)}.flag-icon-ni{background-image:url(../flags/4x3/ni.svg)}.flag-icon-ni.flag-icon-squared{background-image:url(../flags/1x1/ni.svg)}.flag-icon-nl{background-image:url(../flags/4x3/nl.svg)}.flag-icon-nl.flag-icon-squared{background-image:url(../flags/1x1/nl.svg)}.flag-icon-no{background-image:url(../flags/4x3/no.svg)}.flag-icon-no.flag-icon-squared{background-image:url(../flags/1x1/no.svg)}.flag-icon-np{background-image:url(../flags/4x3/np.svg)}.flag-icon-np.flag-icon-squared{background-image:url(../flags/1x1/np.svg)}.flag-icon-nr{background-image:url(../flags/4x3/nr.svg)}.flag-icon-nr.flag-icon-squared{background-image:url(../flags/1x1/nr.svg)}.flag-icon-nu{background-image:url(../flags/4x3/nu.svg)}.flag-icon-nu.flag-icon-squared{background-image:url(../flags/1x1/nu.svg)}.flag-icon-nz{background-image:url(../flags/4x3/nz.svg)}.flag-icon-nz.flag-icon-squared{background-image:url(../flags/1x1/nz.svg)}.flag-icon-om{background-image:url(../flags/4x3/om.svg)}.flag-icon-om.flag-icon-squared{background-image:url(../flags/1x1/om.svg)}.flag-icon-pa{background-image:url(../flags/4x3/pa.svg)}.flag-icon-pa.flag-icon-squared{background-image:url(../flags/1x1/pa.svg)}.flag-icon-pe{background-image:url(../flags/4x3/pe.svg)}.flag-icon-pe.flag-icon-squared{background-image:url(../flags/1x1/pe.svg)}.flag-icon-pf{background-image:url(../flags/4x3/pf.svg)}.flag-icon-pf.flag-icon-squared{background-image:url(../flags/1x1/pf.svg)}.flag-icon-pg{background-image:url(../flags/4x3/pg.svg)}.flag-icon-pg.flag-icon-squared{background-image:url(../flags/1x1/pg.svg)}.flag-icon-ph{background-image:url(../flags/4x3/ph.svg)}.flag-icon-ph.flag-icon-squared{background-image:url(../flags/1x1/ph.svg)}.flag-icon-pk{background-image:url(../flags/4x3/pk.svg)}.flag-icon-pk.flag-icon-squared{background-image:url(../flags/1x1/pk.svg)}.flag-icon-pl{background-image:url(../flags/4x3/pl.svg)}.flag-icon-pl.flag-icon-squared{background-image:url(../flags/1x1/pl.svg)}.flag-icon-pm{background-image:url(../flags/4x3/pm.svg)}.flag-icon-pm.flag-icon-squared{background-image:url(../flags/1x1/pm.svg)}.flag-icon-pn{background-image:url(../flags/4x3/pn.svg)}.flag-icon-pn.flag-icon-squared{background-image:url(../flags/1x1/pn.svg)}.flag-icon-pr{background-image:url(../flags/4x3/pr.svg)}.flag-icon-pr.flag-icon-squared{background-image:url(../flags/1x1/pr.svg)}.flag-icon-ps{background-image:url(../flags/4x3/ps.svg)}.flag-icon-ps.flag-icon-squared{background-image:url(../flags/1x1/ps.svg)}.flag-icon-pt{background-image:url(../flags/4x3/pt.svg)}.flag-icon-pt.flag-icon-squared{background-image:url(../flags/1x1/pt.svg)}.flag-icon-pw{background-image:url(../flags/4x3/pw.svg)}.flag-icon-pw.flag-icon-squared{background-image:url(../flags/1x1/pw.svg)}.flag-icon-py{background-image:url(../flags/4x3/py.svg)}.flag-icon-py.flag-icon-squared{background-image:url(../flags/1x1/py.svg)}.flag-icon-qa{background-image:url(../flags/4x3/qa.svg)}.flag-icon-qa.flag-icon-squared{background-image:url(../flags/1x1/qa.svg)}.flag-icon-re{background-image:url(../flags/4x3/re.svg)}.flag-icon-re.flag-icon-squared{background-image:url(../flags/1x1/re.svg)}.flag-icon-ro{background-image:url(../flags/4x3/ro.svg)}.flag-icon-ro.flag-icon-squared{background-image:url(../flags/1x1/ro.svg)}.flag-icon-rs{background-image:url(../flags/4x3/rs.svg)}.flag-icon-rs.flag-icon-squared{background-image:url(../flags/1x1/rs.svg)}.flag-icon-ru{background-image:url(../flags/4x3/ru.svg)}.flag-icon-ru.flag-icon-squared{background-image:url(../flags/1x1/ru.svg)}.flag-icon-rw{background-image:url(../flags/4x3/rw.svg)}.flag-icon-rw.flag-icon-squared{background-image:url(../flags/1x1/rw.svg)}.flag-icon-sa{background-image:url(../flags/4x3/sa.svg)}.flag-icon-sa.flag-icon-squared{background-image:url(../flags/1x1/sa.svg)}.flag-icon-sb{background-image:url(../flags/4x3/sb.svg)}.flag-icon-sb.flag-icon-squared{background-image:url(../flags/1x1/sb.svg)}.flag-icon-sc{background-image:url(../flags/4x3/sc.svg)}.flag-icon-sc.flag-icon-squared{background-image:url(../flags/1x1/sc.svg)}.flag-icon-sd{background-image:url(../flags/4x3/sd.svg)}.flag-icon-sd.flag-icon-squared{background-image:url(../flags/1x1/sd.svg)}.flag-icon-se{background-image:url(../flags/4x3/se.svg)}.flag-icon-se.flag-icon-squared{background-image:url(../flags/1x1/se.svg)}.flag-icon-sg{background-image:url(../flags/4x3/sg.svg)}.flag-icon-sg.flag-icon-squared{background-image:url(../flags/1x1/sg.svg)}.flag-icon-sh{background-image:url(../flags/4x3/sh.svg)}.flag-icon-sh.flag-icon-squared{background-image:url(../flags/1x1/sh.svg)}.flag-icon-si{background-image:url(../flags/4x3/si.svg)}.flag-icon-si.flag-icon-squared{background-image:url(../flags/1x1/si.svg)}.flag-icon-sj{background-image:url(../flags/4x3/sj.svg)}.flag-icon-sj.flag-icon-squared{background-image:url(../flags/1x1/sj.svg)}.flag-icon-sk{background-image:url(../flags/4x3/sk.svg)}.flag-icon-sk.flag-icon-squared{background-image:url(../flags/1x1/sk.svg)}.flag-icon-sl{background-image:url(../flags/4x3/sl.svg)}.flag-icon-sl.flag-icon-squared{background-image:url(../flags/1x1/sl.svg)}.flag-icon-sm{background-image:url(../flags/4x3/sm.svg)}.flag-icon-sm.flag-icon-squared{background-image:url(../flags/1x1/sm.svg)}.flag-icon-sn{background-image:url(../flags/4x3/sn.svg)}.flag-icon-sn.flag-icon-squared{background-image:url(../flags/1x1/sn.svg)}.flag-icon-so{background-image:url(../flags/4x3/so.svg)}.flag-icon-so.flag-icon-squared{background-image:url(../flags/1x1/so.svg)}.flag-icon-sr{background-image:url(../flags/4x3/sr.svg)}.flag-icon-sr.flag-icon-squared{background-image:url(../flags/1x1/sr.svg)}.flag-icon-ss{background-image:url(../flags/4x3/ss.svg)}.flag-icon-ss.flag-icon-squared{background-image:url(../flags/1x1/ss.svg)}.flag-icon-st{background-image:url(../flags/4x3/st.svg)}.flag-icon-st.flag-icon-squared{background-image:url(../flags/1x1/st.svg)}.flag-icon-sv{background-image:url(../flags/4x3/sv.svg)}.flag-icon-sv.flag-icon-squared{background-image:url(../flags/1x1/sv.svg)}.flag-icon-sx{background-image:url(../flags/4x3/sx.svg)}.flag-icon-sx.flag-icon-squared{background-image:url(../flags/1x1/sx.svg)}.flag-icon-sy{background-image:url(../flags/4x3/sy.svg)}.flag-icon-sy.flag-icon-squared{background-image:url(../flags/1x1/sy.svg)}.flag-icon-sz{background-image:url(../flags/4x3/sz.svg)}.flag-icon-sz.flag-icon-squared{background-image:url(../flags/1x1/sz.svg)}.flag-icon-tc{background-image:url(../flags/4x3/tc.svg)}.flag-icon-tc.flag-icon-squared{background-image:url(../flags/1x1/tc.svg)}.flag-icon-td{background-image:url(../flags/4x3/td.svg)}.flag-icon-td.flag-icon-squared{background-image:url(../flags/1x1/td.svg)}.flag-icon-tf{background-image:url(../flags/4x3/tf.svg)}.flag-icon-tf.flag-icon-squared{background-image:url(../flags/1x1/tf.svg)}.flag-icon-tg{background-image:url(../flags/4x3/tg.svg)}.flag-icon-tg.flag-icon-squared{background-image:url(../flags/1x1/tg.svg)}.flag-icon-th{background-image:url(../flags/4x3/th.svg)}.flag-icon-th.flag-icon-squared{background-image:url(../flags/1x1/th.svg)}.flag-icon-tj{background-image:url(../flags/4x3/tj.svg)}.flag-icon-tj.flag-icon-squared{background-image:url(../flags/1x1/tj.svg)}.flag-icon-tk{background-image:url(../flags/4x3/tk.svg)}.flag-icon-tk.flag-icon-squared{background-image:url(../flags/1x1/tk.svg)}.flag-icon-tl{background-image:url(../flags/4x3/tl.svg)}.flag-icon-tl.flag-icon-squared{background-image:url(../flags/1x1/tl.svg)}.flag-icon-tm{background-image:url(../flags/4x3/tm.svg)}.flag-icon-tm.flag-icon-squared{background-image:url(../flags/1x1/tm.svg)}.flag-icon-tn{background-image:url(../flags/4x3/tn.svg)}.flag-icon-tn.flag-icon-squared{background-image:url(../flags/1x1/tn.svg)}.flag-icon-to{background-image:url(../flags/4x3/to.svg)}.flag-icon-to.flag-icon-squared{background-image:url(../flags/1x1/to.svg)}.flag-icon-tr{background-image:url(../flags/4x3/tr.svg)}.flag-icon-tr.flag-icon-squared{background-image:url(../flags/1x1/tr.svg)}.flag-icon-tt{background-image:url(../flags/4x3/tt.svg)}.flag-icon-tt.flag-icon-squared{background-image:url(../flags/1x1/tt.svg)}.flag-icon-tv{background-image:url(../flags/4x3/tv.svg)}.flag-icon-tv.flag-icon-squared{background-image:url(../flags/1x1/tv.svg)}.flag-icon-tw{background-image:url(../flags/4x3/tw.svg)}.flag-icon-tw.flag-icon-squared{background-image:url(../flags/1x1/tw.svg)}.flag-icon-tz{background-image:url(../flags/4x3/tz.svg)}.flag-icon-tz.flag-icon-squared{background-image:url(../flags/1x1/tz.svg)}.flag-icon-ua{background-image:url(../flags/4x3/ua.svg)}.flag-icon-ua.flag-icon-squared{background-image:url(../flags/1x1/ua.svg)}.flag-icon-ug{background-image:url(../flags/4x3/ug.svg)}.flag-icon-ug.flag-icon-squared{background-image:url(../flags/1x1/ug.svg)}.flag-icon-um{background-image:url(../flags/4x3/um.svg)}.flag-icon-um.flag-icon-squared{background-image:url(../flags/1x1/um.svg)}.flag-icon-us{background-image:url(../flags/4x3/us.svg)}.flag-icon-us.flag-icon-squared{background-image:url(../flags/1x1/us.svg)}.flag-icon-uy{background-image:url(../flags/4x3/uy.svg)}.flag-icon-uy.flag-icon-squared{background-image:url(../flags/1x1/uy.svg)}.flag-icon-uz{background-image:url(../flags/4x3/uz.svg)}.flag-icon-uz.flag-icon-squared{background-image:url(../flags/1x1/uz.svg)}.flag-icon-va{background-image:url(../flags/4x3/va.svg)}.flag-icon-va.flag-icon-squared{background-image:url(../flags/1x1/va.svg)}.flag-icon-vc{background-image:url(../flags/4x3/vc.svg)}.flag-icon-vc.flag-icon-squared{background-image:url(../flags/1x1/vc.svg)}.flag-icon-ve{background-image:url(../flags/4x3/ve.svg)}.flag-icon-ve.flag-icon-squared{background-image:url(../flags/1x1/ve.svg)}.flag-icon-vg{background-image:url(../flags/4x3/vg.svg)}.flag-icon-vg.flag-icon-squared{background-image:url(../flags/1x1/vg.svg)}.flag-icon-vi{background-image:url(../flags/4x3/vi.svg)}.flag-icon-vi.flag-icon-squared{background-image:url(../flags/1x1/vi.svg)}.flag-icon-vn{background-image:url(../flags/4x3/vn.svg)}.flag-icon-vn.flag-icon-squared{background-image:url(../flags/1x1/vn.svg)}.flag-icon-vu{background-image:url(../flags/4x3/vu.svg)}.flag-icon-vu.flag-icon-squared{background-image:url(../flags/1x1/vu.svg)}.flag-icon-wf{background-image:url(../flags/4x3/wf.svg)}.flag-icon-wf.flag-icon-squared{background-image:url(../flags/1x1/wf.svg)}.flag-icon-ws{background-image:url(../flags/4x3/ws.svg)}.flag-icon-ws.flag-icon-squared{background-image:url(../flags/1x1/ws.svg)}.flag-icon-ye{background-image:url(../flags/4x3/ye.svg)}.flag-icon-ye.flag-icon-squared{background-image:url(../flags/1x1/ye.svg)}.flag-icon-yt{background-image:url(../flags/4x3/yt.svg)}.flag-icon-yt.flag-icon-squared{background-image:url(../flags/1x1/yt.svg)}.flag-icon-za{background-image:url(../flags/4x3/za.svg)}.flag-icon-za.flag-icon-squared{background-image:url(../flags/1x1/za.svg)}.flag-icon-zm{background-image:url(../flags/4x3/zm.svg)}.flag-icon-zm.flag-icon-squared{background-image:url(../flags/1x1/zm.svg)}.flag-icon-zw{background-image:url(../flags/4x3/zw.svg)}.flag-icon-zw.flag-icon-squared{background-image:url(../flags/1x1/zw.svg)}.flag-icon-es-ct{background-image:url(../flags/4x3/es-ct.svg)}.flag-icon-es-ct.flag-icon-squared{background-image:url(../flags/1x1/es-ct.svg)}.flag-icon-es-ga{background-image:url(../flags/4x3/es-ga.svg)}.flag-icon-es-ga.flag-icon-squared{background-image:url(../flags/1x1/es-ga.svg)}.flag-icon-eu{background-image:url(../flags/4x3/eu.svg)}.flag-icon-eu.flag-icon-squared{background-image:url(../flags/1x1/eu.svg)}.flag-icon-gb-eng{background-image:url(../flags/4x3/gb-eng.svg)}.flag-icon-gb-eng.flag-icon-squared{background-image:url(../flags/1x1/gb-eng.svg)}.flag-icon-gb-nir{background-image:url(../flags/4x3/gb-nir.svg)}.flag-icon-gb-nir.flag-icon-squared{background-image:url(../flags/1x1/gb-nir.svg)}.flag-icon-gb-sct{background-image:url(../flags/4x3/gb-sct.svg)}.flag-icon-gb-sct.flag-icon-squared{background-image:url(../flags/1x1/gb-sct.svg)}.flag-icon-gb-wls{background-image:url(../flags/4x3/gb-wls.svg)}.flag-icon-gb-wls.flag-icon-squared{background-image:url(../flags/1x1/gb-wls.svg)}.flag-icon-un{background-image:url(../flags/4x3/un.svg)}.flag-icon-un.flag-icon-squared{background-image:url(../flags/1x1/un.svg)}.flag-icon-xk{background-image:url(../flags/4x3/xk.svg)}.flag-icon-xk.flag-icon-squared{background-image:url(../flags/1x1/xk.svg)}
\ No newline at end of file
diff --git a/data/web/css/build/008-mailcow.css b/data/web/css/build/008-mailcow.css
index 717b4d11..c0600657 100644
--- a/data/web/css/build/008-mailcow.css
+++ b/data/web/css/build/008-mailcow.css
@@ -1,26 +1,37 @@
@font-face {
- font-family: 'PT Sans';
+ font-family: 'Noto Sans';
font-style: normal;
font-weight: 400;
- src: local('PT Sans'), local('PTSans-Regular'),
- url('/fonts/pt-sans-v11-latin-ext_cyrillic_latin-regular.woff2') format('woff2'),
- url('/fonts/pt-sans-v11-latin-ext_cyrillic_latin-regular.woff') format('woff');
+ src: local(''),
+ url('/fonts/noto-sans-v12-latin_greek_cyrillic-regular.woff2') format('woff2'),
+ url('/fonts/noto-sans-v12-latin_greek_cyrillic-regular.woff') format('woff');
}
+
@font-face {
- font-family: 'PT Sans';
+ font-family: 'Noto Sans';
font-style: normal;
font-weight: 700;
- src: local('PT Sans Bold'), local('PTSans-Bold'),
- url('/fonts/pt-sans-v11-latin-ext_cyrillic_latin-700.woff2') format('woff2'),
- url('/fonts/pt-sans-v11-latin-ext_cyrillic_latin-700.woff') format('woff');
+ src: local(''),
+ url('/fonts/noto-sans-v12-latin_greek_cyrillic-700.woff2') format('woff2'),
+ url('/fonts/noto-sans-v12-latin_greek_cyrillic-700.woff') format('woff');
}
+
@font-face {
- font-family: 'PT Sans';
+ font-family: 'Noto Sans';
font-style: italic;
font-weight: 400;
- src: local('PT Sans Italic'), local('PTSans-Italic'),
- url('/fonts/pt-sans-v11-latin-ext_cyrillic_latin-italic.woff2') format('woff2'),
- url('/fonts/pt-sans-v11-latin-ext_cyrillic_latin-italic.woff') format('woff');
+ src: local(''),
+ url('/fonts/noto-sans-v12-latin_greek_cyrillic-italic.woff2') format('woff2'),
+ url('/fonts/noto-sans-v12-latin_greek_cyrillic-italic.woff') format('woff');
+}
+
+@font-face {
+ font-family: 'Noto Sans';
+ font-style: italic;
+ font-weight: 700;
+ src: local(''),
+ url('/fonts/noto-sans-v12-latin_greek_cyrillic-700italic.woff2') format('woff2'),
+ url('/fonts/noto-sans-v12-latin_greek_cyrillic-700italic.woff') format('woff');
}
#maxmsgsize { min-width: 80px; }
#slider1 .slider-selection {
@@ -56,10 +67,23 @@
.navbar-fixed-top .navbar-collapse {
max-height: 1000px
}
-.glyphicon-spin {
- font-size:12px;
+.bi {
+ display: inline-block;
+ font-size: 12pt;
+}
+.btn .bi {
+ display: inline-block;
+ font-size: inherit;
+}
+.icon-spin {
+ animation-name: spin;
+ animation-duration: 2000ms;
+ animation-iteration-count: infinite;
+ animation-timing-function: linear;
-webkit-animation: spin 2000ms infinite linear;
- animation: spin 2000ms infinite linear;
+}
+.dropdown-menu {
+ font-size: 0.9rem;
}
@-webkit-keyframes spin {
0% {
@@ -94,7 +118,14 @@ body.modal-open {
padding-right: inherit !important;
}
body {
- font-family: "PT Sans","Helvetica Neue",Helvetica,Arial,sans-serif;
+ font-family: "Noto Sans","Helvetica Neue",Helvetica,Arial,sans-serif;
+ font-size: 10.5pt;
+ line-height: 1.5;
+}
+html {
+ font-family: "Noto Sans","Helvetica Neue",Helvetica,Arial,sans-serif;
+ font-size: 10.5pt;
+ line-height: 1.5;
}
#mailcow-alert {
position: fixed;
@@ -111,7 +142,7 @@ legend {
-ms-user-select: none;
-o-user-select: none;
user-select: none;
- font-size: 12pt;
+ font-size: 1.2rem;
}
.navbar .navbar-brand {
padding-top: 5px;
@@ -128,9 +159,6 @@ legend {
.lang-link-disabled {
cursor: not-allowed;
}
-.dkim-label {
- margin: 0 0 2px !important;
-}
.overlay {
background: #fff;
position: absolute;
@@ -138,9 +166,6 @@ legend {
top: 0; right: 0; bottom: 0; left: 0;
opacity: 0.7;
}
-nav .glyphicon {
- font-size: 12px !important;
-}
#top {
padding-top: 70px;
}
@@ -167,10 +192,10 @@ nav .glyphicon {
}
.tooltip {
font-family: inherit;
- font-size: 12px;
+ font-size: 0.8rem;
}
.progress-bar {
- font-size: 12px;
+ font-size: 0.8rem;
line-height: 14px;
}
.footer {
@@ -197,3 +222,25 @@ nav .glyphicon {
background-color: #ff4136;
border-color: #ff291c;
}
+table.footable>tbody>tr.footable-empty>td {
+ font-style:italic;
+ font-size: 1rem;
+}
+.navbar-nav > li {
+ font-size: 1rem !important;
+}
+.dropdown-menu > li > a {
+ font-size: 1rem !important;
+}
+.label {
+ font-size:inherit;
+}
+[class^="bi-"]::before, [class*=" bi-"]::before {
+ vertical-align: -0.2em !important;
+}
+legend > [class^="bi-"]::before, legend > [class*=" bi-"]::before {
+ vertical-align: 0em !important;
+}
+code {
+ font-size: inherit;
+}
\ No newline at end of file
diff --git a/data/web/css/build/013-bootstrap-icons.css b/data/web/css/build/013-bootstrap-icons.css
new file mode 100644
index 00000000..4ea58d77
--- /dev/null
+++ b/data/web/css/build/013-bootstrap-icons.css
@@ -0,0 +1,1390 @@
+@font-face {
+ font-family: "bootstrap-icons";
+ src: url("/fonts/bootstrap-icons.woff2?856008caa5eb66df68595e734e59580d") format("woff2"),
+url("/fonts/bootstrap-icons.woff?856008caa5eb66df68595e734e59580d") format("woff");
+}
+
+[class^="bi-"]::before,
+[class*=" bi-"]::before {
+ display: inline-block;
+ font-family: bootstrap-icons !important;
+ font-style: normal;
+ font-weight: normal !important;
+ font-variant: normal;
+ text-transform: none;
+ line-height: 1;
+ vertical-align: -.125em;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+
+.bi-alarm-fill::before { content: "\f101"; }
+.bi-alarm::before { content: "\f102"; }
+.bi-align-bottom::before { content: "\f103"; }
+.bi-align-center::before { content: "\f104"; }
+.bi-align-end::before { content: "\f105"; }
+.bi-align-middle::before { content: "\f106"; }
+.bi-align-start::before { content: "\f107"; }
+.bi-align-top::before { content: "\f108"; }
+.bi-alt::before { content: "\f109"; }
+.bi-app-indicator::before { content: "\f10a"; }
+.bi-app::before { content: "\f10b"; }
+.bi-archive-fill::before { content: "\f10c"; }
+.bi-archive::before { content: "\f10d"; }
+.bi-arrow-90deg-down::before { content: "\f10e"; }
+.bi-arrow-90deg-left::before { content: "\f10f"; }
+.bi-arrow-90deg-right::before { content: "\f110"; }
+.bi-arrow-90deg-up::before { content: "\f111"; }
+.bi-arrow-bar-down::before { content: "\f112"; }
+.bi-arrow-bar-left::before { content: "\f113"; }
+.bi-arrow-bar-right::before { content: "\f114"; }
+.bi-arrow-bar-up::before { content: "\f115"; }
+.bi-arrow-clockwise::before { content: "\f116"; }
+.bi-arrow-counterclockwise::before { content: "\f117"; }
+.bi-arrow-down-circle-fill::before { content: "\f118"; }
+.bi-arrow-down-circle::before { content: "\f119"; }
+.bi-arrow-down-left-circle-fill::before { content: "\f11a"; }
+.bi-arrow-down-left-circle::before { content: "\f11b"; }
+.bi-arrow-down-left-square-fill::before { content: "\f11c"; }
+.bi-arrow-down-left-square::before { content: "\f11d"; }
+.bi-arrow-down-left::before { content: "\f11e"; }
+.bi-arrow-down-right-circle-fill::before { content: "\f11f"; }
+.bi-arrow-down-right-circle::before { content: "\f120"; }
+.bi-arrow-down-right-square-fill::before { content: "\f121"; }
+.bi-arrow-down-right-square::before { content: "\f122"; }
+.bi-arrow-down-right::before { content: "\f123"; }
+.bi-arrow-down-short::before { content: "\f124"; }
+.bi-arrow-down-square-fill::before { content: "\f125"; }
+.bi-arrow-down-square::before { content: "\f126"; }
+.bi-arrow-down-up::before { content: "\f127"; }
+.bi-arrow-down::before { content: "\f128"; }
+.bi-arrow-left-circle-fill::before { content: "\f129"; }
+.bi-arrow-left-circle::before { content: "\f12a"; }
+.bi-arrow-left-right::before { content: "\f12b"; }
+.bi-arrow-left-short::before { content: "\f12c"; }
+.bi-arrow-left-square-fill::before { content: "\f12d"; }
+.bi-arrow-left-square::before { content: "\f12e"; }
+.bi-arrow-left::before { content: "\f12f"; }
+.bi-arrow-repeat::before { content: "\f130"; }
+.bi-arrow-return-left::before { content: "\f131"; }
+.bi-arrow-return-right::before { content: "\f132"; }
+.bi-arrow-right-circle-fill::before { content: "\f133"; }
+.bi-arrow-right-circle::before { content: "\f134"; }
+.bi-arrow-right-short::before { content: "\f135"; }
+.bi-arrow-right-square-fill::before { content: "\f136"; }
+.bi-arrow-right-square::before { content: "\f137"; }
+.bi-arrow-right::before { content: "\f138"; }
+.bi-arrow-up-circle-fill::before { content: "\f139"; }
+.bi-arrow-up-circle::before { content: "\f13a"; }
+.bi-arrow-up-left-circle-fill::before { content: "\f13b"; }
+.bi-arrow-up-left-circle::before { content: "\f13c"; }
+.bi-arrow-up-left-square-fill::before { content: "\f13d"; }
+.bi-arrow-up-left-square::before { content: "\f13e"; }
+.bi-arrow-up-left::before { content: "\f13f"; }
+.bi-arrow-up-right-circle-fill::before { content: "\f140"; }
+.bi-arrow-up-right-circle::before { content: "\f141"; }
+.bi-arrow-up-right-square-fill::before { content: "\f142"; }
+.bi-arrow-up-right-square::before { content: "\f143"; }
+.bi-arrow-up-right::before { content: "\f144"; }
+.bi-arrow-up-short::before { content: "\f145"; }
+.bi-arrow-up-square-fill::before { content: "\f146"; }
+.bi-arrow-up-square::before { content: "\f147"; }
+.bi-arrow-up::before { content: "\f148"; }
+.bi-arrows-angle-contract::before { content: "\f149"; }
+.bi-arrows-angle-expand::before { content: "\f14a"; }
+.bi-arrows-collapse::before { content: "\f14b"; }
+.bi-arrows-expand::before { content: "\f14c"; }
+.bi-arrows-fullscreen::before { content: "\f14d"; }
+.bi-arrows-move::before { content: "\f14e"; }
+.bi-aspect-ratio-fill::before { content: "\f14f"; }
+.bi-aspect-ratio::before { content: "\f150"; }
+.bi-asterisk::before { content: "\f151"; }
+.bi-at::before { content: "\f152"; }
+.bi-award-fill::before { content: "\f153"; }
+.bi-award::before { content: "\f154"; }
+.bi-back::before { content: "\f155"; }
+.bi-backspace-fill::before { content: "\f156"; }
+.bi-backspace-reverse-fill::before { content: "\f157"; }
+.bi-backspace-reverse::before { content: "\f158"; }
+.bi-backspace::before { content: "\f159"; }
+.bi-badge-3d-fill::before { content: "\f15a"; }
+.bi-badge-3d::before { content: "\f15b"; }
+.bi-badge-4k-fill::before { content: "\f15c"; }
+.bi-badge-4k::before { content: "\f15d"; }
+.bi-badge-8k-fill::before { content: "\f15e"; }
+.bi-badge-8k::before { content: "\f15f"; }
+.bi-badge-ad-fill::before { content: "\f160"; }
+.bi-badge-ad::before { content: "\f161"; }
+.bi-badge-ar-fill::before { content: "\f162"; }
+.bi-badge-ar::before { content: "\f163"; }
+.bi-badge-cc-fill::before { content: "\f164"; }
+.bi-badge-cc::before { content: "\f165"; }
+.bi-badge-hd-fill::before { content: "\f166"; }
+.bi-badge-hd::before { content: "\f167"; }
+.bi-badge-tm-fill::before { content: "\f168"; }
+.bi-badge-tm::before { content: "\f169"; }
+.bi-badge-vo-fill::before { content: "\f16a"; }
+.bi-badge-vo::before { content: "\f16b"; }
+.bi-badge-vr-fill::before { content: "\f16c"; }
+.bi-badge-vr::before { content: "\f16d"; }
+.bi-badge-wc-fill::before { content: "\f16e"; }
+.bi-badge-wc::before { content: "\f16f"; }
+.bi-bag-check-fill::before { content: "\f170"; }
+.bi-bag-check::before { content: "\f171"; }
+.bi-bag-dash-fill::before { content: "\f172"; }
+.bi-bag-dash::before { content: "\f173"; }
+.bi-bag-fill::before { content: "\f174"; }
+.bi-bag-plus-fill::before { content: "\f175"; }
+.bi-bag-plus::before { content: "\f176"; }
+.bi-bag-x-fill::before { content: "\f177"; }
+.bi-bag-x::before { content: "\f178"; }
+.bi-bag::before { content: "\f179"; }
+.bi-bar-chart-fill::before { content: "\f17a"; }
+.bi-bar-chart-line-fill::before { content: "\f17b"; }
+.bi-bar-chart-line::before { content: "\f17c"; }
+.bi-bar-chart-steps::before { content: "\f17d"; }
+.bi-bar-chart::before { content: "\f17e"; }
+.bi-basket-fill::before { content: "\f17f"; }
+.bi-basket::before { content: "\f180"; }
+.bi-basket2-fill::before { content: "\f181"; }
+.bi-basket2::before { content: "\f182"; }
+.bi-basket3-fill::before { content: "\f183"; }
+.bi-basket3::before { content: "\f184"; }
+.bi-battery-charging::before { content: "\f185"; }
+.bi-battery-full::before { content: "\f186"; }
+.bi-battery-half::before { content: "\f187"; }
+.bi-battery::before { content: "\f188"; }
+.bi-bell-fill::before { content: "\f189"; }
+.bi-bell::before { content: "\f18a"; }
+.bi-bezier::before { content: "\f18b"; }
+.bi-bezier2::before { content: "\f18c"; }
+.bi-bicycle::before { content: "\f18d"; }
+.bi-binoculars-fill::before { content: "\f18e"; }
+.bi-binoculars::before { content: "\f18f"; }
+.bi-blockquote-left::before { content: "\f190"; }
+.bi-blockquote-right::before { content: "\f191"; }
+.bi-book-fill::before { content: "\f192"; }
+.bi-book-half::before { content: "\f193"; }
+.bi-book::before { content: "\f194"; }
+.bi-bookmark-check-fill::before { content: "\f195"; }
+.bi-bookmark-check::before { content: "\f196"; }
+.bi-bookmark-dash-fill::before { content: "\f197"; }
+.bi-bookmark-dash::before { content: "\f198"; }
+.bi-bookmark-fill::before { content: "\f199"; }
+.bi-bookmark-heart-fill::before { content: "\f19a"; }
+.bi-bookmark-heart::before { content: "\f19b"; }
+.bi-bookmark-plus-fill::before { content: "\f19c"; }
+.bi-bookmark-plus::before { content: "\f19d"; }
+.bi-bookmark-star-fill::before { content: "\f19e"; }
+.bi-bookmark-star::before { content: "\f19f"; }
+.bi-bookmark-x-fill::before { content: "\f1a0"; }
+.bi-bookmark-x::before { content: "\f1a1"; }
+.bi-bookmark::before { content: "\f1a2"; }
+.bi-bookmarks-fill::before { content: "\f1a3"; }
+.bi-bookmarks::before { content: "\f1a4"; }
+.bi-bookshelf::before { content: "\f1a5"; }
+.bi-bootstrap-fill::before { content: "\f1a6"; }
+.bi-bootstrap-reboot::before { content: "\f1a7"; }
+.bi-bootstrap::before { content: "\f1a8"; }
+.bi-border-all::before { content: "\f1a9"; }
+.bi-border-bottom::before { content: "\f1aa"; }
+.bi-border-center::before { content: "\f1ab"; }
+.bi-border-inner::before { content: "\f1ac"; }
+.bi-border-left::before { content: "\f1ad"; }
+.bi-border-middle::before { content: "\f1ae"; }
+.bi-border-outer::before { content: "\f1af"; }
+.bi-border-right::before { content: "\f1b0"; }
+.bi-border-style::before { content: "\f1b1"; }
+.bi-border-top::before { content: "\f1b2"; }
+.bi-border-width::before { content: "\f1b3"; }
+.bi-border::before { content: "\f1b4"; }
+.bi-bounding-box-circles::before { content: "\f1b5"; }
+.bi-bounding-box::before { content: "\f1b6"; }
+.bi-box-arrow-down-left::before { content: "\f1b7"; }
+.bi-box-arrow-down-right::before { content: "\f1b8"; }
+.bi-box-arrow-down::before { content: "\f1b9"; }
+.bi-box-arrow-in-down-left::before { content: "\f1ba"; }
+.bi-box-arrow-in-down-right::before { content: "\f1bb"; }
+.bi-box-arrow-in-down::before { content: "\f1bc"; }
+.bi-box-arrow-in-left::before { content: "\f1bd"; }
+.bi-box-arrow-in-right::before { content: "\f1be"; }
+.bi-box-arrow-in-up-left::before { content: "\f1bf"; }
+.bi-box-arrow-in-up-right::before { content: "\f1c0"; }
+.bi-box-arrow-in-up::before { content: "\f1c1"; }
+.bi-box-arrow-left::before { content: "\f1c2"; }
+.bi-box-arrow-right::before { content: "\f1c3"; }
+.bi-box-arrow-up-left::before { content: "\f1c4"; }
+.bi-box-arrow-up-right::before { content: "\f1c5"; }
+.bi-box-arrow-up::before { content: "\f1c6"; }
+.bi-box-seam::before { content: "\f1c7"; }
+.bi-box::before { content: "\f1c8"; }
+.bi-braces::before { content: "\f1c9"; }
+.bi-bricks::before { content: "\f1ca"; }
+.bi-briefcase-fill::before { content: "\f1cb"; }
+.bi-briefcase::before { content: "\f1cc"; }
+.bi-brightness-alt-high-fill::before { content: "\f1cd"; }
+.bi-brightness-alt-high::before { content: "\f1ce"; }
+.bi-brightness-alt-low-fill::before { content: "\f1cf"; }
+.bi-brightness-alt-low::before { content: "\f1d0"; }
+.bi-brightness-high-fill::before { content: "\f1d1"; }
+.bi-brightness-high::before { content: "\f1d2"; }
+.bi-brightness-low-fill::before { content: "\f1d3"; }
+.bi-brightness-low::before { content: "\f1d4"; }
+.bi-broadcast-pin::before { content: "\f1d5"; }
+.bi-broadcast::before { content: "\f1d6"; }
+.bi-brush-fill::before { content: "\f1d7"; }
+.bi-brush::before { content: "\f1d8"; }
+.bi-bucket-fill::before { content: "\f1d9"; }
+.bi-bucket::before { content: "\f1da"; }
+.bi-bug-fill::before { content: "\f1db"; }
+.bi-bug::before { content: "\f1dc"; }
+.bi-building::before { content: "\f1dd"; }
+.bi-bullseye::before { content: "\f1de"; }
+.bi-calculator-fill::before { content: "\f1df"; }
+.bi-calculator::before { content: "\f1e0"; }
+.bi-calendar-check-fill::before { content: "\f1e1"; }
+.bi-calendar-check::before { content: "\f1e2"; }
+.bi-calendar-date-fill::before { content: "\f1e3"; }
+.bi-calendar-date::before { content: "\f1e4"; }
+.bi-calendar-day-fill::before { content: "\f1e5"; }
+.bi-calendar-day::before { content: "\f1e6"; }
+.bi-calendar-event-fill::before { content: "\f1e7"; }
+.bi-calendar-event::before { content: "\f1e8"; }
+.bi-calendar-fill::before { content: "\f1e9"; }
+.bi-calendar-minus-fill::before { content: "\f1ea"; }
+.bi-calendar-minus::before { content: "\f1eb"; }
+.bi-calendar-month-fill::before { content: "\f1ec"; }
+.bi-calendar-month::before { content: "\f1ed"; }
+.bi-calendar-plus-fill::before { content: "\f1ee"; }
+.bi-calendar-plus::before { content: "\f1ef"; }
+.bi-calendar-range-fill::before { content: "\f1f0"; }
+.bi-calendar-range::before { content: "\f1f1"; }
+.bi-calendar-week-fill::before { content: "\f1f2"; }
+.bi-calendar-week::before { content: "\f1f3"; }
+.bi-calendar-x-fill::before { content: "\f1f4"; }
+.bi-calendar-x::before { content: "\f1f5"; }
+.bi-calendar::before { content: "\f1f6"; }
+.bi-calendar2-check-fill::before { content: "\f1f7"; }
+.bi-calendar2-check::before { content: "\f1f8"; }
+.bi-calendar2-date-fill::before { content: "\f1f9"; }
+.bi-calendar2-date::before { content: "\f1fa"; }
+.bi-calendar2-day-fill::before { content: "\f1fb"; }
+.bi-calendar2-day::before { content: "\f1fc"; }
+.bi-calendar2-event-fill::before { content: "\f1fd"; }
+.bi-calendar2-event::before { content: "\f1fe"; }
+.bi-calendar2-fill::before { content: "\f1ff"; }
+.bi-calendar2-minus-fill::before { content: "\f200"; }
+.bi-calendar2-minus::before { content: "\f201"; }
+.bi-calendar2-month-fill::before { content: "\f202"; }
+.bi-calendar2-month::before { content: "\f203"; }
+.bi-calendar2-plus-fill::before { content: "\f204"; }
+.bi-calendar2-plus::before { content: "\f205"; }
+.bi-calendar2-range-fill::before { content: "\f206"; }
+.bi-calendar2-range::before { content: "\f207"; }
+.bi-calendar2-week-fill::before { content: "\f208"; }
+.bi-calendar2-week::before { content: "\f209"; }
+.bi-calendar2-x-fill::before { content: "\f20a"; }
+.bi-calendar2-x::before { content: "\f20b"; }
+.bi-calendar2::before { content: "\f20c"; }
+.bi-calendar3-event-fill::before { content: "\f20d"; }
+.bi-calendar3-event::before { content: "\f20e"; }
+.bi-calendar3-fill::before { content: "\f20f"; }
+.bi-calendar3-range-fill::before { content: "\f210"; }
+.bi-calendar3-range::before { content: "\f211"; }
+.bi-calendar3-week-fill::before { content: "\f212"; }
+.bi-calendar3-week::before { content: "\f213"; }
+.bi-calendar3::before { content: "\f214"; }
+.bi-calendar4-event::before { content: "\f215"; }
+.bi-calendar4-range::before { content: "\f216"; }
+.bi-calendar4-week::before { content: "\f217"; }
+.bi-calendar4::before { content: "\f218"; }
+.bi-camera-fill::before { content: "\f219"; }
+.bi-camera-reels-fill::before { content: "\f21a"; }
+.bi-camera-reels::before { content: "\f21b"; }
+.bi-camera-video-fill::before { content: "\f21c"; }
+.bi-camera-video-off-fill::before { content: "\f21d"; }
+.bi-camera-video-off::before { content: "\f21e"; }
+.bi-camera-video::before { content: "\f21f"; }
+.bi-camera::before { content: "\f220"; }
+.bi-camera2::before { content: "\f221"; }
+.bi-capslock-fill::before { content: "\f222"; }
+.bi-capslock::before { content: "\f223"; }
+.bi-card-checklist::before { content: "\f224"; }
+.bi-card-heading::before { content: "\f225"; }
+.bi-card-image::before { content: "\f226"; }
+.bi-card-list::before { content: "\f227"; }
+.bi-card-text::before { content: "\f228"; }
+.bi-caret-down-fill::before { content: "\f229"; }
+.bi-caret-down-square-fill::before { content: "\f22a"; }
+.bi-caret-down-square::before { content: "\f22b"; }
+.bi-caret-down::before { content: "\f22c"; }
+.bi-caret-left-fill::before { content: "\f22d"; }
+.bi-caret-left-square-fill::before { content: "\f22e"; }
+.bi-caret-left-square::before { content: "\f22f"; }
+.bi-caret-left::before { content: "\f230"; }
+.bi-caret-right-fill::before { content: "\f231"; }
+.bi-caret-right-square-fill::before { content: "\f232"; }
+.bi-caret-right-square::before { content: "\f233"; }
+.bi-caret-right::before { content: "\f234"; }
+.bi-caret-up-fill::before { content: "\f235"; }
+.bi-caret-up-square-fill::before { content: "\f236"; }
+.bi-caret-up-square::before { content: "\f237"; }
+.bi-caret-up::before { content: "\f238"; }
+.bi-cart-check-fill::before { content: "\f239"; }
+.bi-cart-check::before { content: "\f23a"; }
+.bi-cart-dash-fill::before { content: "\f23b"; }
+.bi-cart-dash::before { content: "\f23c"; }
+.bi-cart-fill::before { content: "\f23d"; }
+.bi-cart-plus-fill::before { content: "\f23e"; }
+.bi-cart-plus::before { content: "\f23f"; }
+.bi-cart-x-fill::before { content: "\f240"; }
+.bi-cart-x::before { content: "\f241"; }
+.bi-cart::before { content: "\f242"; }
+.bi-cart2::before { content: "\f243"; }
+.bi-cart3::before { content: "\f244"; }
+.bi-cart4::before { content: "\f245"; }
+.bi-cash-stack::before { content: "\f246"; }
+.bi-cash::before { content: "\f247"; }
+.bi-cast::before { content: "\f248"; }
+.bi-chat-dots-fill::before { content: "\f249"; }
+.bi-chat-dots::before { content: "\f24a"; }
+.bi-chat-fill::before { content: "\f24b"; }
+.bi-chat-left-dots-fill::before { content: "\f24c"; }
+.bi-chat-left-dots::before { content: "\f24d"; }
+.bi-chat-left-fill::before { content: "\f24e"; }
+.bi-chat-left-quote-fill::before { content: "\f24f"; }
+.bi-chat-left-quote::before { content: "\f250"; }
+.bi-chat-left-text-fill::before { content: "\f251"; }
+.bi-chat-left-text::before { content: "\f252"; }
+.bi-chat-left::before { content: "\f253"; }
+.bi-chat-quote-fill::before { content: "\f254"; }
+.bi-chat-quote::before { content: "\f255"; }
+.bi-chat-right-dots-fill::before { content: "\f256"; }
+.bi-chat-right-dots::before { content: "\f257"; }
+.bi-chat-right-fill::before { content: "\f258"; }
+.bi-chat-right-quote-fill::before { content: "\f259"; }
+.bi-chat-right-quote::before { content: "\f25a"; }
+.bi-chat-right-text-fill::before { content: "\f25b"; }
+.bi-chat-right-text::before { content: "\f25c"; }
+.bi-chat-right::before { content: "\f25d"; }
+.bi-chat-square-dots-fill::before { content: "\f25e"; }
+.bi-chat-square-dots::before { content: "\f25f"; }
+.bi-chat-square-fill::before { content: "\f260"; }
+.bi-chat-square-quote-fill::before { content: "\f261"; }
+.bi-chat-square-quote::before { content: "\f262"; }
+.bi-chat-square-text-fill::before { content: "\f263"; }
+.bi-chat-square-text::before { content: "\f264"; }
+.bi-chat-square::before { content: "\f265"; }
+.bi-chat-text-fill::before { content: "\f266"; }
+.bi-chat-text::before { content: "\f267"; }
+.bi-chat::before { content: "\f268"; }
+.bi-check-all::before { content: "\f269"; }
+.bi-check-circle-fill::before { content: "\f26a"; }
+.bi-check-circle::before { content: "\f26b"; }
+.bi-check-square-fill::before { content: "\f26c"; }
+.bi-check-square::before { content: "\f26d"; }
+.bi-check::before { content: "\f26e"; }
+.bi-check2-all::before { content: "\f26f"; }
+.bi-check2-circle::before { content: "\f270"; }
+.bi-check2-square::before { content: "\f271"; }
+.bi-check2::before { content: "\f272"; }
+.bi-chevron-bar-contract::before { content: "\f273"; }
+.bi-chevron-bar-down::before { content: "\f274"; }
+.bi-chevron-bar-expand::before { content: "\f275"; }
+.bi-chevron-bar-left::before { content: "\f276"; }
+.bi-chevron-bar-right::before { content: "\f277"; }
+.bi-chevron-bar-up::before { content: "\f278"; }
+.bi-chevron-compact-down::before { content: "\f279"; }
+.bi-chevron-compact-left::before { content: "\f27a"; }
+.bi-chevron-compact-right::before { content: "\f27b"; }
+.bi-chevron-compact-up::before { content: "\f27c"; }
+.bi-chevron-contract::before { content: "\f27d"; }
+.bi-chevron-double-down::before { content: "\f27e"; }
+.bi-chevron-double-left::before { content: "\f27f"; }
+.bi-chevron-double-right::before { content: "\f280"; }
+.bi-chevron-double-up::before { content: "\f281"; }
+.bi-chevron-down::before { content: "\f282"; }
+.bi-chevron-expand::before { content: "\f283"; }
+.bi-chevron-left::before { content: "\f284"; }
+.bi-chevron-right::before { content: "\f285"; }
+.bi-chevron-up::before { content: "\f286"; }
+.bi-circle-fill::before { content: "\f287"; }
+.bi-circle-half::before { content: "\f288"; }
+.bi-circle-square::before { content: "\f289"; }
+.bi-circle::before { content: "\f28a"; }
+.bi-clipboard-check::before { content: "\f28b"; }
+.bi-clipboard-data::before { content: "\f28c"; }
+.bi-clipboard-minus::before { content: "\f28d"; }
+.bi-clipboard-plus::before { content: "\f28e"; }
+.bi-clipboard-x::before { content: "\f28f"; }
+.bi-clipboard::before { content: "\f290"; }
+.bi-clock-fill::before { content: "\f291"; }
+.bi-clock-history::before { content: "\f292"; }
+.bi-clock::before { content: "\f293"; }
+.bi-cloud-arrow-down-fill::before { content: "\f294"; }
+.bi-cloud-arrow-down::before { content: "\f295"; }
+.bi-cloud-arrow-up-fill::before { content: "\f296"; }
+.bi-cloud-arrow-up::before { content: "\f297"; }
+.bi-cloud-check-fill::before { content: "\f298"; }
+.bi-cloud-check::before { content: "\f299"; }
+.bi-cloud-download-fill::before { content: "\f29a"; }
+.bi-cloud-download::before { content: "\f29b"; }
+.bi-cloud-drizzle-fill::before { content: "\f29c"; }
+.bi-cloud-drizzle::before { content: "\f29d"; }
+.bi-cloud-fill::before { content: "\f29e"; }
+.bi-cloud-fog-fill::before { content: "\f29f"; }
+.bi-cloud-fog::before { content: "\f2a0"; }
+.bi-cloud-fog2-fill::before { content: "\f2a1"; }
+.bi-cloud-fog2::before { content: "\f2a2"; }
+.bi-cloud-hail-fill::before { content: "\f2a3"; }
+.bi-cloud-hail::before { content: "\f2a4"; }
+.bi-cloud-haze-1::before { content: "\f2a5"; }
+.bi-cloud-haze-fill::before { content: "\f2a6"; }
+.bi-cloud-haze::before { content: "\f2a7"; }
+.bi-cloud-haze2-fill::before { content: "\f2a8"; }
+.bi-cloud-lightning-fill::before { content: "\f2a9"; }
+.bi-cloud-lightning-rain-fill::before { content: "\f2aa"; }
+.bi-cloud-lightning-rain::before { content: "\f2ab"; }
+.bi-cloud-lightning::before { content: "\f2ac"; }
+.bi-cloud-minus-fill::before { content: "\f2ad"; }
+.bi-cloud-minus::before { content: "\f2ae"; }
+.bi-cloud-moon-fill::before { content: "\f2af"; }
+.bi-cloud-moon::before { content: "\f2b0"; }
+.bi-cloud-plus-fill::before { content: "\f2b1"; }
+.bi-cloud-plus::before { content: "\f2b2"; }
+.bi-cloud-rain-fill::before { content: "\f2b3"; }
+.bi-cloud-rain-heavy-fill::before { content: "\f2b4"; }
+.bi-cloud-rain-heavy::before { content: "\f2b5"; }
+.bi-cloud-rain::before { content: "\f2b6"; }
+.bi-cloud-slash-fill::before { content: "\f2b7"; }
+.bi-cloud-slash::before { content: "\f2b8"; }
+.bi-cloud-sleet-fill::before { content: "\f2b9"; }
+.bi-cloud-sleet::before { content: "\f2ba"; }
+.bi-cloud-snow-fill::before { content: "\f2bb"; }
+.bi-cloud-snow::before { content: "\f2bc"; }
+.bi-cloud-sun-fill::before { content: "\f2bd"; }
+.bi-cloud-sun::before { content: "\f2be"; }
+.bi-cloud-upload-fill::before { content: "\f2bf"; }
+.bi-cloud-upload::before { content: "\f2c0"; }
+.bi-cloud::before { content: "\f2c1"; }
+.bi-clouds-fill::before { content: "\f2c2"; }
+.bi-clouds::before { content: "\f2c3"; }
+.bi-cloudy-fill::before { content: "\f2c4"; }
+.bi-cloudy::before { content: "\f2c5"; }
+.bi-code-slash::before { content: "\f2c6"; }
+.bi-code-square::before { content: "\f2c7"; }
+.bi-code::before { content: "\f2c8"; }
+.bi-collection-fill::before { content: "\f2c9"; }
+.bi-collection-play-fill::before { content: "\f2ca"; }
+.bi-collection-play::before { content: "\f2cb"; }
+.bi-collection::before { content: "\f2cc"; }
+.bi-columns-gap::before { content: "\f2cd"; }
+.bi-columns::before { content: "\f2ce"; }
+.bi-command::before { content: "\f2cf"; }
+.bi-compass-fill::before { content: "\f2d0"; }
+.bi-compass::before { content: "\f2d1"; }
+.bi-cone-striped::before { content: "\f2d2"; }
+.bi-cone::before { content: "\f2d3"; }
+.bi-controller::before { content: "\f2d4"; }
+.bi-cpu-fill::before { content: "\f2d5"; }
+.bi-cpu::before { content: "\f2d6"; }
+.bi-credit-card-2-back-fill::before { content: "\f2d7"; }
+.bi-credit-card-2-back::before { content: "\f2d8"; }
+.bi-credit-card-2-front-fill::before { content: "\f2d9"; }
+.bi-credit-card-2-front::before { content: "\f2da"; }
+.bi-credit-card-fill::before { content: "\f2db"; }
+.bi-credit-card::before { content: "\f2dc"; }
+.bi-crop::before { content: "\f2dd"; }
+.bi-cup-fill::before { content: "\f2de"; }
+.bi-cup-straw::before { content: "\f2df"; }
+.bi-cup::before { content: "\f2e0"; }
+.bi-cursor-fill::before { content: "\f2e1"; }
+.bi-cursor-text::before { content: "\f2e2"; }
+.bi-cursor::before { content: "\f2e3"; }
+.bi-dash-circle-dotted::before { content: "\f2e4"; }
+.bi-dash-circle-fill::before { content: "\f2e5"; }
+.bi-dash-circle::before { content: "\f2e6"; }
+.bi-dash-square-dotted::before { content: "\f2e7"; }
+.bi-dash-square-fill::before { content: "\f2e8"; }
+.bi-dash-square::before { content: "\f2e9"; }
+.bi-dash::before { content: "\f2ea"; }
+.bi-diagram-2-fill::before { content: "\f2eb"; }
+.bi-diagram-2::before { content: "\f2ec"; }
+.bi-diagram-3-fill::before { content: "\f2ed"; }
+.bi-diagram-3::before { content: "\f2ee"; }
+.bi-diamond-fill::before { content: "\f2ef"; }
+.bi-diamond-half::before { content: "\f2f0"; }
+.bi-diamond::before { content: "\f2f1"; }
+.bi-dice-1-fill::before { content: "\f2f2"; }
+.bi-dice-1::before { content: "\f2f3"; }
+.bi-dice-2-fill::before { content: "\f2f4"; }
+.bi-dice-2::before { content: "\f2f5"; }
+.bi-dice-3-fill::before { content: "\f2f6"; }
+.bi-dice-3::before { content: "\f2f7"; }
+.bi-dice-4-fill::before { content: "\f2f8"; }
+.bi-dice-4::before { content: "\f2f9"; }
+.bi-dice-5-fill::before { content: "\f2fa"; }
+.bi-dice-5::before { content: "\f2fb"; }
+.bi-dice-6-fill::before { content: "\f2fc"; }
+.bi-dice-6::before { content: "\f2fd"; }
+.bi-disc-fill::before { content: "\f2fe"; }
+.bi-disc::before { content: "\f2ff"; }
+.bi-discord::before { content: "\f300"; }
+.bi-display-fill::before { content: "\f301"; }
+.bi-display::before { content: "\f302"; }
+.bi-distribute-horizontal::before { content: "\f303"; }
+.bi-distribute-vertical::before { content: "\f304"; }
+.bi-door-closed-fill::before { content: "\f305"; }
+.bi-door-closed::before { content: "\f306"; }
+.bi-door-open-fill::before { content: "\f307"; }
+.bi-door-open::before { content: "\f308"; }
+.bi-dot::before { content: "\f309"; }
+.bi-download::before { content: "\f30a"; }
+.bi-droplet-fill::before { content: "\f30b"; }
+.bi-droplet-half::before { content: "\f30c"; }
+.bi-droplet::before { content: "\f30d"; }
+.bi-earbuds::before { content: "\f30e"; }
+.bi-easel-fill::before { content: "\f30f"; }
+.bi-easel::before { content: "\f310"; }
+.bi-egg-fill::before { content: "\f311"; }
+.bi-egg-fried::before { content: "\f312"; }
+.bi-egg::before { content: "\f313"; }
+.bi-eject-fill::before { content: "\f314"; }
+.bi-eject::before { content: "\f315"; }
+.bi-emoji-angry-fill::before { content: "\f316"; }
+.bi-emoji-angry::before { content: "\f317"; }
+.bi-emoji-dizzy-fill::before { content: "\f318"; }
+.bi-emoji-dizzy::before { content: "\f319"; }
+.bi-emoji-expressionless-fill::before { content: "\f31a"; }
+.bi-emoji-expressionless::before { content: "\f31b"; }
+.bi-emoji-frown-fill::before { content: "\f31c"; }
+.bi-emoji-frown::before { content: "\f31d"; }
+.bi-emoji-heart-eyes-fill::before { content: "\f31e"; }
+.bi-emoji-heart-eyes::before { content: "\f31f"; }
+.bi-emoji-laughing-fill::before { content: "\f320"; }
+.bi-emoji-laughing::before { content: "\f321"; }
+.bi-emoji-neutral-fill::before { content: "\f322"; }
+.bi-emoji-neutral::before { content: "\f323"; }
+.bi-emoji-smile-fill::before { content: "\f324"; }
+.bi-emoji-smile-upside-down-fill::before { content: "\f325"; }
+.bi-emoji-smile-upside-down::before { content: "\f326"; }
+.bi-emoji-smile::before { content: "\f327"; }
+.bi-emoji-sunglasses-fill::before { content: "\f328"; }
+.bi-emoji-sunglasses::before { content: "\f329"; }
+.bi-emoji-wink-fill::before { content: "\f32a"; }
+.bi-emoji-wink::before { content: "\f32b"; }
+.bi-envelope-fill::before { content: "\f32c"; }
+.bi-envelope-open-fill::before { content: "\f32d"; }
+.bi-envelope-open::before { content: "\f32e"; }
+.bi-envelope::before { content: "\f32f"; }
+.bi-eraser-fill::before { content: "\f330"; }
+.bi-eraser::before { content: "\f331"; }
+.bi-exclamation-circle-fill::before { content: "\f332"; }
+.bi-exclamation-circle::before { content: "\f333"; }
+.bi-exclamation-diamond-fill::before { content: "\f334"; }
+.bi-exclamation-diamond::before { content: "\f335"; }
+.bi-exclamation-octagon-fill::before { content: "\f336"; }
+.bi-exclamation-octagon::before { content: "\f337"; }
+.bi-exclamation-square-fill::before { content: "\f338"; }
+.bi-exclamation-square::before { content: "\f339"; }
+.bi-exclamation-triangle-fill::before { content: "\f33a"; }
+.bi-exclamation-triangle::before { content: "\f33b"; }
+.bi-exclamation::before { content: "\f33c"; }
+.bi-exclude::before { content: "\f33d"; }
+.bi-eye-fill::before { content: "\f33e"; }
+.bi-eye-slash-fill::before { content: "\f33f"; }
+.bi-eye-slash::before { content: "\f340"; }
+.bi-eye::before { content: "\f341"; }
+.bi-eyedropper::before { content: "\f342"; }
+.bi-eyeglasses::before { content: "\f343"; }
+.bi-facebook::before { content: "\f344"; }
+.bi-file-arrow-down-fill::before { content: "\f345"; }
+.bi-file-arrow-down::before { content: "\f346"; }
+.bi-file-arrow-up-fill::before { content: "\f347"; }
+.bi-file-arrow-up::before { content: "\f348"; }
+.bi-file-bar-graph-fill::before { content: "\f349"; }
+.bi-file-bar-graph::before { content: "\f34a"; }
+.bi-file-binary-fill::before { content: "\f34b"; }
+.bi-file-binary::before { content: "\f34c"; }
+.bi-file-break-fill::before { content: "\f34d"; }
+.bi-file-break::before { content: "\f34e"; }
+.bi-file-check-fill::before { content: "\f34f"; }
+.bi-file-check::before { content: "\f350"; }
+.bi-file-code-fill::before { content: "\f351"; }
+.bi-file-code::before { content: "\f352"; }
+.bi-file-diff-fill::before { content: "\f353"; }
+.bi-file-diff::before { content: "\f354"; }
+.bi-file-earmark-arrow-down-fill::before { content: "\f355"; }
+.bi-file-earmark-arrow-down::before { content: "\f356"; }
+.bi-file-earmark-arrow-up-fill::before { content: "\f357"; }
+.bi-file-earmark-arrow-up::before { content: "\f358"; }
+.bi-file-earmark-bar-graph-fill::before { content: "\f359"; }
+.bi-file-earmark-bar-graph::before { content: "\f35a"; }
+.bi-file-earmark-binary-fill::before { content: "\f35b"; }
+.bi-file-earmark-binary::before { content: "\f35c"; }
+.bi-file-earmark-break-fill::before { content: "\f35d"; }
+.bi-file-earmark-break::before { content: "\f35e"; }
+.bi-file-earmark-check-fill::before { content: "\f35f"; }
+.bi-file-earmark-check::before { content: "\f360"; }
+.bi-file-earmark-code-fill::before { content: "\f361"; }
+.bi-file-earmark-code::before { content: "\f362"; }
+.bi-file-earmark-diff-fill::before { content: "\f363"; }
+.bi-file-earmark-diff::before { content: "\f364"; }
+.bi-file-earmark-easel-fill::before { content: "\f365"; }
+.bi-file-earmark-easel::before { content: "\f366"; }
+.bi-file-earmark-excel-fill::before { content: "\f367"; }
+.bi-file-earmark-excel::before { content: "\f368"; }
+.bi-file-earmark-fill::before { content: "\f369"; }
+.bi-file-earmark-font-fill::before { content: "\f36a"; }
+.bi-file-earmark-font::before { content: "\f36b"; }
+.bi-file-earmark-image-fill::before { content: "\f36c"; }
+.bi-file-earmark-image::before { content: "\f36d"; }
+.bi-file-earmark-lock-fill::before { content: "\f36e"; }
+.bi-file-earmark-lock::before { content: "\f36f"; }
+.bi-file-earmark-lock2-fill::before { content: "\f370"; }
+.bi-file-earmark-lock2::before { content: "\f371"; }
+.bi-file-earmark-medical-fill::before { content: "\f372"; }
+.bi-file-earmark-medical::before { content: "\f373"; }
+.bi-file-earmark-minus-fill::before { content: "\f374"; }
+.bi-file-earmark-minus::before { content: "\f375"; }
+.bi-file-earmark-music-fill::before { content: "\f376"; }
+.bi-file-earmark-music::before { content: "\f377"; }
+.bi-file-earmark-person-fill::before { content: "\f378"; }
+.bi-file-earmark-person::before { content: "\f379"; }
+.bi-file-earmark-play-fill::before { content: "\f37a"; }
+.bi-file-earmark-play::before { content: "\f37b"; }
+.bi-file-earmark-plus-fill::before { content: "\f37c"; }
+.bi-file-earmark-plus::before { content: "\f37d"; }
+.bi-file-earmark-post-fill::before { content: "\f37e"; }
+.bi-file-earmark-post::before { content: "\f37f"; }
+.bi-file-earmark-ppt-fill::before { content: "\f380"; }
+.bi-file-earmark-ppt::before { content: "\f381"; }
+.bi-file-earmark-richtext-fill::before { content: "\f382"; }
+.bi-file-earmark-richtext::before { content: "\f383"; }
+.bi-file-earmark-ruled-fill::before { content: "\f384"; }
+.bi-file-earmark-ruled::before { content: "\f385"; }
+.bi-file-earmark-slides-fill::before { content: "\f386"; }
+.bi-file-earmark-slides::before { content: "\f387"; }
+.bi-file-earmark-spreadsheet-fill::before { content: "\f388"; }
+.bi-file-earmark-spreadsheet::before { content: "\f389"; }
+.bi-file-earmark-text-fill::before { content: "\f38a"; }
+.bi-file-earmark-text::before { content: "\f38b"; }
+.bi-file-earmark-word-fill::before { content: "\f38c"; }
+.bi-file-earmark-word::before { content: "\f38d"; }
+.bi-file-earmark-x-fill::before { content: "\f38e"; }
+.bi-file-earmark-x::before { content: "\f38f"; }
+.bi-file-earmark-zip-fill::before { content: "\f390"; }
+.bi-file-earmark-zip::before { content: "\f391"; }
+.bi-file-earmark::before { content: "\f392"; }
+.bi-file-easel-fill::before { content: "\f393"; }
+.bi-file-easel::before { content: "\f394"; }
+.bi-file-excel-fill::before { content: "\f395"; }
+.bi-file-excel::before { content: "\f396"; }
+.bi-file-fill::before { content: "\f397"; }
+.bi-file-font-fill::before { content: "\f398"; }
+.bi-file-font::before { content: "\f399"; }
+.bi-file-image-fill::before { content: "\f39a"; }
+.bi-file-image::before { content: "\f39b"; }
+.bi-file-lock-fill::before { content: "\f39c"; }
+.bi-file-lock::before { content: "\f39d"; }
+.bi-file-lock2-fill::before { content: "\f39e"; }
+.bi-file-lock2::before { content: "\f39f"; }
+.bi-file-medical-fill::before { content: "\f3a0"; }
+.bi-file-medical::before { content: "\f3a1"; }
+.bi-file-minus-fill::before { content: "\f3a2"; }
+.bi-file-minus::before { content: "\f3a3"; }
+.bi-file-music-fill::before { content: "\f3a4"; }
+.bi-file-music::before { content: "\f3a5"; }
+.bi-file-person-fill::before { content: "\f3a6"; }
+.bi-file-person::before { content: "\f3a7"; }
+.bi-file-play-fill::before { content: "\f3a8"; }
+.bi-file-play::before { content: "\f3a9"; }
+.bi-file-plus-fill::before { content: "\f3aa"; }
+.bi-file-plus::before { content: "\f3ab"; }
+.bi-file-post-fill::before { content: "\f3ac"; }
+.bi-file-post::before { content: "\f3ad"; }
+.bi-file-ppt-fill::before { content: "\f3ae"; }
+.bi-file-ppt::before { content: "\f3af"; }
+.bi-file-richtext-fill::before { content: "\f3b0"; }
+.bi-file-richtext::before { content: "\f3b1"; }
+.bi-file-ruled-fill::before { content: "\f3b2"; }
+.bi-file-ruled::before { content: "\f3b3"; }
+.bi-file-slides-fill::before { content: "\f3b4"; }
+.bi-file-slides::before { content: "\f3b5"; }
+.bi-file-spreadsheet-fill::before { content: "\f3b6"; }
+.bi-file-spreadsheet::before { content: "\f3b7"; }
+.bi-file-text-fill::before { content: "\f3b8"; }
+.bi-file-text::before { content: "\f3b9"; }
+.bi-file-word-fill::before { content: "\f3ba"; }
+.bi-file-word::before { content: "\f3bb"; }
+.bi-file-x-fill::before { content: "\f3bc"; }
+.bi-file-x::before { content: "\f3bd"; }
+.bi-file-zip-fill::before { content: "\f3be"; }
+.bi-file-zip::before { content: "\f3bf"; }
+.bi-file::before { content: "\f3c0"; }
+.bi-files-alt::before { content: "\f3c1"; }
+.bi-files::before { content: "\f3c2"; }
+.bi-film::before { content: "\f3c3"; }
+.bi-filter-circle-fill::before { content: "\f3c4"; }
+.bi-filter-circle::before { content: "\f3c5"; }
+.bi-filter-left::before { content: "\f3c6"; }
+.bi-filter-right::before { content: "\f3c7"; }
+.bi-filter-square-fill::before { content: "\f3c8"; }
+.bi-filter-square::before { content: "\f3c9"; }
+.bi-filter::before { content: "\f3ca"; }
+.bi-flag-fill::before { content: "\f3cb"; }
+.bi-flag::before { content: "\f3cc"; }
+.bi-flower1::before { content: "\f3cd"; }
+.bi-flower2::before { content: "\f3ce"; }
+.bi-flower3::before { content: "\f3cf"; }
+.bi-folder-check::before { content: "\f3d0"; }
+.bi-folder-fill::before { content: "\f3d1"; }
+.bi-folder-minus::before { content: "\f3d2"; }
+.bi-folder-plus::before { content: "\f3d3"; }
+.bi-folder-symlink-fill::before { content: "\f3d4"; }
+.bi-folder-symlink::before { content: "\f3d5"; }
+.bi-folder-x::before { content: "\f3d6"; }
+.bi-folder::before { content: "\f3d7"; }
+.bi-folder2-open::before { content: "\f3d8"; }
+.bi-folder2::before { content: "\f3d9"; }
+.bi-fonts::before { content: "\f3da"; }
+.bi-forward-fill::before { content: "\f3db"; }
+.bi-forward::before { content: "\f3dc"; }
+.bi-front::before { content: "\f3dd"; }
+.bi-fullscreen-exit::before { content: "\f3de"; }
+.bi-fullscreen::before { content: "\f3df"; }
+.bi-funnel-fill::before { content: "\f3e0"; }
+.bi-funnel::before { content: "\f3e1"; }
+.bi-gear-fill::before { content: "\f3e2"; }
+.bi-gear-wide-connected::before { content: "\f3e3"; }
+.bi-gear-wide::before { content: "\f3e4"; }
+.bi-gear::before { content: "\f3e5"; }
+.bi-gem::before { content: "\f3e6"; }
+.bi-geo-alt-fill::before { content: "\f3e7"; }
+.bi-geo-alt::before { content: "\f3e8"; }
+.bi-geo-fill::before { content: "\f3e9"; }
+.bi-geo::before { content: "\f3ea"; }
+.bi-gift-fill::before { content: "\f3eb"; }
+.bi-gift::before { content: "\f3ec"; }
+.bi-github::before { content: "\f3ed"; }
+.bi-globe::before { content: "\f3ee"; }
+.bi-globe2::before { content: "\f3ef"; }
+.bi-google::before { content: "\f3f0"; }
+.bi-graph-down::before { content: "\f3f1"; }
+.bi-graph-up::before { content: "\f3f2"; }
+.bi-grid-1x2-fill::before { content: "\f3f3"; }
+.bi-grid-1x2::before { content: "\f3f4"; }
+.bi-grid-3x2-gap-fill::before { content: "\f3f5"; }
+.bi-grid-3x2-gap::before { content: "\f3f6"; }
+.bi-grid-3x2::before { content: "\f3f7"; }
+.bi-grid-3x3-gap-fill::before { content: "\f3f8"; }
+.bi-grid-3x3-gap::before { content: "\f3f9"; }
+.bi-grid-3x3::before { content: "\f3fa"; }
+.bi-grid-fill::before { content: "\f3fb"; }
+.bi-grid::before { content: "\f3fc"; }
+.bi-grip-horizontal::before { content: "\f3fd"; }
+.bi-grip-vertical::before { content: "\f3fe"; }
+.bi-hammer::before { content: "\f3ff"; }
+.bi-hand-index-fill::before { content: "\f400"; }
+.bi-hand-index-thumb-fill::before { content: "\f401"; }
+.bi-hand-index-thumb::before { content: "\f402"; }
+.bi-hand-index::before { content: "\f403"; }
+.bi-hand-thumbs-down-fill::before { content: "\f404"; }
+.bi-hand-thumbs-down::before { content: "\f405"; }
+.bi-hand-thumbs-up-fill::before { content: "\f406"; }
+.bi-hand-thumbs-up::before { content: "\f407"; }
+.bi-handbag-fill::before { content: "\f408"; }
+.bi-handbag::before { content: "\f409"; }
+.bi-hash::before { content: "\f40a"; }
+.bi-hdd-fill::before { content: "\f40b"; }
+.bi-hdd-network-fill::before { content: "\f40c"; }
+.bi-hdd-network::before { content: "\f40d"; }
+.bi-hdd-rack-fill::before { content: "\f40e"; }
+.bi-hdd-rack::before { content: "\f40f"; }
+.bi-hdd-stack-fill::before { content: "\f410"; }
+.bi-hdd-stack::before { content: "\f411"; }
+.bi-hdd::before { content: "\f412"; }
+.bi-headphones::before { content: "\f413"; }
+.bi-headset::before { content: "\f414"; }
+.bi-heart-fill::before { content: "\f415"; }
+.bi-heart-half::before { content: "\f416"; }
+.bi-heart::before { content: "\f417"; }
+.bi-heptagon-fill::before { content: "\f418"; }
+.bi-heptagon-half::before { content: "\f419"; }
+.bi-heptagon::before { content: "\f41a"; }
+.bi-hexagon-fill::before { content: "\f41b"; }
+.bi-hexagon-half::before { content: "\f41c"; }
+.bi-hexagon::before { content: "\f41d"; }
+.bi-hourglass-bottom::before { content: "\f41e"; }
+.bi-hourglass-split::before { content: "\f41f"; }
+.bi-hourglass-top::before { content: "\f420"; }
+.bi-hourglass::before { content: "\f421"; }
+.bi-house-door-fill::before { content: "\f422"; }
+.bi-house-door::before { content: "\f423"; }
+.bi-house-fill::before { content: "\f424"; }
+.bi-house::before { content: "\f425"; }
+.bi-hr::before { content: "\f426"; }
+.bi-hurricane::before { content: "\f427"; }
+.bi-image-alt::before { content: "\f428"; }
+.bi-image-fill::before { content: "\f429"; }
+.bi-image::before { content: "\f42a"; }
+.bi-images::before { content: "\f42b"; }
+.bi-inbox-fill::before { content: "\f42c"; }
+.bi-inbox::before { content: "\f42d"; }
+.bi-inboxes-fill::before { content: "\f42e"; }
+.bi-inboxes::before { content: "\f42f"; }
+.bi-info-circle-fill::before { content: "\f430"; }
+.bi-info-circle::before { content: "\f431"; }
+.bi-info-square-fill::before { content: "\f432"; }
+.bi-info-square::before { content: "\f433"; }
+.bi-info::before { content: "\f434"; }
+.bi-input-cursor-text::before { content: "\f435"; }
+.bi-input-cursor::before { content: "\f436"; }
+.bi-instagram::before { content: "\f437"; }
+.bi-intersect::before { content: "\f438"; }
+.bi-journal-album::before { content: "\f439"; }
+.bi-journal-arrow-down::before { content: "\f43a"; }
+.bi-journal-arrow-up::before { content: "\f43b"; }
+.bi-journal-bookmark-fill::before { content: "\f43c"; }
+.bi-journal-bookmark::before { content: "\f43d"; }
+.bi-journal-check::before { content: "\f43e"; }
+.bi-journal-code::before { content: "\f43f"; }
+.bi-journal-medical::before { content: "\f440"; }
+.bi-journal-minus::before { content: "\f441"; }
+.bi-journal-plus::before { content: "\f442"; }
+.bi-journal-richtext::before { content: "\f443"; }
+.bi-journal-text::before { content: "\f444"; }
+.bi-journal-x::before { content: "\f445"; }
+.bi-journal::before { content: "\f446"; }
+.bi-journals::before { content: "\f447"; }
+.bi-joystick::before { content: "\f448"; }
+.bi-justify-left::before { content: "\f449"; }
+.bi-justify-right::before { content: "\f44a"; }
+.bi-justify::before { content: "\f44b"; }
+.bi-kanban-fill::before { content: "\f44c"; }
+.bi-kanban::before { content: "\f44d"; }
+.bi-key-fill::before { content: "\f44e"; }
+.bi-key::before { content: "\f44f"; }
+.bi-keyboard-fill::before { content: "\f450"; }
+.bi-keyboard::before { content: "\f451"; }
+.bi-ladder::before { content: "\f452"; }
+.bi-lamp-fill::before { content: "\f453"; }
+.bi-lamp::before { content: "\f454"; }
+.bi-laptop-fill::before { content: "\f455"; }
+.bi-laptop::before { content: "\f456"; }
+.bi-layer-backward::before { content: "\f457"; }
+.bi-layer-forward::before { content: "\f458"; }
+.bi-layers-fill::before { content: "\f459"; }
+.bi-layers-half::before { content: "\f45a"; }
+.bi-layers::before { content: "\f45b"; }
+.bi-layout-sidebar-inset-reverse::before { content: "\f45c"; }
+.bi-layout-sidebar-inset::before { content: "\f45d"; }
+.bi-layout-sidebar-reverse::before { content: "\f45e"; }
+.bi-layout-sidebar::before { content: "\f45f"; }
+.bi-layout-split::before { content: "\f460"; }
+.bi-layout-text-sidebar-reverse::before { content: "\f461"; }
+.bi-layout-text-sidebar::before { content: "\f462"; }
+.bi-layout-text-window-reverse::before { content: "\f463"; }
+.bi-layout-text-window::before { content: "\f464"; }
+.bi-layout-three-columns::before { content: "\f465"; }
+.bi-layout-wtf::before { content: "\f466"; }
+.bi-life-preserver::before { content: "\f467"; }
+.bi-lightbulb-fill::before { content: "\f468"; }
+.bi-lightbulb-off-fill::before { content: "\f469"; }
+.bi-lightbulb-off::before { content: "\f46a"; }
+.bi-lightbulb::before { content: "\f46b"; }
+.bi-lightning-charge-fill::before { content: "\f46c"; }
+.bi-lightning-charge::before { content: "\f46d"; }
+.bi-lightning-fill::before { content: "\f46e"; }
+.bi-lightning::before { content: "\f46f"; }
+.bi-link-45deg::before { content: "\f470"; }
+.bi-link::before { content: "\f471"; }
+.bi-linkedin::before { content: "\f472"; }
+.bi-list-check::before { content: "\f473"; }
+.bi-list-nested::before { content: "\f474"; }
+.bi-list-ol::before { content: "\f475"; }
+.bi-list-stars::before { content: "\f476"; }
+.bi-list-task::before { content: "\f477"; }
+.bi-list-ul::before { content: "\f478"; }
+.bi-list::before { content: "\f479"; }
+.bi-lock-fill::before { content: "\f47a"; }
+.bi-lock::before { content: "\f47b"; }
+.bi-mailbox::before { content: "\f47c"; }
+.bi-mailbox2::before { content: "\f47d"; }
+.bi-map-fill::before { content: "\f47e"; }
+.bi-map::before { content: "\f47f"; }
+.bi-markdown-fill::before { content: "\f480"; }
+.bi-markdown::before { content: "\f481"; }
+.bi-mask::before { content: "\f482"; }
+.bi-megaphone-fill::before { content: "\f483"; }
+.bi-megaphone::before { content: "\f484"; }
+.bi-menu-app-fill::before { content: "\f485"; }
+.bi-menu-app::before { content: "\f486"; }
+.bi-menu-button-fill::before { content: "\f487"; }
+.bi-menu-button-wide-fill::before { content: "\f488"; }
+.bi-menu-button-wide::before { content: "\f489"; }
+.bi-menu-button::before { content: "\f48a"; }
+.bi-menu-down::before { content: "\f48b"; }
+.bi-menu-up::before { content: "\f48c"; }
+.bi-mic-fill::before { content: "\f48d"; }
+.bi-mic-mute-fill::before { content: "\f48e"; }
+.bi-mic-mute::before { content: "\f48f"; }
+.bi-mic::before { content: "\f490"; }
+.bi-minecart-loaded::before { content: "\f491"; }
+.bi-minecart::before { content: "\f492"; }
+.bi-moisture::before { content: "\f493"; }
+.bi-moon-fill::before { content: "\f494"; }
+.bi-moon-stars-fill::before { content: "\f495"; }
+.bi-moon-stars::before { content: "\f496"; }
+.bi-moon::before { content: "\f497"; }
+.bi-mouse-fill::before { content: "\f498"; }
+.bi-mouse::before { content: "\f499"; }
+.bi-mouse2-fill::before { content: "\f49a"; }
+.bi-mouse2::before { content: "\f49b"; }
+.bi-mouse3-fill::before { content: "\f49c"; }
+.bi-mouse3::before { content: "\f49d"; }
+.bi-music-note-beamed::before { content: "\f49e"; }
+.bi-music-note-list::before { content: "\f49f"; }
+.bi-music-note::before { content: "\f4a0"; }
+.bi-music-player-fill::before { content: "\f4a1"; }
+.bi-music-player::before { content: "\f4a2"; }
+.bi-newspaper::before { content: "\f4a3"; }
+.bi-node-minus-fill::before { content: "\f4a4"; }
+.bi-node-minus::before { content: "\f4a5"; }
+.bi-node-plus-fill::before { content: "\f4a6"; }
+.bi-node-plus::before { content: "\f4a7"; }
+.bi-nut-fill::before { content: "\f4a8"; }
+.bi-nut::before { content: "\f4a9"; }
+.bi-octagon-fill::before { content: "\f4aa"; }
+.bi-octagon-half::before { content: "\f4ab"; }
+.bi-octagon::before { content: "\f4ac"; }
+.bi-option::before { content: "\f4ad"; }
+.bi-outlet::before { content: "\f4ae"; }
+.bi-paint-bucket::before { content: "\f4af"; }
+.bi-palette-fill::before { content: "\f4b0"; }
+.bi-palette::before { content: "\f4b1"; }
+.bi-palette2::before { content: "\f4b2"; }
+.bi-paperclip::before { content: "\f4b3"; }
+.bi-paragraph::before { content: "\f4b4"; }
+.bi-patch-check-fill::before { content: "\f4b5"; }
+.bi-patch-check::before { content: "\f4b6"; }
+.bi-patch-exclamation-fill::before { content: "\f4b7"; }
+.bi-patch-exclamation::before { content: "\f4b8"; }
+.bi-patch-minus-fill::before { content: "\f4b9"; }
+.bi-patch-minus::before { content: "\f4ba"; }
+.bi-patch-plus-fill::before { content: "\f4bb"; }
+.bi-patch-plus::before { content: "\f4bc"; }
+.bi-patch-question-fill::before { content: "\f4bd"; }
+.bi-patch-question::before { content: "\f4be"; }
+.bi-pause-btn-fill::before { content: "\f4bf"; }
+.bi-pause-btn::before { content: "\f4c0"; }
+.bi-pause-circle-fill::before { content: "\f4c1"; }
+.bi-pause-circle::before { content: "\f4c2"; }
+.bi-pause-fill::before { content: "\f4c3"; }
+.bi-pause::before { content: "\f4c4"; }
+.bi-peace-fill::before { content: "\f4c5"; }
+.bi-peace::before { content: "\f4c6"; }
+.bi-pen-fill::before { content: "\f4c7"; }
+.bi-pen::before { content: "\f4c8"; }
+.bi-pencil-fill::before { content: "\f4c9"; }
+.bi-pencil-square::before { content: "\f4ca"; }
+.bi-pencil::before { content: "\f4cb"; }
+.bi-pentagon-fill::before { content: "\f4cc"; }
+.bi-pentagon-half::before { content: "\f4cd"; }
+.bi-pentagon::before { content: "\f4ce"; }
+.bi-people-fill::before { content: "\f4cf"; }
+.bi-people::before { content: "\f4d0"; }
+.bi-percent::before { content: "\f4d1"; }
+.bi-person-badge-fill::before { content: "\f4d2"; }
+.bi-person-badge::before { content: "\f4d3"; }
+.bi-person-bounding-box::before { content: "\f4d4"; }
+.bi-person-check-fill::before { content: "\f4d5"; }
+.bi-person-check::before { content: "\f4d6"; }
+.bi-person-circle::before { content: "\f4d7"; }
+.bi-person-dash-fill::before { content: "\f4d8"; }
+.bi-person-dash::before { content: "\f4d9"; }
+.bi-person-fill::before { content: "\f4da"; }
+.bi-person-lines-fill::before { content: "\f4db"; }
+.bi-person-plus-fill::before { content: "\f4dc"; }
+.bi-person-plus::before { content: "\f4dd"; }
+.bi-person-square::before { content: "\f4de"; }
+.bi-person-x-fill::before { content: "\f4df"; }
+.bi-person-x::before { content: "\f4e0"; }
+.bi-person::before { content: "\f4e1"; }
+.bi-phone-fill::before { content: "\f4e2"; }
+.bi-phone-landscape-fill::before { content: "\f4e3"; }
+.bi-phone-landscape::before { content: "\f4e4"; }
+.bi-phone-vibrate-fill::before { content: "\f4e5"; }
+.bi-phone-vibrate::before { content: "\f4e6"; }
+.bi-phone::before { content: "\f4e7"; }
+.bi-pie-chart-fill::before { content: "\f4e8"; }
+.bi-pie-chart::before { content: "\f4e9"; }
+.bi-pin-angle-fill::before { content: "\f4ea"; }
+.bi-pin-angle::before { content: "\f4eb"; }
+.bi-pin-fill::before { content: "\f4ec"; }
+.bi-pin::before { content: "\f4ed"; }
+.bi-pip-fill::before { content: "\f4ee"; }
+.bi-pip::before { content: "\f4ef"; }
+.bi-play-btn-fill::before { content: "\f4f0"; }
+.bi-play-btn::before { content: "\f4f1"; }
+.bi-play-circle-fill::before { content: "\f4f2"; }
+.bi-play-circle::before { content: "\f4f3"; }
+.bi-play-fill::before { content: "\f4f4"; }
+.bi-play::before { content: "\f4f5"; }
+.bi-plug-fill::before { content: "\f4f6"; }
+.bi-plug::before { content: "\f4f7"; }
+.bi-plus-circle-dotted::before { content: "\f4f8"; }
+.bi-plus-circle-fill::before { content: "\f4f9"; }
+.bi-plus-circle::before { content: "\f4fa"; }
+.bi-plus-square-dotted::before { content: "\f4fb"; }
+.bi-plus-square-fill::before { content: "\f4fc"; }
+.bi-plus-square::before { content: "\f4fd"; }
+.bi-plus::before { content: "\f4fe"; }
+.bi-power::before { content: "\f4ff"; }
+.bi-printer-fill::before { content: "\f500"; }
+.bi-printer::before { content: "\f501"; }
+.bi-puzzle-fill::before { content: "\f502"; }
+.bi-puzzle::before { content: "\f503"; }
+.bi-question-circle-fill::before { content: "\f504"; }
+.bi-question-circle::before { content: "\f505"; }
+.bi-question-diamond-fill::before { content: "\f506"; }
+.bi-question-diamond::before { content: "\f507"; }
+.bi-question-octagon-fill::before { content: "\f508"; }
+.bi-question-octagon::before { content: "\f509"; }
+.bi-question-square-fill::before { content: "\f50a"; }
+.bi-question-square::before { content: "\f50b"; }
+.bi-question::before { content: "\f50c"; }
+.bi-rainbow::before { content: "\f50d"; }
+.bi-receipt-cutoff::before { content: "\f50e"; }
+.bi-receipt::before { content: "\f50f"; }
+.bi-reception-0::before { content: "\f510"; }
+.bi-reception-1::before { content: "\f511"; }
+.bi-reception-2::before { content: "\f512"; }
+.bi-reception-3::before { content: "\f513"; }
+.bi-reception-4::before { content: "\f514"; }
+.bi-record-btn-fill::before { content: "\f515"; }
+.bi-record-btn::before { content: "\f516"; }
+.bi-record-circle-fill::before { content: "\f517"; }
+.bi-record-circle::before { content: "\f518"; }
+.bi-record-fill::before { content: "\f519"; }
+.bi-record::before { content: "\f51a"; }
+.bi-record2-fill::before { content: "\f51b"; }
+.bi-record2::before { content: "\f51c"; }
+.bi-reply-all-fill::before { content: "\f51d"; }
+.bi-reply-all::before { content: "\f51e"; }
+.bi-reply-fill::before { content: "\f51f"; }
+.bi-reply::before { content: "\f520"; }
+.bi-rss-fill::before { content: "\f521"; }
+.bi-rss::before { content: "\f522"; }
+.bi-rulers::before { content: "\f523"; }
+.bi-save-fill::before { content: "\f524"; }
+.bi-save::before { content: "\f525"; }
+.bi-save2-fill::before { content: "\f526"; }
+.bi-save2::before { content: "\f527"; }
+.bi-scissors::before { content: "\f528"; }
+.bi-screwdriver::before { content: "\f529"; }
+.bi-search::before { content: "\f52a"; }
+.bi-segmented-nav::before { content: "\f52b"; }
+.bi-server::before { content: "\f52c"; }
+.bi-share-fill::before { content: "\f52d"; }
+.bi-share::before { content: "\f52e"; }
+.bi-shield-check::before { content: "\f52f"; }
+.bi-shield-exclamation::before { content: "\f530"; }
+.bi-shield-fill-check::before { content: "\f531"; }
+.bi-shield-fill-exclamation::before { content: "\f532"; }
+.bi-shield-fill-minus::before { content: "\f533"; }
+.bi-shield-fill-plus::before { content: "\f534"; }
+.bi-shield-fill-x::before { content: "\f535"; }
+.bi-shield-fill::before { content: "\f536"; }
+.bi-shield-lock-fill::before { content: "\f537"; }
+.bi-shield-lock::before { content: "\f538"; }
+.bi-shield-minus::before { content: "\f539"; }
+.bi-shield-plus::before { content: "\f53a"; }
+.bi-shield-shaded::before { content: "\f53b"; }
+.bi-shield-slash-fill::before { content: "\f53c"; }
+.bi-shield-slash::before { content: "\f53d"; }
+.bi-shield-x::before { content: "\f53e"; }
+.bi-shield::before { content: "\f53f"; }
+.bi-shift-fill::before { content: "\f540"; }
+.bi-shift::before { content: "\f541"; }
+.bi-shop-window::before { content: "\f542"; }
+.bi-shop::before { content: "\f543"; }
+.bi-shuffle::before { content: "\f544"; }
+.bi-signpost-2-fill::before { content: "\f545"; }
+.bi-signpost-2::before { content: "\f546"; }
+.bi-signpost-fill::before { content: "\f547"; }
+.bi-signpost-split-fill::before { content: "\f548"; }
+.bi-signpost-split::before { content: "\f549"; }
+.bi-signpost::before { content: "\f54a"; }
+.bi-sim-fill::before { content: "\f54b"; }
+.bi-sim::before { content: "\f54c"; }
+.bi-skip-backward-btn-fill::before { content: "\f54d"; }
+.bi-skip-backward-btn::before { content: "\f54e"; }
+.bi-skip-backward-circle-fill::before { content: "\f54f"; }
+.bi-skip-backward-circle::before { content: "\f550"; }
+.bi-skip-backward-fill::before { content: "\f551"; }
+.bi-skip-backward::before { content: "\f552"; }
+.bi-skip-end-btn-fill::before { content: "\f553"; }
+.bi-skip-end-btn::before { content: "\f554"; }
+.bi-skip-end-circle-fill::before { content: "\f555"; }
+.bi-skip-end-circle::before { content: "\f556"; }
+.bi-skip-end-fill::before { content: "\f557"; }
+.bi-skip-end::before { content: "\f558"; }
+.bi-skip-forward-btn-fill::before { content: "\f559"; }
+.bi-skip-forward-btn::before { content: "\f55a"; }
+.bi-skip-forward-circle-fill::before { content: "\f55b"; }
+.bi-skip-forward-circle::before { content: "\f55c"; }
+.bi-skip-forward-fill::before { content: "\f55d"; }
+.bi-skip-forward::before { content: "\f55e"; }
+.bi-skip-start-btn-fill::before { content: "\f55f"; }
+.bi-skip-start-btn::before { content: "\f560"; }
+.bi-skip-start-circle-fill::before { content: "\f561"; }
+.bi-skip-start-circle::before { content: "\f562"; }
+.bi-skip-start-fill::before { content: "\f563"; }
+.bi-skip-start::before { content: "\f564"; }
+.bi-slack::before { content: "\f565"; }
+.bi-slash-circle-fill::before { content: "\f566"; }
+.bi-slash-circle::before { content: "\f567"; }
+.bi-slash-square-fill::before { content: "\f568"; }
+.bi-slash-square::before { content: "\f569"; }
+.bi-slash::before { content: "\f56a"; }
+.bi-sliders::before { content: "\f56b"; }
+.bi-smartwatch::before { content: "\f56c"; }
+.bi-snow::before { content: "\f56d"; }
+.bi-snow2::before { content: "\f56e"; }
+.bi-snow3::before { content: "\f56f"; }
+.bi-sort-alpha-down-alt::before { content: "\f570"; }
+.bi-sort-alpha-down::before { content: "\f571"; }
+.bi-sort-alpha-up-alt::before { content: "\f572"; }
+.bi-sort-alpha-up::before { content: "\f573"; }
+.bi-sort-down-alt::before { content: "\f574"; }
+.bi-sort-down::before { content: "\f575"; }
+.bi-sort-numeric-down-alt::before { content: "\f576"; }
+.bi-sort-numeric-down::before { content: "\f577"; }
+.bi-sort-numeric-up-alt::before { content: "\f578"; }
+.bi-sort-numeric-up::before { content: "\f579"; }
+.bi-sort-up-alt::before { content: "\f57a"; }
+.bi-sort-up::before { content: "\f57b"; }
+.bi-soundwave::before { content: "\f57c"; }
+.bi-speaker-fill::before { content: "\f57d"; }
+.bi-speaker::before { content: "\f57e"; }
+.bi-speedometer::before { content: "\f57f"; }
+.bi-speedometer2::before { content: "\f580"; }
+.bi-spellcheck::before { content: "\f581"; }
+.bi-square-fill::before { content: "\f582"; }
+.bi-square-half::before { content: "\f583"; }
+.bi-square::before { content: "\f584"; }
+.bi-stack::before { content: "\f585"; }
+.bi-star-fill::before { content: "\f586"; }
+.bi-star-half::before { content: "\f587"; }
+.bi-star::before { content: "\f588"; }
+.bi-stars::before { content: "\f589"; }
+.bi-stickies-fill::before { content: "\f58a"; }
+.bi-stickies::before { content: "\f58b"; }
+.bi-sticky-fill::before { content: "\f58c"; }
+.bi-sticky::before { content: "\f58d"; }
+.bi-stop-btn-fill::before { content: "\f58e"; }
+.bi-stop-btn::before { content: "\f58f"; }
+.bi-stop-circle-fill::before { content: "\f590"; }
+.bi-stop-circle::before { content: "\f591"; }
+.bi-stop-fill::before { content: "\f592"; }
+.bi-stop::before { content: "\f593"; }
+.bi-stoplights-fill::before { content: "\f594"; }
+.bi-stoplights::before { content: "\f595"; }
+.bi-stopwatch-fill::before { content: "\f596"; }
+.bi-stopwatch::before { content: "\f597"; }
+.bi-subtract::before { content: "\f598"; }
+.bi-suit-club-fill::before { content: "\f599"; }
+.bi-suit-club::before { content: "\f59a"; }
+.bi-suit-diamond-fill::before { content: "\f59b"; }
+.bi-suit-diamond::before { content: "\f59c"; }
+.bi-suit-heart-fill::before { content: "\f59d"; }
+.bi-suit-heart::before { content: "\f59e"; }
+.bi-suit-spade-fill::before { content: "\f59f"; }
+.bi-suit-spade::before { content: "\f5a0"; }
+.bi-sun-fill::before { content: "\f5a1"; }
+.bi-sun::before { content: "\f5a2"; }
+.bi-sunglasses::before { content: "\f5a3"; }
+.bi-sunrise-fill::before { content: "\f5a4"; }
+.bi-sunrise::before { content: "\f5a5"; }
+.bi-sunset-fill::before { content: "\f5a6"; }
+.bi-sunset::before { content: "\f5a7"; }
+.bi-symmetry-horizontal::before { content: "\f5a8"; }
+.bi-symmetry-vertical::before { content: "\f5a9"; }
+.bi-table::before { content: "\f5aa"; }
+.bi-tablet-fill::before { content: "\f5ab"; }
+.bi-tablet-landscape-fill::before { content: "\f5ac"; }
+.bi-tablet-landscape::before { content: "\f5ad"; }
+.bi-tablet::before { content: "\f5ae"; }
+.bi-tag-fill::before { content: "\f5af"; }
+.bi-tag::before { content: "\f5b0"; }
+.bi-tags-fill::before { content: "\f5b1"; }
+.bi-tags::before { content: "\f5b2"; }
+.bi-telegram::before { content: "\f5b3"; }
+.bi-telephone-fill::before { content: "\f5b4"; }
+.bi-telephone-forward-fill::before { content: "\f5b5"; }
+.bi-telephone-forward::before { content: "\f5b6"; }
+.bi-telephone-inbound-fill::before { content: "\f5b7"; }
+.bi-telephone-inbound::before { content: "\f5b8"; }
+.bi-telephone-minus-fill::before { content: "\f5b9"; }
+.bi-telephone-minus::before { content: "\f5ba"; }
+.bi-telephone-outbound-fill::before { content: "\f5bb"; }
+.bi-telephone-outbound::before { content: "\f5bc"; }
+.bi-telephone-plus-fill::before { content: "\f5bd"; }
+.bi-telephone-plus::before { content: "\f5be"; }
+.bi-telephone-x-fill::before { content: "\f5bf"; }
+.bi-telephone-x::before { content: "\f5c0"; }
+.bi-telephone::before { content: "\f5c1"; }
+.bi-terminal-fill::before { content: "\f5c2"; }
+.bi-terminal::before { content: "\f5c3"; }
+.bi-text-center::before { content: "\f5c4"; }
+.bi-text-indent-left::before { content: "\f5c5"; }
+.bi-text-indent-right::before { content: "\f5c6"; }
+.bi-text-left::before { content: "\f5c7"; }
+.bi-text-paragraph::before { content: "\f5c8"; }
+.bi-text-right::before { content: "\f5c9"; }
+.bi-textarea-resize::before { content: "\f5ca"; }
+.bi-textarea-t::before { content: "\f5cb"; }
+.bi-textarea::before { content: "\f5cc"; }
+.bi-thermometer-half::before { content: "\f5cd"; }
+.bi-thermometer-high::before { content: "\f5ce"; }
+.bi-thermometer-low::before { content: "\f5cf"; }
+.bi-thermometer-snow::before { content: "\f5d0"; }
+.bi-thermometer-sun::before { content: "\f5d1"; }
+.bi-thermometer::before { content: "\f5d2"; }
+.bi-three-dots-vertical::before { content: "\f5d3"; }
+.bi-three-dots::before { content: "\f5d4"; }
+.bi-toggle-off::before { content: "\f5d5"; }
+.bi-toggle-on::before { content: "\f5d6"; }
+.bi-toggle2-off::before { content: "\f5d7"; }
+.bi-toggle2-on::before { content: "\f5d8"; }
+.bi-toggles::before { content: "\f5d9"; }
+.bi-toggles2::before { content: "\f5da"; }
+.bi-tools::before { content: "\f5db"; }
+.bi-tornado::before { content: "\f5dc"; }
+.bi-trash-fill::before { content: "\f5dd"; }
+.bi-trash::before { content: "\f5de"; }
+.bi-trash2-fill::before { content: "\f5df"; }
+.bi-trash2::before { content: "\f5e0"; }
+.bi-tree-fill::before { content: "\f5e1"; }
+.bi-tree::before { content: "\f5e2"; }
+.bi-triangle-fill::before { content: "\f5e3"; }
+.bi-triangle-half::before { content: "\f5e4"; }
+.bi-triangle::before { content: "\f5e5"; }
+.bi-trophy-fill::before { content: "\f5e6"; }
+.bi-trophy::before { content: "\f5e7"; }
+.bi-tropical-storm::before { content: "\f5e8"; }
+.bi-truck-flatbed::before { content: "\f5e9"; }
+.bi-truck::before { content: "\f5ea"; }
+.bi-tsunami::before { content: "\f5eb"; }
+.bi-tv-fill::before { content: "\f5ec"; }
+.bi-tv::before { content: "\f5ed"; }
+.bi-twitch::before { content: "\f5ee"; }
+.bi-twitter::before { content: "\f5ef"; }
+.bi-type-bold::before { content: "\f5f0"; }
+.bi-type-h1::before { content: "\f5f1"; }
+.bi-type-h2::before { content: "\f5f2"; }
+.bi-type-h3::before { content: "\f5f3"; }
+.bi-type-italic::before { content: "\f5f4"; }
+.bi-type-strikethrough::before { content: "\f5f5"; }
+.bi-type-underline::before { content: "\f5f6"; }
+.bi-type::before { content: "\f5f7"; }
+.bi-ui-checks-grid::before { content: "\f5f8"; }
+.bi-ui-checks::before { content: "\f5f9"; }
+.bi-ui-radios-grid::before { content: "\f5fa"; }
+.bi-ui-radios::before { content: "\f5fb"; }
+.bi-umbrella-fill::before { content: "\f5fc"; }
+.bi-umbrella::before { content: "\f5fd"; }
+.bi-union::before { content: "\f5fe"; }
+.bi-unlock-fill::before { content: "\f5ff"; }
+.bi-unlock::before { content: "\f600"; }
+.bi-upc-scan::before { content: "\f601"; }
+.bi-upc::before { content: "\f602"; }
+.bi-upload::before { content: "\f603"; }
+.bi-vector-pen::before { content: "\f604"; }
+.bi-view-list::before { content: "\f605"; }
+.bi-view-stacked::before { content: "\f606"; }
+.bi-vinyl-fill::before { content: "\f607"; }
+.bi-vinyl::before { content: "\f608"; }
+.bi-voicemail::before { content: "\f609"; }
+.bi-volume-down-fill::before { content: "\f60a"; }
+.bi-volume-down::before { content: "\f60b"; }
+.bi-volume-mute-fill::before { content: "\f60c"; }
+.bi-volume-mute::before { content: "\f60d"; }
+.bi-volume-off-fill::before { content: "\f60e"; }
+.bi-volume-off::before { content: "\f60f"; }
+.bi-volume-up-fill::before { content: "\f610"; }
+.bi-volume-up::before { content: "\f611"; }
+.bi-vr::before { content: "\f612"; }
+.bi-wallet-fill::before { content: "\f613"; }
+.bi-wallet::before { content: "\f614"; }
+.bi-wallet2::before { content: "\f615"; }
+.bi-watch::before { content: "\f616"; }
+.bi-water::before { content: "\f617"; }
+.bi-whatsapp::before { content: "\f618"; }
+.bi-wifi-1::before { content: "\f619"; }
+.bi-wifi-2::before { content: "\f61a"; }
+.bi-wifi-off::before { content: "\f61b"; }
+.bi-wifi::before { content: "\f61c"; }
+.bi-wind::before { content: "\f61d"; }
+.bi-window-dock::before { content: "\f61e"; }
+.bi-window-sidebar::before { content: "\f61f"; }
+.bi-window::before { content: "\f620"; }
+.bi-wrench::before { content: "\f621"; }
+.bi-x-circle-fill::before { content: "\f622"; }
+.bi-x-circle::before { content: "\f623"; }
+.bi-x-diamond-fill::before { content: "\f624"; }
+.bi-x-diamond::before { content: "\f625"; }
+.bi-x-octagon-fill::before { content: "\f626"; }
+.bi-x-octagon::before { content: "\f627"; }
+.bi-x-square-fill::before { content: "\f628"; }
+.bi-x-square::before { content: "\f629"; }
+.bi-x::before { content: "\f62a"; }
+.bi-youtube::before { content: "\f62b"; }
+.bi-zoom-in::before { content: "\f62c"; }
+.bi-zoom-out::before { content: "\f62d"; }
+.bi-bank::before { content: "\f62e"; }
+.bi-bank2::before { content: "\f62f"; }
+.bi-bell-slash-fill::before { content: "\f630"; }
+.bi-bell-slash::before { content: "\f631"; }
+.bi-cash-coin::before { content: "\f632"; }
+.bi-check-lg::before { content: "\f633"; }
+.bi-coin::before { content: "\f634"; }
+.bi-currency-bitcoin::before { content: "\f635"; }
+.bi-currency-dollar::before { content: "\f636"; }
+.bi-currency-euro::before { content: "\f637"; }
+.bi-currency-exchange::before { content: "\f638"; }
+.bi-currency-pound::before { content: "\f639"; }
+.bi-currency-yen::before { content: "\f63a"; }
+.bi-dash-lg::before { content: "\f63b"; }
+.bi-exclamation-lg::before { content: "\f63c"; }
+.bi-file-earmark-pdf-fill::before { content: "\f63d"; }
+.bi-file-earmark-pdf::before { content: "\f63e"; }
+.bi-file-pdf-fill::before { content: "\f63f"; }
+.bi-file-pdf::before { content: "\f640"; }
+.bi-gender-ambiguous::before { content: "\f641"; }
+.bi-gender-female::before { content: "\f642"; }
+.bi-gender-male::before { content: "\f643"; }
+.bi-gender-trans::before { content: "\f644"; }
+.bi-headset-vr::before { content: "\f645"; }
+.bi-info-lg::before { content: "\f646"; }
+.bi-mastodon::before { content: "\f647"; }
+.bi-messenger::before { content: "\f648"; }
+.bi-piggy-bank-fill::before { content: "\f649"; }
+.bi-piggy-bank::before { content: "\f64a"; }
+.bi-pin-map-fill::before { content: "\f64b"; }
+.bi-pin-map::before { content: "\f64c"; }
+.bi-plus-lg::before { content: "\f64d"; }
+.bi-question-lg::before { content: "\f64e"; }
+.bi-recycle::before { content: "\f64f"; }
+.bi-reddit::before { content: "\f650"; }
+.bi-safe-fill::before { content: "\f651"; }
+.bi-safe2-fill::before { content: "\f652"; }
+.bi-safe2::before { content: "\f653"; }
+.bi-sd-card-fill::before { content: "\f654"; }
+.bi-sd-card::before { content: "\f655"; }
+.bi-skype::before { content: "\f656"; }
+.bi-slash-lg::before { content: "\f657"; }
+.bi-translate::before { content: "\f658"; }
+.bi-x-lg::before { content: "\f659"; }
+.bi-safe::before { content: "\f65a"; }
diff --git a/data/web/css/build/014-responsive.css b/data/web/css/build/014-responsive.css
new file mode 100644
index 00000000..2085568a
--- /dev/null
+++ b/data/web/css/build/014-responsive.css
@@ -0,0 +1,302 @@
+.space20 {
+ margin-bottom: 20px;
+}
+
+.btn-xs-lg>.lang-sm:after {
+ margin-left: 4px;
+}
+
+.bootstrap-select {
+ max-width: 350px;
+}
+
+.panel-login .apps .btn {
+ width: auto;
+ float: left;
+ margin-right: 10px;
+ margin-top: auto;
+}
+.panel-login .apps .btn:hover {
+ margin-top: 1px !important;
+ border-bottom-width: 3px;
+}
+
+@media (max-width: 767px) {
+ .panel-login .apps .btn {
+ width: 100%;
+ float: none;
+ margin-bottom: 10px;
+ }
+
+ .panel-login .apps .btn {
+ border-bottom-width: 4px;
+ }
+
+ .media-clearfix::after {
+ clear: both;
+ box-sizing: border-box;
+ }
+
+ .media-clearfix::before {
+ display: table;
+ content: " ";
+ box-sizing: border-box;
+ }
+
+ .xs-show {
+ display: block !important;
+ }
+
+ .js-tabcollapse-panel-group .panel{
+ border: none;
+ box-shadow: none;
+ }
+
+ .js-tabcollapse-panel-group .panel-body {
+ padding: 10px 0;
+ }
+
+ .js-tabcollapse-panel-group .js-tabcollapse-panel-body .panel-body {
+ padding: 0;
+ }
+
+ .js-tabcollapse-panel-body .panel-heading {
+ display: none;
+ }
+
+ .js-tabcollapse-panel-body .well,
+ .panel-body .form-inline.well {
+ border: none;
+ padding: 0;
+ margin: 0;
+ box-shadow: none;
+ background-color: #fff;
+ }
+
+ .js-tabcollapse-panel-heading {
+ display: block;
+ height: 37px;
+ line-height: 37px;
+ text-indent: 15px;
+ }
+ .js-tabcollapse-panel-heading:hover {
+ text-decoration: none;
+ }
+ .js-tabcollapse-panel-heading {
+ position: relative;
+ }
+ .js-tabcollapse-panel-heading:after {
+ content: '';
+ display: block;
+ position: absolute;
+ top: 17px;
+ right: 17px;
+ width: 0;
+ height: 0;
+ margin-left: 2px;
+ vertical-align: middle;
+ border-bottom: 4px dashed;
+ border-right: 4px solid transparent;
+ border-left: 4px solid transparent;
+ }
+ .js-tabcollapse-panel-heading.collapsed:after {
+ border-bottom: none;
+ border-top: 4px dashed;
+ }
+
+ .recent-login-success {
+ font-size: 14px;
+ margin-top: 10px !important;
+ }
+ .pull-xs-right {
+ float: right !important;
+ }
+ .pull-xs-right .dropdown-menu {
+ right: 0;
+ left: auto;
+ }
+ .text-xs-left {
+ text-align: left;
+ }
+ .text-xs-bold {
+ font-weight: bold;
+ }
+ .text-xs-bold .small {
+ font-weight: normal;
+ text-align: justify;
+ }
+ .help-block {
+ text-align: justify;
+ }
+ .btn.visible-xs-block {
+ width: 100%;
+ float: none;
+ white-space: normal;
+ }
+ .btn-group.footable-actions .btn.btn-xs-half,
+ .btn.visible-xs-block.btn-xs-half {
+ width: 50%;
+ float: left;
+ }
+ .btn-group.footable-actions .btn.btn-xs-third,
+ .btn.visible-xs-block.btn-xs-third {
+ width: 33.33%;
+ float: left;
+ }
+ .btn-group.footable-actions .btn.btn-xs-quart,
+ .btn.visible-xs-block.btn-xs-quart {
+ width: 25%;
+ float: left;
+ }
+ .btn.visible-xs-block.btn-sm,
+ .btn-xs-lg {
+ padding: 15px 16px 13px;
+ line-height: 15px;
+ }
+ .input-xs-lg {
+ height: 47px;
+ padding: 13px 16px;
+ }
+ .btn-group:not(.input-group-btn) {
+ display: flex;
+ flex-wrap: wrap;
+ }
+ .panel-login .btn-group {
+ display: block;
+ }
+ .mass-actions-user .btn-group {
+ float: none;
+ }
+ div[class^='mass-actions'] .dropdown-menu,
+ .panel-xs-lg .dropdown-menu,
+ .dropdown-menu.login {
+ width: 100%;
+ }
+ div[class^='mass-actions'] .btn-group .dropdown-menu {
+ top: 50%;
+ }
+ div[class^='mass-actions'] .btn-group .btn-group .dropdown-menu,
+ div.mass-actions-quarantine .btn-group .dropdown-menu,
+ .panel-xs-lg .dropdown-menu {
+ top: 100%;
+ }
+ div[class^='mass-actions'] .dropdown-menu>li>a,
+ .panel-xs-lg .dropdown-menu>li>a,
+ .dropdown-menu.login>li>a {
+ padding: 8px 20px;
+ }
+ div[class^='mass-actions'] .dropdown-header {
+ font-size: 14px;
+ font-weight: bold;
+ }
+ .space20 {
+ margin-bottom: 10px;
+ }
+ .top100 {
+ top: 100% !important;
+ }
+ .top33 {
+ top: 33% !important;
+ }
+ .footable-filtering .form {
+ width: 65%;
+ }
+ .btn-xs-lg>.lang-sm:after {
+ top: 1px;
+ }
+ table.footable>tfoot>tr.footable-paging>td {
+ text-align: left;
+ }
+ .footable-first-visible {
+ min-width: 55px;
+ }
+ table>tbody>tr>td>span.footable-toggle {
+ font-size: 24px;
+ margin-right: 14px !important;
+ }
+ table>tbody>tr>td>span.footable-toggle + input {
+ position: absolute;
+ left: 38px;
+ }
+ .pagination {
+ margin-bottom: 5px;
+ }
+ tr.footable-filtering>th>form {
+ width: 270px;
+ }
+ .mass-actions-mailbox {
+ padding: 0;
+ }
+ .panel-xs-lg .panel-heading {
+ height: 66px;
+ line-height: 47px;
+ }
+ .panel-xs-lg .btn-group .btn {
+ padding-right: 5px;
+ padding-left: 5px;
+ }
+ .bootstrap-select:not([class*=col-]):not([class*=form-control]):not(.input-group-btn) {
+ width: 100%;
+ }
+ .btn-group:not(.bootstrap-select) {
+ width: auto !important;
+ }
+ .bootstrap-select {
+ max-width: 100%;
+ }
+ .img-responsive {
+ margin: 0 auto;
+ }
+ .btn-group.footable-actions {
+ position: absolute;
+ width: 90vw !important;
+ left: 0;
+ height: 36px;
+ margin-top: -8px;
+ }
+ .btn-group.footable-actions .btn {
+ padding: 10px 16px 7px;
+ line-height: 15px;
+ display: block;
+ width: 100%;
+ }
+ .btn-group.footable-actions:after {
+ content: "";
+ display: block;
+ clear: both;
+ }
+ .bootstrap-select.btn-group.show-tick .dropdown-menu li a span.text {
+ margin-right: 14px;
+ white-space: normal;
+ }
+ .clearfix {
+ flex-basis: 100%;
+ height: 0;
+ }
+ .btn-group > .btn-group {
+ flex-basis: 100%;
+ }
+ .btn-group .btn {
+ display: flex !important;
+ align-items: center;
+ justify-content: center;
+ }
+ .btn-group .btn i {
+ margin-right: 5px;
+ }
+ .btn-group .btn .caret {
+ margin-left: 5px;
+ }
+ .panel-login .btn-group .btn {
+ display: block !important;
+ }
+ .panel-login .clearfix {
+ height: auto;
+ }
+}
+
+@media (max-width: 350px) {
+ .mailcow-logo img {
+ max-width: 250px;
+ }
+}
diff --git a/data/web/css/flags/1x1/ad.svg b/data/web/css/flags/1x1/ad.svg
new file mode 100644
index 00000000..e7fc56ab
--- /dev/null
+++ b/data/web/css/flags/1x1/ad.svg
@@ -0,0 +1,148 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/ae.svg b/data/web/css/flags/1x1/ae.svg
new file mode 100644
index 00000000..739c5d46
--- /dev/null
+++ b/data/web/css/flags/1x1/ae.svg
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/af.svg b/data/web/css/flags/1x1/af.svg
new file mode 100644
index 00000000..90c34b8d
--- /dev/null
+++ b/data/web/css/flags/1x1/af.svg
@@ -0,0 +1,81 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/ag.svg b/data/web/css/flags/1x1/ag.svg
new file mode 100644
index 00000000..d0b2a83c
--- /dev/null
+++ b/data/web/css/flags/1x1/ag.svg
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/ai.svg b/data/web/css/flags/1x1/ai.svg
new file mode 100644
index 00000000..472be200
--- /dev/null
+++ b/data/web/css/flags/1x1/ai.svg
@@ -0,0 +1,763 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/al.svg b/data/web/css/flags/1x1/al.svg
new file mode 100644
index 00000000..75995ecd
--- /dev/null
+++ b/data/web/css/flags/1x1/al.svg
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/am.svg b/data/web/css/flags/1x1/am.svg
new file mode 100644
index 00000000..1198be03
--- /dev/null
+++ b/data/web/css/flags/1x1/am.svg
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/ao.svg b/data/web/css/flags/1x1/ao.svg
new file mode 100644
index 00000000..a5a25bf1
--- /dev/null
+++ b/data/web/css/flags/1x1/ao.svg
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/aq.svg b/data/web/css/flags/1x1/aq.svg
new file mode 100644
index 00000000..80e682ab
--- /dev/null
+++ b/data/web/css/flags/1x1/aq.svg
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/ar.svg b/data/web/css/flags/1x1/ar.svg
new file mode 100644
index 00000000..1730ecac
--- /dev/null
+++ b/data/web/css/flags/1x1/ar.svg
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/as.svg b/data/web/css/flags/1x1/as.svg
new file mode 100644
index 00000000..b8d8162d
--- /dev/null
+++ b/data/web/css/flags/1x1/as.svg
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/at.svg b/data/web/css/flags/1x1/at.svg
new file mode 100644
index 00000000..649d6efe
--- /dev/null
+++ b/data/web/css/flags/1x1/at.svg
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/au.svg b/data/web/css/flags/1x1/au.svg
new file mode 100644
index 00000000..ca5d607e
--- /dev/null
+++ b/data/web/css/flags/1x1/au.svg
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/aw.svg b/data/web/css/flags/1x1/aw.svg
new file mode 100644
index 00000000..248a08d6
--- /dev/null
+++ b/data/web/css/flags/1x1/aw.svg
@@ -0,0 +1,186 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/ax.svg b/data/web/css/flags/1x1/ax.svg
new file mode 100644
index 00000000..cdeb07e2
--- /dev/null
+++ b/data/web/css/flags/1x1/ax.svg
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/az.svg b/data/web/css/flags/1x1/az.svg
new file mode 100644
index 00000000..0119e1ab
--- /dev/null
+++ b/data/web/css/flags/1x1/az.svg
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/ba.svg b/data/web/css/flags/1x1/ba.svg
new file mode 100644
index 00000000..5b92b0ab
--- /dev/null
+++ b/data/web/css/flags/1x1/ba.svg
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/bb.svg b/data/web/css/flags/1x1/bb.svg
new file mode 100644
index 00000000..9d627842
--- /dev/null
+++ b/data/web/css/flags/1x1/bb.svg
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/bd.svg b/data/web/css/flags/1x1/bd.svg
new file mode 100644
index 00000000..4cb38cf5
--- /dev/null
+++ b/data/web/css/flags/1x1/bd.svg
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/data/web/css/flags/1x1/be.svg b/data/web/css/flags/1x1/be.svg
new file mode 100644
index 00000000..01496c3c
--- /dev/null
+++ b/data/web/css/flags/1x1/be.svg
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/bf.svg b/data/web/css/flags/1x1/bf.svg
new file mode 100644
index 00000000..a3c7c44a
--- /dev/null
+++ b/data/web/css/flags/1x1/bf.svg
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/bg.svg b/data/web/css/flags/1x1/bg.svg
new file mode 100644
index 00000000..5abe67f6
--- /dev/null
+++ b/data/web/css/flags/1x1/bg.svg
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/bh.svg b/data/web/css/flags/1x1/bh.svg
new file mode 100644
index 00000000..22fba621
--- /dev/null
+++ b/data/web/css/flags/1x1/bh.svg
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/bi.svg b/data/web/css/flags/1x1/bi.svg
new file mode 100644
index 00000000..cc11dcff
--- /dev/null
+++ b/data/web/css/flags/1x1/bi.svg
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/bj.svg b/data/web/css/flags/1x1/bj.svg
new file mode 100644
index 00000000..07c4c117
--- /dev/null
+++ b/data/web/css/flags/1x1/bj.svg
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/bl.svg b/data/web/css/flags/1x1/bl.svg
new file mode 100644
index 00000000..0fa74e1c
--- /dev/null
+++ b/data/web/css/flags/1x1/bl.svg
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/bm.svg b/data/web/css/flags/1x1/bm.svg
new file mode 100644
index 00000000..a7057d24
--- /dev/null
+++ b/data/web/css/flags/1x1/bm.svg
@@ -0,0 +1,98 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/bn.svg b/data/web/css/flags/1x1/bn.svg
new file mode 100644
index 00000000..2e93aea3
--- /dev/null
+++ b/data/web/css/flags/1x1/bn.svg
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/bo.svg b/data/web/css/flags/1x1/bo.svg
new file mode 100644
index 00000000..52a534fe
--- /dev/null
+++ b/data/web/css/flags/1x1/bo.svg
@@ -0,0 +1,678 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/bq.svg b/data/web/css/flags/1x1/bq.svg
new file mode 100644
index 00000000..cc872ef1
--- /dev/null
+++ b/data/web/css/flags/1x1/bq.svg
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/br.svg b/data/web/css/flags/1x1/br.svg
new file mode 100644
index 00000000..8353e823
--- /dev/null
+++ b/data/web/css/flags/1x1/br.svg
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/bs.svg b/data/web/css/flags/1x1/bs.svg
new file mode 100644
index 00000000..decdebb1
--- /dev/null
+++ b/data/web/css/flags/1x1/bs.svg
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/bt.svg b/data/web/css/flags/1x1/bt.svg
new file mode 100644
index 00000000..3bbbfaac
--- /dev/null
+++ b/data/web/css/flags/1x1/bt.svg
@@ -0,0 +1,89 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/bv.svg b/data/web/css/flags/1x1/bv.svg
new file mode 100644
index 00000000..01c9ee14
--- /dev/null
+++ b/data/web/css/flags/1x1/bv.svg
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/bw.svg b/data/web/css/flags/1x1/bw.svg
new file mode 100644
index 00000000..0bc5d3d3
--- /dev/null
+++ b/data/web/css/flags/1x1/bw.svg
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/by.svg b/data/web/css/flags/1x1/by.svg
new file mode 100644
index 00000000..73e14f7a
--- /dev/null
+++ b/data/web/css/flags/1x1/by.svg
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/bz.svg b/data/web/css/flags/1x1/bz.svg
new file mode 100644
index 00000000..0e9a27ca
--- /dev/null
+++ b/data/web/css/flags/1x1/bz.svg
@@ -0,0 +1,145 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/ca.svg b/data/web/css/flags/1x1/ca.svg
new file mode 100644
index 00000000..6882f6da
--- /dev/null
+++ b/data/web/css/flags/1x1/ca.svg
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/data/web/css/flags/1x1/cc.svg b/data/web/css/flags/1x1/cc.svg
new file mode 100644
index 00000000..dd6e2000
--- /dev/null
+++ b/data/web/css/flags/1x1/cc.svg
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/cd.svg b/data/web/css/flags/1x1/cd.svg
new file mode 100644
index 00000000..5da2a968
--- /dev/null
+++ b/data/web/css/flags/1x1/cd.svg
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/cf.svg b/data/web/css/flags/1x1/cf.svg
new file mode 100644
index 00000000..e9246215
--- /dev/null
+++ b/data/web/css/flags/1x1/cf.svg
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/cg.svg b/data/web/css/flags/1x1/cg.svg
new file mode 100644
index 00000000..a52ba7e2
--- /dev/null
+++ b/data/web/css/flags/1x1/cg.svg
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/ch.svg b/data/web/css/flags/1x1/ch.svg
new file mode 100644
index 00000000..773cdc8a
--- /dev/null
+++ b/data/web/css/flags/1x1/ch.svg
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/ci.svg b/data/web/css/flags/1x1/ci.svg
new file mode 100644
index 00000000..bd1e3f41
--- /dev/null
+++ b/data/web/css/flags/1x1/ci.svg
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/ck.svg b/data/web/css/flags/1x1/ck.svg
new file mode 100644
index 00000000..f2df0dbe
--- /dev/null
+++ b/data/web/css/flags/1x1/ck.svg
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/cl.svg b/data/web/css/flags/1x1/cl.svg
new file mode 100644
index 00000000..b8088967
--- /dev/null
+++ b/data/web/css/flags/1x1/cl.svg
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/cm.svg b/data/web/css/flags/1x1/cm.svg
new file mode 100644
index 00000000..08b710bb
--- /dev/null
+++ b/data/web/css/flags/1x1/cm.svg
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/cn.svg b/data/web/css/flags/1x1/cn.svg
new file mode 100644
index 00000000..7873c1b4
--- /dev/null
+++ b/data/web/css/flags/1x1/cn.svg
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/co.svg b/data/web/css/flags/1x1/co.svg
new file mode 100644
index 00000000..18d1c5f4
--- /dev/null
+++ b/data/web/css/flags/1x1/co.svg
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/cr.svg b/data/web/css/flags/1x1/cr.svg
new file mode 100644
index 00000000..a60a6dd6
--- /dev/null
+++ b/data/web/css/flags/1x1/cr.svg
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/cs.svg b/data/web/css/flags/1x1/cs.svg
new file mode 100644
index 00000000..9557b6e8
--- /dev/null
+++ b/data/web/css/flags/1x1/cs.svg
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/cu.svg b/data/web/css/flags/1x1/cu.svg
new file mode 100644
index 00000000..39681762
--- /dev/null
+++ b/data/web/css/flags/1x1/cu.svg
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/cv.svg b/data/web/css/flags/1x1/cv.svg
new file mode 100644
index 00000000..a8311b2f
--- /dev/null
+++ b/data/web/css/flags/1x1/cv.svg
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/cw.svg b/data/web/css/flags/1x1/cw.svg
new file mode 100644
index 00000000..d7ba2186
--- /dev/null
+++ b/data/web/css/flags/1x1/cw.svg
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/cx.svg b/data/web/css/flags/1x1/cx.svg
new file mode 100644
index 00000000..ef82c453
--- /dev/null
+++ b/data/web/css/flags/1x1/cx.svg
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/cy.svg b/data/web/css/flags/1x1/cy.svg
new file mode 100644
index 00000000..ba2b0f89
--- /dev/null
+++ b/data/web/css/flags/1x1/cy.svg
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/cz.svg b/data/web/css/flags/1x1/cz.svg
new file mode 100644
index 00000000..9557b6e8
--- /dev/null
+++ b/data/web/css/flags/1x1/cz.svg
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/da.svg b/data/web/css/flags/1x1/da.svg
new file mode 100644
index 00000000..51ff69fe
--- /dev/null
+++ b/data/web/css/flags/1x1/da.svg
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/de.svg b/data/web/css/flags/1x1/de.svg
new file mode 100644
index 00000000..b9ea8a61
--- /dev/null
+++ b/data/web/css/flags/1x1/de.svg
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/dj.svg b/data/web/css/flags/1x1/dj.svg
new file mode 100644
index 00000000..3f6b2e4f
--- /dev/null
+++ b/data/web/css/flags/1x1/dj.svg
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/dk.svg b/data/web/css/flags/1x1/dk.svg
new file mode 100644
index 00000000..51ff69fe
--- /dev/null
+++ b/data/web/css/flags/1x1/dk.svg
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/dm.svg b/data/web/css/flags/1x1/dm.svg
new file mode 100644
index 00000000..405a4b6b
--- /dev/null
+++ b/data/web/css/flags/1x1/dm.svg
@@ -0,0 +1,152 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/do.svg b/data/web/css/flags/1x1/do.svg
new file mode 100644
index 00000000..03d3f354
--- /dev/null
+++ b/data/web/css/flags/1x1/do.svg
@@ -0,0 +1,6745 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/dz.svg b/data/web/css/flags/1x1/dz.svg
new file mode 100644
index 00000000..37df0c8b
--- /dev/null
+++ b/data/web/css/flags/1x1/dz.svg
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/ec.svg b/data/web/css/flags/1x1/ec.svg
new file mode 100644
index 00000000..65fd0bad
--- /dev/null
+++ b/data/web/css/flags/1x1/ec.svg
@@ -0,0 +1,138 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/ee.svg b/data/web/css/flags/1x1/ee.svg
new file mode 100644
index 00000000..fbc9e339
--- /dev/null
+++ b/data/web/css/flags/1x1/ee.svg
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/eg.svg b/data/web/css/flags/1x1/eg.svg
new file mode 100644
index 00000000..2965b6af
--- /dev/null
+++ b/data/web/css/flags/1x1/eg.svg
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/eh.svg b/data/web/css/flags/1x1/eh.svg
new file mode 100644
index 00000000..4c3feba1
--- /dev/null
+++ b/data/web/css/flags/1x1/eh.svg
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/en.svg b/data/web/css/flags/1x1/en.svg
new file mode 100644
index 00000000..ef048dc2
--- /dev/null
+++ b/data/web/css/flags/1x1/en.svg
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/er.svg b/data/web/css/flags/1x1/er.svg
new file mode 100644
index 00000000..86343349
--- /dev/null
+++ b/data/web/css/flags/1x1/er.svg
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/es-ca.svg b/data/web/css/flags/1x1/es-ca.svg
new file mode 100644
index 00000000..2a50685d
--- /dev/null
+++ b/data/web/css/flags/1x1/es-ca.svg
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/data/web/css/flags/1x1/es-ga.svg b/data/web/css/flags/1x1/es-ga.svg
new file mode 100644
index 00000000..5c55ff85
--- /dev/null
+++ b/data/web/css/flags/1x1/es-ga.svg
@@ -0,0 +1,189 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/es.svg b/data/web/css/flags/1x1/es.svg
new file mode 100644
index 00000000..d7030eb2
--- /dev/null
+++ b/data/web/css/flags/1x1/es.svg
@@ -0,0 +1,547 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/et.svg b/data/web/css/flags/1x1/et.svg
new file mode 100644
index 00000000..8b02f6b7
--- /dev/null
+++ b/data/web/css/flags/1x1/et.svg
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/eu.svg b/data/web/css/flags/1x1/eu.svg
new file mode 100644
index 00000000..b031d2d3
--- /dev/null
+++ b/data/web/css/flags/1x1/eu.svg
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/fi.svg b/data/web/css/flags/1x1/fi.svg
new file mode 100644
index 00000000..aff1304c
--- /dev/null
+++ b/data/web/css/flags/1x1/fi.svg
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/fj.svg b/data/web/css/flags/1x1/fj.svg
new file mode 100644
index 00000000..9d9c3029
--- /dev/null
+++ b/data/web/css/flags/1x1/fj.svg
@@ -0,0 +1,125 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/fk.svg b/data/web/css/flags/1x1/fk.svg
new file mode 100644
index 00000000..12a34c46
--- /dev/null
+++ b/data/web/css/flags/1x1/fk.svg
@@ -0,0 +1,93 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/fm.svg b/data/web/css/flags/1x1/fm.svg
new file mode 100644
index 00000000..791fde99
--- /dev/null
+++ b/data/web/css/flags/1x1/fm.svg
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/fo.svg b/data/web/css/flags/1x1/fo.svg
new file mode 100644
index 00000000..b28915c0
--- /dev/null
+++ b/data/web/css/flags/1x1/fo.svg
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/fr.svg b/data/web/css/flags/1x1/fr.svg
new file mode 100644
index 00000000..f8e3ca0d
--- /dev/null
+++ b/data/web/css/flags/1x1/fr.svg
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/ga.svg b/data/web/css/flags/1x1/ga.svg
new file mode 100644
index 00000000..16c81b30
--- /dev/null
+++ b/data/web/css/flags/1x1/ga.svg
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/gb-eng.svg b/data/web/css/flags/1x1/gb-eng.svg
new file mode 100644
index 00000000..18026d29
--- /dev/null
+++ b/data/web/css/flags/1x1/gb-eng.svg
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/gb-nir.svg b/data/web/css/flags/1x1/gb-nir.svg
new file mode 100644
index 00000000..6d8a3a39
--- /dev/null
+++ b/data/web/css/flags/1x1/gb-nir.svg
@@ -0,0 +1,131 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/gb-sct.svg b/data/web/css/flags/1x1/gb-sct.svg
new file mode 100644
index 00000000..6987b088
--- /dev/null
+++ b/data/web/css/flags/1x1/gb-sct.svg
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/data/web/css/flags/1x1/gb-wls.svg b/data/web/css/flags/1x1/gb-wls.svg
new file mode 100644
index 00000000..3931a179
--- /dev/null
+++ b/data/web/css/flags/1x1/gb-wls.svg
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/gb.svg b/data/web/css/flags/1x1/gb.svg
new file mode 100644
index 00000000..ef048dc2
--- /dev/null
+++ b/data/web/css/flags/1x1/gb.svg
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/gd.svg b/data/web/css/flags/1x1/gd.svg
new file mode 100644
index 00000000..cca37ba4
--- /dev/null
+++ b/data/web/css/flags/1x1/gd.svg
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/ge.svg b/data/web/css/flags/1x1/ge.svg
new file mode 100644
index 00000000..ac1d87bb
--- /dev/null
+++ b/data/web/css/flags/1x1/ge.svg
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/gf.svg b/data/web/css/flags/1x1/gf.svg
new file mode 100644
index 00000000..c00a5763
--- /dev/null
+++ b/data/web/css/flags/1x1/gf.svg
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/gg.svg b/data/web/css/flags/1x1/gg.svg
new file mode 100644
index 00000000..2d06a9f8
--- /dev/null
+++ b/data/web/css/flags/1x1/gg.svg
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/gh.svg b/data/web/css/flags/1x1/gh.svg
new file mode 100644
index 00000000..4b6446d6
--- /dev/null
+++ b/data/web/css/flags/1x1/gh.svg
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/gi.svg b/data/web/css/flags/1x1/gi.svg
new file mode 100644
index 00000000..39f5b277
--- /dev/null
+++ b/data/web/css/flags/1x1/gi.svg
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/gl.svg b/data/web/css/flags/1x1/gl.svg
new file mode 100644
index 00000000..7a026d99
--- /dev/null
+++ b/data/web/css/flags/1x1/gl.svg
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/data/web/css/flags/1x1/gm.svg b/data/web/css/flags/1x1/gm.svg
new file mode 100644
index 00000000..b06ab6cb
--- /dev/null
+++ b/data/web/css/flags/1x1/gm.svg
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/gn.svg b/data/web/css/flags/1x1/gn.svg
new file mode 100644
index 00000000..8f8855da
--- /dev/null
+++ b/data/web/css/flags/1x1/gn.svg
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/gp.svg b/data/web/css/flags/1x1/gp.svg
new file mode 100644
index 00000000..0a5bdb00
--- /dev/null
+++ b/data/web/css/flags/1x1/gp.svg
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/gq.svg b/data/web/css/flags/1x1/gq.svg
new file mode 100644
index 00000000..8149406d
--- /dev/null
+++ b/data/web/css/flags/1x1/gq.svg
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/gr.svg b/data/web/css/flags/1x1/gr.svg
new file mode 100644
index 00000000..4bc68fc1
--- /dev/null
+++ b/data/web/css/flags/1x1/gr.svg
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/gs.svg b/data/web/css/flags/1x1/gs.svg
new file mode 100644
index 00000000..48f68b8b
--- /dev/null
+++ b/data/web/css/flags/1x1/gs.svg
@@ -0,0 +1,206 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ L
+
+
+ E
+
+
+ O
+
+
+ T
+
+
+ E
+
+
+ R
+
+
+ R
+
+
+ R
+
+
+ R
+
+
+ R
+
+
+ E
+
+
+ O
+
+
+ O
+
+
+ A
+
+
+ A
+
+
+ A
+
+
+ M
+
+
+ P
+
+
+ P
+
+
+ P
+
+
+ I
+
+
+ T
+
+
+ T
+
+
+ M
+
+
+ G
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/gt.svg b/data/web/css/flags/1x1/gt.svg
new file mode 100644
index 00000000..761801ca
--- /dev/null
+++ b/data/web/css/flags/1x1/gt.svg
@@ -0,0 +1,204 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/gu.svg b/data/web/css/flags/1x1/gu.svg
new file mode 100644
index 00000000..d6f5d535
--- /dev/null
+++ b/data/web/css/flags/1x1/gu.svg
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
+
+ G
+
+
+ U
+
+
+ A
+
+
+ M
+
+
+
+
+
+
+
+ G
+
+
+ U
+
+
+ A
+
+
+ M
+
+
diff --git a/data/web/css/flags/1x1/gw.svg b/data/web/css/flags/1x1/gw.svg
new file mode 100644
index 00000000..064a5934
--- /dev/null
+++ b/data/web/css/flags/1x1/gw.svg
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/gy.svg b/data/web/css/flags/1x1/gy.svg
new file mode 100644
index 00000000..57eb5209
--- /dev/null
+++ b/data/web/css/flags/1x1/gy.svg
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/hk.svg b/data/web/css/flags/1x1/hk.svg
new file mode 100644
index 00000000..024c0705
--- /dev/null
+++ b/data/web/css/flags/1x1/hk.svg
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/hm.svg b/data/web/css/flags/1x1/hm.svg
new file mode 100644
index 00000000..7e1f7e7e
--- /dev/null
+++ b/data/web/css/flags/1x1/hm.svg
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/hn.svg b/data/web/css/flags/1x1/hn.svg
new file mode 100644
index 00000000..c7c4c4ac
--- /dev/null
+++ b/data/web/css/flags/1x1/hn.svg
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/hr.svg b/data/web/css/flags/1x1/hr.svg
new file mode 100644
index 00000000..7ea00410
--- /dev/null
+++ b/data/web/css/flags/1x1/hr.svg
@@ -0,0 +1,56 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/ht.svg b/data/web/css/flags/1x1/ht.svg
new file mode 100644
index 00000000..920833a6
--- /dev/null
+++ b/data/web/css/flags/1x1/ht.svg
@@ -0,0 +1,116 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/hu.svg b/data/web/css/flags/1x1/hu.svg
new file mode 100644
index 00000000..94bc29f1
--- /dev/null
+++ b/data/web/css/flags/1x1/hu.svg
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/id.svg b/data/web/css/flags/1x1/id.svg
new file mode 100644
index 00000000..6d2cf094
--- /dev/null
+++ b/data/web/css/flags/1x1/id.svg
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/ie.svg b/data/web/css/flags/1x1/ie.svg
new file mode 100644
index 00000000..60448a9d
--- /dev/null
+++ b/data/web/css/flags/1x1/ie.svg
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/il.svg b/data/web/css/flags/1x1/il.svg
new file mode 100644
index 00000000..6cb4b1c1
--- /dev/null
+++ b/data/web/css/flags/1x1/il.svg
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/im.svg b/data/web/css/flags/1x1/im.svg
new file mode 100644
index 00000000..0f487f67
--- /dev/null
+++ b/data/web/css/flags/1x1/im.svg
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/in.svg b/data/web/css/flags/1x1/in.svg
new file mode 100644
index 00000000..e6557cd0
--- /dev/null
+++ b/data/web/css/flags/1x1/in.svg
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/io.svg b/data/web/css/flags/1x1/io.svg
new file mode 100644
index 00000000..4d809e03
--- /dev/null
+++ b/data/web/css/flags/1x1/io.svg
@@ -0,0 +1,140 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/iq.svg b/data/web/css/flags/1x1/iq.svg
new file mode 100644
index 00000000..6b96774d
--- /dev/null
+++ b/data/web/css/flags/1x1/iq.svg
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/ir.svg b/data/web/css/flags/1x1/ir.svg
new file mode 100644
index 00000000..79f66324
--- /dev/null
+++ b/data/web/css/flags/1x1/ir.svg
@@ -0,0 +1,219 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/is.svg b/data/web/css/flags/1x1/is.svg
new file mode 100644
index 00000000..08d1e683
--- /dev/null
+++ b/data/web/css/flags/1x1/is.svg
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/it.svg b/data/web/css/flags/1x1/it.svg
new file mode 100644
index 00000000..615c58fb
--- /dev/null
+++ b/data/web/css/flags/1x1/it.svg
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/je.svg b/data/web/css/flags/1x1/je.svg
new file mode 100644
index 00000000..c63ccb29
--- /dev/null
+++ b/data/web/css/flags/1x1/je.svg
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/jm.svg b/data/web/css/flags/1x1/jm.svg
new file mode 100644
index 00000000..c261da09
--- /dev/null
+++ b/data/web/css/flags/1x1/jm.svg
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/jo.svg b/data/web/css/flags/1x1/jo.svg
new file mode 100644
index 00000000..ab1c62aa
--- /dev/null
+++ b/data/web/css/flags/1x1/jo.svg
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/jp.svg b/data/web/css/flags/1x1/jp.svg
new file mode 100644
index 00000000..dc7a64a5
--- /dev/null
+++ b/data/web/css/flags/1x1/jp.svg
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/ke.svg b/data/web/css/flags/1x1/ke.svg
new file mode 100644
index 00000000..0b82f3a6
--- /dev/null
+++ b/data/web/css/flags/1x1/ke.svg
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/kg.svg b/data/web/css/flags/1x1/kg.svg
new file mode 100644
index 00000000..71ee7b8d
--- /dev/null
+++ b/data/web/css/flags/1x1/kg.svg
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/kh.svg b/data/web/css/flags/1x1/kh.svg
new file mode 100644
index 00000000..8c888f16
--- /dev/null
+++ b/data/web/css/flags/1x1/kh.svg
@@ -0,0 +1,61 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/ki.svg b/data/web/css/flags/1x1/ki.svg
new file mode 100644
index 00000000..bfc5ccab
--- /dev/null
+++ b/data/web/css/flags/1x1/ki.svg
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/km.svg b/data/web/css/flags/1x1/km.svg
new file mode 100644
index 00000000..8f842ea8
--- /dev/null
+++ b/data/web/css/flags/1x1/km.svg
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/kn.svg b/data/web/css/flags/1x1/kn.svg
new file mode 100644
index 00000000..4b2a2488
--- /dev/null
+++ b/data/web/css/flags/1x1/kn.svg
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/ko.svg b/data/web/css/flags/1x1/ko.svg
new file mode 100644
index 00000000..2db51b02
--- /dev/null
+++ b/data/web/css/flags/1x1/ko.svg
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/kp.svg b/data/web/css/flags/1x1/kp.svg
new file mode 100644
index 00000000..8eda6be8
--- /dev/null
+++ b/data/web/css/flags/1x1/kp.svg
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/kr.svg b/data/web/css/flags/1x1/kr.svg
new file mode 100644
index 00000000..2db51b02
--- /dev/null
+++ b/data/web/css/flags/1x1/kr.svg
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/kw.svg b/data/web/css/flags/1x1/kw.svg
new file mode 100644
index 00000000..3d4047ff
--- /dev/null
+++ b/data/web/css/flags/1x1/kw.svg
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/ky.svg b/data/web/css/flags/1x1/ky.svg
new file mode 100644
index 00000000..b4ae00aa
--- /dev/null
+++ b/data/web/css/flags/1x1/ky.svg
@@ -0,0 +1,70 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/kz.svg b/data/web/css/flags/1x1/kz.svg
new file mode 100644
index 00000000..f17bd6e0
--- /dev/null
+++ b/data/web/css/flags/1x1/kz.svg
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/la.svg b/data/web/css/flags/1x1/la.svg
new file mode 100644
index 00000000..1e7686a9
--- /dev/null
+++ b/data/web/css/flags/1x1/la.svg
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/lb.svg b/data/web/css/flags/1x1/lb.svg
new file mode 100644
index 00000000..a047b0b9
--- /dev/null
+++ b/data/web/css/flags/1x1/lb.svg
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/lc.svg b/data/web/css/flags/1x1/lc.svg
new file mode 100644
index 00000000..b13b8852
--- /dev/null
+++ b/data/web/css/flags/1x1/lc.svg
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/li.svg b/data/web/css/flags/1x1/li.svg
new file mode 100644
index 00000000..cbed5cc8
--- /dev/null
+++ b/data/web/css/flags/1x1/li.svg
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/lk.svg b/data/web/css/flags/1x1/lk.svg
new file mode 100644
index 00000000..2b112155
--- /dev/null
+++ b/data/web/css/flags/1x1/lk.svg
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/lr.svg b/data/web/css/flags/1x1/lr.svg
new file mode 100644
index 00000000..0ae34e72
--- /dev/null
+++ b/data/web/css/flags/1x1/lr.svg
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/ls.svg b/data/web/css/flags/1x1/ls.svg
new file mode 100644
index 00000000..e71bb5bb
--- /dev/null
+++ b/data/web/css/flags/1x1/ls.svg
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/lt.svg b/data/web/css/flags/1x1/lt.svg
new file mode 100644
index 00000000..aa96cf32
--- /dev/null
+++ b/data/web/css/flags/1x1/lt.svg
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/lu.svg b/data/web/css/flags/1x1/lu.svg
new file mode 100644
index 00000000..62936716
--- /dev/null
+++ b/data/web/css/flags/1x1/lu.svg
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/lv.svg b/data/web/css/flags/1x1/lv.svg
new file mode 100644
index 00000000..5556de1a
--- /dev/null
+++ b/data/web/css/flags/1x1/lv.svg
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/ly.svg b/data/web/css/flags/1x1/ly.svg
new file mode 100644
index 00000000..fe0ed81b
--- /dev/null
+++ b/data/web/css/flags/1x1/ly.svg
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/ma.svg b/data/web/css/flags/1x1/ma.svg
new file mode 100644
index 00000000..85c99b3b
--- /dev/null
+++ b/data/web/css/flags/1x1/ma.svg
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/data/web/css/flags/1x1/mc.svg b/data/web/css/flags/1x1/mc.svg
new file mode 100644
index 00000000..d38822dd
--- /dev/null
+++ b/data/web/css/flags/1x1/mc.svg
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/md.svg b/data/web/css/flags/1x1/md.svg
new file mode 100644
index 00000000..86b2a961
--- /dev/null
+++ b/data/web/css/flags/1x1/md.svg
@@ -0,0 +1,71 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/me.svg b/data/web/css/flags/1x1/me.svg
new file mode 100644
index 00000000..56a19ed0
--- /dev/null
+++ b/data/web/css/flags/1x1/me.svg
@@ -0,0 +1,118 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/mf.svg b/data/web/css/flags/1x1/mf.svg
new file mode 100644
index 00000000..310afce4
--- /dev/null
+++ b/data/web/css/flags/1x1/mf.svg
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/mg.svg b/data/web/css/flags/1x1/mg.svg
new file mode 100644
index 00000000..f0375cc6
--- /dev/null
+++ b/data/web/css/flags/1x1/mg.svg
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/mh.svg b/data/web/css/flags/1x1/mh.svg
new file mode 100644
index 00000000..97f34631
--- /dev/null
+++ b/data/web/css/flags/1x1/mh.svg
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/mk.svg b/data/web/css/flags/1x1/mk.svg
new file mode 100644
index 00000000..da2e9a4c
--- /dev/null
+++ b/data/web/css/flags/1x1/mk.svg
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/ml.svg b/data/web/css/flags/1x1/ml.svg
new file mode 100644
index 00000000..1e4d9890
--- /dev/null
+++ b/data/web/css/flags/1x1/ml.svg
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/mm.svg b/data/web/css/flags/1x1/mm.svg
new file mode 100644
index 00000000..5076184c
--- /dev/null
+++ b/data/web/css/flags/1x1/mm.svg
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/mn.svg b/data/web/css/flags/1x1/mn.svg
new file mode 100644
index 00000000..568fda08
--- /dev/null
+++ b/data/web/css/flags/1x1/mn.svg
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/mo.svg b/data/web/css/flags/1x1/mo.svg
new file mode 100644
index 00000000..83d04ea2
--- /dev/null
+++ b/data/web/css/flags/1x1/mo.svg
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/mp.svg b/data/web/css/flags/1x1/mp.svg
new file mode 100644
index 00000000..54a0ede9
--- /dev/null
+++ b/data/web/css/flags/1x1/mp.svg
@@ -0,0 +1,86 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/mq.svg b/data/web/css/flags/1x1/mq.svg
new file mode 100644
index 00000000..7a69fb58
--- /dev/null
+++ b/data/web/css/flags/1x1/mq.svg
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/mr.svg b/data/web/css/flags/1x1/mr.svg
new file mode 100644
index 00000000..7da23e5f
--- /dev/null
+++ b/data/web/css/flags/1x1/mr.svg
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/ms.svg b/data/web/css/flags/1x1/ms.svg
new file mode 100644
index 00000000..d7d910d0
--- /dev/null
+++ b/data/web/css/flags/1x1/ms.svg
@@ -0,0 +1,70 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/mt.svg b/data/web/css/flags/1x1/mt.svg
new file mode 100644
index 00000000..96acc15e
--- /dev/null
+++ b/data/web/css/flags/1x1/mt.svg
@@ -0,0 +1,50 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/mu.svg b/data/web/css/flags/1x1/mu.svg
new file mode 100644
index 00000000..773d3d59
--- /dev/null
+++ b/data/web/css/flags/1x1/mu.svg
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/mv.svg b/data/web/css/flags/1x1/mv.svg
new file mode 100644
index 00000000..aa5ed533
--- /dev/null
+++ b/data/web/css/flags/1x1/mv.svg
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/mw.svg b/data/web/css/flags/1x1/mw.svg
new file mode 100644
index 00000000..a9521a08
--- /dev/null
+++ b/data/web/css/flags/1x1/mw.svg
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/mx.svg b/data/web/css/flags/1x1/mx.svg
new file mode 100644
index 00000000..a4406568
--- /dev/null
+++ b/data/web/css/flags/1x1/mx.svg
@@ -0,0 +1,378 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/my.svg b/data/web/css/flags/1x1/my.svg
new file mode 100644
index 00000000..7ebe064b
--- /dev/null
+++ b/data/web/css/flags/1x1/my.svg
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/mz.svg b/data/web/css/flags/1x1/mz.svg
new file mode 100644
index 00000000..113a2057
--- /dev/null
+++ b/data/web/css/flags/1x1/mz.svg
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/na.svg b/data/web/css/flags/1x1/na.svg
new file mode 100644
index 00000000..b934fc15
--- /dev/null
+++ b/data/web/css/flags/1x1/na.svg
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/nc.svg b/data/web/css/flags/1x1/nc.svg
new file mode 100644
index 00000000..2bdf6ee5
--- /dev/null
+++ b/data/web/css/flags/1x1/nc.svg
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/ne.svg b/data/web/css/flags/1x1/ne.svg
new file mode 100644
index 00000000..e76e44c0
--- /dev/null
+++ b/data/web/css/flags/1x1/ne.svg
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/nf.svg b/data/web/css/flags/1x1/nf.svg
new file mode 100644
index 00000000..21495222
--- /dev/null
+++ b/data/web/css/flags/1x1/nf.svg
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/ng.svg b/data/web/css/flags/1x1/ng.svg
new file mode 100644
index 00000000..57d65d38
--- /dev/null
+++ b/data/web/css/flags/1x1/ng.svg
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/ni.svg b/data/web/css/flags/1x1/ni.svg
new file mode 100644
index 00000000..8f68d422
--- /dev/null
+++ b/data/web/css/flags/1x1/ni.svg
@@ -0,0 +1,129 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/nl.svg b/data/web/css/flags/1x1/nl.svg
new file mode 100644
index 00000000..9db233dd
--- /dev/null
+++ b/data/web/css/flags/1x1/nl.svg
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/no.svg b/data/web/css/flags/1x1/no.svg
new file mode 100644
index 00000000..08ea5728
--- /dev/null
+++ b/data/web/css/flags/1x1/no.svg
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/np.svg b/data/web/css/flags/1x1/np.svg
new file mode 100644
index 00000000..f34ee8c6
--- /dev/null
+++ b/data/web/css/flags/1x1/np.svg
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/nr.svg b/data/web/css/flags/1x1/nr.svg
new file mode 100644
index 00000000..282d80fa
--- /dev/null
+++ b/data/web/css/flags/1x1/nr.svg
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/nu.svg b/data/web/css/flags/1x1/nu.svg
new file mode 100644
index 00000000..aced440d
--- /dev/null
+++ b/data/web/css/flags/1x1/nu.svg
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/nz.svg b/data/web/css/flags/1x1/nz.svg
new file mode 100644
index 00000000..5283a96e
--- /dev/null
+++ b/data/web/css/flags/1x1/nz.svg
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/om.svg b/data/web/css/flags/1x1/om.svg
new file mode 100644
index 00000000..055d1e69
--- /dev/null
+++ b/data/web/css/flags/1x1/om.svg
@@ -0,0 +1,115 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/pa.svg b/data/web/css/flags/1x1/pa.svg
new file mode 100644
index 00000000..57965b9e
--- /dev/null
+++ b/data/web/css/flags/1x1/pa.svg
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/pe.svg b/data/web/css/flags/1x1/pe.svg
new file mode 100644
index 00000000..40b87bad
--- /dev/null
+++ b/data/web/css/flags/1x1/pe.svg
@@ -0,0 +1,244 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/pf.svg b/data/web/css/flags/1x1/pf.svg
new file mode 100644
index 00000000..94ff90cb
--- /dev/null
+++ b/data/web/css/flags/1x1/pf.svg
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/pg.svg b/data/web/css/flags/1x1/pg.svg
new file mode 100644
index 00000000..73977541
--- /dev/null
+++ b/data/web/css/flags/1x1/pg.svg
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/ph.svg b/data/web/css/flags/1x1/ph.svg
new file mode 100644
index 00000000..681cf23f
--- /dev/null
+++ b/data/web/css/flags/1x1/ph.svg
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/pk.svg b/data/web/css/flags/1x1/pk.svg
new file mode 100644
index 00000000..06b6022f
--- /dev/null
+++ b/data/web/css/flags/1x1/pk.svg
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/pl.svg b/data/web/css/flags/1x1/pl.svg
new file mode 100644
index 00000000..f7c12a18
--- /dev/null
+++ b/data/web/css/flags/1x1/pl.svg
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/pm.svg b/data/web/css/flags/1x1/pm.svg
new file mode 100644
index 00000000..3d4014a4
--- /dev/null
+++ b/data/web/css/flags/1x1/pm.svg
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/pn.svg b/data/web/css/flags/1x1/pn.svg
new file mode 100644
index 00000000..47b0749a
--- /dev/null
+++ b/data/web/css/flags/1x1/pn.svg
@@ -0,0 +1,67 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/pr.svg b/data/web/css/flags/1x1/pr.svg
new file mode 100644
index 00000000..79cf4c09
--- /dev/null
+++ b/data/web/css/flags/1x1/pr.svg
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/ps.svg b/data/web/css/flags/1x1/ps.svg
new file mode 100644
index 00000000..7c1ea3f9
--- /dev/null
+++ b/data/web/css/flags/1x1/ps.svg
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/pt.svg b/data/web/css/flags/1x1/pt.svg
new file mode 100644
index 00000000..42551505
--- /dev/null
+++ b/data/web/css/flags/1x1/pt.svg
@@ -0,0 +1,57 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/pw.svg b/data/web/css/flags/1x1/pw.svg
new file mode 100644
index 00000000..83bc3f71
--- /dev/null
+++ b/data/web/css/flags/1x1/pw.svg
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/py.svg b/data/web/css/flags/1x1/py.svg
new file mode 100644
index 00000000..88f55e6f
--- /dev/null
+++ b/data/web/css/flags/1x1/py.svg
@@ -0,0 +1,156 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/qa.svg b/data/web/css/flags/1x1/qa.svg
new file mode 100644
index 00000000..0bf30ea3
--- /dev/null
+++ b/data/web/css/flags/1x1/qa.svg
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/data/web/css/flags/1x1/re.svg b/data/web/css/flags/1x1/re.svg
new file mode 100644
index 00000000..027c9f32
--- /dev/null
+++ b/data/web/css/flags/1x1/re.svg
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/ro.svg b/data/web/css/flags/1x1/ro.svg
new file mode 100644
index 00000000..994992ab
--- /dev/null
+++ b/data/web/css/flags/1x1/ro.svg
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/rs.svg b/data/web/css/flags/1x1/rs.svg
new file mode 100644
index 00000000..3270f304
--- /dev/null
+++ b/data/web/css/flags/1x1/rs.svg
@@ -0,0 +1,296 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/ru.svg b/data/web/css/flags/1x1/ru.svg
new file mode 100644
index 00000000..d6430874
--- /dev/null
+++ b/data/web/css/flags/1x1/ru.svg
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/rw.svg b/data/web/css/flags/1x1/rw.svg
new file mode 100644
index 00000000..26e41f6a
--- /dev/null
+++ b/data/web/css/flags/1x1/rw.svg
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/sa.svg b/data/web/css/flags/1x1/sa.svg
new file mode 100644
index 00000000..1cc41b80
--- /dev/null
+++ b/data/web/css/flags/1x1/sa.svg
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/sb.svg b/data/web/css/flags/1x1/sb.svg
new file mode 100644
index 00000000..f61bafd6
--- /dev/null
+++ b/data/web/css/flags/1x1/sb.svg
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/sc.svg b/data/web/css/flags/1x1/sc.svg
new file mode 100644
index 00000000..65d8943d
--- /dev/null
+++ b/data/web/css/flags/1x1/sc.svg
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/sd.svg b/data/web/css/flags/1x1/sd.svg
new file mode 100644
index 00000000..72a72970
--- /dev/null
+++ b/data/web/css/flags/1x1/sd.svg
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/se.svg b/data/web/css/flags/1x1/se.svg
new file mode 100644
index 00000000..e824395e
--- /dev/null
+++ b/data/web/css/flags/1x1/se.svg
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/sg.svg b/data/web/css/flags/1x1/sg.svg
new file mode 100644
index 00000000..1444e5b6
--- /dev/null
+++ b/data/web/css/flags/1x1/sg.svg
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/sh.svg b/data/web/css/flags/1x1/sh.svg
new file mode 100644
index 00000000..599a09d7
--- /dev/null
+++ b/data/web/css/flags/1x1/sh.svg
@@ -0,0 +1,81 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/si.svg b/data/web/css/flags/1x1/si.svg
new file mode 100644
index 00000000..6de77ef4
--- /dev/null
+++ b/data/web/css/flags/1x1/si.svg
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/sj.svg b/data/web/css/flags/1x1/sj.svg
new file mode 100644
index 00000000..f4e58297
--- /dev/null
+++ b/data/web/css/flags/1x1/sj.svg
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/sk.svg b/data/web/css/flags/1x1/sk.svg
new file mode 100644
index 00000000..fd728680
--- /dev/null
+++ b/data/web/css/flags/1x1/sk.svg
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/sl.svg b/data/web/css/flags/1x1/sl.svg
new file mode 100644
index 00000000..18c76d78
--- /dev/null
+++ b/data/web/css/flags/1x1/sl.svg
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/sm.svg b/data/web/css/flags/1x1/sm.svg
new file mode 100644
index 00000000..1ed18eed
--- /dev/null
+++ b/data/web/css/flags/1x1/sm.svg
@@ -0,0 +1,89 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ L
+
+
+ I
+
+
+ B
+
+
+ E
+
+
+ R
+
+
+ T
+
+
+ A
+
+
+ S
+
+
+
+
diff --git a/data/web/css/flags/1x1/sn.svg b/data/web/css/flags/1x1/sn.svg
new file mode 100644
index 00000000..fbbad5e0
--- /dev/null
+++ b/data/web/css/flags/1x1/sn.svg
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/so.svg b/data/web/css/flags/1x1/so.svg
new file mode 100644
index 00000000..96d88a99
--- /dev/null
+++ b/data/web/css/flags/1x1/so.svg
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/sr.svg b/data/web/css/flags/1x1/sr.svg
new file mode 100644
index 00000000..a0ca03e3
--- /dev/null
+++ b/data/web/css/flags/1x1/sr.svg
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/ss.svg b/data/web/css/flags/1x1/ss.svg
new file mode 100644
index 00000000..7e6a47ff
--- /dev/null
+++ b/data/web/css/flags/1x1/ss.svg
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/st.svg b/data/web/css/flags/1x1/st.svg
new file mode 100644
index 00000000..da5df299
--- /dev/null
+++ b/data/web/css/flags/1x1/st.svg
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/sv.svg b/data/web/css/flags/1x1/sv.svg
new file mode 100644
index 00000000..e824395e
--- /dev/null
+++ b/data/web/css/flags/1x1/sv.svg
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/sx.svg b/data/web/css/flags/1x1/sx.svg
new file mode 100644
index 00000000..5d9fadfa
--- /dev/null
+++ b/data/web/css/flags/1x1/sx.svg
@@ -0,0 +1,56 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/sy.svg b/data/web/css/flags/1x1/sy.svg
new file mode 100644
index 00000000..904dc1f6
--- /dev/null
+++ b/data/web/css/flags/1x1/sy.svg
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/sz.svg b/data/web/css/flags/1x1/sz.svg
new file mode 100644
index 00000000..b3009e4d
--- /dev/null
+++ b/data/web/css/flags/1x1/sz.svg
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/tc.svg b/data/web/css/flags/1x1/tc.svg
new file mode 100644
index 00000000..1029615f
--- /dev/null
+++ b/data/web/css/flags/1x1/tc.svg
@@ -0,0 +1,74 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/td.svg b/data/web/css/flags/1x1/td.svg
new file mode 100644
index 00000000..e3e81ce2
--- /dev/null
+++ b/data/web/css/flags/1x1/td.svg
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/tf.svg b/data/web/css/flags/1x1/tf.svg
new file mode 100644
index 00000000..2061867c
--- /dev/null
+++ b/data/web/css/flags/1x1/tf.svg
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/tg.svg b/data/web/css/flags/1x1/tg.svg
new file mode 100644
index 00000000..2c1fd98f
--- /dev/null
+++ b/data/web/css/flags/1x1/tg.svg
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/th.svg b/data/web/css/flags/1x1/th.svg
new file mode 100644
index 00000000..86850f5f
--- /dev/null
+++ b/data/web/css/flags/1x1/th.svg
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/tj.svg b/data/web/css/flags/1x1/tj.svg
new file mode 100644
index 00000000..853a4a45
--- /dev/null
+++ b/data/web/css/flags/1x1/tj.svg
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/tk.svg b/data/web/css/flags/1x1/tk.svg
new file mode 100644
index 00000000..c5ff6b4c
--- /dev/null
+++ b/data/web/css/flags/1x1/tk.svg
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/tl.svg b/data/web/css/flags/1x1/tl.svg
new file mode 100644
index 00000000..ec6d44bd
--- /dev/null
+++ b/data/web/css/flags/1x1/tl.svg
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/tm.svg b/data/web/css/flags/1x1/tm.svg
new file mode 100644
index 00000000..d856424f
--- /dev/null
+++ b/data/web/css/flags/1x1/tm.svg
@@ -0,0 +1,203 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/tn.svg b/data/web/css/flags/1x1/tn.svg
new file mode 100644
index 00000000..d46a1cd9
--- /dev/null
+++ b/data/web/css/flags/1x1/tn.svg
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/to.svg b/data/web/css/flags/1x1/to.svg
new file mode 100644
index 00000000..201d6bc3
--- /dev/null
+++ b/data/web/css/flags/1x1/to.svg
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/tr.svg b/data/web/css/flags/1x1/tr.svg
new file mode 100644
index 00000000..861d4ea5
--- /dev/null
+++ b/data/web/css/flags/1x1/tr.svg
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/tt.svg b/data/web/css/flags/1x1/tt.svg
new file mode 100644
index 00000000..87e439a0
--- /dev/null
+++ b/data/web/css/flags/1x1/tt.svg
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/tv.svg b/data/web/css/flags/1x1/tv.svg
new file mode 100644
index 00000000..f30c8f37
--- /dev/null
+++ b/data/web/css/flags/1x1/tv.svg
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/tw.svg b/data/web/css/flags/1x1/tw.svg
new file mode 100644
index 00000000..5f284fc6
--- /dev/null
+++ b/data/web/css/flags/1x1/tw.svg
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/tz.svg b/data/web/css/flags/1x1/tz.svg
new file mode 100644
index 00000000..f993ff15
--- /dev/null
+++ b/data/web/css/flags/1x1/tz.svg
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/ua.svg b/data/web/css/flags/1x1/ua.svg
new file mode 100644
index 00000000..18ebe0d4
--- /dev/null
+++ b/data/web/css/flags/1x1/ua.svg
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/ug.svg b/data/web/css/flags/1x1/ug.svg
new file mode 100644
index 00000000..d9be9459
--- /dev/null
+++ b/data/web/css/flags/1x1/ug.svg
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/um.svg b/data/web/css/flags/1x1/um.svg
new file mode 100644
index 00000000..25b08ce6
--- /dev/null
+++ b/data/web/css/flags/1x1/um.svg
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/un.svg b/data/web/css/flags/1x1/un.svg
new file mode 100644
index 00000000..1d50ea92
--- /dev/null
+++ b/data/web/css/flags/1x1/un.svg
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/us.svg b/data/web/css/flags/1x1/us.svg
new file mode 100644
index 00000000..31f90c6c
--- /dev/null
+++ b/data/web/css/flags/1x1/us.svg
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/uy.svg b/data/web/css/flags/1x1/uy.svg
new file mode 100644
index 00000000..0194a7cb
--- /dev/null
+++ b/data/web/css/flags/1x1/uy.svg
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/uz.svg b/data/web/css/flags/1x1/uz.svg
new file mode 100644
index 00000000..641af1b8
--- /dev/null
+++ b/data/web/css/flags/1x1/uz.svg
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/va.svg b/data/web/css/flags/1x1/va.svg
new file mode 100644
index 00000000..7da6d3e7
--- /dev/null
+++ b/data/web/css/flags/1x1/va.svg
@@ -0,0 +1,479 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/vc.svg b/data/web/css/flags/1x1/vc.svg
new file mode 100644
index 00000000..ee72f781
--- /dev/null
+++ b/data/web/css/flags/1x1/vc.svg
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/ve.svg b/data/web/css/flags/1x1/ve.svg
new file mode 100644
index 00000000..205fe848
--- /dev/null
+++ b/data/web/css/flags/1x1/ve.svg
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/vg.svg b/data/web/css/flags/1x1/vg.svg
new file mode 100644
index 00000000..9572de34
--- /dev/null
+++ b/data/web/css/flags/1x1/vg.svg
@@ -0,0 +1,128 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/vi.svg b/data/web/css/flags/1x1/vi.svg
new file mode 100644
index 00000000..2740f24f
--- /dev/null
+++ b/data/web/css/flags/1x1/vi.svg
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/vn.svg b/data/web/css/flags/1x1/vn.svg
new file mode 100644
index 00000000..6b158145
--- /dev/null
+++ b/data/web/css/flags/1x1/vn.svg
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/vu.svg b/data/web/css/flags/1x1/vu.svg
new file mode 100644
index 00000000..397d30e6
--- /dev/null
+++ b/data/web/css/flags/1x1/vu.svg
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/wf.svg b/data/web/css/flags/1x1/wf.svg
new file mode 100644
index 00000000..bb726a7c
--- /dev/null
+++ b/data/web/css/flags/1x1/wf.svg
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/ws.svg b/data/web/css/flags/1x1/ws.svg
new file mode 100644
index 00000000..155ad7b5
--- /dev/null
+++ b/data/web/css/flags/1x1/ws.svg
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/xk.svg b/data/web/css/flags/1x1/xk.svg
new file mode 100644
index 00000000..69146ca4
--- /dev/null
+++ b/data/web/css/flags/1x1/xk.svg
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/ye.svg b/data/web/css/flags/1x1/ye.svg
new file mode 100644
index 00000000..d49d2c41
--- /dev/null
+++ b/data/web/css/flags/1x1/ye.svg
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/yt.svg b/data/web/css/flags/1x1/yt.svg
new file mode 100644
index 00000000..7bf38373
--- /dev/null
+++ b/data/web/css/flags/1x1/yt.svg
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/za.svg b/data/web/css/flags/1x1/za.svg
new file mode 100644
index 00000000..9bae96fe
--- /dev/null
+++ b/data/web/css/flags/1x1/za.svg
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/zh.svg b/data/web/css/flags/1x1/zh.svg
new file mode 100644
index 00000000..7873c1b4
--- /dev/null
+++ b/data/web/css/flags/1x1/zh.svg
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/zm.svg b/data/web/css/flags/1x1/zm.svg
new file mode 100644
index 00000000..105f1076
--- /dev/null
+++ b/data/web/css/flags/1x1/zm.svg
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/1x1/zw.svg b/data/web/css/flags/1x1/zw.svg
new file mode 100644
index 00000000..ca5b7a20
--- /dev/null
+++ b/data/web/css/flags/1x1/zw.svg
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/ad.svg b/data/web/css/flags/4x3/ad.svg
new file mode 100644
index 00000000..7320bf23
--- /dev/null
+++ b/data/web/css/flags/4x3/ad.svg
@@ -0,0 +1,150 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/ae.svg b/data/web/css/flags/4x3/ae.svg
new file mode 100644
index 00000000..a7bdb17a
--- /dev/null
+++ b/data/web/css/flags/4x3/ae.svg
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/af.svg b/data/web/css/flags/4x3/af.svg
new file mode 100644
index 00000000..399c1c73
--- /dev/null
+++ b/data/web/css/flags/4x3/af.svg
@@ -0,0 +1,81 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/ag.svg b/data/web/css/flags/4x3/ag.svg
new file mode 100644
index 00000000..bdeee48f
--- /dev/null
+++ b/data/web/css/flags/4x3/ag.svg
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/ai.svg b/data/web/css/flags/4x3/ai.svg
new file mode 100644
index 00000000..29e6586f
--- /dev/null
+++ b/data/web/css/flags/4x3/ai.svg
@@ -0,0 +1,763 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/al.svg b/data/web/css/flags/4x3/al.svg
new file mode 100644
index 00000000..e831b028
--- /dev/null
+++ b/data/web/css/flags/4x3/al.svg
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/am.svg b/data/web/css/flags/4x3/am.svg
new file mode 100644
index 00000000..0cd21781
--- /dev/null
+++ b/data/web/css/flags/4x3/am.svg
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/ao.svg b/data/web/css/flags/4x3/ao.svg
new file mode 100644
index 00000000..a0b6dbbd
--- /dev/null
+++ b/data/web/css/flags/4x3/ao.svg
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/aq.svg b/data/web/css/flags/4x3/aq.svg
new file mode 100644
index 00000000..2000e3c8
--- /dev/null
+++ b/data/web/css/flags/4x3/aq.svg
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/ar.svg b/data/web/css/flags/4x3/ar.svg
new file mode 100644
index 00000000..4553b08a
--- /dev/null
+++ b/data/web/css/flags/4x3/ar.svg
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/as.svg b/data/web/css/flags/4x3/as.svg
new file mode 100644
index 00000000..81b0d9f5
--- /dev/null
+++ b/data/web/css/flags/4x3/as.svg
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/at.svg b/data/web/css/flags/4x3/at.svg
new file mode 100644
index 00000000..e63fb2e2
--- /dev/null
+++ b/data/web/css/flags/4x3/at.svg
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/au.svg b/data/web/css/flags/4x3/au.svg
new file mode 100644
index 00000000..18394ab7
--- /dev/null
+++ b/data/web/css/flags/4x3/au.svg
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/aw.svg b/data/web/css/flags/4x3/aw.svg
new file mode 100644
index 00000000..66adb116
--- /dev/null
+++ b/data/web/css/flags/4x3/aw.svg
@@ -0,0 +1,186 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/ax.svg b/data/web/css/flags/4x3/ax.svg
new file mode 100644
index 00000000..dad7d94c
--- /dev/null
+++ b/data/web/css/flags/4x3/ax.svg
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/az.svg b/data/web/css/flags/4x3/az.svg
new file mode 100644
index 00000000..4293e9f5
--- /dev/null
+++ b/data/web/css/flags/4x3/az.svg
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/ba.svg b/data/web/css/flags/4x3/ba.svg
new file mode 100644
index 00000000..6402c103
--- /dev/null
+++ b/data/web/css/flags/4x3/ba.svg
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/bb.svg b/data/web/css/flags/4x3/bb.svg
new file mode 100644
index 00000000..73f53291
--- /dev/null
+++ b/data/web/css/flags/4x3/bb.svg
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/bd.svg b/data/web/css/flags/4x3/bd.svg
new file mode 100644
index 00000000..1e3433e5
--- /dev/null
+++ b/data/web/css/flags/4x3/bd.svg
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/data/web/css/flags/4x3/be.svg b/data/web/css/flags/4x3/be.svg
new file mode 100644
index 00000000..eaf016d0
--- /dev/null
+++ b/data/web/css/flags/4x3/be.svg
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/bf.svg b/data/web/css/flags/4x3/bf.svg
new file mode 100644
index 00000000..c92cce6b
--- /dev/null
+++ b/data/web/css/flags/4x3/bf.svg
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/bg.svg b/data/web/css/flags/4x3/bg.svg
new file mode 100644
index 00000000..15ba696c
--- /dev/null
+++ b/data/web/css/flags/4x3/bg.svg
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/bh.svg b/data/web/css/flags/4x3/bh.svg
new file mode 100644
index 00000000..d4a32c0b
--- /dev/null
+++ b/data/web/css/flags/4x3/bh.svg
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/bi.svg b/data/web/css/flags/4x3/bi.svg
new file mode 100644
index 00000000..091f2df6
--- /dev/null
+++ b/data/web/css/flags/4x3/bi.svg
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/bj.svg b/data/web/css/flags/4x3/bj.svg
new file mode 100644
index 00000000..fa0df27c
--- /dev/null
+++ b/data/web/css/flags/4x3/bj.svg
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/bl.svg b/data/web/css/flags/4x3/bl.svg
new file mode 100644
index 00000000..ecf6ec43
--- /dev/null
+++ b/data/web/css/flags/4x3/bl.svg
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/bm.svg b/data/web/css/flags/4x3/bm.svg
new file mode 100644
index 00000000..5e2c873d
--- /dev/null
+++ b/data/web/css/flags/4x3/bm.svg
@@ -0,0 +1,98 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/bn.svg b/data/web/css/flags/4x3/bn.svg
new file mode 100644
index 00000000..ab6678fa
--- /dev/null
+++ b/data/web/css/flags/4x3/bn.svg
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/bo.svg b/data/web/css/flags/4x3/bo.svg
new file mode 100644
index 00000000..c2dcbc83
--- /dev/null
+++ b/data/web/css/flags/4x3/bo.svg
@@ -0,0 +1,676 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/bq.svg b/data/web/css/flags/4x3/bq.svg
new file mode 100644
index 00000000..4b74eb40
--- /dev/null
+++ b/data/web/css/flags/4x3/bq.svg
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/br.svg b/data/web/css/flags/4x3/br.svg
new file mode 100644
index 00000000..3252a8ec
--- /dev/null
+++ b/data/web/css/flags/4x3/br.svg
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/bs.svg b/data/web/css/flags/4x3/bs.svg
new file mode 100644
index 00000000..a14500bc
--- /dev/null
+++ b/data/web/css/flags/4x3/bs.svg
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/bt.svg b/data/web/css/flags/4x3/bt.svg
new file mode 100644
index 00000000..f4b6e636
--- /dev/null
+++ b/data/web/css/flags/4x3/bt.svg
@@ -0,0 +1,89 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/bv.svg b/data/web/css/flags/4x3/bv.svg
new file mode 100644
index 00000000..71d098a2
--- /dev/null
+++ b/data/web/css/flags/4x3/bv.svg
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/bw.svg b/data/web/css/flags/4x3/bw.svg
new file mode 100644
index 00000000..3d65eda2
--- /dev/null
+++ b/data/web/css/flags/4x3/bw.svg
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/by.svg b/data/web/css/flags/4x3/by.svg
new file mode 100644
index 00000000..9566c6a7
--- /dev/null
+++ b/data/web/css/flags/4x3/by.svg
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/bz.svg b/data/web/css/flags/4x3/bz.svg
new file mode 100644
index 00000000..68249577
--- /dev/null
+++ b/data/web/css/flags/4x3/bz.svg
@@ -0,0 +1,145 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/ca.svg b/data/web/css/flags/4x3/ca.svg
new file mode 100644
index 00000000..4843fc32
--- /dev/null
+++ b/data/web/css/flags/4x3/ca.svg
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/data/web/css/flags/4x3/cc.svg b/data/web/css/flags/4x3/cc.svg
new file mode 100644
index 00000000..2c456980
--- /dev/null
+++ b/data/web/css/flags/4x3/cc.svg
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/cd.svg b/data/web/css/flags/4x3/cd.svg
new file mode 100644
index 00000000..739fab77
--- /dev/null
+++ b/data/web/css/flags/4x3/cd.svg
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/cf.svg b/data/web/css/flags/4x3/cf.svg
new file mode 100644
index 00000000..9f19138b
--- /dev/null
+++ b/data/web/css/flags/4x3/cf.svg
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/cg.svg b/data/web/css/flags/4x3/cg.svg
new file mode 100644
index 00000000..8b66be24
--- /dev/null
+++ b/data/web/css/flags/4x3/cg.svg
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/ch.svg b/data/web/css/flags/4x3/ch.svg
new file mode 100644
index 00000000..f5ec8d46
--- /dev/null
+++ b/data/web/css/flags/4x3/ch.svg
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/ci.svg b/data/web/css/flags/4x3/ci.svg
new file mode 100644
index 00000000..2bd71047
--- /dev/null
+++ b/data/web/css/flags/4x3/ci.svg
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/ck.svg b/data/web/css/flags/4x3/ck.svg
new file mode 100644
index 00000000..e4f0d0c4
--- /dev/null
+++ b/data/web/css/flags/4x3/ck.svg
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/cl.svg b/data/web/css/flags/4x3/cl.svg
new file mode 100644
index 00000000..287a37e8
--- /dev/null
+++ b/data/web/css/flags/4x3/cl.svg
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/cm.svg b/data/web/css/flags/4x3/cm.svg
new file mode 100644
index 00000000..b4272b7d
--- /dev/null
+++ b/data/web/css/flags/4x3/cm.svg
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/cn.svg b/data/web/css/flags/4x3/cn.svg
new file mode 100644
index 00000000..72080b6b
--- /dev/null
+++ b/data/web/css/flags/4x3/cn.svg
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/co.svg b/data/web/css/flags/4x3/co.svg
new file mode 100644
index 00000000..0d74127a
--- /dev/null
+++ b/data/web/css/flags/4x3/co.svg
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/cr.svg b/data/web/css/flags/4x3/cr.svg
new file mode 100644
index 00000000..133b0293
--- /dev/null
+++ b/data/web/css/flags/4x3/cr.svg
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/cs.svg b/data/web/css/flags/4x3/cs.svg
new file mode 100644
index 00000000..87675814
--- /dev/null
+++ b/data/web/css/flags/4x3/cs.svg
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/cu.svg b/data/web/css/flags/4x3/cu.svg
new file mode 100644
index 00000000..079a29b1
--- /dev/null
+++ b/data/web/css/flags/4x3/cu.svg
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/cv.svg b/data/web/css/flags/4x3/cv.svg
new file mode 100644
index 00000000..39a7b7e8
--- /dev/null
+++ b/data/web/css/flags/4x3/cv.svg
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/cw.svg b/data/web/css/flags/4x3/cw.svg
new file mode 100644
index 00000000..533644d5
--- /dev/null
+++ b/data/web/css/flags/4x3/cw.svg
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/cx.svg b/data/web/css/flags/4x3/cx.svg
new file mode 100644
index 00000000..f5b89b7d
--- /dev/null
+++ b/data/web/css/flags/4x3/cx.svg
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/cy.svg b/data/web/css/flags/4x3/cy.svg
new file mode 100644
index 00000000..3d483a12
--- /dev/null
+++ b/data/web/css/flags/4x3/cy.svg
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/cz.svg b/data/web/css/flags/4x3/cz.svg
new file mode 100644
index 00000000..87675814
--- /dev/null
+++ b/data/web/css/flags/4x3/cz.svg
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/da.svg b/data/web/css/flags/4x3/da.svg
new file mode 100644
index 00000000..ab47e0cb
--- /dev/null
+++ b/data/web/css/flags/4x3/da.svg
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/de.svg b/data/web/css/flags/4x3/de.svg
new file mode 100644
index 00000000..1acf302d
--- /dev/null
+++ b/data/web/css/flags/4x3/de.svg
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/dj.svg b/data/web/css/flags/4x3/dj.svg
new file mode 100644
index 00000000..f328f3a9
--- /dev/null
+++ b/data/web/css/flags/4x3/dj.svg
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/dk.svg b/data/web/css/flags/4x3/dk.svg
new file mode 100644
index 00000000..ab47e0cb
--- /dev/null
+++ b/data/web/css/flags/4x3/dk.svg
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/dm.svg b/data/web/css/flags/4x3/dm.svg
new file mode 100644
index 00000000..e35d7cae
--- /dev/null
+++ b/data/web/css/flags/4x3/dm.svg
@@ -0,0 +1,152 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/do.svg b/data/web/css/flags/4x3/do.svg
new file mode 100644
index 00000000..97bae5f4
--- /dev/null
+++ b/data/web/css/flags/4x3/do.svg
@@ -0,0 +1,6745 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/dz.svg b/data/web/css/flags/4x3/dz.svg
new file mode 100644
index 00000000..aa4eca2b
--- /dev/null
+++ b/data/web/css/flags/4x3/dz.svg
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/ec.svg b/data/web/css/flags/4x3/ec.svg
new file mode 100644
index 00000000..42fbef52
--- /dev/null
+++ b/data/web/css/flags/4x3/ec.svg
@@ -0,0 +1,138 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/ee.svg b/data/web/css/flags/4x3/ee.svg
new file mode 100644
index 00000000..aa917bbe
--- /dev/null
+++ b/data/web/css/flags/4x3/ee.svg
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/eg.svg b/data/web/css/flags/4x3/eg.svg
new file mode 100644
index 00000000..a755cd71
--- /dev/null
+++ b/data/web/css/flags/4x3/eg.svg
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/eh.svg b/data/web/css/flags/4x3/eh.svg
new file mode 100644
index 00000000..438c382a
--- /dev/null
+++ b/data/web/css/flags/4x3/eh.svg
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/en.svg b/data/web/css/flags/4x3/en.svg
new file mode 100644
index 00000000..132dbedb
--- /dev/null
+++ b/data/web/css/flags/4x3/en.svg
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/er.svg b/data/web/css/flags/4x3/er.svg
new file mode 100644
index 00000000..7a257982
--- /dev/null
+++ b/data/web/css/flags/4x3/er.svg
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/es-ca.svg b/data/web/css/flags/4x3/es-ca.svg
new file mode 100644
index 00000000..fc7c2e8c
--- /dev/null
+++ b/data/web/css/flags/4x3/es-ca.svg
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/data/web/css/flags/4x3/es-ga.svg b/data/web/css/flags/4x3/es-ga.svg
new file mode 100644
index 00000000..4e213f52
--- /dev/null
+++ b/data/web/css/flags/4x3/es-ga.svg
@@ -0,0 +1,189 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/es.svg b/data/web/css/flags/4x3/es.svg
new file mode 100644
index 00000000..576dd67c
--- /dev/null
+++ b/data/web/css/flags/4x3/es.svg
@@ -0,0 +1,544 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/et.svg b/data/web/css/flags/4x3/et.svg
new file mode 100644
index 00000000..9145fd7b
--- /dev/null
+++ b/data/web/css/flags/4x3/et.svg
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/eu.svg b/data/web/css/flags/4x3/eu.svg
new file mode 100644
index 00000000..b6a39f5f
--- /dev/null
+++ b/data/web/css/flags/4x3/eu.svg
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/fi.svg b/data/web/css/flags/4x3/fi.svg
new file mode 100644
index 00000000..c3451a47
--- /dev/null
+++ b/data/web/css/flags/4x3/fi.svg
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/fj.svg b/data/web/css/flags/4x3/fj.svg
new file mode 100644
index 00000000..c7549b56
--- /dev/null
+++ b/data/web/css/flags/4x3/fj.svg
@@ -0,0 +1,122 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/fk.svg b/data/web/css/flags/4x3/fk.svg
new file mode 100644
index 00000000..28ad7027
--- /dev/null
+++ b/data/web/css/flags/4x3/fk.svg
@@ -0,0 +1,90 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/fm.svg b/data/web/css/flags/4x3/fm.svg
new file mode 100644
index 00000000..54f3e779
--- /dev/null
+++ b/data/web/css/flags/4x3/fm.svg
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/fo.svg b/data/web/css/flags/4x3/fo.svg
new file mode 100644
index 00000000..341d52ee
--- /dev/null
+++ b/data/web/css/flags/4x3/fo.svg
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/fr.svg b/data/web/css/flags/4x3/fr.svg
new file mode 100644
index 00000000..712c8a5d
--- /dev/null
+++ b/data/web/css/flags/4x3/fr.svg
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/ga.svg b/data/web/css/flags/4x3/ga.svg
new file mode 100644
index 00000000..1f0a9ca2
--- /dev/null
+++ b/data/web/css/flags/4x3/ga.svg
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/gb-eng.svg b/data/web/css/flags/4x3/gb-eng.svg
new file mode 100644
index 00000000..eab52bd5
--- /dev/null
+++ b/data/web/css/flags/4x3/gb-eng.svg
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/gb-nir.svg b/data/web/css/flags/4x3/gb-nir.svg
new file mode 100644
index 00000000..e043b3e3
--- /dev/null
+++ b/data/web/css/flags/4x3/gb-nir.svg
@@ -0,0 +1,132 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/gb-sct.svg b/data/web/css/flags/4x3/gb-sct.svg
new file mode 100644
index 00000000..169bfba4
--- /dev/null
+++ b/data/web/css/flags/4x3/gb-sct.svg
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/data/web/css/flags/4x3/gb-wls.svg b/data/web/css/flags/4x3/gb-wls.svg
new file mode 100644
index 00000000..48a64205
--- /dev/null
+++ b/data/web/css/flags/4x3/gb-wls.svg
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/gb.svg b/data/web/css/flags/4x3/gb.svg
new file mode 100644
index 00000000..132dbedb
--- /dev/null
+++ b/data/web/css/flags/4x3/gb.svg
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/gd.svg b/data/web/css/flags/4x3/gd.svg
new file mode 100644
index 00000000..f2254f34
--- /dev/null
+++ b/data/web/css/flags/4x3/gd.svg
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/ge.svg b/data/web/css/flags/4x3/ge.svg
new file mode 100644
index 00000000..8abdee30
--- /dev/null
+++ b/data/web/css/flags/4x3/ge.svg
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/gf.svg b/data/web/css/flags/4x3/gf.svg
new file mode 100644
index 00000000..e3828837
--- /dev/null
+++ b/data/web/css/flags/4x3/gf.svg
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/gg.svg b/data/web/css/flags/4x3/gg.svg
new file mode 100644
index 00000000..deb4af5a
--- /dev/null
+++ b/data/web/css/flags/4x3/gg.svg
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/gh.svg b/data/web/css/flags/4x3/gh.svg
new file mode 100644
index 00000000..3f978344
--- /dev/null
+++ b/data/web/css/flags/4x3/gh.svg
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/gi.svg b/data/web/css/flags/4x3/gi.svg
new file mode 100644
index 00000000..1b44fd6f
--- /dev/null
+++ b/data/web/css/flags/4x3/gi.svg
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/gl.svg b/data/web/css/flags/4x3/gl.svg
new file mode 100644
index 00000000..20f5c5ee
--- /dev/null
+++ b/data/web/css/flags/4x3/gl.svg
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/data/web/css/flags/4x3/gm.svg b/data/web/css/flags/4x3/gm.svg
new file mode 100644
index 00000000..76d03afc
--- /dev/null
+++ b/data/web/css/flags/4x3/gm.svg
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/gn.svg b/data/web/css/flags/4x3/gn.svg
new file mode 100644
index 00000000..7841e8d0
--- /dev/null
+++ b/data/web/css/flags/4x3/gn.svg
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/gp.svg b/data/web/css/flags/4x3/gp.svg
new file mode 100644
index 00000000..24a82602
--- /dev/null
+++ b/data/web/css/flags/4x3/gp.svg
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/gq.svg b/data/web/css/flags/4x3/gq.svg
new file mode 100644
index 00000000..4cec1a52
--- /dev/null
+++ b/data/web/css/flags/4x3/gq.svg
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/gr.svg b/data/web/css/flags/4x3/gr.svg
new file mode 100644
index 00000000..581af285
--- /dev/null
+++ b/data/web/css/flags/4x3/gr.svg
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/gs.svg b/data/web/css/flags/4x3/gs.svg
new file mode 100644
index 00000000..68335551
--- /dev/null
+++ b/data/web/css/flags/4x3/gs.svg
@@ -0,0 +1,201 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ L
+
+
+ E
+
+
+ O
+
+
+ T
+
+
+ E
+
+
+ R
+
+
+ R
+
+
+ R
+
+
+ R
+
+
+ R
+
+
+ E
+
+
+ O
+
+
+ O
+
+
+ A
+
+
+ A
+
+
+ A
+
+
+ M
+
+
+ P
+
+
+ P
+
+
+ P
+
+
+ I
+
+
+ T
+
+
+ T
+
+
+ M
+
+
+ G
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/gt.svg b/data/web/css/flags/4x3/gt.svg
new file mode 100644
index 00000000..724e9702
--- /dev/null
+++ b/data/web/css/flags/4x3/gt.svg
@@ -0,0 +1,204 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/gu.svg b/data/web/css/flags/4x3/gu.svg
new file mode 100644
index 00000000..00e86151
--- /dev/null
+++ b/data/web/css/flags/4x3/gu.svg
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
+
+ G
+
+
+ U
+
+
+ A
+
+
+ M
+
+
+
+
+
+
+
+ G
+
+
+ U
+
+
+ A
+
+
+ M
+
+
diff --git a/data/web/css/flags/4x3/gw.svg b/data/web/css/flags/4x3/gw.svg
new file mode 100644
index 00000000..ae173140
--- /dev/null
+++ b/data/web/css/flags/4x3/gw.svg
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/gy.svg b/data/web/css/flags/4x3/gy.svg
new file mode 100644
index 00000000..e957f3ef
--- /dev/null
+++ b/data/web/css/flags/4x3/gy.svg
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/hk.svg b/data/web/css/flags/4x3/hk.svg
new file mode 100644
index 00000000..9e452928
--- /dev/null
+++ b/data/web/css/flags/4x3/hk.svg
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/hm.svg b/data/web/css/flags/4x3/hm.svg
new file mode 100644
index 00000000..a9dda074
--- /dev/null
+++ b/data/web/css/flags/4x3/hm.svg
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/hn.svg b/data/web/css/flags/4x3/hn.svg
new file mode 100644
index 00000000..47af518e
--- /dev/null
+++ b/data/web/css/flags/4x3/hn.svg
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/hr.svg b/data/web/css/flags/4x3/hr.svg
new file mode 100644
index 00000000..7b699d33
--- /dev/null
+++ b/data/web/css/flags/4x3/hr.svg
@@ -0,0 +1,58 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/ht.svg b/data/web/css/flags/4x3/ht.svg
new file mode 100644
index 00000000..14f67d78
--- /dev/null
+++ b/data/web/css/flags/4x3/ht.svg
@@ -0,0 +1,116 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/hu.svg b/data/web/css/flags/4x3/hu.svg
new file mode 100644
index 00000000..177da97a
--- /dev/null
+++ b/data/web/css/flags/4x3/hu.svg
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/id.svg b/data/web/css/flags/4x3/id.svg
new file mode 100644
index 00000000..0663baff
--- /dev/null
+++ b/data/web/css/flags/4x3/id.svg
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/ie.svg b/data/web/css/flags/4x3/ie.svg
new file mode 100644
index 00000000..53f34643
--- /dev/null
+++ b/data/web/css/flags/4x3/ie.svg
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/il.svg b/data/web/css/flags/4x3/il.svg
new file mode 100644
index 00000000..fc10b47d
--- /dev/null
+++ b/data/web/css/flags/4x3/il.svg
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/im.svg b/data/web/css/flags/4x3/im.svg
new file mode 100644
index 00000000..31b22223
--- /dev/null
+++ b/data/web/css/flags/4x3/im.svg
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/in.svg b/data/web/css/flags/4x3/in.svg
new file mode 100644
index 00000000..6b831bcf
--- /dev/null
+++ b/data/web/css/flags/4x3/in.svg
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/io.svg b/data/web/css/flags/4x3/io.svg
new file mode 100644
index 00000000..d6e58547
--- /dev/null
+++ b/data/web/css/flags/4x3/io.svg
@@ -0,0 +1,138 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/iq.svg b/data/web/css/flags/4x3/iq.svg
new file mode 100644
index 00000000..ab90fd01
--- /dev/null
+++ b/data/web/css/flags/4x3/iq.svg
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/ir.svg b/data/web/css/flags/4x3/ir.svg
new file mode 100644
index 00000000..ffb37b47
--- /dev/null
+++ b/data/web/css/flags/4x3/ir.svg
@@ -0,0 +1,219 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/is.svg b/data/web/css/flags/4x3/is.svg
new file mode 100644
index 00000000..a7524b56
--- /dev/null
+++ b/data/web/css/flags/4x3/is.svg
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/it.svg b/data/web/css/flags/4x3/it.svg
new file mode 100644
index 00000000..5cb92aaa
--- /dev/null
+++ b/data/web/css/flags/4x3/it.svg
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/je.svg b/data/web/css/flags/4x3/je.svg
new file mode 100644
index 00000000..d90f124d
--- /dev/null
+++ b/data/web/css/flags/4x3/je.svg
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/jm.svg b/data/web/css/flags/4x3/jm.svg
new file mode 100644
index 00000000..535daf44
--- /dev/null
+++ b/data/web/css/flags/4x3/jm.svg
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/jo.svg b/data/web/css/flags/4x3/jo.svg
new file mode 100644
index 00000000..3a17678c
--- /dev/null
+++ b/data/web/css/flags/4x3/jo.svg
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/jp.svg b/data/web/css/flags/4x3/jp.svg
new file mode 100644
index 00000000..d4158164
--- /dev/null
+++ b/data/web/css/flags/4x3/jp.svg
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/ke.svg b/data/web/css/flags/4x3/ke.svg
new file mode 100644
index 00000000..c1fd2d8e
--- /dev/null
+++ b/data/web/css/flags/4x3/ke.svg
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/kg.svg b/data/web/css/flags/4x3/kg.svg
new file mode 100644
index 00000000..936767bf
--- /dev/null
+++ b/data/web/css/flags/4x3/kg.svg
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/kh.svg b/data/web/css/flags/4x3/kh.svg
new file mode 100644
index 00000000..7cd890aa
--- /dev/null
+++ b/data/web/css/flags/4x3/kh.svg
@@ -0,0 +1,61 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/ki.svg b/data/web/css/flags/4x3/ki.svg
new file mode 100644
index 00000000..604890cb
--- /dev/null
+++ b/data/web/css/flags/4x3/ki.svg
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/km.svg b/data/web/css/flags/4x3/km.svg
new file mode 100644
index 00000000..a6ccb8d0
--- /dev/null
+++ b/data/web/css/flags/4x3/km.svg
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/kn.svg b/data/web/css/flags/4x3/kn.svg
new file mode 100644
index 00000000..d13066b5
--- /dev/null
+++ b/data/web/css/flags/4x3/kn.svg
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/ko.svg b/data/web/css/flags/4x3/ko.svg
new file mode 100644
index 00000000..39508cbf
--- /dev/null
+++ b/data/web/css/flags/4x3/ko.svg
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/kp.svg b/data/web/css/flags/4x3/kp.svg
new file mode 100644
index 00000000..06b4e388
--- /dev/null
+++ b/data/web/css/flags/4x3/kp.svg
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/kr.svg b/data/web/css/flags/4x3/kr.svg
new file mode 100644
index 00000000..39508cbf
--- /dev/null
+++ b/data/web/css/flags/4x3/kr.svg
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/kw.svg b/data/web/css/flags/4x3/kw.svg
new file mode 100644
index 00000000..b4a4bf29
--- /dev/null
+++ b/data/web/css/flags/4x3/kw.svg
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/ky.svg b/data/web/css/flags/4x3/ky.svg
new file mode 100644
index 00000000..1a2d9c9a
--- /dev/null
+++ b/data/web/css/flags/4x3/ky.svg
@@ -0,0 +1,63 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/kz.svg b/data/web/css/flags/4x3/kz.svg
new file mode 100644
index 00000000..049d6fdb
--- /dev/null
+++ b/data/web/css/flags/4x3/kz.svg
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/la.svg b/data/web/css/flags/4x3/la.svg
new file mode 100644
index 00000000..a87bc987
--- /dev/null
+++ b/data/web/css/flags/4x3/la.svg
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/lb.svg b/data/web/css/flags/4x3/lb.svg
new file mode 100644
index 00000000..09678429
--- /dev/null
+++ b/data/web/css/flags/4x3/lb.svg
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/lc.svg b/data/web/css/flags/4x3/lc.svg
new file mode 100644
index 00000000..8ba746c5
--- /dev/null
+++ b/data/web/css/flags/4x3/lc.svg
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/li.svg b/data/web/css/flags/4x3/li.svg
new file mode 100644
index 00000000..6b4160c8
--- /dev/null
+++ b/data/web/css/flags/4x3/li.svg
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/lk.svg b/data/web/css/flags/4x3/lk.svg
new file mode 100644
index 00000000..4a377f15
--- /dev/null
+++ b/data/web/css/flags/4x3/lk.svg
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/lr.svg b/data/web/css/flags/4x3/lr.svg
new file mode 100644
index 00000000..00e3629e
--- /dev/null
+++ b/data/web/css/flags/4x3/lr.svg
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/ls.svg b/data/web/css/flags/4x3/ls.svg
new file mode 100644
index 00000000..5dd5b0c3
--- /dev/null
+++ b/data/web/css/flags/4x3/ls.svg
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/lt.svg b/data/web/css/flags/4x3/lt.svg
new file mode 100644
index 00000000..36e30e9e
--- /dev/null
+++ b/data/web/css/flags/4x3/lt.svg
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/lu.svg b/data/web/css/flags/4x3/lu.svg
new file mode 100644
index 00000000..bf6ca167
--- /dev/null
+++ b/data/web/css/flags/4x3/lu.svg
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/lv.svg b/data/web/css/flags/4x3/lv.svg
new file mode 100644
index 00000000..efa5a328
--- /dev/null
+++ b/data/web/css/flags/4x3/lv.svg
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/ly.svg b/data/web/css/flags/4x3/ly.svg
new file mode 100644
index 00000000..fcc2dc91
--- /dev/null
+++ b/data/web/css/flags/4x3/ly.svg
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/ma.svg b/data/web/css/flags/4x3/ma.svg
new file mode 100644
index 00000000..8de40777
--- /dev/null
+++ b/data/web/css/flags/4x3/ma.svg
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/data/web/css/flags/4x3/mc.svg b/data/web/css/flags/4x3/mc.svg
new file mode 100644
index 00000000..45133dcb
--- /dev/null
+++ b/data/web/css/flags/4x3/mc.svg
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/md.svg b/data/web/css/flags/4x3/md.svg
new file mode 100644
index 00000000..a86be909
--- /dev/null
+++ b/data/web/css/flags/4x3/md.svg
@@ -0,0 +1,70 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/me.svg b/data/web/css/flags/4x3/me.svg
new file mode 100644
index 00000000..613083ff
--- /dev/null
+++ b/data/web/css/flags/4x3/me.svg
@@ -0,0 +1,116 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/mf.svg b/data/web/css/flags/4x3/mf.svg
new file mode 100644
index 00000000..bf46785c
--- /dev/null
+++ b/data/web/css/flags/4x3/mf.svg
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/mg.svg b/data/web/css/flags/4x3/mg.svg
new file mode 100644
index 00000000..76e84fc4
--- /dev/null
+++ b/data/web/css/flags/4x3/mg.svg
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/mh.svg b/data/web/css/flags/4x3/mh.svg
new file mode 100644
index 00000000..f74e99d7
--- /dev/null
+++ b/data/web/css/flags/4x3/mh.svg
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/mk.svg b/data/web/css/flags/4x3/mk.svg
new file mode 100644
index 00000000..1f1eaf68
--- /dev/null
+++ b/data/web/css/flags/4x3/mk.svg
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/ml.svg b/data/web/css/flags/4x3/ml.svg
new file mode 100644
index 00000000..66da1bb9
--- /dev/null
+++ b/data/web/css/flags/4x3/ml.svg
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/mm.svg b/data/web/css/flags/4x3/mm.svg
new file mode 100644
index 00000000..c77927ec
--- /dev/null
+++ b/data/web/css/flags/4x3/mm.svg
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/mn.svg b/data/web/css/flags/4x3/mn.svg
new file mode 100644
index 00000000..6c629732
--- /dev/null
+++ b/data/web/css/flags/4x3/mn.svg
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/mo.svg b/data/web/css/flags/4x3/mo.svg
new file mode 100644
index 00000000..30e9129d
--- /dev/null
+++ b/data/web/css/flags/4x3/mo.svg
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/mp.svg b/data/web/css/flags/4x3/mp.svg
new file mode 100644
index 00000000..bf9ed634
--- /dev/null
+++ b/data/web/css/flags/4x3/mp.svg
@@ -0,0 +1,86 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/mq.svg b/data/web/css/flags/4x3/mq.svg
new file mode 100644
index 00000000..b389d439
--- /dev/null
+++ b/data/web/css/flags/4x3/mq.svg
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/mr.svg b/data/web/css/flags/4x3/mr.svg
new file mode 100644
index 00000000..b53ce61a
--- /dev/null
+++ b/data/web/css/flags/4x3/mr.svg
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/ms.svg b/data/web/css/flags/4x3/ms.svg
new file mode 100644
index 00000000..57968635
--- /dev/null
+++ b/data/web/css/flags/4x3/ms.svg
@@ -0,0 +1,78 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/mt.svg b/data/web/css/flags/4x3/mt.svg
new file mode 100644
index 00000000..93fe67b7
--- /dev/null
+++ b/data/web/css/flags/4x3/mt.svg
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/mu.svg b/data/web/css/flags/4x3/mu.svg
new file mode 100644
index 00000000..102ed3be
--- /dev/null
+++ b/data/web/css/flags/4x3/mu.svg
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/mv.svg b/data/web/css/flags/4x3/mv.svg
new file mode 100644
index 00000000..2525c923
--- /dev/null
+++ b/data/web/css/flags/4x3/mv.svg
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/mw.svg b/data/web/css/flags/4x3/mw.svg
new file mode 100644
index 00000000..5bd17f89
--- /dev/null
+++ b/data/web/css/flags/4x3/mw.svg
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/mx.svg b/data/web/css/flags/4x3/mx.svg
new file mode 100644
index 00000000..7de1b66a
--- /dev/null
+++ b/data/web/css/flags/4x3/mx.svg
@@ -0,0 +1,382 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/my.svg b/data/web/css/flags/4x3/my.svg
new file mode 100644
index 00000000..a08f0859
--- /dev/null
+++ b/data/web/css/flags/4x3/my.svg
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/mz.svg b/data/web/css/flags/4x3/mz.svg
new file mode 100644
index 00000000..2e98e991
--- /dev/null
+++ b/data/web/css/flags/4x3/mz.svg
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/na.svg b/data/web/css/flags/4x3/na.svg
new file mode 100644
index 00000000..f2f571fa
--- /dev/null
+++ b/data/web/css/flags/4x3/na.svg
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/nc.svg b/data/web/css/flags/4x3/nc.svg
new file mode 100644
index 00000000..4a2ac30c
--- /dev/null
+++ b/data/web/css/flags/4x3/nc.svg
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/ne.svg b/data/web/css/flags/4x3/ne.svg
new file mode 100644
index 00000000..f4709078
--- /dev/null
+++ b/data/web/css/flags/4x3/ne.svg
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/nf.svg b/data/web/css/flags/4x3/nf.svg
new file mode 100644
index 00000000..46d7e8fd
--- /dev/null
+++ b/data/web/css/flags/4x3/nf.svg
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/ng.svg b/data/web/css/flags/4x3/ng.svg
new file mode 100644
index 00000000..42ee5ad4
--- /dev/null
+++ b/data/web/css/flags/4x3/ng.svg
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/ni.svg b/data/web/css/flags/4x3/ni.svg
new file mode 100644
index 00000000..f77ed63e
--- /dev/null
+++ b/data/web/css/flags/4x3/ni.svg
@@ -0,0 +1,129 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/nl.svg b/data/web/css/flags/4x3/nl.svg
new file mode 100644
index 00000000..20269372
--- /dev/null
+++ b/data/web/css/flags/4x3/nl.svg
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/no.svg b/data/web/css/flags/4x3/no.svg
new file mode 100644
index 00000000..56e78e15
--- /dev/null
+++ b/data/web/css/flags/4x3/no.svg
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/np.svg b/data/web/css/flags/4x3/np.svg
new file mode 100644
index 00000000..4e151e41
--- /dev/null
+++ b/data/web/css/flags/4x3/np.svg
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/nr.svg b/data/web/css/flags/4x3/nr.svg
new file mode 100644
index 00000000..cfdc0d14
--- /dev/null
+++ b/data/web/css/flags/4x3/nr.svg
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/nu.svg b/data/web/css/flags/4x3/nu.svg
new file mode 100644
index 00000000..b38585e1
--- /dev/null
+++ b/data/web/css/flags/4x3/nu.svg
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/nz.svg b/data/web/css/flags/4x3/nz.svg
new file mode 100644
index 00000000..ba7cac02
--- /dev/null
+++ b/data/web/css/flags/4x3/nz.svg
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/om.svg b/data/web/css/flags/4x3/om.svg
new file mode 100644
index 00000000..68fe15d6
--- /dev/null
+++ b/data/web/css/flags/4x3/om.svg
@@ -0,0 +1,115 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/pa.svg b/data/web/css/flags/4x3/pa.svg
new file mode 100644
index 00000000..09b3e119
--- /dev/null
+++ b/data/web/css/flags/4x3/pa.svg
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/pe.svg b/data/web/css/flags/4x3/pe.svg
new file mode 100644
index 00000000..71a1be11
--- /dev/null
+++ b/data/web/css/flags/4x3/pe.svg
@@ -0,0 +1,244 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/pf.svg b/data/web/css/flags/4x3/pf.svg
new file mode 100644
index 00000000..7a325563
--- /dev/null
+++ b/data/web/css/flags/4x3/pf.svg
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/pg.svg b/data/web/css/flags/4x3/pg.svg
new file mode 100644
index 00000000..5fb0cd58
--- /dev/null
+++ b/data/web/css/flags/4x3/pg.svg
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/ph.svg b/data/web/css/flags/4x3/ph.svg
new file mode 100644
index 00000000..a0c37d83
--- /dev/null
+++ b/data/web/css/flags/4x3/ph.svg
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/pk.svg b/data/web/css/flags/4x3/pk.svg
new file mode 100644
index 00000000..387265cd
--- /dev/null
+++ b/data/web/css/flags/4x3/pk.svg
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/pl.svg b/data/web/css/flags/4x3/pl.svg
new file mode 100644
index 00000000..c00513a2
--- /dev/null
+++ b/data/web/css/flags/4x3/pl.svg
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/pm.svg b/data/web/css/flags/4x3/pm.svg
new file mode 100644
index 00000000..07ea24cc
--- /dev/null
+++ b/data/web/css/flags/4x3/pm.svg
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/pn.svg b/data/web/css/flags/4x3/pn.svg
new file mode 100644
index 00000000..80682ab2
--- /dev/null
+++ b/data/web/css/flags/4x3/pn.svg
@@ -0,0 +1,62 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/pr.svg b/data/web/css/flags/4x3/pr.svg
new file mode 100644
index 00000000..191c56a7
--- /dev/null
+++ b/data/web/css/flags/4x3/pr.svg
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/ps.svg b/data/web/css/flags/4x3/ps.svg
new file mode 100644
index 00000000..a6dedf5f
--- /dev/null
+++ b/data/web/css/flags/4x3/ps.svg
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/pt.svg b/data/web/css/flags/4x3/pt.svg
new file mode 100644
index 00000000..fa9621f2
--- /dev/null
+++ b/data/web/css/flags/4x3/pt.svg
@@ -0,0 +1,57 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/pw.svg b/data/web/css/flags/4x3/pw.svg
new file mode 100644
index 00000000..8127a2c0
--- /dev/null
+++ b/data/web/css/flags/4x3/pw.svg
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/py.svg b/data/web/css/flags/4x3/py.svg
new file mode 100644
index 00000000..14025209
--- /dev/null
+++ b/data/web/css/flags/4x3/py.svg
@@ -0,0 +1,157 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/qa.svg b/data/web/css/flags/4x3/qa.svg
new file mode 100644
index 00000000..0f93a9d0
--- /dev/null
+++ b/data/web/css/flags/4x3/qa.svg
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/data/web/css/flags/4x3/re.svg b/data/web/css/flags/4x3/re.svg
new file mode 100644
index 00000000..827103b1
--- /dev/null
+++ b/data/web/css/flags/4x3/re.svg
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/ro.svg b/data/web/css/flags/4x3/ro.svg
new file mode 100644
index 00000000..d43c1e26
--- /dev/null
+++ b/data/web/css/flags/4x3/ro.svg
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/rs.svg b/data/web/css/flags/4x3/rs.svg
new file mode 100644
index 00000000..6469365d
--- /dev/null
+++ b/data/web/css/flags/4x3/rs.svg
@@ -0,0 +1,292 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/ru.svg b/data/web/css/flags/4x3/ru.svg
new file mode 100644
index 00000000..f56fddb9
--- /dev/null
+++ b/data/web/css/flags/4x3/ru.svg
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/rw.svg b/data/web/css/flags/4x3/rw.svg
new file mode 100644
index 00000000..afed4e3e
--- /dev/null
+++ b/data/web/css/flags/4x3/rw.svg
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/sa.svg b/data/web/css/flags/4x3/sa.svg
new file mode 100644
index 00000000..c9e8cca5
--- /dev/null
+++ b/data/web/css/flags/4x3/sa.svg
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/sb.svg b/data/web/css/flags/4x3/sb.svg
new file mode 100644
index 00000000..b8e1f8e9
--- /dev/null
+++ b/data/web/css/flags/4x3/sb.svg
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/sc.svg b/data/web/css/flags/4x3/sc.svg
new file mode 100644
index 00000000..2286e147
--- /dev/null
+++ b/data/web/css/flags/4x3/sc.svg
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/sd.svg b/data/web/css/flags/4x3/sd.svg
new file mode 100644
index 00000000..99814945
--- /dev/null
+++ b/data/web/css/flags/4x3/sd.svg
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/se.svg b/data/web/css/flags/4x3/se.svg
new file mode 100644
index 00000000..e9977d71
--- /dev/null
+++ b/data/web/css/flags/4x3/se.svg
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/sg.svg b/data/web/css/flags/4x3/sg.svg
new file mode 100644
index 00000000..89316600
--- /dev/null
+++ b/data/web/css/flags/4x3/sg.svg
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/sh.svg b/data/web/css/flags/4x3/sh.svg
new file mode 100644
index 00000000..a768813f
--- /dev/null
+++ b/data/web/css/flags/4x3/sh.svg
@@ -0,0 +1,74 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/si.svg b/data/web/css/flags/4x3/si.svg
new file mode 100644
index 00000000..a83515b8
--- /dev/null
+++ b/data/web/css/flags/4x3/si.svg
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/sj.svg b/data/web/css/flags/4x3/sj.svg
new file mode 100644
index 00000000..8e5d104a
--- /dev/null
+++ b/data/web/css/flags/4x3/sj.svg
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/sk.svg b/data/web/css/flags/4x3/sk.svg
new file mode 100644
index 00000000..3d9ea274
--- /dev/null
+++ b/data/web/css/flags/4x3/sk.svg
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/sl.svg b/data/web/css/flags/4x3/sl.svg
new file mode 100644
index 00000000..51e2676b
--- /dev/null
+++ b/data/web/css/flags/4x3/sl.svg
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/sm.svg b/data/web/css/flags/4x3/sm.svg
new file mode 100644
index 00000000..dbe605ed
--- /dev/null
+++ b/data/web/css/flags/4x3/sm.svg
@@ -0,0 +1,91 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ L
+
+
+ I
+
+
+ B
+
+
+ E
+
+
+ R
+
+
+ T
+
+
+ A
+
+
+ S
+
+
+
+
diff --git a/data/web/css/flags/4x3/sn.svg b/data/web/css/flags/4x3/sn.svg
new file mode 100644
index 00000000..c06616fc
--- /dev/null
+++ b/data/web/css/flags/4x3/sn.svg
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/so.svg b/data/web/css/flags/4x3/so.svg
new file mode 100644
index 00000000..def9fa35
--- /dev/null
+++ b/data/web/css/flags/4x3/so.svg
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/sr.svg b/data/web/css/flags/4x3/sr.svg
new file mode 100644
index 00000000..9a169fd9
--- /dev/null
+++ b/data/web/css/flags/4x3/sr.svg
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/ss.svg b/data/web/css/flags/4x3/ss.svg
new file mode 100644
index 00000000..4efa5986
--- /dev/null
+++ b/data/web/css/flags/4x3/ss.svg
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/st.svg b/data/web/css/flags/4x3/st.svg
new file mode 100644
index 00000000..a6bd2187
--- /dev/null
+++ b/data/web/css/flags/4x3/st.svg
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/sv.svg b/data/web/css/flags/4x3/sv.svg
new file mode 100644
index 00000000..e9977d71
--- /dev/null
+++ b/data/web/css/flags/4x3/sv.svg
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/sx.svg b/data/web/css/flags/4x3/sx.svg
new file mode 100644
index 00000000..9ddaf0d1
--- /dev/null
+++ b/data/web/css/flags/4x3/sx.svg
@@ -0,0 +1,56 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/sy.svg b/data/web/css/flags/4x3/sy.svg
new file mode 100644
index 00000000..b30a3ba9
--- /dev/null
+++ b/data/web/css/flags/4x3/sy.svg
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/sz.svg b/data/web/css/flags/4x3/sz.svg
new file mode 100644
index 00000000..4904c424
--- /dev/null
+++ b/data/web/css/flags/4x3/sz.svg
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/tc.svg b/data/web/css/flags/4x3/tc.svg
new file mode 100644
index 00000000..a78d1fed
--- /dev/null
+++ b/data/web/css/flags/4x3/tc.svg
@@ -0,0 +1,67 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/td.svg b/data/web/css/flags/4x3/td.svg
new file mode 100644
index 00000000..734473e7
--- /dev/null
+++ b/data/web/css/flags/4x3/td.svg
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/tf.svg b/data/web/css/flags/4x3/tf.svg
new file mode 100644
index 00000000..3913728c
--- /dev/null
+++ b/data/web/css/flags/4x3/tf.svg
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/tg.svg b/data/web/css/flags/4x3/tg.svg
new file mode 100644
index 00000000..09ba198a
--- /dev/null
+++ b/data/web/css/flags/4x3/tg.svg
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/th.svg b/data/web/css/flags/4x3/th.svg
new file mode 100644
index 00000000..66fcd8ed
--- /dev/null
+++ b/data/web/css/flags/4x3/th.svg
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/tj.svg b/data/web/css/flags/4x3/tj.svg
new file mode 100644
index 00000000..92ac160a
--- /dev/null
+++ b/data/web/css/flags/4x3/tj.svg
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/tk.svg b/data/web/css/flags/4x3/tk.svg
new file mode 100644
index 00000000..312e8811
--- /dev/null
+++ b/data/web/css/flags/4x3/tk.svg
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/tl.svg b/data/web/css/flags/4x3/tl.svg
new file mode 100644
index 00000000..94738dd0
--- /dev/null
+++ b/data/web/css/flags/4x3/tl.svg
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/tm.svg b/data/web/css/flags/4x3/tm.svg
new file mode 100644
index 00000000..a0fadb62
--- /dev/null
+++ b/data/web/css/flags/4x3/tm.svg
@@ -0,0 +1,206 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/tn.svg b/data/web/css/flags/4x3/tn.svg
new file mode 100644
index 00000000..c6aaecb8
--- /dev/null
+++ b/data/web/css/flags/4x3/tn.svg
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/to.svg b/data/web/css/flags/4x3/to.svg
new file mode 100644
index 00000000..4dd38e41
--- /dev/null
+++ b/data/web/css/flags/4x3/to.svg
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/tr.svg b/data/web/css/flags/4x3/tr.svg
new file mode 100644
index 00000000..f1910ee3
--- /dev/null
+++ b/data/web/css/flags/4x3/tr.svg
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/tt.svg b/data/web/css/flags/4x3/tt.svg
new file mode 100644
index 00000000..19add521
--- /dev/null
+++ b/data/web/css/flags/4x3/tt.svg
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/tv.svg b/data/web/css/flags/4x3/tv.svg
new file mode 100644
index 00000000..a63338d7
--- /dev/null
+++ b/data/web/css/flags/4x3/tv.svg
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/tw.svg b/data/web/css/flags/4x3/tw.svg
new file mode 100644
index 00000000..b614e44d
--- /dev/null
+++ b/data/web/css/flags/4x3/tw.svg
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/tz.svg b/data/web/css/flags/4x3/tz.svg
new file mode 100644
index 00000000..5c801732
--- /dev/null
+++ b/data/web/css/flags/4x3/tz.svg
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/ua.svg b/data/web/css/flags/4x3/ua.svg
new file mode 100644
index 00000000..a93d06db
--- /dev/null
+++ b/data/web/css/flags/4x3/ua.svg
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/ug.svg b/data/web/css/flags/4x3/ug.svg
new file mode 100644
index 00000000..c996cbdb
--- /dev/null
+++ b/data/web/css/flags/4x3/ug.svg
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/um.svg b/data/web/css/flags/4x3/um.svg
new file mode 100644
index 00000000..1aa11625
--- /dev/null
+++ b/data/web/css/flags/4x3/um.svg
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/un.svg b/data/web/css/flags/4x3/un.svg
new file mode 100644
index 00000000..b6236504
--- /dev/null
+++ b/data/web/css/flags/4x3/un.svg
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/us.svg b/data/web/css/flags/4x3/us.svg
new file mode 100644
index 00000000..5b552671
--- /dev/null
+++ b/data/web/css/flags/4x3/us.svg
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/uy.svg b/data/web/css/flags/4x3/uy.svg
new file mode 100644
index 00000000..bad58e5c
--- /dev/null
+++ b/data/web/css/flags/4x3/uy.svg
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/uz.svg b/data/web/css/flags/4x3/uz.svg
new file mode 100644
index 00000000..3ede7f16
--- /dev/null
+++ b/data/web/css/flags/4x3/uz.svg
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/va.svg b/data/web/css/flags/4x3/va.svg
new file mode 100644
index 00000000..258cca9d
--- /dev/null
+++ b/data/web/css/flags/4x3/va.svg
@@ -0,0 +1,479 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/vc.svg b/data/web/css/flags/4x3/vc.svg
new file mode 100644
index 00000000..99ba90f2
--- /dev/null
+++ b/data/web/css/flags/4x3/vc.svg
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/ve.svg b/data/web/css/flags/4x3/ve.svg
new file mode 100644
index 00000000..98ef473c
--- /dev/null
+++ b/data/web/css/flags/4x3/ve.svg
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/vg.svg b/data/web/css/flags/4x3/vg.svg
new file mode 100644
index 00000000..2c69495c
--- /dev/null
+++ b/data/web/css/flags/4x3/vg.svg
@@ -0,0 +1,127 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/vi.svg b/data/web/css/flags/4x3/vi.svg
new file mode 100644
index 00000000..db0e62c4
--- /dev/null
+++ b/data/web/css/flags/4x3/vi.svg
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/vn.svg b/data/web/css/flags/4x3/vn.svg
new file mode 100644
index 00000000..3fc2279b
--- /dev/null
+++ b/data/web/css/flags/4x3/vn.svg
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/vu.svg b/data/web/css/flags/4x3/vu.svg
new file mode 100644
index 00000000..a7732851
--- /dev/null
+++ b/data/web/css/flags/4x3/vu.svg
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/wf.svg b/data/web/css/flags/4x3/wf.svg
new file mode 100644
index 00000000..9d43586b
--- /dev/null
+++ b/data/web/css/flags/4x3/wf.svg
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/ws.svg b/data/web/css/flags/4x3/ws.svg
new file mode 100644
index 00000000..1b3ecf46
--- /dev/null
+++ b/data/web/css/flags/4x3/ws.svg
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/xk.svg b/data/web/css/flags/4x3/xk.svg
new file mode 100644
index 00000000..7e41b1d2
--- /dev/null
+++ b/data/web/css/flags/4x3/xk.svg
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/ye.svg b/data/web/css/flags/4x3/ye.svg
new file mode 100644
index 00000000..1befdeca
--- /dev/null
+++ b/data/web/css/flags/4x3/ye.svg
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/yt.svg b/data/web/css/flags/4x3/yt.svg
new file mode 100644
index 00000000..f198fff4
--- /dev/null
+++ b/data/web/css/flags/4x3/yt.svg
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/za.svg b/data/web/css/flags/4x3/za.svg
new file mode 100644
index 00000000..7a420a0f
--- /dev/null
+++ b/data/web/css/flags/4x3/za.svg
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/zh.svg b/data/web/css/flags/4x3/zh.svg
new file mode 100644
index 00000000..72080b6b
--- /dev/null
+++ b/data/web/css/flags/4x3/zh.svg
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/zm.svg b/data/web/css/flags/4x3/zm.svg
new file mode 100644
index 00000000..9e723c60
--- /dev/null
+++ b/data/web/css/flags/4x3/zm.svg
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/flags/4x3/zw.svg b/data/web/css/flags/4x3/zw.svg
new file mode 100644
index 00000000..1b18e845
--- /dev/null
+++ b/data/web/css/flags/4x3/zw.svg
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/css/site/admin.css b/data/web/css/site/admin.css
index 37395a2d..bff4f204 100644
--- a/data/web/css/site/admin.css
+++ b/data/web/css/site/admin.css
@@ -1,7 +1,3 @@
-table.footable>tbody>tr.footable-empty>td {
- font-size:15px !important;
- font-style:italic;
-}
.pagination a {
text-decoration: none !important;
}
@@ -75,7 +71,7 @@ table tbody tr td input[type="checkbox"] {
}
.regex-input {
font-family: Consolas,monaco,monospace;
- font-size: 14px;
+ font-size: 1rem;
}
.label-keys {
font-size:100%;
@@ -85,4 +81,7 @@ table tbody tr td input[type="checkbox"] {
.key-action {
font-weight:bold;
color:white !important;
+}
+.dkim-label {
+ margin: 0 0 8px !important;
}
\ No newline at end of file
diff --git a/data/web/css/site/debug.css b/data/web/css/site/debug.css
index 2209462a..39c3a866 100644
--- a/data/web/css/site/debug.css
+++ b/data/web/css/site/debug.css
@@ -1,7 +1,3 @@
-table.footable>tbody>tr.footable-empty>td {
- font-size:15px !important;
- font-style:italic;
-}
.pagination a {
text-decoration: none !important;
}
@@ -38,10 +34,4 @@ table.footable>tbody>tr.footable-empty>td {
.table-lines {
vertical-align: inherit;
}
-tbody {
- font-size:14px;
-}
-.status-indicator {
- width: 15px;
- height: 15px;
-}
+
diff --git a/data/web/css/site/edit.css b/data/web/css/site/edit.css
index 8a594ee1..e8fb819d 100644
--- a/data/web/css/site/edit.css
+++ b/data/web/css/site/edit.css
@@ -1,7 +1,3 @@
-table.footable>tbody>tr.footable-empty>td {
- font-size:15px !important;
- font-style:italic;
-}
.pagination a {
text-decoration: none !important;
}
@@ -41,7 +37,7 @@ table.footable>tbody>tr.footable-empty>td {
-webkit-transform:rotateX(180deg);
transform:rotateX(180deg);
}
-.input-group-addon-xmpp {
- background-color: #fff;
- border: 0px solid #fff;
-}
\ No newline at end of file
+#sender_acl_disabled {
+ display:none;
+ margin-top:10px;
+}
diff --git a/data/web/css/site/index.css b/data/web/css/site/index.css
index 10f7c0d7..41438a87 100644
--- a/data/web/css/site/index.css
+++ b/data/web/css/site/index.css
@@ -2,4 +2,7 @@
#top {
padding-top: 15px !important;
}
-}
\ No newline at end of file
+}
+.ui-announcement-alert {
+ margin: 10px 0px 10px 0px;
+}
diff --git a/data/web/css/site/mailbox.css b/data/web/css/site/mailbox.css
index 8b5bad6e..4822a6fd 100644
--- a/data/web/css/site/mailbox.css
+++ b/data/web/css/site/mailbox.css
@@ -1,7 +1,3 @@
-table.footable>tbody>tr.footable-empty>td {
- font-size:15px !important;
- font-style:italic;
-}
.pagination a {
text-decoration: none !important;
}
@@ -25,6 +21,11 @@ table.footable>tbody>tr.footable-empty>td {
background: #F5F5F5;
}
@media (min-width: 992px) {
+ .container {
+ width: 100%;
+ }
+}
+@media (min-width: 1920px) {
.container {
width: 80%;
}
@@ -57,8 +58,13 @@ table tbody tr {
table tbody tr td input[type="checkbox"] {
cursor: pointer;
}
+.label-last-login .bi {
+ font-size: 8pt !important;
+}
.label-last-login {
- line-height: 2.5;
+ line-height: 2.2;
color: #4a4a4a!important;
+ padding: .2em .4em .3em !important;
background-color: #ececec!important;
}
+
diff --git a/data/web/css/site/quarantine.css b/data/web/css/site/quarantine.css
index 7a127498..36346965 100644
--- a/data/web/css/site/quarantine.css
+++ b/data/web/css/site/quarantine.css
@@ -1,8 +1,3 @@
-table.footable>tbody>tr.footable-empty>td {
- font-size: 15px !important;
- font-style: italic;
-}
-
.pagination a {
text-decoration: none !important;
}
@@ -29,13 +24,18 @@ table.footable>tbody>tr.footable-empty>td {
@media (min-width: 992px) {
.container {
- width: 80%;
+ width: 100%;
+ }
+}
+@media (min-width: 1920px) {
+ .container {
+ width: 80%;
}
}
.mass-actions-quarantine {
user-select: none;
- padding: 10px 0 10px 10px;
+ padding: 10px;
}
.inputMissingAttr {
@@ -100,3 +100,4 @@ table tbody tr td input[type="checkbox"] {
font-size:110%;
margin:20px;
}
+
diff --git a/data/web/css/site/user.css b/data/web/css/site/user.css
index 965562c7..0ec07b74 100644
--- a/data/web/css/site/user.css
+++ b/data/web/css/site/user.css
@@ -1,7 +1,3 @@
-table.footable>tbody>tr.footable-empty>td {
- font-size:15px !important;
- font-style:italic;
-}
.pagination a {
text-decoration: none !important;
}
@@ -47,17 +43,6 @@ table tbody tr {
table tbody tr td input[type="checkbox"] {
cursor: pointer;
}
-.rotate {
- -moz-transition: all 0.3s linear;
- -webkit-transition: all 0.3s linear;
- transition: all 0.3s linear;
-}
-.rotate.animation {
- -ms-transform:rotateX(180deg);
- -moz-transform:rotateX(180deg);
- -webkit-transform:rotateX(180deg);
- transform:rotateX(180deg);
-}
.label-keys {
font-size:100%;
margin: 0px !important;
@@ -66,4 +51,79 @@ table tbody tr td input[type="checkbox"] {
.key-action {
font-weight:bold;
color:white !important;
-}
\ No newline at end of file
+}
+svg {
+ display: inline-block;
+ vertical-align: middle;
+}
+.c-1-color, .label-ham {
+ background: #28b62c;
+ background: -webkit-linear-gradient(to right, #28b62c, #fff233);
+ background: linear-gradient(to right, #28b62c, #fff233);
+ color: #000;
+}
+.c-2-color, .label-spam {
+ background: #fff233;
+ background: -webkit-linear-gradient(to right, #fff233, #ff4136);
+ background: linear-gradient(to right, #fff233, #ff4136);
+ color: #000;
+}
+.c-3-color, .label-reject{
+ background: #ff4136;
+ color: #fff;
+}
+#spam_score {
+ margin-bottom: 10px;
+}
+.noUi-handle {
+ border: 1px solid #e2e2e2;
+ border-radius: 0px;
+ background: #eee;
+ cursor: default;
+ box-shadow: none;
+ border-top-width: 0px;
+ border-right-width: 1px;
+ border-bottom-width: 4px;
+ border-left-width: 1px;
+}
+.noUi-handle:hover {
+ background-color: #eee;
+ border-color: #e2e2e2;
+ margin-top: 1px;
+border-bottom-width: 3px;
+}
+.noUi-handle::after, .noUi-handle::before {
+ background: #c6c6c6;
+ width: 2px;
+}
+.noUi-target {
+ background: transparent;
+ border-radius: 0px;
+ border: 1px solid #D3D3D3;
+ box-shadow: none;
+}
+.noUi-connects {
+ border-radius: 0px;
+}
+.label-ham,
+.label-spam,
+.label-reject {
+ padding: .1em .5em .1em;
+ font-size: inherit;
+ font-weight: 400;
+}
+.clear-last-logins {
+ cursor: pointer;
+ font-size:90%;
+ font-style: italic;
+ color: #158cba;
+ user-select:none;
+}
+.ip-location-flag {
+ border-radius: 4px;
+ top: 3px;
+}
+.recent-login-success {
+ margin-top:2px;
+ margin-right:10px;
+}
diff --git a/data/web/debug.php b/data/web/debug.php
index bcc16d0a..f8ad527e 100644
--- a/data/web/debug.php
+++ b/data/web/debug.php
@@ -6,7 +6,6 @@ require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/header.inc.php';
$_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
$solr_status = (preg_match("/^([yY][eE][sS]|[yY])+$/", $_ENV["SKIP_SOLR"])) ? false : solr_status();
$clamd_status = (preg_match("/^([yY][eE][sS]|[yY])+$/", $_ENV["SKIP_CLAMD"])) ? false : true;
-$xmpp_status = xmpp_control('status');
?>
@@ -29,6 +28,7 @@ $xmpp_status = xmpp_control('status');
Rspamd
=$lang['debug']['static_logs'];?>
mailcow UI
+
SASL
@@ -49,7 +49,7 @@ $xmpp_status = xmpp_control('status');
-
Mail disk space via =$vmail_df[0];?>
+
=$vmail_df[0];?>
=$vmail_df[2];?> / =$vmail_df[1];?> (=$vmail_df[4];?>)
@@ -76,14 +76,14 @@ $xmpp_status = xmpp_control('status');
-
=$lang['debug']['jvm_memory_solr'];?>: =$solr_status['jvm']['memory']['total'] - $solr_status['jvm']['memory']['free'];?> / =$solr_status['jvm']['memory']['total'];?>
+
=$lang['debug']['jvm_memory_solr'];?>: =(int)$solr_status['jvm']['memory']['total'] - (int)$solr_status['jvm']['memory']['free'];?> / =$solr_status['jvm']['memory']['total'];?>
(=round($solr_status['jvm']['memory']['raw']['used%']);?>%)
-
=$lang['debug']['uptime'];?>: ~=round($solr_status['status']['dovecot-fts']['uptime'] / 1000 / 60 / 60);?>h
+
=$lang['debug']['uptime'];?>: =round($solr_status['status']['dovecot-fts']['uptime'] / 1000 / 60 / 60);?>h
=$lang['debug']['started_at'];?>: =$solr_status['status']['dovecot-fts']['startTime'];?>
=$lang['debug']['last_modified'];?>: =$solr_status['status']['dovecot-fts']['index']['lastModified'];?>
=$lang['debug']['size'];?>: =$solr_status['status']['dovecot-fts']['index']['size'];?>
-
=$lang['debug']['docs'];?>: =$solr_status['status']['dovecot-fts']['index']['numDocs'];?>
+
=$lang['debug']['docs'];?>: =$solr_status['status']['dovecot-fts']['index']['numDocs'];?>
@@ -95,47 +95,6 @@ $xmpp_status = xmpp_control('status');
-
-
-
=$lang['debug']['xmpp_status'];?>
-
-
-
-
-
-
-
-
-
=$lang['debug']['online_users'];?>: =(empty($xmpp_status['onlineusers'])) ? '-' : $xmpp_status['onlineusers'];?>
-
=$lang['debug']['started_at'];?>: =$xmpp_status['uptimeseconds'];?>
-
-
MUCs:
-
-
- =$room;?>
-
-
-
-
=$lang['debug']['xmpp_dead'];?>
-
-
-
-
-
=$lang['debug']['containers_info'];?>
@@ -171,9 +130,10 @@ $xmpp_status = xmpp_control('status');
$started = '?';
}
?>
-
(=$lang['debug']['started_on'];?> =$started;?> ),
- =$lang['debug']['restart_container'];?>
-
+
+
(=$lang['debug']['started_on'];?> =$started;?> )
+
=$lang['debug']['restart_container'];?>
+
-
+
Postfix
- + 100
- + 1000
- =$lang['admin']['refresh'];?>
+ + 100
+ + 1000
+ =$lang['admin']['refresh'];?>
@@ -201,12 +161,12 @@ $xmpp_status = xmpp_control('status');
-
+
mailcow UI
- + 1000
- + 10000
- =$lang['admin']['refresh'];?>
+ + 1000
+ + 10000
+ =$lang['admin']['refresh'];?>
@@ -217,13 +177,30 @@ $xmpp_status = xmpp_control('status');
+
+
+
SASL
+
+ + 1000
+ + 10000
+ =$lang['admin']['refresh'];?>
+
+
+
+
+
+
-
+
Dovecot
- + 100
- + 1000
- =$lang['admin']['refresh'];?>
+ + 100
+ + 1000
+ =$lang['admin']['refresh'];?>
@@ -235,12 +212,12 @@ $xmpp_status = xmpp_control('status');
-
+
SOGo
- + 100
- + 1000
- =$lang['admin']['refresh'];?>
+ + 100
+ + 1000
+ =$lang['admin']['refresh'];?>
@@ -252,12 +229,12 @@ $xmpp_status = xmpp_control('status');
-
+
Netfilter
- + 100
- + 1000
- =$lang['admin']['refresh'];?>
+ + 100
+ + 1000
+ =$lang['admin']['refresh'];?>
@@ -269,12 +246,12 @@ $xmpp_status = xmpp_control('status');
-
+
Rspamd history
- + 100
- + 1000
- =$lang['admin']['refresh'];?>
+ + 100
+ + 1000
+ =$lang['admin']['refresh'];?>
@@ -291,12 +268,12 @@ $xmpp_status = xmpp_control('status');
-
+
Autodiscover
- + 100
- + 1000
- =$lang['admin']['refresh'];?>
+ + 100
+ + 1000
+ =$lang['admin']['refresh'];?>
@@ -308,12 +285,12 @@ $xmpp_status = xmpp_control('status');
-
+
Watchdog
- + 100
- + 1000
- =$lang['admin']['refresh'];?>
+ + 100
+ + 1000
+ =$lang['admin']['refresh'];?>
@@ -325,12 +302,12 @@ $xmpp_status = xmpp_control('status');
-
+
ACME
- + 100
- + 1000
- =$lang['admin']['refresh'];?>
+ + 100
+ + 1000
+ =$lang['admin']['refresh'];?>
@@ -342,12 +319,12 @@ $xmpp_status = xmpp_control('status');
-
+
API
- + 100
- + 1000
- =$lang['admin']['refresh'];?>
+ + 100
+ + 1000
+ =$lang['admin']['refresh'];?>
@@ -359,12 +336,12 @@ $xmpp_status = xmpp_control('status');
-
+
Ratelimits
- + 100
- + 1000
- =$lang['admin']['refresh'];?>
+ + 100
+ + 1000
+ =$lang['admin']['refresh'];?>
diff --git a/data/web/edit.php b/data/web/edit.php
index dcdbea7d..30d584f3 100644
--- a/data/web/edit.php
+++ b/data/web/edit.php
@@ -2,19 +2,19 @@
require_once $_SERVER['DOCUMENT_ROOT'] . '/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();
+ header('Location: /');
+ exit();
}
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/header.inc.php';
?>
-
-
-
-
-
=$lang['edit']['title'];?>
-
-
+
+
+
+
+
=$lang['edit']['title'];?>
+
+
@@ -113,7 +113,7 @@ if (isset($_SESSION['mailcow_cc_role'])) {
- =$lang['admin']['save'];?>
+ =$lang['admin']['save'];?>
@@ -237,7 +237,7 @@ if (isset($_SESSION['mailcow_cc_role'])) {
@@ -254,14 +254,16 @@ if (isset($_SESSION['mailcow_cc_role'])) {
!empty($_GET["domain"])) {
$domain = $_GET["domain"];
$result = mailbox('get', 'domain_details', $domain);
+ $quota_notification_bcc = quota_notification_bcc('get', $domain);
$rl = ratelimit('get', 'domain', $domain);
$rlyhosts = relayhost('get');
if (!empty($result)) {
?>
-
-
-
-
-
@@ -383,7 +364,7 @@ if (isset($_SESSION['mailcow_cc_role'])) {
@@ -392,10 +373,10 @@ if (isset($_SESSION['mailcow_cc_role'])) {
?>
-
+
Domain: =htmlspecialchars($result['domain_name']);?> (=$dkim['dkim_selector'];?>._domainkey)
-
@@ -407,18 +388,18 @@ if (isset($_SESSION['mailcow_cc_role'])) {
=$lang['edit']['ratelimit'];?>
-
+
- >msgs / second
- >msgs / minute
- >msgs / hour
- >msgs / day
+ >=$lang['ratelimit']['second']?>
+ >=$lang['ratelimit']['minute']?>
+ >=$lang['ratelimit']['hour']?>
+ >=$lang['ratelimit']['day']?>
- =$lang['admin']['save'];?>
+ =$lang['admin']['save'];?>
@@ -427,13 +408,7 @@ if (isset($_SESSION['mailcow_cc_role'])) {
=$lang['user']['spamfilter_wl'];?>
=$lang['user']['spamfilter_wl_desc'];?>
-
-
+
=$lang['user']['spamfilter_bl'];?>
=$lang['user']['spamfilter_bl_desc'];?>
-
-
+
+
+
+
+
+
+
=$lang['edit']['quota_warning_bcc'];?>
+
=$lang['edit']['quota_warning_bcc_info'];?>
+
+
+
+
+
+
@@ -512,7 +523,7 @@ if (isset($_SESSION['mailcow_cc_role'])) {
@@ -558,7 +569,7 @@ if (isset($_SESSION['mailcow_cc_role'])) {
@@ -566,18 +577,18 @@ if (isset($_SESSION['mailcow_cc_role'])) {
=$lang['acl']['ratelimit'];?>
-
+
- >msgs / second
- >msgs / minute
- >msgs / hour
- >msgs / day
+ >=$lang['ratelimit']['second']?>
+ >=$lang['ratelimit']['minute']?>
+ >=$lang['ratelimit']['hour']?>
+ >=$lang['ratelimit']['day']?>
- =$lang['admin']['save'];?>
+ =$lang['admin']['save'];?>
-
+
Domain: =htmlspecialchars($result['alias_domain']);?> (=$dkim['dkim_selector'];?>._domainkey)
-
@@ -609,385 +620,402 @@ if (isset($_SESSION['mailcow_cc_role'])) {
$quarantine_notification = mailbox('get', 'quarantine_notification', $mailbox);
$quarantine_category = mailbox('get', 'quarantine_category', $mailbox);
$get_tls_policy = mailbox('get', 'tls_policy', $mailbox);
+ $rlyhosts = relayhost('get');
if (!empty($result)) {
?>
-
=$lang['edit']['mailbox'];?>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
=sprintf($lang['edit']['pushover_info'], $mailbox);?>
-
=$lang['edit']['pushover_vars'];?>: {SUBJECT}, {SENDER}
+
+
+
← =$lang['edit']['previous'];?>
-add('/web/js/site/user.js');
-$js_minifier->add('/web/js/site/pwgen.js');
-require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/footer.inc.php';
-}
-else {
- header('Location: /');
- exit();
-}
-?>
+ "get_friendly_names"));
+ $username = $_SESSION['mailcow_cc_username'];
+ print_r(mailbox('get', 'mailbox_count'));
+
+?>
+
+
+
=$lang['user']['user_settings'];?>
+
+
=$lang['user']['user_settings'];?>
+
+
+
+
+ // TFA ?>
+
+
=$lang['tfa']['tfa'];?>
+
+
=$tfa_data['pretty'];?>
+
+
+
+
+
+
=$lang['tfa']['set_tfa'];?>
+
+
+ =$lang['tfa']['yubi_otp'];?>
+ =$lang['tfa']['u2f'];?>
+ =$lang['tfa']['totp'];?>
+ =$lang['tfa']['none'];?>
+
+
+
+
+
+ // FIDO2 ?>
+
+
+
=$lang['fido2']['fido2_auth'];?>
+
+
+
+
=$lang['fido2']['known_ids'];?>:
+
+
+
+
+ ID
+ =$lang['admin']['action'];?>
+
+
+
+
+ =($_SESSION['fido2_cid'] == $key_info['cid']) ? '→ ' : NULL; ?>=(!empty($key_info['fn']))?$key_info['fn']:$key_info['subject'];?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ =$lang['fido2']['set_fido2'];?>
+
+
+
+
+
=$lang['fido2']['register_status'];?>:
+
+
+
+
+
+
+
+ "get_friendly_names"));
+
+ $clientconfigstr = "host=" . urlencode($mailcow_hostname) . "&email=" . urlencode($username) . "&name=" . urlencode($mailboxdata['name']) . "&ui=" . urlencode(strtok($_SERVER['HTTP_HOST'], ':')) . "&port=" . urlencode($autodiscover_config['caldav']['port']);
+ if ($autodiscover_config['useEASforOutlook'] == 'yes')
+ $clientconfigstr .= "&outlookEAS=1";
+ if (file_exists('thunderbird-plugins/version.csv')) {
+ $fh = fopen('thunderbird-plugins/version.csv', 'r');
+ if ($fh) {
+ while (($row = fgetcsv($fh, 1000, ';')) !== FALSE) {
+ if ($row[0] == 'sogo-connector@inverse.ca') {
+ $clientconfigstr .= "&connector=" . urlencode($row[1]);
+ }
+ }
+ fclose($fh);
+ }
+ }
+?>
+
+
+
+
+
+
+
+
+
+
+
=$lang['user']['mailbox_general'];?>
+
+
+
+
+
+
+
=$lang['user']['in_use'];?>:
+
+
+
+ =$mailboxdata['percent_in_use'];?>%
+
+
+
=formatBytes($mailboxdata['quota_used'], 2);?> / =($mailboxdata['quota'] == 0) ? '∞' : formatBytes($mailboxdata['quota'], 2);?> =$mailboxdata['messages'];?> =$lang['user']['messages'];?>
+
+
=$lang['user']['change_password'];?>
+
+
+
+ // FIDO2 ?>
+
+
+
=$lang['fido2']['fido2_auth'];?>
+
+
+
+
+ =$lang['fido2']['known_ids'];?>:
+
+
+
+
+
+ ID
+ =$lang['admin']['action'];?>
+
+
+
+
+ =($_SESSION['fido2_cid'] == $key_info['cid']) ? ' ' : NULL; ?>=(!empty($key_info['fn']))?$key_info['fn']:$key_info['subject'];?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ =$lang['fido2']['set_fido2'];?>
+
+
+
+
+
=$lang['fido2']['register_status'];?>:
+
+
+
+
+
+
=$lang['user']['apple_connection_profile'];?>:
+
+
+
+
+
+
+
+
+
+
+
=$lang['user']['mailbox_details'];?>
+
+
+
+
=$lang['user']['direct_aliases'];?>:
+
=$lang['user']['direct_aliases_desc'];?>
+
+
+ ';
+ }
+ else {
+ foreach (array_filter($user_get_alias_details['direct_aliases']) as $direct_alias => $direct_alias_meta) {
+ (!empty($direct_alias_meta['public_comment'])) ?
+ printf('%s — %s ', $direct_alias, $direct_alias_meta['public_comment']) :
+ printf('%s ', $direct_alias);
+ }
+ }
+ ?>
+
+
+
+
+
=$lang['user']['shared_aliases'];?>:
+
=$lang['user']['shared_aliases_desc'];?>
+
+
+ ';
+ }
+ else {
+ foreach (array_filter($user_get_alias_details['shared_aliases']) as $shared_alias => $shared_alias_meta) {
+ (!empty($shared_alias_meta['public_comment'])) ?
+ printf('%s — %s ', $shared_alias, $shared_alias_meta['public_comment']) :
+ printf('%s ', $shared_alias);
+ }
+ }
+ ?>
+
+
+
+
+
=$lang['user']['aliases_also_send_as'];?>:
+
+
=($user_get_alias_details['aliases_also_send_as'] == '*') ? $lang['user']['sender_acl_disabled'] : ((empty($user_get_alias_details['aliases_also_send_as'])) ? ' ' : $user_get_alias_details['aliases_also_send_as']);?>
+
+
+
+
=$lang['user']['aliases_send_as_all'];?>:
+
+
=(empty($user_get_alias_details['aliases_send_as_all'])) ? ' ' : '' ;?>
+
+
+
+
=$lang['user']['is_catch_all'];?>:
+
+
=(empty($user_get_alias_details['is_catch_all'])) ? ' ' : '' ;?>
+
+
+
+
+
+
+
+
+
=$lang['user']['mailbox_settings'];?>
+
+
+
+
=$lang['user']['tag_handling'];?>:
+
+
+
"
+ data-action="edit_selected"
+ data-item="= htmlentities($username); ?>"
+ data-id="delimiter_action"
+ data-api-url='edit/delimiter_action'
+ data-api-attr='{"tagged_mail_handler":"subfolder"}'>=$lang['user']['tag_in_subfolder'];?>
+
"
+ data-action="edit_selected"
+ data-item="= htmlentities($username); ?>"
+ data-id="delimiter_action"
+ data-api-url='edit/delimiter_action'
+ data-api-attr='{"tagged_mail_handler":"subject"}'>=$lang['user']['tag_in_subject'];?>
+
"
+ data-action="edit_selected"
+ data-item="= htmlentities($username); ?>"
+ data-id="delimiter_action"
+ data-api-url='edit/delimiter_action'
+ data-api-attr='{"tagged_mail_handler":"none"}'>=$lang['user']['tag_in_none'];?>
+
+
+
=$lang['user']['tag_help_explain'];?>
+
=$lang['user']['tag_help_example'];?>
+
+
+
+
+
=$lang['user']['tls_policy'];?>:
+
+
+
"
+ data-action="edit_selected"
+ data-item="= htmlentities($username); ?>"
+ data-id="tls_policy"
+ data-api-url='edit/tls_policy'
+ data-api-attr='{"tls_enforce_in":=($get_tls_policy['tls_enforce_in'] == "1") ? "0" : "1";?>}'>=$lang['user']['tls_enforce_in'];?>
+
"
+ data-action="edit_selected"
+ data-item="= htmlentities($username); ?>"
+ data-id="tls_policy"
+ data-api-url='edit/tls_policy'
+ data-api-attr='{"tls_enforce_out":=($get_tls_policy['tls_enforce_out'] == "1") ? "0" : "1";?>}'>=$lang['user']['tls_enforce_out'];?>
+
+
+
=$lang['user']['tls_policy_warning'];?>
+
+
+
+
+
=$lang['user']['quarantine_notification'];?>:
+
+
+
"
+ data-action="edit_selected"
+ data-item="= htmlentities($username); ?>"
+ data-id="quarantine_notification"
+ data-api-url='edit/quarantine_notification'
+ data-api-attr='{"quarantine_notification":"never"}'>=$lang['user']['never'];?>
+
"
+ data-action="edit_selected"
+ data-item="= htmlentities($username); ?>"
+ data-id="quarantine_notification"
+ data-api-url='edit/quarantine_notification'
+ data-api-attr='{"quarantine_notification":"hourly"}'>=$lang['user']['hourly'];?>
+
"
+ data-action="edit_selected"
+ data-item="= htmlentities($username); ?>"
+ data-id="quarantine_notification"
+ data-api-url='edit/quarantine_notification'
+ data-api-attr='{"quarantine_notification":"daily"}'>=$lang['user']['daily'];?>
+
"
+ data-action="edit_selected"
+ data-item="= htmlentities($username); ?>"
+ data-id="quarantine_notification"
+ data-api-url='edit/quarantine_notification'
+ data-api-attr='{"quarantine_notification":"weekly"}'>=$lang['user']['weekly'];?>
+
+
+
=$lang['user']['quarantine_notification_info'];?>
+
+
+
+
=$lang['user']['quarantine_category'];?>:
+
+
+
"
+ data-action="edit_selected"
+ data-item="= htmlentities($username); ?>"
+ data-id="quarantine_category"
+ data-api-url='edit/quarantine_category'
+ data-api-attr='{"quarantine_category":"reject"}'>=$lang['user']['q_reject'];?>
+
"
+ data-action="edit_selected"
+ data-item="= htmlentities($username); ?>"
+ data-id="quarantine_category"
+ data-api-url='edit/quarantine_category'
+ data-api-attr='{"quarantine_category":"add_header"}'>=$lang['user']['q_add_header'];?>
+
"
+ data-action="edit_selected"
+ data-item="= htmlentities($username); ?>"
+ data-id="quarantine_category"
+ data-api-url='edit/quarantine_category'
+ data-api-attr='{"quarantine_category":"all"}'>=$lang['user']['q_all'];?>
+
+
+
=$lang['user']['quarantine_category_info'];?>
+
+
+
+
+
+
=$lang['user']['eas_reset'];?>:
+
+
=$lang['user']['eas_reset_now'];?>
+
=$lang['user']['eas_reset_help'];?>
+
+
+
+
=$lang['user']['sogo_profile_reset'];?>:
+
+
=$lang['user']['sogo_profile_reset_now'];?>
+
=$lang['user']['sogo_profile_reset_help'];?>
+
+
+
+
+
+
+
+
+
+
+
=$lang['user']['spamfilter_behavior'];?>
+
+
+
+
+
=$lang['user']['spamfilter_wl'];?>
+
=$lang['user']['spamfilter_wl_desc'];?>
+
+
+
+
+ =$lang['user']['spamfilter_table_add'];?>
+
+
+
+
+
+
+
+
=$lang['user']['spamfilter_bl'];?>
+
=$lang['user']['spamfilter_bl_desc'];?>
+
+
+
+
+ =$lang['user']['spamfilter_table_add'];?>
+
+
+
+
+
+
+
+
+
+
+
+
+
=$lang['user']['app_hint'];?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
=sprintf($lang['user']['pushover_info'], $username);?>
+
=$lang['user']['pushover_vars'];?>: {SUBJECT}, {SENDER}
+
+
+
+
+
+
+
+
+
+
+
+
+add('/web/js/site/user.js');
+$js_minifier->add('/web/js/site/pwgen.js');
+require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/footer.inc.php';
+}
+else {
+ header('Location: /');
+ exit();
+}
+?>
diff --git a/docker-compose.yml b/docker-compose.yml
index 99e515aa..26b28c3c 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -41,7 +41,7 @@ services:
- mysql
redis-mailcow:
- image: redis:5-alpine
+ image: redis:6-alpine
volumes:
- redis-vol-1:/data/:Z
restart: always
@@ -49,6 +49,8 @@ services:
- "${REDIS_PORT:-127.0.0.1:7654}:6379"
environment:
- TZ=${TZ}
+ sysctls:
+ - net.core.somaxconn=4096
networks:
mailcow-network:
ipv4_address: ${IPV4_NETWORK:-172.22.1}.249
@@ -71,7 +73,7 @@ services:
- clamd
rspamd-mailcow:
- image: mailcow/rspamd:1.76
+ image: mailcow/rspamd:1.77
stop_grace_period: 30s
depends_on:
- dovecot-mailcow
@@ -101,7 +103,7 @@ services:
- rspamd
php-fpm-mailcow:
- image: mailcow/phpfpm:1.75
+ image: mailcow/phpfpm:1.76
command: "php-fpm -d date.timezone=${TZ} -d expose_php=0"
depends_on:
- redis-mailcow
@@ -122,7 +124,6 @@ services:
- ./data/conf/dovecot/global_sieve_before:/global_sieve/before:z
- ./data/conf/dovecot/global_sieve_after:/global_sieve/after:z
- ./data/assets/templates:/tpls:z
- - ./data/conf/ejabberd/autogen:/ejabberd/:z
- ./data/conf/nginx/:/etc/nginx/conf.d/:z
dns:
- ${IPV4_NETWORK:-172.22.1}.254
@@ -146,8 +147,6 @@ services:
- SUBMISSION_PORT=${SUBMISSION_PORT:-587}
- SMTPS_PORT=${SMTPS_PORT:-465}
- SMTP_PORT=${SMTP_PORT:-25}
- - XMPP_C2S_PORT=${XMPP_C2S_PORT:-5222}
- - XMPP_S2S_PORT=${XMPP_S2S_PORT:-5269}
- API_KEY=${API_KEY:-invalid}
- API_KEY_READ_ONLY=${API_KEY_READ_ONLY:-invalid}
- API_ALLOW_FROM=${API_ALLOW_FROM:-invalid}
@@ -164,7 +163,7 @@ services:
- phpfpm
sogo-mailcow:
- image: mailcow/sogo:1.99
+ image: mailcow/sogo:1.101
environment:
- DBNAME=${DBNAME}
- DBUSER=${DBUSER}
@@ -184,6 +183,7 @@ services:
dns:
- ${IPV4_NETWORK:-172.22.1}.254
volumes:
+ - ./data/hooks/sogo:/hooks:Z
- ./data/conf/sogo/:/etc/sogo/:z
- ./data/web/inc/init_db.inc.php:/init_db.inc.php:Z
- ./data/conf/sogo/custom-favicon.ico:/usr/lib/GNUstep/SOGo/WebServerResources/img/sogo.ico:z
@@ -198,9 +198,9 @@ services:
ofelia.job-exec.sogo_sessions.command: "/bin/bash -c \"[[ $${MASTER} == y ]] && /usr/local/bin/gosu sogo /usr/sbin/sogo-tool expire-sessions $${SOGO_EXPIRE_SESSION} || exit 0\""
ofelia.job-exec.sogo_ealarms.schedule: "@every 1m"
ofelia.job-exec.sogo_ealarms.command: "/bin/bash -c \"[[ $${MASTER} == y ]] && /usr/local/bin/gosu sogo /usr/sbin/sogo-ealarms-notify -p /etc/sogo/sieve.creds || exit 0\""
- ofelia.job-exec.sogo_eautoreply.schedule: "@every 1d"
+ ofelia.job-exec.sogo_eautoreply.schedule: "@every 24h"
ofelia.job-exec.sogo_eautoreply.command: "/bin/bash -c \"[[ $${MASTER} == y ]] && /usr/local/bin/gosu sogo /usr/sbin/sogo-tool update-autoreply -p /etc/sogo/sieve.creds || exit 0\""
- ofelia.job-exec.sogo_backup.schedule: "@every 1d"
+ ofelia.job-exec.sogo_backup.schedule: "@every 24h"
ofelia.job-exec.sogo_backup.command: "/bin/bash -c \"[[ $${MASTER} == y ]] && /usr/local/bin/gosu sogo /usr/sbin/sogo-tool backup /sogo_backup ALL || exit 0\""
restart: always
networks:
@@ -210,7 +210,7 @@ services:
- sogo
dovecot-mailcow:
- image: mailcow/dovecot:1.145
+ image: mailcow/dovecot:1.155
depends_on:
- mysql-mailcow
dns:
@@ -242,7 +242,7 @@ services:
- MAILCOW_PASS_SCHEME=${MAILCOW_PASS_SCHEME:-BLF-CRYPT}
- IPV4_NETWORK=${IPV4_NETWORK:-172.22.1}
- ALLOW_ADMIN_EMAIL_LOGIN=${ALLOW_ADMIN_EMAIL_LOGIN:-n}
- - MAILDIR_GC_TIME=${MAILDIR_GC_TIME:-1440}
+ - MAILDIR_GC_TIME=${MAILDIR_GC_TIME:-7200}
- ACL_ANYONE=${ACL_ANYONE:-disallow}
- SKIP_SOLR=${SKIP_SOLR:-y}
- MAILDIR_SUB=${MAILDIR_SUB:-}
@@ -268,13 +268,13 @@ services:
ofelia.job-exec.dovecot_trim_logs.command: "/bin/bash -c \"[[ $${MASTER} == y ]] && /usr/local/bin/gosu vmail /usr/local/bin/trim_logs.sh || exit 0\""
ofelia.job-exec.dovecot_quarantine.schedule: "@every 20m"
ofelia.job-exec.dovecot_quarantine.command: "/bin/bash -c \"[[ $${MASTER} == y ]] && /usr/local/bin/gosu vmail /usr/local/bin/quarantine_notify.py || exit 0\""
- ofelia.job-exec.dovecot_clean_q_aged.schedule: "@every 1d"
+ ofelia.job-exec.dovecot_clean_q_aged.schedule: "@every 24h"
ofelia.job-exec.dovecot_clean_q_aged.command: "/bin/bash -c \"[[ $${MASTER} == y ]] && /usr/local/bin/gosu vmail /usr/local/bin/clean_q_aged.sh || exit 0\""
ofelia.job-exec.dovecot_maildir_gc.schedule: "@every 30m"
ofelia.job-exec.dovecot_maildir_gc.command: "/bin/bash -c \"source /source_env.sh ; /usr/local/bin/gosu vmail /usr/local/bin/maildir_gc.sh\""
- ofelia.job-exec.dovecot_sarules.schedule: "@every 1d"
+ ofelia.job-exec.dovecot_sarules.schedule: "@every 24h"
ofelia.job-exec.dovecot_sarules.command: "/bin/bash -c \"/usr/local/bin/sa-rules.sh\""
- ofelia.job-exec.dovecot_fts.schedule: "@every 1d"
+ ofelia.job-exec.dovecot_fts.schedule: "@every 24h"
ofelia.job-exec.dovecot_fts.command: "/usr/bin/curl http://solr:8983/solr/dovecot-fts/update?optimize=true"
ofelia.job-exec.dovecot_repl_health.schedule: "@every 5m"
ofelia.job-exec.dovecot_repl_health.command: "/bin/bash -c \"/usr/local/bin/gosu vmail /usr/local/bin/repl_health.sh\""
@@ -290,7 +290,7 @@ services:
- dovecot
postfix-mailcow:
- image: mailcow/postfix:1.61
+ image: mailcow/postfix:1.66
depends_on:
- mysql-mailcow
volumes:
@@ -321,6 +321,7 @@ services:
- ${IPV4_NETWORK:-172.22.1}.254
networks:
mailcow-network:
+ ipv4_address: ${IPV4_NETWORK:-172.22.1}.253
aliases:
- postfix
@@ -345,7 +346,6 @@ services:
command: /bin/sh -c "envsubst < /etc/nginx/conf.d/templates/listen_plain.template > /etc/nginx/conf.d/listen_plain.active &&
envsubst < /etc/nginx/conf.d/templates/listen_ssl.template > /etc/nginx/conf.d/listen_ssl.active &&
envsubst < /etc/nginx/conf.d/templates/sogo.template > /etc/nginx/conf.d/sogo.active &&
- . /etc/nginx/conf.d/templates/sogo.auth_request.template.sh > /etc/nginx/conf.d/sogo_proxy_auth.active &&
. /etc/nginx/conf.d/templates/server_name.template.sh > /etc/nginx/conf.d/server_name.active &&
. /etc/nginx/conf.d/templates/sites.template.sh > /etc/nginx/conf.d/sites.active &&
. /etc/nginx/conf.d/templates/sogo_eas.template.sh > /etc/nginx/conf.d/sogo_eas.active &&
@@ -354,7 +354,6 @@ services:
until ping sogo -c1 > /dev/null; do sleep 1; done &&
until ping redis -c1 > /dev/null; do sleep 1; done &&
until ping rspamd -c1 > /dev/null; do sleep 1; done &&
- until ping ejabberd -c1 > /dev/null; do sleep 1; done &&
exec nginx -g 'daemon off;'"
environment:
- HTTPS_PORT=${HTTPS_PORT:-443}
@@ -470,7 +469,7 @@ services:
- /lib/modules:/lib/modules:ro
watchdog-mailcow:
- image: mailcow/watchdog:1.91
+ image: mailcow/watchdog:1.92
# Debug
#command: /watchdog.sh
dns:
@@ -523,7 +522,6 @@ services:
- RATELIMIT_THRESHOLD=${RATELIMIT_THRESHOLD:-1}
- FAIL2BAN_THRESHOLD=${FAIL2BAN_THRESHOLD:-1}
- ACME_THRESHOLD=${ACME_THRESHOLD:-1}
- - IPV6NAT_THRESHOLD=${IPV6NAT_THRESHOLD:-1}
- RSPAMD_THRESHOLD=${RSPAMD_THRESHOLD:-5}
- OLEFY_THRESHOLD=${OLEFY_THRESHOLD:-5}
- MAILQ_THRESHOLD=${MAILQ_THRESHOLD:-20}
@@ -585,44 +583,6 @@ services:
aliases:
- olefy
- ejabberd-mailcow:
- image: mailcow/ejabberd:1.6
- volumes:
- - ./data/conf/ejabberd/ejabberd.yml:/home/ejabberd/conf/ejabberd.yml:z
- - xmpp-vol-1:/home/ejabberd/database:z
- - xmpp-upload-vol-1:/var/www/upload:z
- - ./data/assets/ejabberd/sqlite:/sqlite:z
- - ./data/conf/ejabberd/autogen:/ejabberd/:z
- - mysql-socket-vol-1:/var/run/mysqld/:z
- - ./data/assets/ssl:/ssl:ro,z
- restart: always
- dns:
- - ${IPV4_NETWORK:-172.22.1}.254
- hostname: ejabberd.mailcow.local
- labels:
- ofelia.enabled: "true"
- ofelia.job-exec.ejabberd_certs.schedule: "@every 14d"
- ofelia.job-exec.ejabberd_certs.command: "/sbin/su-exec ejabberd /home/ejabberd/bin/ejabberdctl --node ejabberd@$${MAILCOW_HOSTNAME} request-certificate all"
- extra_hosts:
- - "${MAILCOW_HOSTNAME}:127.0.0.1"
- environment:
- - TZ=${TZ}
- - MAILCOW_HOSTNAME=${MAILCOW_HOSTNAME}
- - MASTER=${MASTER:-y}
- - IPV4_NETWORK=${IPV4_NETWORK:-172.22.1}
- - XMPP_HTTPS_PORT=${XMPP_HTTPS_PORT:-5443}
- - DBNAME=${DBNAME}
- - DBUSER=${DBUSER}
- - DBPASS=${DBPASS}
- ports:
- - "${XMPP_C2S_PORT:-5222}:5222"
- - "${XMPP_S2S_PORT:-5269}:5269"
- - "${XMPP_HTTPS_PORT:-5443}:5443"
- networks:
- mailcow-network:
- aliases:
- - ejabberd
-
ofelia-mailcow:
image: mcuadros/ofelia:latest
restart: always
@@ -631,7 +591,6 @@ services:
depends_on:
- sogo-mailcow
- dovecot-mailcow
- - ejabberd-mailcow
labels:
ofelia.enabled: "true"
volumes:
@@ -697,5 +656,3 @@ volumes:
crypt-vol-1:
sogo-web-vol-1:
sogo-userdata-backup-vol-1:
- xmpp-vol-1:
- xmpp-upload-vol-1:
diff --git a/generate_config.sh b/generate_config.sh
index 1c6886b8..33823a7f 100755
--- a/generate_config.sh
+++ b/generate_config.sh
@@ -144,6 +144,7 @@ DBROOT=$(LC_ALL=C /etc/docker/daemon.json
+ ip6tables -F -t nat
+ [[ -e /etc/alpine-release ]] && rc-service docker restart || systemctl restart docker.service
+ if [[ $? -ne 0 ]]; then
+ echo -e "\e[31mError:\e[0m Failed to activate IPv6 NAT! Reverting and exiting."
+ rm /etc/docker/daemon.json
+ if [[ -e /etc/alpine-release ]]; then
+ rc-service docker restart
+ else
+ systemctl reset-failed docker.service
+ systemctl restart docker.service
+ fi
+ return 1
+ fi
+ fi
+ # Removing legacy container
+ sed -i '/ipv6nat-mailcow:$/,/^$/d' docker-compose.yml
+ if [ -s docker-compose.override.yml ]; then
+ sed -i '/ipv6nat-mailcow:$/,/^$/d' docker-compose.override.yml
+ if [[ "$(cat docker-compose.override.yml | sed '/^\s*$/d' | wc -l)" == "2" ]]; then
+ mv docker-compose.override.yml docker-compose.override.yml_backup
+ fi
+ fi
+ echo -e "\e[32mGreat! \e[0mNative IPv6 NAT is active.\e[0m"
+ else
+ echo -e "\e[31mPlease upgrade Docker to version ${DOCKERV_REQ} or above.\e[0m"
+ return 0
+ fi
+}
+
while (($#)); do
case "${1}" in
--check|-c)
@@ -123,7 +207,7 @@ while (($#)); do
exit 99
fi
if [[ -z $(git log HEAD --pretty=format:"%H" | grep "${LATEST_REV}") ]]; then
- echo "Updated code is available."
+ echo -e "Updated code is available.\nThe changes can be found here: https://github.com/mailcow/mailcow-dockerized/commits/master"
git log --date=short --pretty=format:"%ad - %s" $(git rev-parse --short HEAD)..origin/master
exit 0
else
@@ -181,6 +265,7 @@ if [ ${#DOTS} -lt 2 ]; then
fi
if grep --help 2>&1 | head -n 1 | grep -q -i "busybox"; then echo "BusyBox grep detected, please install gnu grep, \"apk add --no-cache --upgrade grep\""; exit 1; fi
+# This will also cover sort
if cp --help 2>&1 | head -n 1 | grep -q -i "busybox"; then echo "BusyBox cp detected, please install coreutils, \"apk add --no-cache --upgrade coreutils\""; exit 1; fi
if sed --help 2>&1 | head -n 1 | grep -q -i "busybox"; then echo "BusyBox sed detected, please install gnu sed, \"apk add --no-cache --upgrade sed\""; exit 1; fi
@@ -219,9 +304,6 @@ CONFIG_ARRAY=(
"DOVECOT_MASTER_USER"
"DOVECOT_MASTER_PASS"
"MAILCOW_PASS_SCHEME"
- "XMPP_C2S_PORT"
- "XMPP_S2S_PORT"
- "XMPP_HTTPS_PORT"
"ADDITIONAL_SERVER_NAMES"
"ACME_CONTACT"
)
@@ -411,14 +493,6 @@ for option in ${CONFIG_ARRAY[@]}; do
echo '# see https://mailcow.github.io/mailcow-dockerized-docs/model-passwd/' >> mailcow.conf
echo "MAILCOW_PASS_SCHEME=BLF-CRYPT" >> mailcow.conf
fi
- elif [[ ${option} == "XMPP_C2S_PORT" ]]; then
- if ! grep -q ${option} mailcow.conf; then
- echo "XMPP_C2S_PORT=5222" >> mailcow.conf
- fi
- elif [[ ${option} == "XMPP_S2S_PORT" ]]; then
- if ! grep -q ${option} mailcow.conf; then
- echo "XMPP_S2S_PORT=5269" >> mailcow.conf
- fi
elif [[ ${option} == "ADDITIONAL_SERVER_NAMES" ]]; then
if ! grep -q ${option} mailcow.conf; then
echo '# Additional server names for mailcow UI' >> mailcow.conf
@@ -430,10 +504,6 @@ for option in ${CONFIG_ARRAY[@]}; do
echo '# Comma separated list without spaces! Example: ADDITIONAL_SERVER_NAMES=a.b.c,d.e.f' >> mailcow.conf
echo 'ADDITIONAL_SERVER_NAMES=' >> mailcow.conf
fi
- elif [[ ${option} == "XMPP_HTTPS_PORT" ]]; then
- if ! grep -q ${option} mailcow.conf; then
- echo "XMPP_HTTPS_PORT=5443" >> mailcow.conf
- fi
elif [[ ${option} == "ACME_CONTACT" ]]; then
if ! grep -q ${option} mailcow.conf; then
echo '# Lets Encrypt registration contact information' >> mailcow.conf
@@ -477,10 +547,11 @@ fi
if [ ! $FORCE ]; then
read -r -p "Are you sure you want to update mailcow: dockerized? All containers will be stopped. [y/N] " response
- if [[ ! "$response" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
+ if [[ ! "${response}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
echo "OK, exiting."
exit 0
fi
+ migrate_docker_nat
fi
echo -e "\e[32mValidating docker-compose stack configuration...\e[0m"
@@ -518,7 +589,7 @@ for container in "${MAILCOW_CONTAINERS[@]}"; do
docker rm -f "$container" 2> /dev/null
done
-[[ -f data/conf/nginx/ejabberd.conf ]] && mv data/conf/nginx/ejabberd.conf data/conf/nginx/ZZZ-ejabberd.conf
+[[ -f data/conf/nginx/ZZZ-ejabberd.conf ]] && rm data/conf/nginx/ZZZ-ejabberd.conf
# Silently fixing remote url from andryyy to mailcow
git remote set-url origin https://github.com/mailcow/mailcow-dockerized
@@ -645,6 +716,11 @@ fi
echo -e "\e[32mCollecting garbage...\e[0m"
docker_garbage
+# Run post-update-hook
+if [ -f "${SCRIPT_DIR}/post_update_hook.sh" ]; then
+ bash "${SCRIPT_DIR}/post_update_hook.sh"
+fi
+
#echo "In case you encounter any problem, hard-reset to a state before updating mailcow:"
#echo
#git reflog --color=always | grep "Before update on "