mirror of
https://github.com/mailcow/mailcow-dockerized.git
synced 2026-06-15 11:00:29 +00:00
Compare commits
132 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3028a18a37 | |||
| 26a5fcf989 | |||
| 509086ef54 | |||
| 963510ed22 | |||
| 2c0f8cda50 | |||
| 50d2671d75 | |||
| b73d879f3c | |||
| 725a5fe5b9 | |||
| 65ca42ca42 | |||
| b22ff59f7b | |||
| 58527857d9 | |||
| 6306c4555c | |||
| 922603f906 | |||
| f8d45de749 | |||
| cb1602c2de | |||
| 8026b6c874 | |||
| 042676fff7 | |||
| 44d53146af | |||
| ce4fb069d5 | |||
| 9444000d46 | |||
| eacd9ac240 | |||
| cf38d6ca69 | |||
| 905993d66e | |||
| 0d7fe2e347 | |||
| 33bd871a63 | |||
| 772122b255 | |||
| 9fb346751c | |||
| cb058e91a3 | |||
| 27e7407407 | |||
| 7d46de33d8 | |||
| 5470b51cc7 | |||
| 8e0b1d8aee | |||
| 2834459b22 | |||
| 000894dabd | |||
| 494620cdea | |||
| a502eb239d | |||
| caf775093e | |||
| f28e18e676 | |||
| b4bab1d5b9 | |||
| c4d5072e5c | |||
| 852bf750ca | |||
| 47359c4113 | |||
| 15f2c4c769 | |||
| a0174c61e8 | |||
| 5b30dce609 | |||
| 8f6099e3e4 | |||
| 7c44375223 | |||
| 72e204f8fd | |||
| b5f5b53e37 | |||
| 1c15133a52 | |||
| 7c9c2c35f8 | |||
| 9806e568c0 | |||
| b4bb4e2938 | |||
| de7b809229 | |||
| a40df1ff87 | |||
| a161aa2c92 | |||
| cad0f25345 | |||
| 2ed453a400 | |||
| 452d8a686f | |||
| 90f77f6d5c | |||
| 0c11cf747a | |||
| 6d36475ed3 | |||
| fee6ff43bf | |||
| 57cd5ec818 | |||
| 02512e0f4f | |||
| 555f4a8a6d | |||
| 3633766544 | |||
| e98a984417 | |||
| bc9141753f | |||
| 1f9f4157a6 | |||
| 778a3ed551 | |||
| 5ea4305185 | |||
| ef311f22bf | |||
| e202530afb | |||
| 85deeaf806 | |||
| 825c8a6abe | |||
| cdc8f63b4b | |||
| 9db9818ede | |||
| 4f7ee669d3 | |||
| 77f9947613 | |||
| a8eb3b6ac5 | |||
| 575eab1cf0 | |||
| 7a23e4fd4e | |||
| b16b276f36 | |||
| 4f380debb5 | |||
| f34d3620b1 | |||
| 70e99447f9 | |||
| 047c4aa3a0 | |||
| 925b220905 | |||
| 6708059227 | |||
| 1f3d9d4e1c | |||
| 0dcfac8f15 | |||
| ad8b7f0894 | |||
| 55f810b23f | |||
| 65eddee63e | |||
| 7b57b3392c | |||
| 492451bfee | |||
| 4322c98f73 | |||
| edcf789126 | |||
| b985ba4f0e | |||
| 67c0405274 | |||
| 9b32151ab5 | |||
| a1e8077f45 | |||
| 956e4e2aa7 | |||
| b51a659515 | |||
| 44a6f09a09 | |||
| 4c10525078 | |||
| c9ab8b2eff | |||
| 4bf38bf00f | |||
| 7c7c67948e | |||
| 263cb96786 | |||
| b6e3e7a658 | |||
| ceaf1423f4 | |||
| c2e0a275e1 | |||
| c8620a066d | |||
| 9598b503ec | |||
| 1ca566f670 | |||
| 94f4ec8b96 | |||
| 7aab2c55ff | |||
| 6abb4d34c1 | |||
| c8ccf080f3 | |||
| 0342ae926c | |||
| be08742653 | |||
| 514079fe96 | |||
| 27e9210d52 | |||
| 1b6e5b7116 | |||
| 0dab215e27 | |||
| 6ec136e63f | |||
| bba5671eaf | |||
| 88d7593d89 | |||
| bd23b80d45 | |||
| f96e0c4071 |
@@ -0,0 +1,13 @@
|
||||
## :memo: Brief description
|
||||
<!-- Diff summary - START -->
|
||||
<!-- Diff summary - END -->
|
||||
|
||||
|
||||
## :computer: Commits
|
||||
<!-- Diff commits - START -->
|
||||
<!-- Diff commits - END -->
|
||||
|
||||
|
||||
## :file_folder: Modified files
|
||||
<!-- Diff files - START -->
|
||||
<!-- Diff files - END -->
|
||||
@@ -14,7 +14,7 @@ jobs:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Mark/Close Stale Issues and Pull Requests 🗑️
|
||||
uses: actions/stale@v5.0.0
|
||||
uses: actions/stale@v6.0.1
|
||||
with:
|
||||
repo-token: ${{ secrets.STALE_ACTION_PAT }}
|
||||
days-before-stale: 60
|
||||
@@ -30,6 +30,7 @@ jobs:
|
||||
stale-issue-label: "stale"
|
||||
stale-pr-label: "stale"
|
||||
exempt-draft-pr: "true"
|
||||
close-issue-reason: "not_planned"
|
||||
operations-per-run: "250"
|
||||
ascending: "true"
|
||||
#DRY-RUN
|
||||
|
||||
@@ -1,27 +1,31 @@
|
||||
name: Build Mailcow Docker Images
|
||||
name: Build mailcow Docker Images
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "master", "staging" ]
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read # to fetch code (actions/checkout)
|
||||
|
||||
jobs:
|
||||
docker_image_builds:
|
||||
strategy:
|
||||
matrix:
|
||||
images: ["acme-mailcow",
|
||||
"clamd-mailcow",
|
||||
"dockerapi-mailcow",
|
||||
"dovecot-mailcow",
|
||||
"netfilter-mailcow",
|
||||
"olefy-mailcow",
|
||||
"php-fpm-mailcow",
|
||||
"postfix-mailcow",
|
||||
"rspamd-mailcow",
|
||||
"sogo-mailcow",
|
||||
"solr-mailcow",
|
||||
"unbound-mailcow",
|
||||
"watchdog-mailcow"]
|
||||
images:
|
||||
- "acme-mailcow"
|
||||
- "clamd-mailcow"
|
||||
- "dockerapi-mailcow"
|
||||
- "dovecot-mailcow"
|
||||
- "netfilter-mailcow"
|
||||
- "olefy-mailcow"
|
||||
- "php-fpm-mailcow"
|
||||
- "postfix-mailcow"
|
||||
- "rspamd-mailcow"
|
||||
- "sogo-mailcow"
|
||||
- "solr-mailcow"
|
||||
- "unbound-mailcow"
|
||||
- "watchdog-mailcow"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
name: Mailcow Integration Tests
|
||||
name: mailcow Integration Tests
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "master", "staging" ]
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
integration_tests:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
name: Create PR to merge to nightly from staging
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- staging
|
||||
jobs:
|
||||
action-pull-request:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Run the Action
|
||||
uses: devops-infra/action-pull-request@v0.5.1
|
||||
with:
|
||||
github_token: ${{ secrets.PRTONIGHTLY_ACTION_PAT }}
|
||||
title: Automatic PR to nightly from ${{ github.event.repository.updated_at}}
|
||||
assignee: DerLinkman
|
||||
source_branch: staging
|
||||
target_branch: nightly
|
||||
reviewer: DerLinkman
|
||||
label: upstream
|
||||
template: .github/ISSUE_TEMPLATE/pr_to_nighty_template.yml
|
||||
get_diff: true
|
||||
@@ -10,8 +10,8 @@ jobs:
|
||||
- name: Tweet-trigger-publish-release
|
||||
uses: mugi111/tweet-trigger-release@v1.1
|
||||
with:
|
||||
consumer_key: ${{ secrets.TWITTER_CONSUMER_KEY }}
|
||||
consumer_secret: ${{ secrets.TWITTER_CONSUMER_SECRET }}
|
||||
access_token_key: ${{ secrets.TWITTER_ACCESS_TOKEN_KEY }}
|
||||
access_token_secret: ${{ secrets.TWITTER_ACCESS_TOKEN_SECRET }}
|
||||
consumer_key: ${{ secrets.CONSUMER_KEY }}
|
||||
consumer_secret: ${{ secrets.CONSUMER_SECRET }}
|
||||
access_token_key: ${{ secrets.ACCESS_TOKEN_KEY }}
|
||||
access_token_secret: ${{ secrets.ACCESS_TOKEN_SECRET }}
|
||||
tweet_body: 'A new mailcow-dockerized Release has been Released on GitHub! Checkout our GitHub Page for the latest Release: github.com/mailcow/mailcow-dockerized/releases/latest'
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
## We stand with 🇺🇦
|
||||
|
||||
[](https://github.com/mailcow/mailcow-dockerized/actions/workflows/integration_tests.yml)
|
||||
[](https://translate.mailcow.email/engage/mailcow-dockerized/)
|
||||
[](https://twitter.com/mailcow_email)
|
||||
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
FROM debian:bullseye-slim
|
||||
|
||||
RUN apt update && apt install pigz
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM clamav/clamav:0.105.0_base
|
||||
FROM clamav/clamav:0.105.1_base
|
||||
|
||||
LABEL maintainer "André Peters <andre.peters@servercow.de>"
|
||||
|
||||
|
||||
@@ -307,6 +307,7 @@ namespace {
|
||||
}
|
||||
EOF
|
||||
|
||||
|
||||
cat <<EOF > /etc/dovecot/sogo_trusted_ip.conf
|
||||
# Autogenerated by mailcow
|
||||
remote ${IPV4_NETWORK}.248 {
|
||||
@@ -349,6 +350,14 @@ sievec /var/vmail/sieve/global_sieve_after.sieve
|
||||
sievec /usr/lib/dovecot/sieve/report-spam.sieve
|
||||
sievec /usr/lib/dovecot/sieve/report-ham.sieve
|
||||
|
||||
for file in /var/vmail/*/*/sieve/*.sieve ; do
|
||||
if [[ "$file" == "/var/vmail/*/*/sieve/*.sieve" ]]; then
|
||||
continue
|
||||
fi
|
||||
sievec "$file" "$(dirname "$file")/../.dovecot.svbin"
|
||||
chown vmail:vmail "$(dirname "$file")/../.dovecot.svbin"
|
||||
done
|
||||
|
||||
# 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/passwd-verify.lua
|
||||
|
||||
@@ -50,7 +50,7 @@ try:
|
||||
def query_mysql(query, headers = True, update = False):
|
||||
while True:
|
||||
try:
|
||||
cnx = mysql.connector.connect(unix_socket = '/var/run/mysqld/mysqld.sock', user=os.environ.get('DBUSER'), passwd=os.environ.get('DBPASS'), database=os.environ.get('DBNAME'), charset="utf8")
|
||||
cnx = mysql.connector.connect(unix_socket = '/var/run/mysqld/mysqld.sock', user=os.environ.get('DBUSER'), passwd=os.environ.get('DBPASS'), database=os.environ.get('DBNAME'), charset="utf8mb4", collation="utf8mb4_general_ci")
|
||||
except Exception as ex:
|
||||
print('%s - trying again...' % (ex))
|
||||
time.sleep(3)
|
||||
@@ -166,4 +166,4 @@ try:
|
||||
notify_rcpt(record['rcpt'], record['counter'], record['quarantine_acl'], attrs['quarantine_category'])
|
||||
|
||||
finally:
|
||||
os.unlink(pidfile)
|
||||
os.unlink(pidfile)
|
||||
|
||||
@@ -252,7 +252,7 @@ def permBan(net, unban=False):
|
||||
if rule not in chain.rules and not unban:
|
||||
logCrit('Add host/network %s to blacklist' % net)
|
||||
chain.insert_rule(rule)
|
||||
r.hset('F2B_PERM_BANS', '%s' % net, int(round(time.time())))
|
||||
r.hset('F2B_PERM_BANS', '%s' % net, int(round(time.time())))
|
||||
elif rule in chain.rules and unban:
|
||||
logCrit('Remove host/network %s from blacklist' % net)
|
||||
chain.delete_rule(rule)
|
||||
@@ -267,7 +267,7 @@ def permBan(net, unban=False):
|
||||
if rule not in chain.rules and not unban:
|
||||
logCrit('Add host/network %s to blacklist' % net)
|
||||
chain.insert_rule(rule)
|
||||
r.hset('F2B_PERM_BANS', '%s' % net, int(round(time.time())))
|
||||
r.hset('F2B_PERM_BANS', '%s' % net, int(round(time.time())))
|
||||
elif rule in chain.rules and unban:
|
||||
logCrit('Remove host/network %s from blacklist' % net)
|
||||
chain.delete_rule(rule)
|
||||
@@ -346,6 +346,8 @@ def snat4(snat_target):
|
||||
rule.dst = '!' + rule.src
|
||||
target = rule.create_target("SNAT")
|
||||
target.to_source = snat_target
|
||||
match = rule.create_match("comment")
|
||||
match.comment = f'{int(round(time.time()))}'
|
||||
return rule
|
||||
|
||||
while not quit_now:
|
||||
@@ -356,19 +358,26 @@ def snat4(snat_target):
|
||||
table.refresh()
|
||||
chain = iptc.Chain(table, 'POSTROUTING')
|
||||
table.autocommit = False
|
||||
if get_snat4_rule() not in chain.rules:
|
||||
logCrit('Added POSTROUTING rule for source network %s to SNAT target %s' % (get_snat4_rule().src, snat_target))
|
||||
chain.insert_rule(get_snat4_rule())
|
||||
table.commit()
|
||||
else:
|
||||
for position, item in enumerate(chain.rules):
|
||||
if item == get_snat4_rule():
|
||||
if position != 0:
|
||||
chain.delete_rule(get_snat4_rule())
|
||||
table.commit()
|
||||
new_rule = get_snat4_rule()
|
||||
for position, rule in enumerate(chain.rules):
|
||||
match = all((
|
||||
new_rule.get_src() == rule.get_src(),
|
||||
new_rule.get_dst() == rule.get_dst(),
|
||||
new_rule.target.parameters == rule.target.parameters,
|
||||
new_rule.target.name == rule.target.name
|
||||
))
|
||||
if position == 0:
|
||||
if not match:
|
||||
logInfo(f'Added POSTROUTING rule for source network {new_rule.src} to SNAT target {snat_target}')
|
||||
chain.insert_rule(new_rule)
|
||||
else:
|
||||
if match:
|
||||
logInfo(f'Remove rule for source network {new_rule.src} to SNAT target {snat_target} from POSTROUTING chain at position {position}')
|
||||
chain.delete_rule(rule)
|
||||
table.commit()
|
||||
table.autocommit = True
|
||||
except:
|
||||
print('Error running SNAT4, retrying...')
|
||||
print('Error running SNAT4, retrying...')
|
||||
|
||||
def snat6(snat_target):
|
||||
global lock
|
||||
@@ -402,7 +411,7 @@ def snat6(snat_target):
|
||||
table.commit()
|
||||
table.autocommit = True
|
||||
except:
|
||||
print('Error running SNAT6, retrying...')
|
||||
print('Error running SNAT6, retrying...')
|
||||
|
||||
def autopurge():
|
||||
while not quit_now:
|
||||
@@ -468,7 +477,7 @@ def whitelistUpdate():
|
||||
if Counter(new_whitelist) != Counter(WHITELIST):
|
||||
WHITELIST = new_whitelist
|
||||
logInfo('Whitelist was changed, it has %s entries' % len(WHITELIST))
|
||||
time.sleep(60.0 - ((time.time() - start_time) % 60.0))
|
||||
time.sleep(60.0 - ((time.time() - start_time) % 60.0))
|
||||
|
||||
def blacklistUpdate():
|
||||
global quit_now
|
||||
@@ -479,7 +488,7 @@ def blacklistUpdate():
|
||||
new_blacklist = []
|
||||
if list:
|
||||
new_blacklist = genNetworkList(list)
|
||||
if Counter(new_blacklist) != Counter(BLACKLIST):
|
||||
if Counter(new_blacklist) != Counter(BLACKLIST):
|
||||
addban = set(new_blacklist).difference(BLACKLIST)
|
||||
delban = set(BLACKLIST).difference(new_blacklist)
|
||||
BLACKLIST = new_blacklist
|
||||
@@ -490,7 +499,7 @@ def blacklistUpdate():
|
||||
if delban:
|
||||
for net in delban:
|
||||
permBan(net=net, unban=True)
|
||||
time.sleep(60.0 - ((time.time() - start_time) % 60.0))
|
||||
time.sleep(60.0 - ((time.time() - start_time) % 60.0))
|
||||
|
||||
def initChain():
|
||||
# Is called before threads start, no locking
|
||||
|
||||
@@ -323,7 +323,19 @@ hosts = unix:/var/run/mysqld/mysqld.sock
|
||||
dbname = ${DBNAME}
|
||||
# First select queries domain and alias_domain to determine if domains are active.
|
||||
query = SELECT goto FROM alias
|
||||
WHERE address='%s'
|
||||
WHERE id IN (
|
||||
SELECT COALESCE (
|
||||
(
|
||||
SELECT id FROM alias
|
||||
WHERE address='%s'
|
||||
AND (active='1' OR active='2')
|
||||
), (
|
||||
SELECT id FROM alias
|
||||
WHERE address='@%d'
|
||||
AND (active='1' OR active='2')
|
||||
)
|
||||
)
|
||||
)
|
||||
AND active='1'
|
||||
AND (domain IN
|
||||
(SELECT domain FROM domain
|
||||
@@ -354,7 +366,7 @@ query = SELECT goto FROM alias
|
||||
WHERE alias_domain.alias_domain = '%d'
|
||||
AND mailbox.username = CONCAT('%u','@',alias_domain.target_domain)
|
||||
AND (mailbox.active = '1' OR mailbox.active ='2')
|
||||
AND alias_domain.active='1'
|
||||
AND alias_domain.active='1';
|
||||
EOF
|
||||
|
||||
# MX based routing
|
||||
|
||||
@@ -2,7 +2,7 @@ FROM debian:bullseye-slim
|
||||
LABEL maintainer "Andre Peters <andre.peters@servercow.de>"
|
||||
|
||||
ARG DEBIAN_FRONTEND=noninteractive
|
||||
ARG SOGO_DEBIAN_REPOSITORY=http://packages.inverse.ca/SOGo/nightly/5/debian/
|
||||
ARG SOGO_DEBIAN_REPOSITORY=http://packages.sogo.nu/nightly/5/debian/
|
||||
ENV LC_ALL C
|
||||
ENV GOSU_VERSION 1.14
|
||||
|
||||
@@ -30,7 +30,7 @@ RUN echo "Building from repository $SOGO_DEBIAN_REPOSITORY" \
|
||||
&& gosu nobody true \
|
||||
&& mkdir /usr/share/doc/sogo \
|
||||
&& touch /usr/share/doc/sogo/empty.sh \
|
||||
&& apt-key adv --keyserver keyserver.ubuntu.com --recv-key 0x810273C4 \
|
||||
&& apt-key adv --keyserver keys.openpgp.org --recv-key 74FFC6D72B925A34B5D356BDF8A27B36A6E2EAE9 \
|
||||
&& echo "deb ${SOGO_DEBIAN_REPOSITORY} bullseye bullseye" > /etc/apt/sources.list.d/sogo.list \
|
||||
&& apt-get update && apt-get install -y --no-install-recommends \
|
||||
sogo \
|
||||
@@ -52,4 +52,4 @@ RUN chmod +x /bootstrap-sogo.sh \
|
||||
|
||||
ENTRYPOINT ["/docker-entrypoint.sh"]
|
||||
|
||||
CMD exec /usr/bin/supervisord -c /etc/supervisor/supervisord.conf
|
||||
CMD exec /usr/bin/supervisord -c /etc/supervisor/supervisord.conf
|
||||
@@ -142,6 +142,10 @@ cat <<EOF > /var/lib/sogo/GNUstep/Defaults/sogod.plist
|
||||
<string>mysql://${DBUSER}:${DBPASS}@%2Fvar%2Frun%2Fmysqld%2Fmysqld.sock/${DBNAME}/sogo_acl</string>
|
||||
<key>SOGoIMAPServer</key>
|
||||
<string>imap://${IPV4_NETWORK}.250:143/?TLS=YES&tlsVerifyMode=none</string>
|
||||
<key>SOGoSieveServer</key>
|
||||
<string>sieve://${IPV4_NETWORK}.250:4190/?TLS=YES&tlsVerifyMode=none</string>
|
||||
<key>SOGoSMTPServer</key>
|
||||
<string>smtp://${IPV4_NETWORK}.253:588/?TLS=YES&tlsVerifyMode=none</string>
|
||||
<key>SOGoTrustProxyAuthentication</key>
|
||||
<string>YES</string>
|
||||
<key>SOGoEncryptionKey</key>
|
||||
|
||||
@@ -32,8 +32,6 @@
|
||||
// );
|
||||
|
||||
// self-signed is not trusted anymore
|
||||
SOGoSieveServer = "sieve://dovecot:4190/?TLS=YES&tlsVerifyMode=none";
|
||||
SOGoSMTPServer = "smtp://postfix:588/?TLS=YES&tlsVerifyMode=none";
|
||||
WOPort = "0.0.0.0:20000";
|
||||
SOGoMemcachedHost = "memcached";
|
||||
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
html {
|
||||
box-sizing: border-box;
|
||||
overflow: -moz-scrollbars-vertical;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
*,
|
||||
*:before,
|
||||
*:after {
|
||||
box-sizing: inherit;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
background: #fafafa;
|
||||
}
|
||||
+2
-43
@@ -5,56 +5,15 @@
|
||||
<meta charset="UTF-8">
|
||||
<title>Swagger UI</title>
|
||||
<link rel="stylesheet" type="text/css" href="./swagger-ui.css" />
|
||||
<link rel="stylesheet" type="text/css" href="index.css" />
|
||||
<link rel="icon" type="image/png" href="./favicon-32x32.png" sizes="32x32" />
|
||||
<link rel="icon" type="image/png" href="./favicon-16x16.png" sizes="16x16" />
|
||||
<style>
|
||||
html
|
||||
{
|
||||
box-sizing: border-box;
|
||||
overflow: -moz-scrollbars-vertical;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
*,
|
||||
*:before,
|
||||
*:after
|
||||
{
|
||||
box-sizing: inherit;
|
||||
}
|
||||
|
||||
body
|
||||
{
|
||||
margin:0;
|
||||
background: #fafafa;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="swagger-ui"></div>
|
||||
|
||||
<script src="./swagger-ui-bundle.js" charset="UTF-8"> </script>
|
||||
<script src="./swagger-ui-standalone-preset.js" charset="UTF-8"> </script>
|
||||
<script>
|
||||
window.onload = function() {
|
||||
// Begin Swagger UI call region
|
||||
const ui = SwaggerUIBundle({
|
||||
urls: [{url: "/api/openapi.yaml", name: "mailcow API"}],
|
||||
dom_id: '#swagger-ui',
|
||||
deepLinking: true,
|
||||
presets: [
|
||||
SwaggerUIBundle.presets.apis,
|
||||
SwaggerUIStandalonePreset
|
||||
],
|
||||
plugins: [
|
||||
SwaggerUIBundle.plugins.DownloadUrl
|
||||
],
|
||||
layout: "StandaloneLayout"
|
||||
});
|
||||
// End Swagger UI call region
|
||||
|
||||
window.ui = ui;
|
||||
};
|
||||
</script>
|
||||
<script src="./swagger-initializer.js" charset="UTF-8"> </script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
var isValid, qp, arr;
|
||||
|
||||
if (/code|token|error/.test(window.location.hash)) {
|
||||
qp = window.location.hash.substring(1);
|
||||
qp = window.location.hash.substring(1).replace('?', '&');
|
||||
} else {
|
||||
qp = location.search.substring(1);
|
||||
}
|
||||
@@ -38,7 +38,7 @@
|
||||
authId: oauth2.auth.name,
|
||||
source: "auth",
|
||||
level: "warning",
|
||||
message: "Authorization may be unsafe, passed state was changed in server Passed state wasn't returned from auth server"
|
||||
message: "Authorization may be unsafe, passed state was changed in server. The passed state wasn't returned from auth server."
|
||||
});
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
authId: oauth2.auth.name,
|
||||
source: "auth",
|
||||
level: "error",
|
||||
message: oauthErrorMsg || "[Authorization failed]: no accessCode received from the server"
|
||||
message: oauthErrorMsg || "[Authorization failed]: no accessCode received from the server."
|
||||
});
|
||||
}
|
||||
} else {
|
||||
@@ -67,9 +67,13 @@
|
||||
window.close();
|
||||
}
|
||||
|
||||
window.addEventListener('DOMContentLoaded', function () {
|
||||
run();
|
||||
});
|
||||
if (document.readyState !== 'loading') {
|
||||
run();
|
||||
} else {
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
run();
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+91
-51
@@ -518,21 +518,23 @@ paths:
|
||||
- domain.tld
|
||||
type: success
|
||||
schema:
|
||||
properties:
|
||||
log:
|
||||
description: contains request object
|
||||
items: {}
|
||||
type: array
|
||||
msg:
|
||||
items: {}
|
||||
type: array
|
||||
type:
|
||||
enum:
|
||||
- success
|
||||
- danger
|
||||
- error
|
||||
type: string
|
||||
type: object
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
log:
|
||||
description: contains request object
|
||||
items: {}
|
||||
type: array
|
||||
msg:
|
||||
items: {}
|
||||
type: array
|
||||
type:
|
||||
enum:
|
||||
- success
|
||||
- danger
|
||||
- error
|
||||
type: string
|
||||
description: OK
|
||||
headers: {}
|
||||
tags:
|
||||
@@ -579,6 +581,11 @@ paths:
|
||||
domain:
|
||||
description: Fully qualified domain name
|
||||
type: string
|
||||
gal:
|
||||
description: >-
|
||||
is domain global address list active or not, it enables
|
||||
shared contacts accross domain in SOGo webmail
|
||||
type: boolean
|
||||
mailboxes:
|
||||
description: limit count of mailboxes associated with this domain
|
||||
type: number
|
||||
@@ -596,6 +603,9 @@ paths:
|
||||
if not, them you have to create "dummy" mailbox for each
|
||||
address to relay
|
||||
type: boolean
|
||||
relay_unknown_only:
|
||||
description: Relay non-existing mailboxes only. Existing mailboxes will be delivered locally.
|
||||
type: boolean
|
||||
rl_frame:
|
||||
enum:
|
||||
- s
|
||||
@@ -606,6 +616,11 @@ paths:
|
||||
rl_value:
|
||||
description: rate limit value
|
||||
type: number
|
||||
tags:
|
||||
description: tags for this Domain
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
type: object
|
||||
summary: Create domain
|
||||
/api/v1/add/domain-admin:
|
||||
@@ -1952,21 +1967,23 @@ paths:
|
||||
- domain2.tld
|
||||
type: success
|
||||
schema:
|
||||
properties:
|
||||
log:
|
||||
description: contains request object
|
||||
items: {}
|
||||
type: array
|
||||
msg:
|
||||
items: {}
|
||||
type: array
|
||||
type:
|
||||
enum:
|
||||
- success
|
||||
- danger
|
||||
- error
|
||||
type: string
|
||||
type: object
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
log:
|
||||
description: contains request object
|
||||
items: {}
|
||||
type: array
|
||||
msg:
|
||||
items: {}
|
||||
type: array
|
||||
type:
|
||||
enum:
|
||||
- success
|
||||
- danger
|
||||
- error
|
||||
type: string
|
||||
description: OK
|
||||
headers: {}
|
||||
tags:
|
||||
@@ -1977,14 +1994,15 @@ paths:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
example:
|
||||
- domain.tld
|
||||
- domain2.tld
|
||||
properties:
|
||||
items:
|
||||
description: contains list of domains you want to delete
|
||||
type: object
|
||||
type: object
|
||||
items:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
summary: Delete domain
|
||||
/api/v1/delete/domain-admin:
|
||||
post:
|
||||
@@ -2972,23 +2990,25 @@ paths:
|
||||
$ref: "#/components/responses/Unauthorized"
|
||||
"200":
|
||||
content:
|
||||
"*/*":
|
||||
application/json:
|
||||
schema:
|
||||
properties:
|
||||
log:
|
||||
description: contains request object
|
||||
items: {}
|
||||
type: array
|
||||
msg:
|
||||
items: {}
|
||||
type: array
|
||||
type:
|
||||
enum:
|
||||
- success
|
||||
- danger
|
||||
- error
|
||||
type: string
|
||||
type: object
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
log:
|
||||
type: array
|
||||
description: contains request object
|
||||
items: {}
|
||||
msg:
|
||||
type: array
|
||||
items: {}
|
||||
type:
|
||||
enum:
|
||||
- success
|
||||
- danger
|
||||
- error
|
||||
type: string
|
||||
description: OK
|
||||
headers: {}
|
||||
tags:
|
||||
@@ -3056,13 +3076,33 @@ paths:
|
||||
if not, them you have to create "dummy" mailbox for each
|
||||
address to relay
|
||||
type: boolean
|
||||
relay_unknown_only:
|
||||
description: Relay non-existing mailboxes only. Existing mailboxes will be delivered locally.
|
||||
type: boolean
|
||||
relayhost:
|
||||
description: id of relayhost
|
||||
type: number
|
||||
rl_frame:
|
||||
enum:
|
||||
- s
|
||||
- m
|
||||
- h
|
||||
- d
|
||||
type: string
|
||||
rl_value:
|
||||
description: rate limit value
|
||||
type: number
|
||||
tags:
|
||||
description: tags for this Domain
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
type: object
|
||||
items:
|
||||
description: contains list of domain names you want update
|
||||
type: object
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
type: object
|
||||
summary: Update domain
|
||||
/api/v1/edit/fail2ban:
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
window.onload = function() {
|
||||
// Begin Swagger UI call region
|
||||
const ui = SwaggerUIBundle({
|
||||
urls: [{url: "/api/openapi.yaml", name: "mailcow API"}],
|
||||
dom_id: '#swagger-ui',
|
||||
deepLinking: true,
|
||||
presets: [
|
||||
SwaggerUIBundle.presets.apis,
|
||||
SwaggerUIStandalonePreset
|
||||
],
|
||||
plugins: [
|
||||
SwaggerUIBundle.plugins.DownloadUrl
|
||||
],
|
||||
layout: "StandaloneLayout"
|
||||
});
|
||||
// End Swagger UI call region
|
||||
|
||||
window.ui = ui;
|
||||
};
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+2
-2
@@ -128,10 +128,10 @@ table.footable > tbody > tr.footable-empty > th {
|
||||
content: "\f130";
|
||||
}
|
||||
.fooicon-plus:before {
|
||||
content: "\f4fc";
|
||||
content: "\f4fd";
|
||||
}
|
||||
.fooicon-minus:before {
|
||||
content: "\f2e8";
|
||||
content: "\f2e9";
|
||||
}
|
||||
.fooicon-search:before {
|
||||
content: "\f52a";
|
||||
|
||||
+1
-1
File diff suppressed because one or more lines are too long
@@ -269,6 +269,7 @@ code {
|
||||
padding: 10px;
|
||||
background: #fbfbfb;
|
||||
border: 1px solid #ededed;
|
||||
min-height: 110px;
|
||||
}
|
||||
|
||||
.tag-box {
|
||||
|
||||
@@ -48,7 +48,14 @@ if (isset($pending_tfa_authmechs['u2f'])) {
|
||||
$globalVariables = [
|
||||
'mailcow_info' => array(
|
||||
'version_tag' => $GLOBALS['MAILCOW_GIT_VERSION'],
|
||||
'git_project_url' => $GLOBALS['MAILCOW_GIT_URL']
|
||||
'last_version_tag' => $GLOBALS['MAILCOW_LAST_GIT_VERSION'],
|
||||
'git_owner' => $GLOBALS['MAILCOW_GIT_OWNER'],
|
||||
'git_repo' => $GLOBALS['MAILCOW_GIT_REPO'],
|
||||
'git_project_url' => $GLOBALS['MAILCOW_GIT_URL'],
|
||||
'git_commit' => $GLOBALS['MAILCOW_GIT_COMMIT'],
|
||||
'git_commit_date' => $GLOBALS['MAILCOW_GIT_COMMIT_DATE'],
|
||||
'mailcow_branch' => $GLOBALS['MAILCOW_BRANCH'],
|
||||
'updated_at' => $GLOBALS['MAILCOW_UPDATEDAT']
|
||||
),
|
||||
'js_path' => '/cache/'.basename($JSPath),
|
||||
'pending_tfa_methods' => @$_SESSION['pending_tfa_methods'],
|
||||
|
||||
@@ -935,38 +935,47 @@ function check_login($user, $pass, $app_passwd_data = false) {
|
||||
$stmt->execute(array(':user' => $user));
|
||||
$rows = array_merge($rows, $stmt->fetchAll(PDO::FETCH_ASSOC));
|
||||
}
|
||||
foreach ($rows as $row) {
|
||||
foreach ($rows as $row) {
|
||||
// verify password
|
||||
if (verify_hash($row['password'], $pass) !== false) {
|
||||
// check for tfa authenticators
|
||||
$authenticators = get_tfa($user);
|
||||
if (isset($authenticators['additional']) && is_array($authenticators['additional']) && count($authenticators['additional']) > 0) {
|
||||
$_SESSION['pending_mailcow_cc_username'] = $user;
|
||||
$_SESSION['pending_mailcow_cc_role'] = "user";
|
||||
$_SESSION['pending_tfa_methods'] = $authenticators['additional'];
|
||||
unset($_SESSION['ldelay']);
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'success',
|
||||
'log' => array(__FUNCTION__, $user, '*'),
|
||||
'msg' => array('logged_in_as', $user)
|
||||
);
|
||||
return "pending";
|
||||
} else {
|
||||
if ($app_passwd_data['eas'] === true || $app_passwd_data['dav'] === true) {
|
||||
$service = ($app_passwd_data['eas'] === true) ? 'EAS' : 'DAV';
|
||||
$stmt = $pdo->prepare("REPLACE INTO sasl_log (`service`, `app_password`, `username`, `real_rip`) VALUES (:service, :app_id, :username, :remote_addr)");
|
||||
$stmt->execute(array(
|
||||
':service' => $service,
|
||||
':app_id' => $row['app_passwd_id'],
|
||||
':username' => $user,
|
||||
':remote_addr' => ($_SERVER['HTTP_X_REAL_IP'] ?? $_SERVER['REMOTE_ADDR'])
|
||||
));
|
||||
if (!array_key_exists("app_passwd_id", $row)){
|
||||
// password is not a app password
|
||||
// check for tfa authenticators
|
||||
$authenticators = get_tfa($user);
|
||||
if (isset($authenticators['additional']) && is_array($authenticators['additional']) && count($authenticators['additional']) > 0 &&
|
||||
$app_passwd_data['eas'] !== true && $app_passwd_data['dav'] !== true) {
|
||||
// authenticators found, init TFA flow
|
||||
$_SESSION['pending_mailcow_cc_username'] = $user;
|
||||
$_SESSION['pending_mailcow_cc_role'] = "user";
|
||||
$_SESSION['pending_tfa_methods'] = $authenticators['additional'];
|
||||
unset($_SESSION['ldelay']);
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'success',
|
||||
'log' => array(__FUNCTION__, $user, '*'),
|
||||
'msg' => array('logged_in_as', $user)
|
||||
);
|
||||
return "pending";
|
||||
} else if (!isset($authenticators['additional']) || !is_array($authenticators['additional']) || count($authenticators['additional']) == 0) {
|
||||
// no authenticators found, login successfull
|
||||
// Reactivate TFA if it was set to "deactivate TFA for next login"
|
||||
$stmt = $pdo->prepare("UPDATE `tfa` SET `active`='1' WHERE `username` = :user");
|
||||
$stmt->execute(array(':user' => $user));
|
||||
|
||||
unset($_SESSION['ldelay']);
|
||||
return "user";
|
||||
}
|
||||
} elseif ($app_passwd_data['eas'] === true || $app_passwd_data['dav'] === true) {
|
||||
// password is a app password
|
||||
$service = ($app_passwd_data['eas'] === true) ? 'EAS' : 'DAV';
|
||||
$stmt = $pdo->prepare("REPLACE INTO sasl_log (`service`, `app_password`, `username`, `real_rip`) VALUES (:service, :app_id, :username, :remote_addr)");
|
||||
$stmt->execute(array(
|
||||
':service' => $service,
|
||||
':app_id' => $row['app_passwd_id'],
|
||||
':username' => $user,
|
||||
':remote_addr' => ($_SERVER['HTTP_X_REAL_IP'] ?? $_SERVER['REMOTE_ADDR'])
|
||||
));
|
||||
|
||||
unset($_SESSION['ldelay']);
|
||||
// Reactivate TFA if it was set to "deactivate TFA for next login"
|
||||
$stmt = $pdo->prepare("UPDATE `tfa` SET `active`='1' WHERE `username` = :user");
|
||||
$stmt->execute(array(':user' => $user));
|
||||
return "user";
|
||||
}
|
||||
}
|
||||
@@ -1626,12 +1635,8 @@ function verify_tfa_login($username, $_data) {
|
||||
global $WebAuthn;
|
||||
|
||||
if ($_data['tfa_method'] != 'u2f'){
|
||||
$stmt = $pdo->prepare("SELECT `authmech` FROM `tfa`
|
||||
WHERE `username` = :username AND `id` = :id AND `active` = '1'");
|
||||
$stmt->execute(array(':username' => $username, ':id' => $_data['id']));
|
||||
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
switch ($row["authmech"]) {
|
||||
switch ($_data["tfa_method"]) {
|
||||
case "yubi_otp":
|
||||
if (!ctype_alnum($_data['token']) || strlen($_data['token']) != 44) {
|
||||
$_SESSION['return'][] = array(
|
||||
@@ -1645,10 +1650,9 @@ function verify_tfa_login($username, $_data) {
|
||||
$stmt = $pdo->prepare("SELECT `id`, `secret` FROM `tfa`
|
||||
WHERE `username` = :username
|
||||
AND `authmech` = 'yubi_otp'
|
||||
AND `id` = :id
|
||||
AND `active` = '1'
|
||||
AND `secret` LIKE :modhex");
|
||||
$stmt->execute(array(':username' => $username, ':modhex' => '%' . $yubico_modhex_id, ':id' => $_data['id']));
|
||||
$stmt->execute(array(':username' => $username, ':modhex' => '%' . $yubico_modhex_id));
|
||||
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
$yubico_auth = explode(':', $row['secret']);
|
||||
$yubi = new Auth_Yubico($yubico_auth[0], $yubico_auth[1]);
|
||||
|
||||
@@ -3,7 +3,7 @@ function init_db_schema() {
|
||||
try {
|
||||
global $pdo;
|
||||
|
||||
$db_version = "13072022_1700";
|
||||
$db_version = "25072022_2300";
|
||||
|
||||
$stmt = $pdo->query("SHOW TABLES LIKE 'versions'");
|
||||
$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
|
||||
@@ -738,7 +738,7 @@ function init_db_schema() {
|
||||
"username" => "VARCHAR(255) NOT NULL",
|
||||
"authmech" => "ENUM('yubi_otp', 'u2f', 'hotp', 'totp', 'webauthn')",
|
||||
"secret" => "VARCHAR(255) DEFAULT NULL",
|
||||
"keyHandle" => "VARCHAR(255) DEFAULT NULL",
|
||||
"keyHandle" => "VARCHAR(1023) DEFAULT NULL",
|
||||
"publicKey" => "VARCHAR(4096) DEFAULT NULL",
|
||||
"counter" => "INT NOT NULL DEFAULT '0'",
|
||||
"certificate" => "TEXT",
|
||||
|
||||
Generated
+8
-8
@@ -1604,16 +1604,16 @@
|
||||
},
|
||||
{
|
||||
"name": "twig/twig",
|
||||
"version": "v3.3.8",
|
||||
"version": "v3.4.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/twigphp/Twig.git",
|
||||
"reference": "972d8604a92b7054828b539f2febb0211dd5945c"
|
||||
"reference": "c38fd6b0b7f370c198db91ffd02e23b517426b58"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/twigphp/Twig/zipball/972d8604a92b7054828b539f2febb0211dd5945c",
|
||||
"reference": "972d8604a92b7054828b539f2febb0211dd5945c",
|
||||
"url": "https://api.github.com/repos/twigphp/Twig/zipball/c38fd6b0b7f370c198db91ffd02e23b517426b58",
|
||||
"reference": "c38fd6b0b7f370c198db91ffd02e23b517426b58",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -1628,7 +1628,7 @@
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "3.3-dev"
|
||||
"dev-master": "3.4-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
@@ -1664,7 +1664,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/twigphp/Twig/issues",
|
||||
"source": "https://github.com/twigphp/Twig/tree/v3.3.8"
|
||||
"source": "https://github.com/twigphp/Twig/tree/v3.4.3"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -1676,7 +1676,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2022-02-04T06:59:48+00:00"
|
||||
"time": "2022-09-28T08:42:51+00:00"
|
||||
},
|
||||
{
|
||||
"name": "yubico/u2flib-server",
|
||||
@@ -1728,5 +1728,5 @@
|
||||
"prefer-lowest": false,
|
||||
"platform": [],
|
||||
"platform-dev": [],
|
||||
"plugin-api-version": "2.2.0"
|
||||
"plugin-api-version": "2.3.0"
|
||||
}
|
||||
|
||||
Vendored
+5
@@ -2,6 +2,11 @@
|
||||
|
||||
// autoload.php @generated by Composer
|
||||
|
||||
if (PHP_VERSION_ID < 50600) {
|
||||
echo 'Composer 2.3.0 dropped support for autoloading on PHP <5.6 and you are running '.PHP_VERSION.', please upgrade PHP or use Composer 2.2 LTS via "composer self-update --2.2". Aborting.'.PHP_EOL;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
require_once __DIR__ . '/composer/autoload_real.php';
|
||||
|
||||
return ComposerAutoloaderInit873464e4bd965a3168f133248b1b218b::getLoader();
|
||||
|
||||
+9
-7
@@ -21,12 +21,14 @@ use Composer\Semver\VersionParser;
|
||||
* See also https://getcomposer.org/doc/07-runtime.md#installed-versions
|
||||
*
|
||||
* To require its presence, you can require `composer-runtime-api ^2.0`
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
class InstalledVersions
|
||||
{
|
||||
/**
|
||||
* @var mixed[]|null
|
||||
* @psalm-var array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}|array{}|null
|
||||
* @psalm-var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}|array{}|null
|
||||
*/
|
||||
private static $installed;
|
||||
|
||||
@@ -37,7 +39,7 @@ class InstalledVersions
|
||||
|
||||
/**
|
||||
* @var array[]
|
||||
* @psalm-var array<string, array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}>
|
||||
* @psalm-var array<string, array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
|
||||
*/
|
||||
private static $installedByVendor = array();
|
||||
|
||||
@@ -241,7 +243,7 @@ class InstalledVersions
|
||||
|
||||
/**
|
||||
* @return array
|
||||
* @psalm-return array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}
|
||||
* @psalm-return array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}
|
||||
*/
|
||||
public static function getRootPackage()
|
||||
{
|
||||
@@ -255,7 +257,7 @@ class InstalledVersions
|
||||
*
|
||||
* @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect.
|
||||
* @return array[]
|
||||
* @psalm-return array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}
|
||||
* @psalm-return array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}
|
||||
*/
|
||||
public static function getRawData()
|
||||
{
|
||||
@@ -278,7 +280,7 @@ class InstalledVersions
|
||||
* Returns the raw data of all installed.php which are currently loaded for custom implementations
|
||||
*
|
||||
* @return array[]
|
||||
* @psalm-return list<array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}>
|
||||
* @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
|
||||
*/
|
||||
public static function getAllRawData()
|
||||
{
|
||||
@@ -301,7 +303,7 @@ class InstalledVersions
|
||||
* @param array[] $data A vendor/composer/installed.php data set
|
||||
* @return void
|
||||
*
|
||||
* @psalm-param array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>} $data
|
||||
* @psalm-param array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $data
|
||||
*/
|
||||
public static function reload($data)
|
||||
{
|
||||
@@ -311,7 +313,7 @@ class InstalledVersions
|
||||
|
||||
/**
|
||||
* @return array[]
|
||||
* @psalm-return list<array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}>
|
||||
* @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
|
||||
*/
|
||||
private static function getInstalled()
|
||||
{
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
|
||||
// autoload_classmap.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(dirname(__FILE__));
|
||||
$vendorDir = dirname(__DIR__);
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
|
||||
+2
-2
@@ -2,15 +2,15 @@
|
||||
|
||||
// autoload_files.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(dirname(__FILE__));
|
||||
$vendorDir = dirname(__DIR__);
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php',
|
||||
'320cde22f66dd4f5d3fd621d3e88b98f' => $vendorDir . '/symfony/polyfill-ctype/bootstrap.php',
|
||||
'a4a119a56e50fbb293281d9a48007e0e' => $vendorDir . '/symfony/polyfill-php80/bootstrap.php',
|
||||
'a1105708a18b76903365ca1c4aa61b02' => $vendorDir . '/symfony/translation/Resources/functions.php',
|
||||
'667aeda72477189d0494fecd327c3641' => $vendorDir . '/symfony/var-dumper/Resources/functions/dump.php',
|
||||
'320cde22f66dd4f5d3fd621d3e88b98f' => $vendorDir . '/symfony/polyfill-ctype/bootstrap.php',
|
||||
'fe62ba7e10580d903cc46d808b5961a4' => $vendorDir . '/tightenco/collect/src/Collect/Support/helpers.php',
|
||||
'caf31cc6ec7cf2241cb6f12c226c3846' => $vendorDir . '/tightenco/collect/src/Collect/Support/alias.php',
|
||||
'04c6c5c2f7095ccf6c481d3e53e1776f' => $vendorDir . '/mustangostang/spyc/Spyc.php',
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
// autoload_namespaces.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(dirname(__FILE__));
|
||||
$vendorDir = dirname(__DIR__);
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
|
||||
// autoload_psr4.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(dirname(__FILE__));
|
||||
$vendorDir = dirname(__DIR__);
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
|
||||
+4
-27
@@ -25,38 +25,15 @@ class ComposerAutoloaderInit873464e4bd965a3168f133248b1b218b
|
||||
require __DIR__ . '/platform_check.php';
|
||||
|
||||
spl_autoload_register(array('ComposerAutoloaderInit873464e4bd965a3168f133248b1b218b', 'loadClassLoader'), true, true);
|
||||
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(\dirname(__FILE__)));
|
||||
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__));
|
||||
spl_autoload_unregister(array('ComposerAutoloaderInit873464e4bd965a3168f133248b1b218b', 'loadClassLoader'));
|
||||
|
||||
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
|
||||
if ($useStaticLoader) {
|
||||
require __DIR__ . '/autoload_static.php';
|
||||
|
||||
call_user_func(\Composer\Autoload\ComposerStaticInit873464e4bd965a3168f133248b1b218b::getInitializer($loader));
|
||||
} else {
|
||||
$map = require __DIR__ . '/autoload_namespaces.php';
|
||||
foreach ($map as $namespace => $path) {
|
||||
$loader->set($namespace, $path);
|
||||
}
|
||||
|
||||
$map = require __DIR__ . '/autoload_psr4.php';
|
||||
foreach ($map as $namespace => $path) {
|
||||
$loader->setPsr4($namespace, $path);
|
||||
}
|
||||
|
||||
$classMap = require __DIR__ . '/autoload_classmap.php';
|
||||
if ($classMap) {
|
||||
$loader->addClassMap($classMap);
|
||||
}
|
||||
}
|
||||
require __DIR__ . '/autoload_static.php';
|
||||
call_user_func(\Composer\Autoload\ComposerStaticInit873464e4bd965a3168f133248b1b218b::getInitializer($loader));
|
||||
|
||||
$loader->register(true);
|
||||
|
||||
if ($useStaticLoader) {
|
||||
$includeFiles = Composer\Autoload\ComposerStaticInit873464e4bd965a3168f133248b1b218b::$files;
|
||||
} else {
|
||||
$includeFiles = require __DIR__ . '/autoload_files.php';
|
||||
}
|
||||
$includeFiles = \Composer\Autoload\ComposerStaticInit873464e4bd965a3168f133248b1b218b::$files;
|
||||
foreach ($includeFiles as $fileIdentifier => $file) {
|
||||
composerRequire873464e4bd965a3168f133248b1b218b($fileIdentifier, $file);
|
||||
}
|
||||
|
||||
+1
-1
@@ -8,10 +8,10 @@ class ComposerStaticInit873464e4bd965a3168f133248b1b218b
|
||||
{
|
||||
public static $files = array (
|
||||
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php',
|
||||
'320cde22f66dd4f5d3fd621d3e88b98f' => __DIR__ . '/..' . '/symfony/polyfill-ctype/bootstrap.php',
|
||||
'a4a119a56e50fbb293281d9a48007e0e' => __DIR__ . '/..' . '/symfony/polyfill-php80/bootstrap.php',
|
||||
'a1105708a18b76903365ca1c4aa61b02' => __DIR__ . '/..' . '/symfony/translation/Resources/functions.php',
|
||||
'667aeda72477189d0494fecd327c3641' => __DIR__ . '/..' . '/symfony/var-dumper/Resources/functions/dump.php',
|
||||
'320cde22f66dd4f5d3fd621d3e88b98f' => __DIR__ . '/..' . '/symfony/polyfill-ctype/bootstrap.php',
|
||||
'fe62ba7e10580d903cc46d808b5961a4' => __DIR__ . '/..' . '/tightenco/collect/src/Collect/Support/helpers.php',
|
||||
'caf31cc6ec7cf2241cb6f12c226c3846' => __DIR__ . '/..' . '/tightenco/collect/src/Collect/Support/alias.php',
|
||||
'04c6c5c2f7095ccf6c481d3e53e1776f' => __DIR__ . '/..' . '/mustangostang/spyc/Spyc.php',
|
||||
|
||||
+8
-8
@@ -1654,17 +1654,17 @@
|
||||
},
|
||||
{
|
||||
"name": "twig/twig",
|
||||
"version": "v3.3.8",
|
||||
"version_normalized": "3.3.8.0",
|
||||
"version": "v3.4.3",
|
||||
"version_normalized": "3.4.3.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/twigphp/Twig.git",
|
||||
"reference": "972d8604a92b7054828b539f2febb0211dd5945c"
|
||||
"reference": "c38fd6b0b7f370c198db91ffd02e23b517426b58"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/twigphp/Twig/zipball/972d8604a92b7054828b539f2febb0211dd5945c",
|
||||
"reference": "972d8604a92b7054828b539f2febb0211dd5945c",
|
||||
"url": "https://api.github.com/repos/twigphp/Twig/zipball/c38fd6b0b7f370c198db91ffd02e23b517426b58",
|
||||
"reference": "c38fd6b0b7f370c198db91ffd02e23b517426b58",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -1676,11 +1676,11 @@
|
||||
"psr/container": "^1.0",
|
||||
"symfony/phpunit-bridge": "^4.4.9|^5.0.9|^6.0"
|
||||
},
|
||||
"time": "2022-02-04T06:59:48+00:00",
|
||||
"time": "2022-09-28T08:42:51+00:00",
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "3.3-dev"
|
||||
"dev-master": "3.4-dev"
|
||||
}
|
||||
},
|
||||
"installation-source": "dist",
|
||||
@@ -1717,7 +1717,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/twigphp/Twig/issues",
|
||||
"source": "https://github.com/twigphp/Twig/tree/v3.3.8"
|
||||
"source": "https://github.com/twigphp/Twig/tree/v3.4.3"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
||||
+34
-34
@@ -1,49 +1,49 @@
|
||||
<?php return array(
|
||||
'root' => array(
|
||||
'pretty_version' => '1.0.0+no-version-set',
|
||||
'version' => '1.0.0.0',
|
||||
'name' => '__root__',
|
||||
'pretty_version' => 'dev-master',
|
||||
'version' => 'dev-master',
|
||||
'reference' => '8e0b1d8aee4af02311692cb031695cc2ac3850fd',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../../',
|
||||
'aliases' => array(),
|
||||
'reference' => NULL,
|
||||
'name' => '__root__',
|
||||
'dev' => true,
|
||||
),
|
||||
'versions' => array(
|
||||
'__root__' => array(
|
||||
'pretty_version' => '1.0.0+no-version-set',
|
||||
'version' => '1.0.0.0',
|
||||
'pretty_version' => 'dev-master',
|
||||
'version' => 'dev-master',
|
||||
'reference' => '8e0b1d8aee4af02311692cb031695cc2ac3850fd',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../../',
|
||||
'aliases' => array(),
|
||||
'reference' => NULL,
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'bshaffer/oauth2-server-php' => array(
|
||||
'pretty_version' => 'v1.11.1',
|
||||
'version' => '1.11.1.0',
|
||||
'reference' => '5a0c8000d4763b276919e2106f54eddda6bc50fa',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../bshaffer/oauth2-server-php',
|
||||
'aliases' => array(),
|
||||
'reference' => '5a0c8000d4763b276919e2106f54eddda6bc50fa',
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'ddeboer/imap' => array(
|
||||
'pretty_version' => '1.13.1',
|
||||
'version' => '1.13.1.0',
|
||||
'reference' => '8b772d04b1deadb5df13782fb78c4b648f77496e',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../ddeboer/imap',
|
||||
'aliases' => array(),
|
||||
'reference' => '8b772d04b1deadb5df13782fb78c4b648f77496e',
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'directorytree/ldaprecord' => array(
|
||||
'pretty_version' => 'v2.10.1',
|
||||
'version' => '2.10.1.0',
|
||||
'reference' => 'bf512d9af7a7b0e2ed7a666ab29cefdd027bee88',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../directorytree/ldaprecord',
|
||||
'aliases' => array(),
|
||||
'reference' => 'bf512d9af7a7b0e2ed7a666ab29cefdd027bee88',
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'exorus/php-mime-mail-parser' => array(
|
||||
@@ -55,28 +55,28 @@
|
||||
'illuminate/contracts' => array(
|
||||
'pretty_version' => 'v9.3.0',
|
||||
'version' => '9.3.0.0',
|
||||
'reference' => 'bf4b3c254c49d28157645d01e4883b5951b1e1d0',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../illuminate/contracts',
|
||||
'aliases' => array(),
|
||||
'reference' => 'bf4b3c254c49d28157645d01e4883b5951b1e1d0',
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'matthiasmullie/minify' => array(
|
||||
'pretty_version' => '1.3.66',
|
||||
'version' => '1.3.66.0',
|
||||
'reference' => '45fd3b0f1dfa2c965857c6d4a470bea52adc31a6',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../matthiasmullie/minify',
|
||||
'aliases' => array(),
|
||||
'reference' => '45fd3b0f1dfa2c965857c6d4a470bea52adc31a6',
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'matthiasmullie/path-converter' => array(
|
||||
'pretty_version' => '1.1.3',
|
||||
'version' => '1.1.3.0',
|
||||
'reference' => 'e7d13b2c7e2f2268e1424aaed02085518afa02d9',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../matthiasmullie/path-converter',
|
||||
'aliases' => array(),
|
||||
'reference' => 'e7d13b2c7e2f2268e1424aaed02085518afa02d9',
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'messaged/php-mime-mail-parser' => array(
|
||||
@@ -88,136 +88,136 @@
|
||||
'mustangostang/spyc' => array(
|
||||
'pretty_version' => '0.6.3',
|
||||
'version' => '0.6.3.0',
|
||||
'reference' => '4627c838b16550b666d15aeae1e5289dd5b77da0',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../mustangostang/spyc',
|
||||
'aliases' => array(),
|
||||
'reference' => '4627c838b16550b666d15aeae1e5289dd5b77da0',
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'nesbot/carbon' => array(
|
||||
'pretty_version' => '2.57.0',
|
||||
'version' => '2.57.0.0',
|
||||
'reference' => '4a54375c21eea4811dbd1149fe6b246517554e78',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../nesbot/carbon',
|
||||
'aliases' => array(),
|
||||
'reference' => '4a54375c21eea4811dbd1149fe6b246517554e78',
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'paragonie/random_compat' => array(
|
||||
'pretty_version' => 'v9.99.100',
|
||||
'version' => '9.99.100.0',
|
||||
'reference' => '996434e5492cb4c3edcb9168db6fbb1359ef965a',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../paragonie/random_compat',
|
||||
'aliases' => array(),
|
||||
'reference' => '996434e5492cb4c3edcb9168db6fbb1359ef965a',
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'php-mime-mail-parser/php-mime-mail-parser' => array(
|
||||
'pretty_version' => '7.0.0',
|
||||
'version' => '7.0.0.0',
|
||||
'reference' => '9d09a017f3f103fec8456211a4a538b80e0eca0d',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../php-mime-mail-parser/php-mime-mail-parser',
|
||||
'aliases' => array(),
|
||||
'reference' => '9d09a017f3f103fec8456211a4a538b80e0eca0d',
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'phpmailer/phpmailer' => array(
|
||||
'pretty_version' => 'v6.6.0',
|
||||
'version' => '6.6.0.0',
|
||||
'reference' => 'e43bac82edc26ca04b36143a48bde1c051cfd5b1',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../phpmailer/phpmailer',
|
||||
'aliases' => array(),
|
||||
'reference' => 'e43bac82edc26ca04b36143a48bde1c051cfd5b1',
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'psr/container' => array(
|
||||
'pretty_version' => '2.0.2',
|
||||
'version' => '2.0.2.0',
|
||||
'reference' => 'c71ecc56dfe541dbd90c5360474fbc405f8d5963',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../psr/container',
|
||||
'aliases' => array(),
|
||||
'reference' => 'c71ecc56dfe541dbd90c5360474fbc405f8d5963',
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'psr/log' => array(
|
||||
'pretty_version' => '3.0.0',
|
||||
'version' => '3.0.0.0',
|
||||
'reference' => 'fe5ea303b0887d5caefd3d431c3e61ad47037001',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../psr/log',
|
||||
'aliases' => array(),
|
||||
'reference' => 'fe5ea303b0887d5caefd3d431c3e61ad47037001',
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'psr/simple-cache' => array(
|
||||
'pretty_version' => '2.0.0',
|
||||
'version' => '2.0.0.0',
|
||||
'reference' => '8707bf3cea6f710bf6ef05491234e3ab06f6432a',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../psr/simple-cache',
|
||||
'aliases' => array(),
|
||||
'reference' => '8707bf3cea6f710bf6ef05491234e3ab06f6432a',
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'robthree/twofactorauth' => array(
|
||||
'pretty_version' => '1.8.1',
|
||||
'version' => '1.8.1.0',
|
||||
'reference' => '5afcb45282f1c75562a48d479ecd1732c9bdb11b',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../robthree/twofactorauth',
|
||||
'aliases' => array(),
|
||||
'reference' => '5afcb45282f1c75562a48d479ecd1732c9bdb11b',
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'soundasleep/html2text' => array(
|
||||
'pretty_version' => '0.5.0',
|
||||
'version' => '0.5.0.0',
|
||||
'reference' => 'cdb89f6ffa2c4cc78f8ed9ea6ee0594a9133ccad',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../soundasleep/html2text',
|
||||
'aliases' => array(),
|
||||
'reference' => 'cdb89f6ffa2c4cc78f8ed9ea6ee0594a9133ccad',
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'symfony/polyfill-ctype' => array(
|
||||
'pretty_version' => 'v1.24.0',
|
||||
'version' => '1.24.0.0',
|
||||
'reference' => '30885182c981ab175d4d034db0f6f469898070ab',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../symfony/polyfill-ctype',
|
||||
'aliases' => array(),
|
||||
'reference' => '30885182c981ab175d4d034db0f6f469898070ab',
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'symfony/polyfill-mbstring' => array(
|
||||
'pretty_version' => 'v1.24.0',
|
||||
'version' => '1.24.0.0',
|
||||
'reference' => '0abb51d2f102e00a4eefcf46ba7fec406d245825',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../symfony/polyfill-mbstring',
|
||||
'aliases' => array(),
|
||||
'reference' => '0abb51d2f102e00a4eefcf46ba7fec406d245825',
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'symfony/polyfill-php80' => array(
|
||||
'pretty_version' => 'v1.24.0',
|
||||
'version' => '1.24.0.0',
|
||||
'reference' => '57b712b08eddb97c762a8caa32c84e037892d2e9',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../symfony/polyfill-php80',
|
||||
'aliases' => array(),
|
||||
'reference' => '57b712b08eddb97c762a8caa32c84e037892d2e9',
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'symfony/translation' => array(
|
||||
'pretty_version' => 'v6.0.5',
|
||||
'version' => '6.0.5.0',
|
||||
'reference' => 'e69501c71107cc3146b32aaa45f4edd0c3427875',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../symfony/translation',
|
||||
'aliases' => array(),
|
||||
'reference' => 'e69501c71107cc3146b32aaa45f4edd0c3427875',
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'symfony/translation-contracts' => array(
|
||||
'pretty_version' => 'v3.0.0',
|
||||
'version' => '3.0.0.0',
|
||||
'reference' => '1b6ea5a7442af5a12dba3dbd6d71034b5b234e77',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../symfony/translation-contracts',
|
||||
'aliases' => array(),
|
||||
'reference' => '1b6ea5a7442af5a12dba3dbd6d71034b5b234e77',
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'symfony/translation-implementation' => array(
|
||||
@@ -229,37 +229,37 @@
|
||||
'symfony/var-dumper' => array(
|
||||
'pretty_version' => 'v6.0.5',
|
||||
'version' => '6.0.5.0',
|
||||
'reference' => '60d6a756d5f485df5e6e40b337334848f79f61ce',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../symfony/var-dumper',
|
||||
'aliases' => array(),
|
||||
'reference' => '60d6a756d5f485df5e6e40b337334848f79f61ce',
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'tightenco/collect' => array(
|
||||
'pretty_version' => 'v8.83.2',
|
||||
'version' => '8.83.2.0',
|
||||
'reference' => 'd9c66d586ec2d216d8a31283d73f8df1400cc722',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../tightenco/collect',
|
||||
'aliases' => array(),
|
||||
'reference' => 'd9c66d586ec2d216d8a31283d73f8df1400cc722',
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'twig/twig' => array(
|
||||
'pretty_version' => 'v3.3.8',
|
||||
'version' => '3.3.8.0',
|
||||
'pretty_version' => 'v3.4.3',
|
||||
'version' => '3.4.3.0',
|
||||
'reference' => 'c38fd6b0b7f370c198db91ffd02e23b517426b58',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../twig/twig',
|
||||
'aliases' => array(),
|
||||
'reference' => '972d8604a92b7054828b539f2febb0211dd5945c',
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'yubico/u2flib-server' => array(
|
||||
'pretty_version' => '1.0.2',
|
||||
'version' => '1.0.2.0',
|
||||
'reference' => '55d813acf68212ad2cadecde07551600d6971939',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../yubico/u2flib-server',
|
||||
'aliases' => array(),
|
||||
'reference' => '55d813acf68212ad2cadecde07551600d6971939',
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
),
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/doc/** export-ignore
|
||||
/extra/** export-ignore
|
||||
/tests export-ignore
|
||||
/doc/ export-ignore
|
||||
/extra/ export-ignore
|
||||
/tests/ export-ignore
|
||||
/phpunit.xml.dist export-ignore
|
||||
|
||||
+11
-37
@@ -9,6 +9,9 @@ on:
|
||||
env:
|
||||
SYMFONY_PHPUNIT_DISABLE_RESULT_CACHE: 1
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
tests:
|
||||
name: "PHP ${{ matrix.php-version }}"
|
||||
@@ -25,36 +28,23 @@ jobs:
|
||||
- '7.4'
|
||||
- '8.0'
|
||||
- '8.1'
|
||||
composer-options: ['']
|
||||
experimental: [false]
|
||||
|
||||
steps:
|
||||
- name: "Checkout code"
|
||||
uses: actions/checkout@v2.3.3
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: "Install PHP with extensions"
|
||||
uses: shivammathur/setup-php@2.7.0
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
coverage: "none"
|
||||
php-version: ${{ matrix.php-version }}
|
||||
ini-values: memory_limit=-1
|
||||
tools: composer:v2
|
||||
|
||||
- name: "Add PHPUnit matcher"
|
||||
run: echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json"
|
||||
|
||||
- name: "Set composer cache directory"
|
||||
id: composer-cache
|
||||
run: echo "::set-output name=dir::$(composer config cache-files-dir)"
|
||||
|
||||
- name: "Cache composer"
|
||||
uses: actions/cache@v2.1.2
|
||||
with:
|
||||
path: ${{ steps.composer-cache.outputs.dir }}
|
||||
key: ${{ runner.os }}-${{ matrix.php-version }}-composer-${{ hashFiles('composer.json') }}
|
||||
restore-keys: ${{ runner.os }}-${{ matrix.php-version }}-composer-
|
||||
|
||||
- run: composer install ${{ matrix.composer-options }}
|
||||
- run: composer install
|
||||
|
||||
- name: "Install PHPUnit"
|
||||
run: vendor/bin/simple-phpunit install
|
||||
@@ -92,35 +82,22 @@ jobs:
|
||||
- 'extra/markdown-extra'
|
||||
- 'extra/string-extra'
|
||||
- 'extra/twig-extra-bundle'
|
||||
composer-options: ['']
|
||||
experimental: [false]
|
||||
|
||||
steps:
|
||||
- name: "Checkout code"
|
||||
uses: actions/checkout@v2.3.3
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: "Install PHP with extensions"
|
||||
uses: shivammathur/setup-php@2.7.0
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
coverage: "none"
|
||||
php-version: ${{ matrix.php-version }}
|
||||
ini-values: memory_limit=-1
|
||||
tools: composer:v2
|
||||
|
||||
- name: "Add PHPUnit matcher"
|
||||
run: echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json"
|
||||
|
||||
- name: "Set composer cache directory"
|
||||
id: composer-cache
|
||||
run: echo "::set-output name=dir::$(composer config cache-files-dir)"
|
||||
|
||||
- name: "Cache composer"
|
||||
uses: actions/cache@v2.1.2
|
||||
with:
|
||||
path: ${{ steps.composer-cache.outputs.dir }}
|
||||
key: ${{ runner.os }}-${{ matrix.php-version }}-${{ matrix.extension }}-${{ hashFiles('composer.json') }}
|
||||
restore-keys: ${{ runner.os }}-${{ matrix.php-version }}-${{ matrix.extension }}-
|
||||
|
||||
- run: composer install
|
||||
|
||||
- name: "Install PHPUnit"
|
||||
@@ -129,10 +106,6 @@ jobs:
|
||||
- name: "PHPUnit version"
|
||||
run: vendor/bin/simple-phpunit --version
|
||||
|
||||
- if: matrix.extension == 'extra/markdown-extra' && matrix.php-version == '8.0'
|
||||
working-directory: ${{ matrix.extension}}
|
||||
run: composer config platform.php 7.4.99
|
||||
|
||||
- name: "Composer install"
|
||||
working-directory: ${{ matrix.extension}}
|
||||
run: composer install
|
||||
@@ -140,6 +113,7 @@ jobs:
|
||||
- name: "Run tests"
|
||||
working-directory: ${{ matrix.extension}}
|
||||
run: ../../vendor/bin/simple-phpunit
|
||||
|
||||
#
|
||||
# Drupal does not support Twig 3 now!
|
||||
#
|
||||
@@ -160,10 +134,10 @@ jobs:
|
||||
#
|
||||
# steps:
|
||||
# - name: "Checkout code"
|
||||
# uses: actions/checkout@v2.3.3
|
||||
# uses: actions/checkout@v2
|
||||
#
|
||||
# - name: "Install PHP with extensions"
|
||||
# uses: shivammathur/setup-php@2.7.0
|
||||
# uses: shivammathur/setup-php@2
|
||||
# with:
|
||||
# coverage: "none"
|
||||
# extensions: "gd, pdo_sqlite"
|
||||
|
||||
@@ -4,8 +4,12 @@ on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- '2.x'
|
||||
- '3.x'
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: "Build"
|
||||
@@ -16,32 +20,32 @@ jobs:
|
||||
- name: "Checkout code"
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: "Set up Python 3.7"
|
||||
uses: actions/setup-python@v1
|
||||
- name: "Set-up PHP"
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
python-version: '3.7' # Semantic version range syntax or exact version of a Python version
|
||||
php-version: 8.1
|
||||
coverage: none
|
||||
tools: "composer:v2"
|
||||
|
||||
- name: "Display Python version"
|
||||
run: python -c "import sys; print(sys.version)"
|
||||
- name: Get composer cache directory
|
||||
id: composercache
|
||||
working-directory: doc/_build
|
||||
run: echo "::set-output name=dir::$(composer config cache-files-dir)"
|
||||
|
||||
- name: "Install Sphinx dependencies"
|
||||
run: sudo apt-get install python-dev build-essential
|
||||
|
||||
- name: "Cache pip"
|
||||
- name: Cache dependencies
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/.cache/pip
|
||||
key: ${{ runner.os }}-pip-${{ hashFiles('_build/.requirements.txt') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pip-
|
||||
path: ${{ steps.composercache.outputs.dir }}
|
||||
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
|
||||
restore-keys: ${{ runner.os }}-composer-
|
||||
|
||||
- name: "Install Sphinx + requirements via pip"
|
||||
working-directory: "doc"
|
||||
run: pip install -r _build/.requirements.txt
|
||||
- name: "Install dependencies"
|
||||
working-directory: doc/_build
|
||||
run: composer install --prefer-dist --no-progress
|
||||
|
||||
- name: "Build documentation"
|
||||
working-directory: "doc"
|
||||
run: make -C _build SPHINXOPTS="-nqW -j auto" html
|
||||
- name: "Build the docs"
|
||||
working-directory: doc/_build
|
||||
run: php build.php --disable-cache
|
||||
|
||||
doctor-rst:
|
||||
name: "DOCtor-RST"
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
/doc/_build/vendor
|
||||
/doc/_build/output
|
||||
/composer.lock
|
||||
/phpunit.xml
|
||||
/vendor
|
||||
|
||||
+30
@@ -1,6 +1,36 @@
|
||||
# 3.4.3 (2022-09-28)
|
||||
|
||||
* Fix a security issue on filesystem loader (possibility to load a template outside a configured directory)
|
||||
|
||||
# 3.4.2 (2022-08-12)
|
||||
|
||||
* Allow inherited magic method to still run with calling class
|
||||
* Fix CallExpression::reflectCallable() throwing TypeError
|
||||
* Fix typo in naming (currency_code)
|
||||
|
||||
# 3.4.1 (2022-05-17)
|
||||
|
||||
* Fix optimizing non-public named closures
|
||||
|
||||
# 3.4.0 (2022-05-22)
|
||||
|
||||
* Add support for named closures
|
||||
|
||||
# 3.3.10 (2022-04-06)
|
||||
|
||||
* Enable bytecode invalidation when auto_reload is enabled
|
||||
|
||||
# 3.3.9 (2022-03-25)
|
||||
|
||||
* Fix custom escapers when using multiple Twig environments
|
||||
* Add support for "constant('class', object)"
|
||||
* Do not reuse internally generated variable names during parsing
|
||||
|
||||
# 3.3.8 (2022-02-04)
|
||||
|
||||
* Fix a security issue when in a sandbox: the `sort` filter must require a Closure for the `arrow` parameter
|
||||
* Fix deprecation notice on `round`
|
||||
* Fix call to deprecated `convertToHtml` method
|
||||
|
||||
# 3.3.7 (2022-01-03)
|
||||
|
||||
|
||||
+1
-1
@@ -44,7 +44,7 @@
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "3.3-dev"
|
||||
"dev-master": "3.4-dev"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+5
-5
@@ -38,11 +38,11 @@ use Twig\TokenParser\TokenParserInterface;
|
||||
*/
|
||||
class Environment
|
||||
{
|
||||
public const VERSION = '3.3.8';
|
||||
public const VERSION_ID = 30308;
|
||||
public const VERSION = '3.4.3';
|
||||
public const VERSION_ID = 30403;
|
||||
public const MAJOR_VERSION = 3;
|
||||
public const MINOR_VERSION = 3;
|
||||
public const RELEASE_VERSION = 8;
|
||||
public const MINOR_VERSION = 4;
|
||||
public const RELEASE_VERSION = 3;
|
||||
public const EXTRA_VERSION = '';
|
||||
|
||||
private $charset;
|
||||
@@ -228,7 +228,7 @@ class Environment
|
||||
{
|
||||
if (\is_string($cache)) {
|
||||
$this->originalCache = $cache;
|
||||
$this->cache = new FilesystemCache($cache);
|
||||
$this->cache = new FilesystemCache($cache, $this->autoReload ? FilesystemCache::FORCE_BYTECODE_INVALIDATION : 0);
|
||||
} elseif (false === $cache) {
|
||||
$this->originalCache = $cache;
|
||||
$this->cache = new NullCache();
|
||||
|
||||
@@ -485,7 +485,7 @@ class ExpressionParser
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new SyntaxError('Expected name or number.', $lineno, $stream->getSourceContext());
|
||||
throw new SyntaxError(sprintf('Expected name or number, got value "%s" of type %s.', $token->getValue(), Token::typeToEnglish($token->getType())), $lineno, $stream->getSourceContext());
|
||||
}
|
||||
|
||||
if ($node instanceof NameExpression && null !== $this->parser->getImportedSymbol('template', $node->getAttribute('name'))) {
|
||||
|
||||
@@ -1359,6 +1359,10 @@ function twig_source(Environment $env, $name, $ignoreMissing = false)
|
||||
function twig_constant($constant, $object = null)
|
||||
{
|
||||
if (null !== $object) {
|
||||
if ('class' === $constant) {
|
||||
return \get_class($object);
|
||||
}
|
||||
|
||||
$constant = \get_class($object).'::'.$constant;
|
||||
}
|
||||
|
||||
@@ -1376,6 +1380,10 @@ function twig_constant($constant, $object = null)
|
||||
function twig_constant_is_defined($constant, $object = null)
|
||||
{
|
||||
if (null !== $object) {
|
||||
if ('class' === $constant) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$constant = \get_class($object).'::'.$constant;
|
||||
}
|
||||
|
||||
|
||||
@@ -387,13 +387,8 @@ function twig_escape_filter(Environment $env, $string, $strategy = 'html', $char
|
||||
return rawurlencode($string);
|
||||
|
||||
default:
|
||||
static $escapers;
|
||||
|
||||
if (null === $escapers) {
|
||||
$escapers = $env->getExtension(EscaperExtension::class)->getEscapers();
|
||||
}
|
||||
|
||||
if (isset($escapers[$strategy])) {
|
||||
$escapers = $env->getExtension(EscaperExtension::class)->getEscapers();
|
||||
if (array_key_exists($strategy, $escapers)) {
|
||||
return $escapers[$strategy]($env, $string, $charset);
|
||||
}
|
||||
|
||||
|
||||
@@ -91,11 +91,11 @@ final class SandboxExtension extends AbstractExtension
|
||||
}
|
||||
}
|
||||
|
||||
public function checkPropertyAllowed($obj, $method, int $lineno = -1, Source $source = null): void
|
||||
public function checkPropertyAllowed($obj, $property, int $lineno = -1, Source $source = null): void
|
||||
{
|
||||
if ($this->isSandboxed()) {
|
||||
try {
|
||||
$this->policy->checkPropertyAllowed($obj, $method);
|
||||
$this->policy->checkPropertyAllowed($obj, $property);
|
||||
} catch (SecurityNotAllowedPropertyError $e) {
|
||||
$e->setSourceContext($source);
|
||||
$e->setTemplateLine($lineno);
|
||||
|
||||
@@ -183,9 +183,9 @@ class FilesystemLoader implements LoaderInterface
|
||||
}
|
||||
|
||||
try {
|
||||
$this->validateName($name);
|
||||
|
||||
list($namespace, $shortname) = $this->parseName($name);
|
||||
|
||||
$this->validateName($shortname);
|
||||
} catch (LoaderError $e) {
|
||||
if (!$throw) {
|
||||
return null;
|
||||
|
||||
@@ -24,19 +24,20 @@ abstract class CallExpression extends AbstractExpression
|
||||
{
|
||||
$callable = $this->getAttribute('callable');
|
||||
|
||||
$closingParenthesis = false;
|
||||
$isArray = false;
|
||||
if (\is_string($callable) && false === strpos($callable, '::')) {
|
||||
$compiler->raw($callable);
|
||||
} else {
|
||||
list($r, $callable) = $this->reflectCallable($callable);
|
||||
if ($r instanceof \ReflectionMethod && \is_string($callable[0])) {
|
||||
if ($r->isStatic()) {
|
||||
[$r, $callable] = $this->reflectCallable($callable);
|
||||
|
||||
if (\is_string($callable)) {
|
||||
$compiler->raw($callable);
|
||||
} elseif (\is_array($callable) && \is_string($callable[0])) {
|
||||
if (!$r instanceof \ReflectionMethod || $r->isStatic()) {
|
||||
$compiler->raw(sprintf('%s::%s', $callable[0], $callable[1]));
|
||||
} else {
|
||||
$compiler->raw(sprintf('$this->env->getRuntime(\'%s\')->%s', $callable[0], $callable[1]));
|
||||
}
|
||||
} elseif ($r instanceof \ReflectionMethod && $callable[0] instanceof ExtensionInterface) {
|
||||
} elseif (\is_array($callable) && $callable[0] instanceof ExtensionInterface) {
|
||||
$class = \get_class($callable[0]);
|
||||
if (!$compiler->getEnvironment()->hasExtension($class)) {
|
||||
// Compile a non-optimized call to trigger a \Twig\Error\RuntimeError, which cannot be a compile-time error
|
||||
@@ -47,17 +48,11 @@ abstract class CallExpression extends AbstractExpression
|
||||
|
||||
$compiler->raw(sprintf('->%s', $callable[1]));
|
||||
} else {
|
||||
$closingParenthesis = true;
|
||||
$isArray = true;
|
||||
$compiler->raw(sprintf('call_user_func_array($this->env->get%s(\'%s\')->getCallable(), ', ucfirst($this->getAttribute('type')), $this->getAttribute('name')));
|
||||
$compiler->raw(sprintf('$this->env->get%s(\'%s\')->getCallable()', ucfirst($this->getAttribute('type')), $this->getAttribute('name')));
|
||||
}
|
||||
}
|
||||
|
||||
$this->compileArguments($compiler, $isArray);
|
||||
|
||||
if ($closingParenthesis) {
|
||||
$compiler->raw(')');
|
||||
}
|
||||
$this->compileArguments($compiler);
|
||||
}
|
||||
|
||||
protected function compileArguments(Compiler $compiler, $isArray = false): void
|
||||
@@ -244,10 +239,7 @@ abstract class CallExpression extends AbstractExpression
|
||||
|
||||
private function getCallableParameters($callable, bool $isVariadic): array
|
||||
{
|
||||
list($r) = $this->reflectCallable($callable);
|
||||
if (null === $r) {
|
||||
return [[], false];
|
||||
}
|
||||
[$r, , $callableName] = $this->reflectCallable($callable);
|
||||
|
||||
$parameters = $r->getParameters();
|
||||
if ($this->hasNode('node')) {
|
||||
@@ -274,11 +266,6 @@ abstract class CallExpression extends AbstractExpression
|
||||
array_pop($parameters);
|
||||
$isPhpVariadic = true;
|
||||
} else {
|
||||
$callableName = $r->name;
|
||||
if ($r instanceof \ReflectionMethod) {
|
||||
$callableName = $r->getDeclaringClass()->name.'::'.$callableName;
|
||||
}
|
||||
|
||||
throw new \LogicException(sprintf('The last parameter of "%s" for %s "%s" must be an array with default value, eg. "array $arg = []".', $callableName, $this->getAttribute('type'), $this->getAttribute('name')));
|
||||
}
|
||||
}
|
||||
@@ -292,29 +279,41 @@ abstract class CallExpression extends AbstractExpression
|
||||
return $this->reflector;
|
||||
}
|
||||
|
||||
if (\is_array($callable)) {
|
||||
if (!method_exists($callable[0], $callable[1])) {
|
||||
// __call()
|
||||
return [null, []];
|
||||
}
|
||||
$r = new \ReflectionMethod($callable[0], $callable[1]);
|
||||
} elseif (\is_object($callable) && !$callable instanceof \Closure) {
|
||||
$r = new \ReflectionObject($callable);
|
||||
$r = $r->getMethod('__invoke');
|
||||
$callable = [$callable, '__invoke'];
|
||||
} elseif (\is_string($callable) && false !== $pos = strpos($callable, '::')) {
|
||||
$class = substr($callable, 0, $pos);
|
||||
$method = substr($callable, $pos + 2);
|
||||
if (!method_exists($class, $method)) {
|
||||
// __staticCall()
|
||||
return [null, []];
|
||||
}
|
||||
$r = new \ReflectionMethod($callable);
|
||||
$callable = [$class, $method];
|
||||
} else {
|
||||
$r = new \ReflectionFunction($callable);
|
||||
if (\is_string($callable) && false !== $pos = strpos($callable, '::')) {
|
||||
$callable = [substr($callable, 0, $pos), substr($callable, 2 + $pos)];
|
||||
}
|
||||
|
||||
return $this->reflector = [$r, $callable];
|
||||
if (\is_array($callable) && method_exists($callable[0], $callable[1])) {
|
||||
$r = new \ReflectionMethod($callable[0], $callable[1]);
|
||||
|
||||
return $this->reflector = [$r, $callable, $r->class.'::'.$r->name];
|
||||
}
|
||||
|
||||
$checkVisibility = $callable instanceof \Closure;
|
||||
try {
|
||||
$closure = \Closure::fromCallable($callable);
|
||||
} catch (\TypeError $e) {
|
||||
throw new \LogicException(sprintf('Callback for %s "%s" is not callable in the current scope.', $this->getAttribute('type'), $this->getAttribute('name')), 0, $e);
|
||||
}
|
||||
$r = new \ReflectionFunction($closure);
|
||||
|
||||
if (false !== strpos($r->name, '{closure}')) {
|
||||
return $this->reflector = [$r, $callable, 'Closure'];
|
||||
}
|
||||
|
||||
if ($object = $r->getClosureThis()) {
|
||||
$callable = [$object, $r->name];
|
||||
$callableName = (\function_exists('get_debug_type') ? get_debug_type($object) : \get_class($object)).'::'.$r->name;
|
||||
} elseif ($class = $r->getClosureScopeClass()) {
|
||||
$callableName = (\is_array($callable) ? $callable[0] : $class->name).'::'.$r->name;
|
||||
} else {
|
||||
$callable = $callableName = $r->name;
|
||||
}
|
||||
|
||||
if ($checkVisibility && \is_array($callable) && method_exists(...$callable) && !(new \ReflectionMethod(...$callable))->isPublic()) {
|
||||
$callable = $r->getClosure();
|
||||
}
|
||||
|
||||
return $this->reflector = [$r, $callable, $callableName];
|
||||
}
|
||||
}
|
||||
|
||||
+1
-2
@@ -58,7 +58,7 @@ class Parser
|
||||
public function parse(TokenStream $stream, $test = null, bool $dropNeedle = false): ModuleNode
|
||||
{
|
||||
$vars = get_object_vars($this);
|
||||
unset($vars['stack'], $vars['env'], $vars['handlers'], $vars['visitors'], $vars['expressionParser'], $vars['reservedMacroNames']);
|
||||
unset($vars['stack'], $vars['env'], $vars['handlers'], $vars['visitors'], $vars['expressionParser'], $vars['reservedMacroNames'], $vars['varNameSalt']);
|
||||
$this->stack[] = $vars;
|
||||
|
||||
// node visitors
|
||||
@@ -78,7 +78,6 @@ class Parser
|
||||
$this->blockStack = [];
|
||||
$this->importedSymbols = [[]];
|
||||
$this->embeddedTemplates = [];
|
||||
$this->varNameSalt = 0;
|
||||
|
||||
try {
|
||||
$body = $this->subparse($test, $dropNeedle);
|
||||
|
||||
@@ -19,17 +19,27 @@ namespace Twig\Sandbox;
|
||||
interface SecurityPolicyInterface
|
||||
{
|
||||
/**
|
||||
* @param string[] $tags
|
||||
* @param string[] $filters
|
||||
* @param string[] $functions
|
||||
*
|
||||
* @throws SecurityError
|
||||
*/
|
||||
public function checkSecurity($tags, $filters, $functions): void;
|
||||
|
||||
/**
|
||||
* @param object $obj
|
||||
* @param string $method
|
||||
*
|
||||
* @throws SecurityNotAllowedMethodError
|
||||
*/
|
||||
public function checkMethodAllowed($obj, $method): void;
|
||||
|
||||
/**
|
||||
* @param object $obj
|
||||
* @param string $property
|
||||
*
|
||||
* @throws SecurityNotAllowedPropertyError
|
||||
*/
|
||||
public function checkPropertyAllowed($obj, $method): void;
|
||||
public function checkPropertyAllowed($obj, $property): void;
|
||||
}
|
||||
|
||||
@@ -195,9 +195,65 @@ require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/sessions.inc.php';
|
||||
// Set language
|
||||
if (!isset($_SESSION['mailcow_locale']) && !isset($_COOKIE['mailcow_locale'])) {
|
||||
if ($DETECT_LANGUAGE && isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
|
||||
$header_lang = strtolower(substr($_SERVER['HTTP_ACCEPT_LANGUAGE'], 0, 2));
|
||||
if (array_key_exists($header_lang, $AVAILABLE_LANGUAGES)) {
|
||||
$_SESSION['mailcow_locale'] = $header_lang;
|
||||
// regex inspired from @GabrielAnderson on http://stackoverflow.com/questions/6038236/http-accept-language
|
||||
preg_match_all('/([a-z]{1,8}(-[a-z]{1,8})*)\s*(;\s*q\s*=\s*(1|0\.[0-9]+))?/i', $_SERVER['HTTP_ACCEPT_LANGUAGE'], $lang_parse);
|
||||
|
||||
$langs = $lang_parse[1];
|
||||
$ranks = $lang_parse[4];
|
||||
|
||||
// (create an associative array 'language' => 'preference')
|
||||
$lang2pref = array();
|
||||
for ($i=0; $i<count($langs); $i++) {
|
||||
$lang2pref[strtolower($langs[$i])] = (float) (!empty($ranks[$i]) ? $ranks[$i] : 1);
|
||||
}
|
||||
|
||||
// (comparison function for uksort)
|
||||
$cmpLangs = function ($a, $b) use ($lang2pref) {
|
||||
if ($lang2pref[$a] > $lang2pref[$b])
|
||||
return -1;
|
||||
elseif ($lang2pref[$a] < $lang2pref[$b])
|
||||
return 1;
|
||||
elseif (strlen($a) > strlen($b))
|
||||
return -1;
|
||||
elseif (strlen($a) < strlen($b))
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
};
|
||||
|
||||
// sort the languages by prefered language and by the most specific region
|
||||
uksort($lang2pref, $cmpLangs);
|
||||
|
||||
// generate language array without the region part
|
||||
$AVAILABLE_BASE_LANGUAGES=array();
|
||||
foreach ($AVAILABLE_LANGUAGES as $code => $lang) {
|
||||
$base_code = substr($code, 0, 2);
|
||||
if (!array_key_exists($base_code, $AVAILABLE_BASE_LANGUAGES)) {
|
||||
$AVAILABLE_BASE_LANGUAGES[$base_code] = $code;
|
||||
}
|
||||
}
|
||||
|
||||
// Find a perfect match or partial match
|
||||
// Match en-gb or en
|
||||
foreach ($lang2pref as $lang => $q) {
|
||||
if (array_key_exists($lang, $AVAILABLE_LANGUAGES)) {
|
||||
$_SESSION['mailcow_locale'] = $lang;
|
||||
break;
|
||||
} elseif (array_key_exists($lang, $AVAILABLE_BASE_LANGUAGES)) {
|
||||
$_SESSION['mailcow_locale'] = $AVAILABLE_BASE_LANGUAGES[$lang];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Try suggest match
|
||||
// e.g. suggest en-gb when only en-us is provided
|
||||
if (!isset($_COOKIE['mailcow_locale'])) {
|
||||
foreach ($lang2pref as $lang => $q) {
|
||||
if (array_key_exists(substr($lang, 0, 2), $AVAILABLE_BASE_LANGUAGES)) {
|
||||
$_SESSION['mailcow_locale'] = $AVAILABLE_BASE_LANGUAGES[substr($lang, 0, 2)];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -215,7 +271,7 @@ if (isset($_GET['lang']) && array_key_exists($_GET['lang'], $AVAILABLE_LANGUAGES
|
||||
/*
|
||||
* load language
|
||||
*/
|
||||
$lang = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . '/lang/lang.en.json'), true);
|
||||
$lang = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . '/lang/lang.en-gb.json'), true);
|
||||
|
||||
$langFile = $_SERVER['DOCUMENT_ROOT'] . '/lang/lang.'.$_SESSION['mailcow_locale'].'.json';
|
||||
if(file_exists($langFile)) {
|
||||
|
||||
+25
-22
@@ -76,32 +76,35 @@ $autodiscover_config = array(
|
||||
$DETECT_LANGUAGE = true;
|
||||
|
||||
// Change default language
|
||||
$DEFAULT_LANG = 'en';
|
||||
$DEFAULT_LANG = 'en-gb';
|
||||
|
||||
// Available languages
|
||||
// https://www.iso.org/obp/ui/#search
|
||||
// https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes
|
||||
// https://en.wikipedia.org/wiki/IETF_language_tag
|
||||
$AVAILABLE_LANGUAGES = array(
|
||||
'cs' => 'Čeština (Czech)',
|
||||
'da' => 'Danish (Dansk)',
|
||||
'de' => 'Deutsch (German)',
|
||||
'en' => 'English',
|
||||
'es' => 'Español (Spanish)',
|
||||
'fi' => 'Suomi (Finish)',
|
||||
'fr' => 'Français (French)',
|
||||
'hu' => 'Magyar (Hungarian)',
|
||||
'it' => 'Italiano (Italian)',
|
||||
'ko' => '한국어 (Korean)',
|
||||
'lv' => 'latviešu (Latvian)',
|
||||
'nl' => 'Nederlands (Dutch)',
|
||||
'pl' => 'Język Polski (Polish)',
|
||||
'pt' => 'Português (Portuguese)',
|
||||
'ro' => 'Română (Romanian)',
|
||||
'ru' => 'Pусский (Russian)',
|
||||
'sk' => 'Slovenčina (Slovak)',
|
||||
'sv' => 'Svenska (Swedish)',
|
||||
'uk' => 'Українська (Ukrainian)',
|
||||
'zh' => '中文 (Chinese)'
|
||||
// 'ca-es' => 'Català (Catalan)',
|
||||
'cs-cz' => 'Čeština (Czech)',
|
||||
'da-dk' => 'Danish (Dansk)',
|
||||
'de-de' => 'Deutsch (German)',
|
||||
'en-gb' => 'English',
|
||||
'es-es' => 'Español (Spanish)',
|
||||
'fi-fi' => 'Suomi (Finish)',
|
||||
'fr-fr' => 'Français (French)',
|
||||
'hu-hu' => 'Magyar (Hungarian)',
|
||||
'it-it' => 'Italiano (Italian)',
|
||||
'ko-kr' => '한국어 (Korean)',
|
||||
'lv-lv' => 'latviešu (Latvian)',
|
||||
'nl-nl' => 'Nederlands (Dutch)',
|
||||
'pl-pl' => 'Język Polski (Polish)',
|
||||
'pt-pt' => 'Português (Portuguese)',
|
||||
'ro-ro' => 'Română (Romanian)',
|
||||
'ru-ru' => 'Pусский (Russian)',
|
||||
'sk-sk' => 'Slovenčina (Slovak)',
|
||||
'sv-se' => 'Svenska (Swedish)',
|
||||
'tr-tr' => 'Türkçe (Turkish)',
|
||||
'uk-ua' => 'Українська (Ukrainian)',
|
||||
'zh-cn' => '简体中文 (Simplified Chinese)',
|
||||
'zh-tw' => '繁體中文 (Traditional Chinese)',
|
||||
);
|
||||
|
||||
// Change theme (default: lumen)
|
||||
|
||||
@@ -839,7 +839,7 @@
|
||||
"confirm_delete": "Potvrdit smazání prvku.",
|
||||
"danger": "Nebezpečí",
|
||||
"deliver_inbox": "Doručit do schránky",
|
||||
"disabled_by_config": "Funkce karanténa je momentálně vypnuta v nastavení systému.",
|
||||
"disabled_by_config": "Funkce karanténa je momentálně vypnuta v nastavení systému. Nastavte, prosím, prvkům karantény hodnoty \"počet zadržených zpráv\" a \"maximální velikost\".",
|
||||
"download_eml": "Stáhnout (.eml)",
|
||||
"empty": "Žádné výsledky",
|
||||
"high_danger": "Vysoké nebezpečí",
|
||||
@@ -102,7 +102,8 @@
|
||||
"timeout2": "Délai d'expiration pour la connexion à l'hôte local",
|
||||
"username": "Nom d'utilisateur",
|
||||
"validate": "Valider",
|
||||
"validation_success": "Validation réussie"
|
||||
"validation_success": "Validation réussie",
|
||||
"bcc_dest_format": "La destination Cci doit être une seule adresse e-mail valide.<br>Si vous avez besoin d'envoyer une copie à plusieurs adresses, créez un alias et utilisez-le ici."
|
||||
},
|
||||
"admin": {
|
||||
"access": "Accès",
|
||||
@@ -322,7 +323,9 @@
|
||||
"yes": "✓",
|
||||
"api_read_write": "Accès Lecture-Écriture",
|
||||
"oauth2_add_client": "Ajouter un client OAuth2",
|
||||
"password_policy": "Politique de mots de passe"
|
||||
"password_policy": "Politique de mots de passe",
|
||||
"admins": "Administrateurs",
|
||||
"api_read_only": "Accès lecture-seule"
|
||||
},
|
||||
"danger": {
|
||||
"access_denied": "Accès refusé ou données de formulaire non valides",
|
||||
@@ -973,7 +973,8 @@
|
||||
"verified_fido2_login": "Verified FIDO2 login",
|
||||
"verified_totp_login": "Verified TOTP login",
|
||||
"verified_webauthn_login": "Verified WebAuthn login",
|
||||
"verified_yotp_login": "Verified Yubico OTP login"
|
||||
"verified_yotp_login": "Verified Yubico OTP login",
|
||||
"domain_add_dkim_available": "Esisteva già una chiave DKIM"
|
||||
},
|
||||
"tfa": {
|
||||
"api_register": "%s usa le API Yubico Cloud. Richiedi una chiave API <a href=\"https://upgrade.yubico.com/getapikey/\" target=\"_blank\">qui</a>",
|
||||
@@ -979,7 +979,8 @@
|
||||
"verified_totp_login": "Autentificarea TOTP verificată",
|
||||
"verified_webauthn_login": "Autentificarea WebAuthn verificată",
|
||||
"verified_fido2_login": "Conectare FIDO2 verificată",
|
||||
"verified_yotp_login": "Autentificarea Yubico OTP verificată"
|
||||
"verified_yotp_login": "Autentificarea Yubico OTP verificată",
|
||||
"domain_add_dkim_available": "O cheie DKIM deja a existat"
|
||||
},
|
||||
"tfa": {
|
||||
"api_register": "%s utilizează API-ul Yubico Cloud. Obțineți o cheie API pentru cheia dvs. de <a href=\"https://upgrade.yubico.com/getapikey/\" target=\"_blank\">aici</a>",
|
||||
@@ -990,7 +991,7 @@
|
||||
"enter_qr_code": "Codul tău TOTP dacă dispozitivul tău nu poate scana codurile QR",
|
||||
"error_code": "Cod de eroare",
|
||||
"init_webauthn": "Inițializare, vă rugăm așteptați...",
|
||||
"key_id": "Un identificator pentru YubiKey",
|
||||
"key_id": "Un identificator pentru dispozitiv",
|
||||
"key_id_totp": "Un identificator pentru cheia ta",
|
||||
"none": "Dezactivează",
|
||||
"reload_retry": "- (reîncărcați browserul dacă eroarea persistă)",
|
||||
@@ -0,0 +1,86 @@
|
||||
{
|
||||
"acl": {
|
||||
"alias_domains": "Takma alan adı ekle",
|
||||
"app_passwds": "Uygulama şifrelerini yönet",
|
||||
"delimiter_action": "Sınırlama işlemi",
|
||||
"domain_relayhost": "Bir alan adı için relayhost sunucusunu değiştir",
|
||||
"eas_reset": "EAS cihazlarını sıfırla",
|
||||
"mailbox_relayhost": "Bir posta kutusunun relayhost sunucularını değiştir",
|
||||
"pushover": "Bildirim",
|
||||
"quarantine": "Karantina işlemleri",
|
||||
"quarantine_attachments": "Ekleri karantinaya al",
|
||||
"quarantine_notification": "Karantina bildirimlerini değiştir",
|
||||
"smtp_ip_access": "SMTP sunucularının değiştirilmesine izin ver",
|
||||
"sogo_access": "SOGo erişiminin yönetilmesine izin ver",
|
||||
"domain_desc": "Alan adı açıklamasını değiştir",
|
||||
"extend_sender_acl": "Gönderenin acl'sini harici adreslere göre genişletmeye izin ver",
|
||||
"spam_policy": "Engellenenler / İzin verilenler",
|
||||
"filters": "Fitreler"
|
||||
},
|
||||
"add": {
|
||||
"activate_filter_warn": "Aktif edilirse diğer tüm filtreler devre dışı bırakılacak.",
|
||||
"add_domain_only": "Sadece alan adı ekle",
|
||||
"alias_address": "Takma ad adres(leri)",
|
||||
"alias_domain": "Takma alan adı",
|
||||
"alias_domain_info": "<small>Sadece geçerli alan adları (virgülle ayırın).</small>",
|
||||
"backup_mx_options": "İletme ayarları",
|
||||
"delete2": "Kaynakta olmayan hedefteki mesajları sil",
|
||||
"delete2duplicates": "Hedefteki kopyaları sil",
|
||||
"disable_login": "Giriş yapmaya izin verme ( Gelen mailler yine de kabul edilir)",
|
||||
"domain": "Alan adı",
|
||||
"domain_matches_hostname": "Alan adı %s ana bilgisayar adıyla eşleşiyor",
|
||||
"add_domain_restart": "Alan adı ekleyin ve SOGo'yu yeniden başlatın",
|
||||
"alias_address_info": "<small>Bir alan adına ilişkin tüm iletileri yakalamak için tam e-posta adresi veya @example.com olacak şeklinde girin (virgülle ayırın).<b>sadece mailcow alan adları</b>.</small>",
|
||||
"domain_quota_m": "Toplam alan adı kotası (MiB)",
|
||||
"generate": "oluştur",
|
||||
"goto_ham": "Ham olarak<span class=\"text-success\"><b>işaretle</b></span>",
|
||||
"goto_null": "Postaları sessizce çöpe at",
|
||||
"goto_spam": "Spam olarak<span class=\"text-danger\"><b>işaretle</b></span>",
|
||||
"hostname": "Ana sunucu",
|
||||
"kind": "Tür",
|
||||
"mailbox_quota_m": "Posta kutusu başına maksimum kota (MiB)",
|
||||
"max_aliases": "Maksimum olası takma adı",
|
||||
"max_mailboxes": "Maksimum olası posta kutusu",
|
||||
"nexthop": "Sonraki atlama",
|
||||
"port": "Port",
|
||||
"public_comment": "Genel yorum",
|
||||
"relay_all": "Tüm alıcılara ilet",
|
||||
"relay_all_info": "Eğer <b>hiçbir</b> alıcıya iletilmemesini seçerseniz, aktarılması gereken her alıcı için bir (\"kör\") posta kutusu eklemeniz gerekecektir.",
|
||||
"relay_domain": "Bu alan adını ilet",
|
||||
"relay_transport_info": "<div class=\"label label-info\">Bilgi</div> Bu etki alanı için özel bir hedef için aktarım eşlemeleri tanımlayabilirsiniz. Ayarlanmazsa, bir MX araması yapılacaktır.",
|
||||
"relay_unknown_only": "Yalnızca mevcut olmayan posta kutularını ilet. Mevcut posta kutuları yerel olarak teslim edilecektir.",
|
||||
"relayhost_wrapped_tls_info": "Lütfen TLS ile örtülmüş portları <b> kullanmayın</b> (çoğu 465 portunda çalışır).<br>\nÖrtülmemiş port kullan ve STARTTLS üzerinden yayınla. TLS'yi zorlamak için bir TLS ilkesi \"TLS ilke eşlemeleri\" sayfası içinde oluşturulabilir.",
|
||||
"skipcrossduplicates": "Klasörler arasında yinelenen mesajları atlayın (ilk mesaj seçilir)",
|
||||
"target_address": "Adreslere git",
|
||||
"target_address_info": "<small>Tam e-posta adres(leri) girin ( virgülle ayırın).</small>",
|
||||
"target_domain": "Hedef alan adı",
|
||||
"timeout1": "Uzak ana bilgisayara bağlantısı zaman aşımına uğradı",
|
||||
"timeout2": "Yerel ana bilgisayara bağlantı zaman aşımına uğradı"
|
||||
},
|
||||
"admin": {
|
||||
"action": "İşlem",
|
||||
"add_forwarding_host": "Yönlendirme sunucusu ekle",
|
||||
"add_transport": "İletim ekle",
|
||||
"admin_details": "Yönetici detaylarını düzenle",
|
||||
"admin_domains": "Alan adı atamaları",
|
||||
"add_domain_admin": "Alan adı yöneticisi ekle",
|
||||
"api_info": "API üzerinde çalışmalar devam etmektedir. Belgeler <a href=\"/api\">/api</a>adresinde bulunabilir",
|
||||
"apps_name": "\"mailcow Uygulamaları\" adı",
|
||||
"authed_user": "Yetkili kullanıcı",
|
||||
"ban_list_info": "Aşağıdaki yasaklı IP'lerin listesine bakın: <b>ağ (kalan yasak süresi) - [işlemler]</b>.<br />Yasağı kaldırılmak üzere sıraya alınan IP'ler birkaç saniye içinde aktif yasak listesinden kaldırılacaktır.<br />Kırmızı etiketler, kara listeye alınarak aktif kalıcı yasakları gösterir.",
|
||||
"configuration": "Yapılandırma",
|
||||
"dkim_from_title": "Verilerin kopyalanacağı kaynak alan adı",
|
||||
"dkim_to": "Kime",
|
||||
"dkim_to_title": "Hedef alan ad(ları) üzerinde yazılacak",
|
||||
"dkim_domains_wo_keys": "Eksik anahtarları olan alan adlarını seçin",
|
||||
"domain": "Alan adı",
|
||||
"domain_admin": "Alan adı yöneticisi",
|
||||
"domain_admins": "Alan adı yöneticileri",
|
||||
"domain_s": "Alan ad(ları)",
|
||||
"duplicate": "Çift",
|
||||
"duplicate_dkim": "Çift DKIM kayıtları",
|
||||
"f2b_ban_time": "Yasaklama süresi (saniye)",
|
||||
"f2b_max_attempts": "Maksimum giriş denemesi",
|
||||
"f2b_retry_window": "Maksimum girişim için deneme pencere(leri)"
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -32,12 +32,12 @@
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
{% if mailcow_locale %}
|
||||
<li class="dropdown{% if available_languages|length == 1 %}lang-link-disabled{% endif %}">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false"><span class="flag-icon flag-icon-{{ mailcow_locale }}"></span><span class="caret"></span></a>
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false"><span class="flag-icon flag-icon-{{ mailcow_locale[-2:] }}"></span><span class="caret"></span></a>
|
||||
<ul class="dropdown-menu" role="menu">
|
||||
{% for key, val in available_languages %}
|
||||
<li{% if mailcow_locale == key %} class="active"{% endif %}>
|
||||
<a href="?{{ query_string({'lang': key}) }}">
|
||||
<span class="flag-icon flag-icon-{{ key }}"></span>{{ val }}
|
||||
<span class="flag-icon flag-icon-{{ key[-2:] }}"></span>{{ val }}
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
@@ -182,30 +182,19 @@ function recursiveBase64StrToArrayBuffer(obj) {
|
||||
keyboard: false
|
||||
});
|
||||
|
||||
// validate Yubi OTP tfa
|
||||
$("#pending_tfa_tab_yubi_otp").click(function(){
|
||||
$(".totp-authenticator-selection").removeClass("active");
|
||||
$(".webauthn-authenticator-selection").removeClass("active");
|
||||
|
||||
$("#collapseTotpTFA").collapse('hide');
|
||||
$("#collapseWebAuthnTFA").collapse('hide');
|
||||
});
|
||||
$(".yubi-authenticator-selection").click(function(){
|
||||
$(".yubi-authenticator-selection").removeClass("active");
|
||||
$(this).addClass("active");
|
||||
|
||||
var id = $(this).children('input').first().val();
|
||||
$("#yubi_selected_id").val(id);
|
||||
|
||||
$("#collapseYubiTFA").collapse('show');
|
||||
});
|
||||
// validate Time based OTP tfa
|
||||
$("#pending_tfa_tab_totp").click(function(){
|
||||
$(".yubi-authenticator-selection").removeClass("active");
|
||||
$(".webauthn-authenticator-selection").removeClass("active");
|
||||
|
||||
$("#collapseYubiTFA").collapse('hide');
|
||||
$("#collapseWebAuthnTFA").collapse('hide');
|
||||
|
||||
// select default if only one authenticator exists
|
||||
if ($('.totp-authenticator-selection').length == 1){
|
||||
$('.totp-authenticator-selection').addClass("active");
|
||||
var id = $('.totp-authenticator-selection').children('input').first().val();
|
||||
$("#totp_selected_id").val(id);
|
||||
$("#collapseTotpTFA").collapse('show');
|
||||
}
|
||||
});
|
||||
$(".totp-authenticator-selection").click(function(){
|
||||
$(".totp-authenticator-selection").removeClass("active");
|
||||
@@ -216,13 +205,37 @@ function recursiveBase64StrToArrayBuffer(obj) {
|
||||
|
||||
$("#collapseTotpTFA").collapse('show');
|
||||
});
|
||||
if ($('.totp-authenticator-selection').length == 1 &&
|
||||
$('#pending_tfa_tab_yubi_otp').length == 0 &&
|
||||
$('.webauthn-authenticator-selection').length == 0){
|
||||
|
||||
// select default if only one authenticator exists
|
||||
$('.totp-authenticator-selection').addClass("active");
|
||||
|
||||
var id = $('.totp-authenticator-selection').children('input').first().val();
|
||||
$("#totp_selected_id").val(id);
|
||||
|
||||
$("#collapseTotpTFA").collapse('show');
|
||||
setTimeout(function() { $("#collapseTotpTFA").find('input[name="token"]').focus(); }, 1000);
|
||||
}
|
||||
$('#pending_tfa_tab_totp').on('shown.bs.tab', function() {
|
||||
// autofocus
|
||||
setTimeout(function() { $("#collapseTotpTFA").find('input[name="token"]').focus(); }, 200);
|
||||
});
|
||||
// validate Yubi OTP tfa
|
||||
if ($('.webauthn-authenticator-selection').length == 0){
|
||||
// autofocus
|
||||
setTimeout(function() { $("#collapseYubiTFA").find('input[name="token"]').focus(); }, 1000);
|
||||
}
|
||||
$('#pending_tfa_tab_yubi_otp').on('shown.bs.tab', function() {
|
||||
// autofocus
|
||||
$("#collapseYubiTFA").find('input[name="token"]').focus();
|
||||
});
|
||||
// validate WebAuthn tfa
|
||||
$("#pending_tfa_tab_webauthn").click(function(){
|
||||
$(".totp-authenticator-selection").removeClass("active");
|
||||
$(".yubi-authenticator-selection").removeClass("active");
|
||||
|
||||
$("#collapseTotpTFA").collapse('hide');
|
||||
$("#collapseYubiTFA").collapse('hide');
|
||||
});
|
||||
$(".webauthn-authenticator-selection").click(function(){
|
||||
$(".webauthn-authenticator-selection").removeClass("active");
|
||||
@@ -477,13 +490,20 @@ function recursiveBase64StrToArrayBuffer(obj) {
|
||||
{% if ui_texts.ui_footer %}
|
||||
<hr><span class="rot-enc">{{ ui_texts.ui_footer|rot13|raw }}</span>
|
||||
{% endif %}
|
||||
{% if mailcow_cc_username and mailcow_info.version_tag|default %}
|
||||
{% if mailcow_cc_username and mailcow_info.mailcow_branch|lower == "master" and mailcow_info.version_tag|default %}
|
||||
<span class="version">
|
||||
🐮 + 🐋 = 💕
|
||||
<a href="{{ mailcow_info.git_project_url }}/releases/tag/{{ mailcow_info.version_tag }}" target="_blank">
|
||||
Version: {{ mailcow_info.version_tag }}
|
||||
Version: <a href="{{ mailcow_info.git_project_url }}/releases/tag/{{ mailcow_info.version_tag }}" target="_blank">{{ mailcow_info.version_tag }}
|
||||
</a>
|
||||
</span>
|
||||
{% endif %}
|
||||
{% if mailcow_cc_username and mailcow_info.mailcow_branch|lower == "nightly" and mailcow_info.version_tag|default %}
|
||||
<span class="version">
|
||||
🛠️🐮 + 🐋 = 💕
|
||||
Nightly: <a href="{{ mailcow_info.git_project_url }}/commit/{{ mailcow_info.git_commit }}" target="_blank">{{ mailcow_info.version_tag }}
|
||||
</a><br>
|
||||
<span style="text-align:right;display:block;">Build: {{ mailcow_info.git_commit_date }}</span>
|
||||
</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</body>
|
||||
|
||||
@@ -45,13 +45,13 @@
|
||||
</div>
|
||||
{% if not oauth2_request %}
|
||||
<button type="button" {% if available_languages|length == 1 %}disabled="true"{% endif %} class="btn btn-xs-lg btn-default pull-right dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<span class="flag-icon flag-icon-{{ mailcow_locale }}"></span> <span class="caret"></span>
|
||||
<span class="flag-icon flag-icon-{{ mailcow_locale[-2:] }}"></span> <span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu pull-right login">
|
||||
{% for key, val in available_languages %}
|
||||
<li{% if mailcow_locale == key %} class="active"{% endif %}>
|
||||
<a href="?{{ query_string({'lang': key}) }}">
|
||||
<span class="flag-icon flag-icon-{{ key }}"></span>{{ val }}
|
||||
<span class="flag-icon flag-icon-{{ key[-2:] }}"></span>{{ val }}
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
|
||||
@@ -206,20 +206,9 @@
|
||||
<form role="form" method="post">
|
||||
<legend>
|
||||
<i class="bi bi-shield-fill-check"></i>
|
||||
Authenticators
|
||||
Authenticate
|
||||
</legend>
|
||||
<div class="list-group">
|
||||
{% for authenticator in pending_tfa_methods %}
|
||||
{% if authenticator["authmech"] == "yubi_otp" %}
|
||||
<a href="#" class="list-group-item yubi-authenticator-selection">
|
||||
<i class="bi bi-key-fill" style="margin-right: 5px"></i>
|
||||
<span>{{ authenticator["key_id"] }}</span>
|
||||
<input type="hidden" value="{{ authenticator["id"] }}" />
|
||||
</a>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div class="collapse pending-tfa-collapse" id="collapseYubiTFA">
|
||||
<div class="collapse in pending-tfa-collapse" id="collapseYubiTFA">
|
||||
<div class="form-group">
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon" id="yubi-addon"><img alt="Yubicon Icon" src="/img/yubi.ico"></span>
|
||||
|
||||
@@ -18,10 +18,6 @@
|
||||
<i class="bi bi-inbox-fill"></i> {{ lang.user.open_webmail_sso }}
|
||||
</a>
|
||||
{% endif %}
|
||||
<div>
|
||||
<hr>
|
||||
<p><a href="#pwChangeModal" data-toggle="modal"><i class="bi bi-pencil-fill"></i> {{ lang.user.change_password }}</a></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
@@ -47,6 +43,8 @@
|
||||
</div>
|
||||
</div>
|
||||
<p>{{ mailboxdata.quota_used|formatBytes(2) }} / {% if mailboxdata.quota == 0 %}∞{% else %}{{ mailboxdata.quota|formatBytes(2) }}{% endif %}<br>{{ mailboxdata.messages }} {{ lang.user.messages }}</p>
|
||||
<hr>
|
||||
<p><a href="#pwChangeModal" data-toggle="modal"><i class="bi bi-pencil-fill"></i> {{ lang.user.change_password }}</a></p>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
|
||||
+6
-6
@@ -41,7 +41,7 @@ services:
|
||||
- mysql
|
||||
|
||||
redis-mailcow:
|
||||
image: redis:6-alpine
|
||||
image: redis:7-alpine
|
||||
volumes:
|
||||
- redis-vol-1:/data/
|
||||
restart: always
|
||||
@@ -58,7 +58,7 @@ services:
|
||||
- redis
|
||||
|
||||
clamd-mailcow:
|
||||
image: mailcow/clamd:1.53
|
||||
image: mailcow/clamd:1.54
|
||||
restart: always
|
||||
depends_on:
|
||||
- unbound-mailcow
|
||||
@@ -76,7 +76,7 @@ services:
|
||||
- clamd
|
||||
|
||||
rspamd-mailcow:
|
||||
image: mailcow/rspamd:1.90
|
||||
image: mailcow/rspamd:1.91
|
||||
stop_grace_period: 30s
|
||||
depends_on:
|
||||
- dovecot-mailcow
|
||||
@@ -168,7 +168,7 @@ services:
|
||||
- phpfpm
|
||||
|
||||
sogo-mailcow:
|
||||
image: mailcow/sogo:1.109
|
||||
image: mailcow/sogo:1.111
|
||||
environment:
|
||||
- DBNAME=${DBNAME}
|
||||
- DBUSER=${DBUSER}
|
||||
@@ -215,7 +215,7 @@ services:
|
||||
- sogo
|
||||
|
||||
dovecot-mailcow:
|
||||
image: mailcow/dovecot:1.17
|
||||
image: mailcow/dovecot:1.20
|
||||
depends_on:
|
||||
- mysql-mailcow
|
||||
dns:
|
||||
@@ -295,7 +295,7 @@ services:
|
||||
- dovecot
|
||||
|
||||
postfix-mailcow:
|
||||
image: mailcow/postfix:1.67
|
||||
image: mailcow/postfix:1.68
|
||||
depends_on:
|
||||
- mysql-mailcow
|
||||
volumes:
|
||||
|
||||
+102
-12
@@ -16,19 +16,49 @@ if [[ "$(uname -r)" =~ ^4\.4\. ]]; then
|
||||
fi
|
||||
fi
|
||||
|
||||
if grep --help 2>&1 | grep -q -i "busybox"; then
|
||||
echo "BusyBox grep detected, please install gnu grep, \"apk add --no-cache --upgrade grep\""
|
||||
exit 1
|
||||
fi
|
||||
if cp --help 2>&1 | grep -q -i "busybox"; then
|
||||
echo "BusyBox cp detected, please install coreutils, \"apk add --no-cache --upgrade coreutils\""
|
||||
exit 1
|
||||
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
|
||||
|
||||
for bin in openssl curl docker docker-compose git awk sha1sum; do
|
||||
for bin in openssl curl docker git awk sha1sum; do
|
||||
if [[ -z $(which ${bin}) ]]; then echo "Cannot find ${bin}, exiting..."; exit 1; fi
|
||||
done
|
||||
|
||||
if docker compose > /dev/null 2>&1; then
|
||||
if docker compose version --short | grep "^2." > /dev/null 2>&1; then
|
||||
COMPOSE_VERSION=native
|
||||
echo -e "\e[31mFound Docker Compose Plugin (native).\e[0m"
|
||||
echo -e "\e[31mSetting the DOCKER_COMPOSE_VERSION Variable to native\e[0m"
|
||||
sleep 2
|
||||
echo -e "\e[33mNotice: You´ll have to update this Compose Version via your Package Manager manually!\e[0m"
|
||||
else
|
||||
echo -e "\e[31mCannot find Docker Compose with a Version Higher than 2.X.X.\e[0m"
|
||||
echo -e "\e[31mPlease update/install it manually regarding to this doc site: https://mailcow.github.io/mailcow-dockerized-docs/i_u_m/i_u_m_install/\e[0m"
|
||||
exit 1
|
||||
fi
|
||||
elif docker-compose > /dev/null 2>&1; then
|
||||
if ! [[ $(alias docker-compose 2> /dev/null) ]] ; then
|
||||
if docker-compose version --short | grep "^2." > /dev/null 2>&1; then
|
||||
COMPOSE_VERSION=standalone
|
||||
echo -e "\e[31mFound Docker Compose Standalone.\e[0m"
|
||||
echo -e "\e[31mSetting the DOCKER_COMPOSE_VERSION Variable to standalone\e[0m"
|
||||
sleep 2
|
||||
echo -e "\e[33mNotice: For an automatic update of docker-compose please use the update_compose.sh scripts located at the helper-scripts folder.\e[0m"
|
||||
else
|
||||
echo -e "\e[31mCannot find Docker Compose with a Version Higher than 2.X.X.\e[0m"
|
||||
echo -e "\e[31mPlease update/install manually regarding to this doc site: https://mailcow.github.io/mailcow-dockerized-docs/i_u_m/i_u_m_install/\e[0m"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
else
|
||||
echo -e "\e[31mCannot find Docker Compose.\e[0m"
|
||||
echo -e "\e[31mPlease install it regarding to this doc site: https://mailcow.github.io/mailcow-dockerized-docs/i_u_m/i_u_m_install/\e[0m"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
if [ -f mailcow.conf ]; then
|
||||
read -r -p "A config file exists and will be overwritten, are you sure you want to continue? [y/N] " response
|
||||
case $response in
|
||||
@@ -105,6 +135,32 @@ else
|
||||
SKIP_SOLR=n
|
||||
fi
|
||||
|
||||
echo "Which branch of mailcow do you want to use?"
|
||||
echo ""
|
||||
echo "Available Branches:"
|
||||
echo "- master branch (stable updates) | default, recommended [1]"
|
||||
echo "- nightly branch (unstable updates, testing) | not-production ready [2]"
|
||||
sleep 1
|
||||
|
||||
while [ -z "${MAILCOW_BRANCH}" ]; do
|
||||
read -r -p "Choose the Branch with it´s number [1/2] " branch
|
||||
case $branch in
|
||||
[2])
|
||||
MAILCOW_BRANCH="nightly"
|
||||
;;
|
||||
*)
|
||||
MAILCOW_BRANCH="master"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ ! -z "${MAILCOW_BRANCH}" ]; then
|
||||
git_branch=${MAILCOW_BRANCH}
|
||||
fi
|
||||
|
||||
git fetch --all
|
||||
git checkout -f $git_branch
|
||||
|
||||
[ ! -f ./data/conf/rspamd/override.d/worker-controller-password.inc ] && echo '# Placeholder' > ./data/conf/rspamd/override.d/worker-controller-password.inc
|
||||
|
||||
cat << EOF > mailcow.conf
|
||||
@@ -183,6 +239,14 @@ TZ=${MAILCOW_TZ}
|
||||
|
||||
COMPOSE_PROJECT_NAME=mailcowdockerized
|
||||
|
||||
# Used Docker Compose version
|
||||
# Switch here between native (compose plugin) and standalone
|
||||
# For more informations take a look at the mailcow docs regarding the configuration options.
|
||||
# Normally this should be untouched but if you decided to use either of those you can switch it manually here.
|
||||
# Please be aware that at least one of those variants should be installed on your maschine or mailcow will fail.
|
||||
|
||||
DOCKER_COMPOSE_VERSION=${COMPOSE_VERSION}
|
||||
|
||||
# Set this to "allow" to enable the anyone pseudo user. Disabled by default.
|
||||
# When enabled, ACL can be created, that apply to "All authenticated users"
|
||||
# This should probably only be activated on mail hosts, that are used exclusivly by one organisation.
|
||||
@@ -363,16 +427,42 @@ echo "Copying snake-oil certificate..."
|
||||
cp -n -d data/assets/ssl-example/*.pem data/assets/ssl/
|
||||
|
||||
# Set app_info.inc.php
|
||||
mailcow_git_version=$(git describe --tags `git rev-list --tags --max-count=1`)
|
||||
if [ ${git_branch} == "master" ]; then
|
||||
mailcow_git_version=$(git describe --tags `git rev-list --tags --max-count=1`)
|
||||
elif [ ${git_branch} == "nightly" ]; then
|
||||
mailcow_git_version=$(git rev-parse --short $(git rev-parse @{upstream}))
|
||||
mailcow_last_git_version=""
|
||||
else
|
||||
mailcow_git_version=$(git rev-parse --short HEAD)
|
||||
mailcow_last_git_version=""
|
||||
fi
|
||||
|
||||
mailcow_git_commit=$(git rev-parse origin/${git_branch})
|
||||
mailcow_git_commit_date=$(git log -1 --format=%ci @{upstream} )
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
echo '<?php' > data/web/inc/app_info.inc.php
|
||||
echo ' $MAILCOW_GIT_VERSION="'$mailcow_git_version'";' >> data/web/inc/app_info.inc.php
|
||||
echo ' $MAILCOW_LAST_GIT_VERSION="";' >> data/web/inc/app_info.inc.php
|
||||
echo ' $MAILCOW_GIT_OWNER="mailcow";' >> data/web/inc/app_info.inc.php
|
||||
echo ' $MAILCOW_GIT_REPO="mailcow-dockerized";' >> data/web/inc/app_info.inc.php
|
||||
echo ' $MAILCOW_GIT_URL="https://github.com/mailcow/mailcow-dockerized";' >> data/web/inc/app_info.inc.php
|
||||
echo ' $MAILCOW_GIT_COMMIT="'$mailcow_git_commit'";' >> data/web/inc/app_info.inc.php
|
||||
echo ' $MAILCOW_GIT_COMMIT_DATE="'$mailcow_git_commit_date'";' >> data/web/inc/app_info.inc.php
|
||||
echo ' $MAILCOW_BRANCH="'$git_branch'";' >> data/web/inc/app_info.inc.php
|
||||
echo ' $MAILCOW_UPDATEDAT='$(date +%s)';' >> data/web/inc/app_info.inc.php
|
||||
echo '?>' >> data/web/inc/app_info.inc.php
|
||||
else
|
||||
echo '<?php' > data/web/inc/app_info.inc.php
|
||||
echo ' $MAILCOW_GIT_VERSION="";' >> data/web/inc/app_info.inc.php
|
||||
echo ' $MAILCOW_GIT_URL="";' >> data/web/inc/app_info.inc.php
|
||||
echo ' $MAILCOW_GIT_VERSION="'$mailcow_git_version'";' >> data/web/inc/app_info.inc.php
|
||||
echo ' $MAILCOW_LAST_GIT_VERSION="";' >> data/web/inc/app_info.inc.php
|
||||
echo ' $MAILCOW_GIT_OWNER="mailcow";' >> data/web/inc/app_info.inc.php
|
||||
echo ' $MAILCOW_GIT_REPO="mailcow-dockerized";' >> data/web/inc/app_info.inc.php
|
||||
echo ' $MAILCOW_GIT_URL="https://github.com/mailcow/mailcow-dockerized";' >> data/web/inc/app_info.inc.php
|
||||
echo ' $MAILCOW_GIT_COMMIT="";' >> data/web/inc/app_info.inc.php
|
||||
echo ' $MAILCOW_GIT_COMMIT_DATE="";' >> data/web/inc/app_info.inc.php
|
||||
echo ' $MAILCOW_BRANCH="'$git_branch'";' >> data/web/inc/app_info.inc.php
|
||||
echo ' $MAILCOW_UPDATEDAT='$(date +%s)';' >> data/web/inc/app_info.inc.php
|
||||
echo '?>' >> data/web/inc/app_info.inc.php
|
||||
echo -e "\e[33mCannot determine current git repository version...\e[0m"
|
||||
fi
|
||||
|
||||
@@ -77,7 +77,7 @@ function preflight_local_checks() {
|
||||
exit 1
|
||||
fi
|
||||
|
||||
for bin in rsync docker docker-compose grep cut; do
|
||||
for bin in rsync docker grep cut; do
|
||||
if [[ -z $(which ${bin}) ]]; then
|
||||
>&2 echo -e "\e[31mCannot find ${bin} in local PATH, exiting...\e[0m"
|
||||
exit 1
|
||||
@@ -111,7 +111,7 @@ function preflight_remote_checks() {
|
||||
exit 1
|
||||
fi
|
||||
|
||||
for bin in rsync docker docker-compose; do
|
||||
for bin in rsync docker; do
|
||||
if ! ssh -o StrictHostKeyChecking=no \
|
||||
-i "${REMOTE_SSH_KEY}" \
|
||||
${REMOTE_SSH_HOST} \
|
||||
@@ -121,17 +121,44 @@ function preflight_remote_checks() {
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
ssh -o StrictHostKeyChecking=no \
|
||||
-i "${REMOTE_SSH_KEY}" \
|
||||
${REMOTE_SSH_HOST} \
|
||||
-p ${REMOTE_SSH_PORT} \
|
||||
"bash -s" << "EOF"
|
||||
if docker compose > /dev/null 2>&1; then
|
||||
exit 0
|
||||
elif docker-compose version --short | grep "^2." > /dev/null 2>&1; then
|
||||
exit 1
|
||||
else
|
||||
exit 2
|
||||
fi
|
||||
EOF
|
||||
|
||||
if [ $? = 0 ]; then
|
||||
COMPOSE_COMMAND="docker compose"
|
||||
echo "DEBUG: Using native docker compose on remote"
|
||||
|
||||
elif [ $? = 1 ]; then
|
||||
COMPOSE_COMMAND="docker-compose"
|
||||
echo "DEBUG: Using standalone docker compose on remote"
|
||||
|
||||
else
|
||||
echo -e "\e[31mCannot find any Docker Compose on remote, exiting...\e[0m"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
SCRIPT_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
|
||||
source "${SCRIPT_DIR}/../mailcow.conf"
|
||||
COMPOSE_FILE="${SCRIPT_DIR}/../docker-compose.yml"
|
||||
CMPS_PRJ=$(echo ${COMPOSE_PROJECT_NAME} | tr -cd 'A-Za-z-_')
|
||||
SQLIMAGE=$(grep -iEo '(mysql|mariadb)\:.+' "${COMPOSE_FILE}")
|
||||
|
||||
preflight_local_checks
|
||||
preflight_remote_checks
|
||||
|
||||
SCRIPT_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
|
||||
COMPOSE_FILE="${SCRIPT_DIR}/../docker-compose.yml"
|
||||
source "${SCRIPT_DIR}/../mailcow.conf"
|
||||
CMPS_PRJ=$(echo ${COMPOSE_PROJECT_NAME} | tr -cd 'A-Za-z-_')
|
||||
SQLIMAGE=$(grep -iEo '(mysql|mariadb)\:.+' "${COMPOSE_FILE}")
|
||||
|
||||
echo
|
||||
echo -e "\033[1mFound compose project name ${CMPS_PRJ} for ${MAILCOW_HOSTNAME}\033[0m"
|
||||
echo -e "\033[1mFound SQL ${SQLIMAGE}\033[0m"
|
||||
@@ -258,7 +285,7 @@ echo "OK"
|
||||
-i "${REMOTE_SSH_KEY}" \
|
||||
${REMOTE_SSH_HOST} \
|
||||
-p ${REMOTE_SSH_PORT} \
|
||||
docker-compose -f "${SCRIPT_DIR}/../docker-compose.yml" pull --no-parallel --quiet 2>&1 ; then
|
||||
${COMPOSE_COMMAND} -f "${SCRIPT_DIR}/../docker-compose.yml" pull --no-parallel --quiet 2>&1 ; then
|
||||
>&2 echo -e "\e[31m[ERR]\e[0m - Could not pull images on remote"
|
||||
fi
|
||||
|
||||
@@ -271,13 +298,4 @@ if ! ssh -o StrictHostKeyChecking=no \
|
||||
>&2 echo -e "\e[31m[ERR]\e[0m - Could not cleanup old images on remote"
|
||||
fi
|
||||
|
||||
echo -e "\033[1mExecuting update script and checking for new docker-compose Version on remote...\033[0m"
|
||||
if ! ssh -o StrictHostKeyChecking=no \
|
||||
-i "${REMOTE_SSH_KEY}" \
|
||||
${REMOTE_SSH_HOST} \
|
||||
-p ${REMOTE_SSH_PORT} \
|
||||
${SCRIPT_DIR}/../update.sh -f --update-compose ; then
|
||||
>&2 echo -e "\e[31m[ERR]\e[0m - Could not fetch docker-compose on remote"
|
||||
fi
|
||||
|
||||
echo -e "\e[32mDone\e[0m"
|
||||
echo -e "\e[32mDone\e[0m"
|
||||
@@ -26,7 +26,7 @@ if(empty($targetLang)) {
|
||||
}
|
||||
|
||||
// load master lang
|
||||
$masterLang = file_get_contents(__DIR__.'/../data/web/lang/lang.en.json');
|
||||
$masterLang = file_get_contents(__DIR__.'/../data/web/lang/lang.en-gb.json');
|
||||
$masterLang = json_decode($masterLang, true);
|
||||
|
||||
// load target lang
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
DEBIAN_DOCKER_IMAGE="debian:bullseye-slim"
|
||||
DEBIAN_DOCKER_IMAGE="mailcow/backup:1.0"
|
||||
|
||||
if [[ ! -z ${MAILCOW_BACKUP_LOCATION} ]]; then
|
||||
BACKUP_LOCATION="${MAILCOW_BACKUP_LOCATION}"
|
||||
@@ -52,6 +52,15 @@ BACKUP_LOCATION=$(echo ${BACKUP_LOCATION} | sed 's#/$##')
|
||||
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
COMPOSE_FILE=${SCRIPT_DIR}/../docker-compose.yml
|
||||
ENV_FILE=${SCRIPT_DIR}/../.env
|
||||
THREADS=$(echo ${THREADS:-1})
|
||||
|
||||
if ! [[ "${THREADS}" =~ ^[1-9]+$ ]] ; then
|
||||
echo "Thread input is not a number!"
|
||||
exit 1
|
||||
elif [[ "${THREADS}" =~ ^[1-9]+$ ]] ; then
|
||||
echo "Using ${THREADS} Thread(s) for this run."
|
||||
echo "Notice: You can set the Thread count with the THREADS Variable before you run this script."
|
||||
fi
|
||||
|
||||
if [ ! -f ${COMPOSE_FILE} ]; then
|
||||
echo "Compose file not found"
|
||||
@@ -99,32 +108,32 @@ function backup() {
|
||||
docker run --name mailcow-backup --rm \
|
||||
-v ${BACKUP_LOCATION}/mailcow-${DATE}:/backup:z \
|
||||
-v $(docker volume ls -qf name=^${CMPS_PRJ}_vmail-vol-1$):/vmail:ro,z \
|
||||
${DEBIAN_DOCKER_IMAGE} /bin/tar --warning='no-file-ignored' --use-compress-program="gzip --rsyncable" -Pcvpf /backup/backup_vmail.tar.gz /vmail
|
||||
${DEBIAN_DOCKER_IMAGE} /bin/tar --warning='no-file-ignored' --use-compress-program="pigz --rsyncable -p ${THREADS}" -Pcvpf /backup/backup_vmail.tar.gz /vmail
|
||||
;;&
|
||||
crypt|all)
|
||||
docker run --name mailcow-backup --rm \
|
||||
-v ${BACKUP_LOCATION}/mailcow-${DATE}:/backup:z \
|
||||
-v $(docker volume ls -qf name=^${CMPS_PRJ}_crypt-vol-1$):/crypt:ro,z \
|
||||
${DEBIAN_DOCKER_IMAGE} /bin/tar --warning='no-file-ignored' --use-compress-program="gzip --rsyncable" -Pcvpf /backup/backup_crypt.tar.gz /crypt
|
||||
${DEBIAN_DOCKER_IMAGE} /bin/tar --warning='no-file-ignored' --use-compress-program="pigz --rsyncable -p ${THREADS}" -Pcvpf /backup/backup_crypt.tar.gz /crypt
|
||||
;;&
|
||||
redis|all)
|
||||
docker exec $(docker ps -qf name=redis-mailcow) redis-cli save
|
||||
docker run --name mailcow-backup --rm \
|
||||
-v ${BACKUP_LOCATION}/mailcow-${DATE}:/backup:z \
|
||||
-v $(docker volume ls -qf name=^${CMPS_PRJ}_redis-vol-1$):/redis:ro,z \
|
||||
${DEBIAN_DOCKER_IMAGE} /bin/tar --warning='no-file-ignored' --use-compress-program="gzip --rsyncable" -Pcvpf /backup/backup_redis.tar.gz /redis
|
||||
${DEBIAN_DOCKER_IMAGE} /bin/tar --warning='no-file-ignored' --use-compress-program="pigz --rsyncable -p ${THREADS}" -Pcvpf /backup/backup_redis.tar.gz /redis
|
||||
;;&
|
||||
rspamd|all)
|
||||
docker run --name mailcow-backup --rm \
|
||||
-v ${BACKUP_LOCATION}/mailcow-${DATE}:/backup:z \
|
||||
-v $(docker volume ls -qf name=^${CMPS_PRJ}_rspamd-vol-1$):/rspamd:ro,z \
|
||||
${DEBIAN_DOCKER_IMAGE} /bin/tar --warning='no-file-ignored' --use-compress-program="gzip --rsyncable" -Pcvpf /backup/backup_rspamd.tar.gz /rspamd
|
||||
${DEBIAN_DOCKER_IMAGE} /bin/tar --warning='no-file-ignored' --use-compress-program="pigz --rsyncable -p ${THREADS}" -Pcvpf /backup/backup_rspamd.tar.gz /rspamd
|
||||
;;&
|
||||
postfix|all)
|
||||
docker run --name mailcow-backup --rm \
|
||||
-v ${BACKUP_LOCATION}/mailcow-${DATE}:/backup:z \
|
||||
-v $(docker volume ls -qf name=^${CMPS_PRJ}_postfix-vol-1$):/postfix:ro,z \
|
||||
${DEBIAN_DOCKER_IMAGE} /bin/tar --warning='no-file-ignored' --use-compress-program="gzip --rsyncable" -Pcvpf /backup/backup_postfix.tar.gz /postfix
|
||||
${DEBIAN_DOCKER_IMAGE} /bin/tar --warning='no-file-ignored' --use-compress-program="pigz --rsyncable -p ${THREADS}" -Pcvpf /backup/backup_postfix.tar.gz /postfix
|
||||
;;&
|
||||
mysql|all)
|
||||
SQLIMAGE=$(grep -iEo '(mysql|mariadb)\:.+' ${COMPOSE_FILE})
|
||||
@@ -160,12 +169,24 @@ function backup() {
|
||||
}
|
||||
|
||||
function restore() {
|
||||
for bin in docker docker-compose; do
|
||||
for bin in docker; do
|
||||
if [[ -z $(which ${bin}) ]]; then
|
||||
>&2 echo -e "\e[31mCannot find ${bin} in local PATH, exiting...\e[0m"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
done
|
||||
|
||||
if [ "${DOCKER_COMPOSE_VERSION}" == "native" ]; then
|
||||
COMPOSE_COMMAND="docker compose"
|
||||
|
||||
elif [ "${DOCKER_COMPOSE_VERSION}" == "standalone" ]; then
|
||||
COMPOSE_COMMAND="docker-compose"
|
||||
|
||||
else
|
||||
echo -e "\e[31mCan not read DOCKER_COMPOSE_VERSION variable from mailcow.conf! Is your mailcow up to date? Exiting...\e[0m"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "Stopping watchdog-mailcow..."
|
||||
docker stop $(docker ps -qf name=watchdog-mailcow)
|
||||
@@ -179,7 +200,7 @@ function restore() {
|
||||
docker run -it --name mailcow-backup --rm \
|
||||
-v ${RESTORE_LOCATION}:/backup:z \
|
||||
-v $(docker volume ls -qf name=^${CMPS_PRJ}_vmail-vol-1$):/vmail:z \
|
||||
${DEBIAN_DOCKER_IMAGE} /bin/tar -Pxvzf /backup/backup_vmail.tar.gz
|
||||
${DEBIAN_DOCKER_IMAGE} /bin/tar --use-compress-program="pigz -d -p ${THREADS}" -Pxvf /backup/backup_vmail.tar.gz
|
||||
docker start $(docker ps -aqf name=dovecot-mailcow)
|
||||
echo
|
||||
echo "In most cases it is not required to run a full resync, you can run the command printed below at any time after testing wether the restore process broke a mailbox:"
|
||||
@@ -198,7 +219,7 @@ function restore() {
|
||||
docker run -it --name mailcow-backup --rm \
|
||||
-v ${RESTORE_LOCATION}:/backup:z \
|
||||
-v $(docker volume ls -qf name=^${CMPS_PRJ}_redis-vol-1$):/redis:z \
|
||||
${DEBIAN_DOCKER_IMAGE} /bin/tar -Pxvzf /backup/backup_redis.tar.gz
|
||||
${DEBIAN_DOCKER_IMAGE} /bin/tar --use-compress-program="pigz -d -p ${THREADS}" -Pxvf /backup/backup_redis.tar.gz
|
||||
docker start $(docker ps -aqf name=redis-mailcow)
|
||||
;;
|
||||
crypt)
|
||||
@@ -206,7 +227,7 @@ function restore() {
|
||||
docker run -it --name mailcow-backup --rm \
|
||||
-v ${RESTORE_LOCATION}:/backup:z \
|
||||
-v $(docker volume ls -qf name=^${CMPS_PRJ}_crypt-vol-1$):/crypt:z \
|
||||
${DEBIAN_DOCKER_IMAGE} /bin/tar -Pxvzf /backup/backup_crypt.tar.gz
|
||||
${DEBIAN_DOCKER_IMAGE} /bin/tar --use-compress-program="pigz -d -p ${THREADS}" -Pxvf /backup/backup_crypt.tar.gz
|
||||
docker start $(docker ps -aqf name=dovecot-mailcow)
|
||||
;;
|
||||
rspamd)
|
||||
@@ -214,7 +235,7 @@ function restore() {
|
||||
docker run -it --name mailcow-backup --rm \
|
||||
-v ${RESTORE_LOCATION}:/backup:z \
|
||||
-v $(docker volume ls -qf name=^${CMPS_PRJ}_rspamd-vol-1$):/rspamd:z \
|
||||
${DEBIAN_DOCKER_IMAGE} /bin/tar -Pxvzf /backup/backup_rspamd.tar.gz
|
||||
${DEBIAN_DOCKER_IMAGE} /bin/tar --use-compress-program="pigz -d -p ${THREADS}" -Pxvf /backup/backup_rspamd.tar.gz
|
||||
docker start $(docker ps -aqf name=rspamd-mailcow)
|
||||
;;
|
||||
postfix)
|
||||
@@ -222,7 +243,7 @@ function restore() {
|
||||
docker run -it --name mailcow-backup --rm \
|
||||
-v ${RESTORE_LOCATION}:/backup:z \
|
||||
-v $(docker volume ls -qf name=^${CMPS_PRJ}_postfix-vol-1$):/postfix:z \
|
||||
${DEBIAN_DOCKER_IMAGE} /bin/tar -Pxvzf /backup/backup_postfix.tar.gz
|
||||
${DEBIAN_DOCKER_IMAGE} /bin/tar --use-compress-program="pigz -d -p ${THREADS}" -Pxvf /backup/backup_postfix.tar.gz
|
||||
docker start $(docker ps -aqf name=postfix-mailcow)
|
||||
;;
|
||||
mysql|mariadb)
|
||||
@@ -244,7 +265,7 @@ function restore() {
|
||||
continue
|
||||
else
|
||||
echo "Stopping mailcow..."
|
||||
docker-compose -f ${COMPOSE_FILE} --env-file ${ENV_FILE} down
|
||||
${COMPOSE_COMMAND} -f ${COMPOSE_FILE} --env-file ${ENV_FILE} down
|
||||
fi
|
||||
#docker stop $(docker ps -qf name=mysql-mailcow)
|
||||
if [[ -d "${RESTORE_LOCATION}/mysql" ]]; then
|
||||
@@ -282,7 +303,7 @@ function restore() {
|
||||
sed -i --follow-symlinks "/DBROOT/c\DBROOT=${DBROOT}" ${SCRIPT_DIR}/../mailcow.conf
|
||||
source ${SCRIPT_DIR}/../mailcow.conf
|
||||
echo "Starting mailcow..."
|
||||
docker-compose -f ${COMPOSE_FILE} --env-file ${ENV_FILE} up -d
|
||||
${COMPOSE_COMMAND} -f ${COMPOSE_FILE} --env-file ${ENV_FILE} up -d
|
||||
#docker start $(docker ps -aqf name=mysql-mailcow)
|
||||
fi
|
||||
;;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env ruby
|
||||
|
||||
MASTER="en"
|
||||
MASTER="en-gb"
|
||||
|
||||
DIR = "#{__dir__}/.."
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user