Compare commits

...

24 Commits

Author SHA1 Message Date
renovate[bot]
a8dfa95126
Update dependency phpredis/phpredis to v6.0.2
Signed-off-by: milkmaker <milkmaker@mailcow.de>
2023-11-21 09:03:02 +00:00
Niklas Meyer
3b3c2b7141
Merge pull request #5546 from mailcow/fix/domain-wide-footer
Fix: Domain Wide Disclaimer breaks attachments visualization on Gmail and Outlook #5529
2023-11-21 10:01:38 +01:00
Niklas Meyer
f55c3c0887
Merge pull request #5548 from mailcow/fix-5547
[Web] escape quarantine html
2023-11-21 10:01:04 +01:00
FreddleSpl0it
f423ad77f3
[Web] escape quarantine html 2023-11-21 08:49:18 +01:00
FreddleSpl0it
8ba1e1ba9e
[Rspamd] workaround - remove "--\x0D\x0A" prefix from rewritten cts 2023-11-20 12:38:37 +01:00
Niklas Meyer
55576084fc
Merge pull request #5544 from mailcow/feat/update-renovate 2023-11-18 12:33:12 +01:00
Peter
03311b06c9
Ignore everything in vendor subdirs 2023-11-18 11:40:57 +01:00
milkmaker
b5c3d01834
Translations update from Weblate (#5538)
* [Web] Updated lang.cs-cz.json

Co-authored-by: Peter <magic@kthx.at>
Co-authored-by: milkmaker <milkmaker@mailcow.de>

* [Web] Updated lang.fr-fr.json

Co-authored-by: Quiwy <github@quiwy.ninja>
Co-authored-by: milkmaker <milkmaker@mailcow.de>

---------

Co-authored-by: Peter <magic@kthx.at>
Co-authored-by: Quiwy <github@quiwy.ninja>
2023-11-16 17:07:33 +01:00
Niklas Meyer
f398ecbe39
Merge pull request #5487 from artemislena/master
Add a helper script for generating CAA records
2023-11-16 11:42:11 +01:00
Niklas Meyer
8f1ae0f099
Merge pull request #5530 from Quiwy/staging
fix: support utf-8 in password synchronization
2023-11-16 11:21:27 +01:00
Niklas Meyer
c8bee57732
Merge pull request #5521 from raph-topo/fix/impasync-options
Add `--dry` IMAPsync Parameter as Button to select for SyncJobs
2023-11-16 11:19:47 +01:00
DerLinkman
85641794c3
Added f1f2 + sorted whitelist for imapsync 2023-11-16 11:18:50 +01:00
Niklas Meyer
849decaa59
Merge pull request #5532 from mailcow/renovate/actions-cache-3.x
Update actions/cache action to v3
2023-11-16 10:46:28 +01:00
Niklas Meyer
6e88550f92
Merge pull request #5533 from mailcow/renovate/actions-checkout-4.x
Update actions/checkout action to v4
2023-11-16 10:46:03 +01:00
renovate[bot]
7c52483887
Update actions/checkout action to v4
Signed-off-by: milkmaker <milkmaker@mailcow.de>
2023-11-16 09:29:22 +00:00
renovate[bot]
0aa520c030
Update actions/cache action to v3
Signed-off-by: milkmaker <milkmaker@mailcow.de>
2023-11-16 09:29:15 +00:00
Niklas Meyer
548999f163
Merge pull request #5498 from mailcow:feat/fix-5497
Update nextcloud.conf when updating nextcloud
2023-11-16 10:28:54 +01:00
DerLinkman
63df547306
Tweaked German Translation 2023-11-15 16:45:27 +01:00
DerLinkman
547d2ca308
Add Dry Mode Option for ImapSyncs (Button) 2023-11-15 16:18:18 +01:00
Quiwy
46b995f9e3
fix: support utf-8 in password synchronization 2023-11-14 10:11:25 +01:00
Raphael
afed94cc0e
Allow --dry IMAPsync 2023-11-09 15:24:16 +01:00
Peter
cfa47eb873
Update nextcloud.conf 2023-10-27 22:59:46 +02:00
Peter
7079000ee0
Update nextcloud.conf when updating nextcloud 2023-10-27 22:56:51 +02:00
artemislena
03ed81dc3f
T.: Added a script for generating CAA records 2023-10-23 19:44:28 +02:00
25 changed files with 303 additions and 130 deletions

View File

@ -12,7 +12,7 @@
"baseBranches": ["staging"],
"enabledManagers": ["github-actions", "regex", "docker-compose"],
"ignorePaths": [
"data\/web\/inc\/lib\/vendor\/matthiasmullie\/minify\/**"
"data\/web\/inc\/lib\/vendor\/**"
],
"regexManagers": [
{

View File

@ -75,7 +75,8 @@ my $sth = $dbh->prepare("SELECT id,
custom_params,
subscribeall,
timeout1,
timeout2
timeout2,
dry
FROM imapsync
WHERE active = 1
AND is_running = 0
@ -111,13 +112,16 @@ while ($row = $sth->fetchrow_arrayref()) {
$subscribeall = @$row[18];
$timeout1 = @$row[19];
$timeout2 = @$row[20];
$dry = @$row[21];
if ($enc1 eq "TLS") { $enc1 = "--tls1"; } elsif ($enc1 eq "SSL") { $enc1 = "--ssl1"; } else { undef $enc1; }
my $template = $run_dir . '/imapsync.XXXXXXX';
my $passfile1 = File::Temp->new(TEMPLATE => $template);
my $passfile2 = File::Temp->new(TEMPLATE => $template);
binmode( $passfile1, ":utf8" );
print $passfile1 "$password1\n";
print $passfile2 trim($master_pass) . "\n";
@ -148,6 +152,7 @@ while ($row = $sth->fetchrow_arrayref()) {
"--host2", "localhost",
"--user2", $user2 . '*' . trim($master_user),
"--passfile2", $passfile2->filename,
($dry eq "1" ? ('--dry') : ()),
'--no-modulesversion',
'--noreleasecheck'];

View File

@ -10,7 +10,7 @@ ARG MAILPARSE_PECL_VERSION=3.1.6
# renovate: datasource=github-tags depName=php-memcached-dev/php-memcached versioning=semver-coerced extractVersion=^v(?<version>.*)$
ARG MEMCACHED_PECL_VERSION=3.2.0
# renovate: datasource=github-tags depName=phpredis/phpredis versioning=semver-coerced extractVersion=(?<version>.*)$
ARG REDIS_PECL_VERSION=6.0.1
ARG REDIS_PECL_VERSION=6.0.2
# renovate: datasource=github-tags depName=composer/composer versioning=semver-coerced extractVersion=(?<version>.*)$
ARG COMPOSER_VERSION=2.6.5

View File

@ -86,7 +86,7 @@ server {
deny all;
}
location ~ ^\/(?:index|remote|public|cron|core\/ajax\/update|status|ocs\/v[12]|updater\/.+|oc[ms]-provider\/.+)\.php(?:$|\/) {
location ~ ^\/(?:index|remote|public|cron|core\/ajax\/update|status|ocs\/v[12]|updater\/.+|ocs-provider\/.+)\.php(?:$|\/) {
fastcgi_split_path_info ^(.+?\.php)(\/.*|)$;
set $path_info $fastcgi_path_info;
try_files $fastcgi_script_name =404;
@ -105,7 +105,7 @@ server {
fastcgi_read_timeout 1200;
}
location ~ ^\/(?:updater|oc[ms]-provider)(?:$|\/) {
location ~ ^\/(?:updater|ocs-provider)(?:$|\/) {
try_files $uri/ =404;
index index.php;
}

View File

@ -631,15 +631,19 @@ rspamd_config:register_symbol({
end
local out_parts = {}
for _,o in ipairs(out) do
if type(o) ~= 'table' then
out_parts[#out_parts + 1] = o
out_parts[#out_parts + 1] = newline_s
else
out_parts[#out_parts + 1] = o[1]
if o[2] then
out_parts[#out_parts + 1] = newline_s
end
end
if type(o) ~= 'table' then
out_parts[#out_parts + 1] = o
out_parts[#out_parts + 1] = newline_s
else
local removePrefix = "--\x0D\x0AContent-Type"
if string.lower(string.sub(tostring(o[1]), 1, string.len(removePrefix))) == string.lower(removePrefix) then
o[1] = string.sub(tostring(o[1]), string.len("--\x0D\x0A") + 1)
end
out_parts[#out_parts + 1] = o[1]
if o[2] then
out_parts[#out_parts + 1] = newline_s
end
end
end
task:set_message(out_parts)
else

View File

@ -325,6 +325,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
$timeout2 = intval($_data['timeout2']);
$skipcrossduplicates = intval($_data['skipcrossduplicates']);
$automap = intval($_data['automap']);
$dry = intval($_data['dry']);
$port1 = $_data['port1'];
$host1 = strtolower($_data['host1']);
$password1 = $_data['password1'];
@ -435,8 +436,8 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
);
return false;
}
$stmt = $pdo->prepare("INSERT INTO `imapsync` (`user2`, `exclude`, `delete1`, `delete2`, `timeout1`, `timeout2`, `automap`, `skipcrossduplicates`, `maxbytespersecond`, `subscribeall`, `maxage`, `subfolder2`, `host1`, `authmech1`, `user1`, `password1`, `mins_interval`, `port1`, `enc1`, `delete2duplicates`, `custom_params`, `active`)
VALUES (:user2, :exclude, :delete1, :delete2, :timeout1, :timeout2, :automap, :skipcrossduplicates, :maxbytespersecond, :subscribeall, :maxage, :subfolder2, :host1, :authmech1, :user1, :password1, :mins_interval, :port1, :enc1, :delete2duplicates, :custom_params, :active)");
$stmt = $pdo->prepare("INSERT INTO `imapsync` (`user2`, `exclude`, `delete1`, `delete2`, `timeout1`, `timeout2`, `automap`, `skipcrossduplicates`, `maxbytespersecond`, `subscribeall`, `dry`, `maxage`, `subfolder2`, `host1`, `authmech1`, `user1`, `password1`, `mins_interval`, `port1`, `enc1`, `delete2duplicates`, `custom_params`, `active`)
VALUES (:user2, :exclude, :delete1, :delete2, :timeout1, :timeout2, :automap, :skipcrossduplicates, :maxbytespersecond, :subscribeall, :dry, :maxage, :subfolder2, :host1, :authmech1, :user1, :password1, :mins_interval, :port1, :enc1, :delete2duplicates, :custom_params, :active)");
$stmt->execute(array(
':user2' => $username,
':custom_params' => $custom_params,
@ -450,6 +451,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
':skipcrossduplicates' => $skipcrossduplicates,
':maxbytespersecond' => $maxbytespersecond,
':subscribeall' => $subscribeall,
':dry' => $dry,
':subfolder2' => $subfolder2,
':host1' => $host1,
':authmech1' => 'PLAIN',
@ -2031,6 +2033,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
$success = (isset($_data['success'])) ? NULL : $is_now['success'];
$delete2duplicates = (isset($_data['delete2duplicates'])) ? intval($_data['delete2duplicates']) : $is_now['delete2duplicates'];
$subscribeall = (isset($_data['subscribeall'])) ? intval($_data['subscribeall']) : $is_now['subscribeall'];
$dry = (isset($_data['dry'])) ? intval($_data['dry']) : $is_now['dry'];
$delete1 = (isset($_data['delete1'])) ? intval($_data['delete1']) : $is_now['delete1'];
$delete2 = (isset($_data['delete2'])) ? intval($_data['delete2']) : $is_now['delete2'];
$automap = (isset($_data['automap'])) ? intval($_data['automap']) : $is_now['automap'];
@ -2164,6 +2167,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
`timeout1` = :timeout1,
`timeout2` = :timeout2,
`subscribeall` = :subscribeall,
`dry` = :dry,
`active` = :active
WHERE `id` = :id");
$stmt->execute(array(
@ -2189,6 +2193,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
':timeout1' => $timeout1,
':timeout2' => $timeout2,
':subscribeall' => $subscribeall,
':dry' => $dry,
':active' => $active,
));
$_SESSION['return'][] = array(

View File

@ -3,7 +3,7 @@ function init_db_schema() {
try {
global $pdo;
$db_version = "14022023_1000";
$db_version = "15112023_1536";
$stmt = $pdo->query("SHOW TABLES LIKE 'versions'");
$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
@ -704,6 +704,7 @@ function init_db_schema() {
"timeout1" => "SMALLINT NOT NULL DEFAULT '600'",
"timeout2" => "SMALLINT NOT NULL DEFAULT '600'",
"subscribeall" => "TINYINT(1) NOT NULL DEFAULT '1'",
"dry" => "TINYINT(1) NOT NULL DEFAULT '0'",
"is_running" => "TINYINT(1) NOT NULL DEFAULT '0'",
"returned_text" => "LONGTEXT",
"last_run" => "TIMESTAMP NULL DEFAULT NULL",

View File

@ -19,10 +19,10 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v2
uses: actions/checkout@v4
- name: Cache dependencies
uses: actions/cache@v2
uses: actions/cache@v3
with:
path: ~/.composer/cache/files
key: dependencies-php-${{ matrix.php }}-composer-${{ hashFiles('composer.json') }}
@ -52,10 +52,10 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v2
uses: actions/checkout@v4
- name: Cache dependencies
uses: actions/cache@v2
uses: actions/cache@v3
with:
path: ~/.composer/cache/files
key: dependencies-php-${{ matrix.php }}-composer-${{ hashFiles('composer.json') }}

View File

@ -12,7 +12,7 @@ jobs:
dependency-version: [prefer-lowest, prefer-stable]
steps:
- name: Checkout code
uses: actions/checkout@v1
uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@v2
@ -31,7 +31,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v1
uses: actions/checkout@v4
- name: Install dependencies
run: composer update --no-progress --ignore-platform-reqs
@ -43,7 +43,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v1
uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@v2

View File

@ -13,7 +13,7 @@ jobs:
php-version: ['5.6', '7.0', '7.1', '7.2', '7.3', '7.4', '8.0']
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- uses: shivammathur/setup-php@v2
with:

View File

@ -25,7 +25,7 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v1
uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@v2

View File

@ -32,7 +32,7 @@ jobs:
steps:
- name: "Checkout code"
uses: actions/checkout@v2
uses: actions/checkout@v4
- name: "Install PHP with extensions"
uses: shivammathur/setup-php@v2
@ -86,7 +86,7 @@ jobs:
steps:
- name: "Checkout code"
uses: actions/checkout@v2
uses: actions/checkout@v4
- name: "Install PHP with extensions"
uses: shivammathur/setup-php@v2

View File

@ -18,7 +18,7 @@ jobs:
steps:
- name: "Checkout code"
uses: actions/checkout@v2
uses: actions/checkout@v4
- name: "Set-up PHP"
uses: shivammathur/setup-php@v2
@ -33,7 +33,7 @@ jobs:
run: echo "::set-output name=dir::$(composer config cache-files-dir)"
- name: Cache dependencies
uses: actions/cache@v2
uses: actions/cache@v3
with:
path: ${{ steps.composercache.outputs.dir }}
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
@ -54,7 +54,7 @@ jobs:
steps:
- name: "Checkout code"
uses: actions/checkout@v2
uses: actions/checkout@v4
- name: "Run DOCtor-RST"
uses: docker://oskarstark/doctor-rst

View File

@ -236,118 +236,120 @@ $RSPAMD_MAPS = array(
$IMAPSYNC_OPTIONS = array(
'whitelist' => array(
'abort',
'authmd51',
'authmd52',
'authmech1',
'authmech2',
'authuser1',
'authuser2',
'debugcontent',
'disarmreadreceipts',
'logdir',
'debugcrossduplicates',
'maxsize',
'minsize',
'minage',
'search',
'noabletosearch',
'pidfile',
'pidfilelocking',
'search1',
'search2',
'sslargs1',
'sslargs2',
'syncduplicates',
'usecache',
'synclabels',
'truncmess',
'domino2',
'expunge1',
'filterbuggyflags',
'justconnect',
'justfolders',
'maxlinelength',
'useheader',
'noabletosearch1',
'nolog',
'prefix1',
'prefix2',
'sep1',
'sep2',
'nofoldersizesatend',
'justfoldersizes',
'proxyauth1',
'skipemptyfolders',
'include',
'subfolder1',
'subscribed',
'subscribe',
'debug',
'debugcontent',
'debugcrossduplicates',
'debugflags',
'debugfolders',
'debugimap',
'debugimap1',
'debugimap2',
'debugmemory',
'debugssl',
'delete1emptyfolders',
'delete2folders',
'disarmreadreceipts',
'domain1',
'domain2',
'domino1',
'domino2',
'dry',
'errorsmax',
'exchange1',
'exchange2',
'exitwhenover',
'expunge1',
'f1f2',
'filterbuggyflags',
'folder',
'folderfirst',
'folderlast',
'folderrec',
'gmail1',
'gmail2',
'idatefromheader',
'include',
'inet4',
'inet6',
'justconnect',
'justfolders',
'justfoldersizes',
'justlogin',
'keepalive1',
'keepalive2',
'log',
'logdir',
'logfile',
'maxbytesafter',
'maxlinelength',
'maxmessagespersecond',
'maxsize',
'maxsleep',
'minage',
'minsize',
'noabletosearch',
'noabletosearch1',
'noabletosearch2',
'noexpunge1',
'noexpunge2',
'nofoldersizesatend',
'noid',
'nolog',
'nomixfolders',
'noresyncflags',
'nossl1',
'nouidexpunge2',
'syncinternaldates',
'idatefromheader',
'useuid',
'debugflags',
'debugimap',
'delete1emptyfolders',
'delete2folders',
'gmail2',
'office1',
'testslive6',
'debugimap1',
'errorsmax',
'tests',
'gmail1',
'maxmessagespersecond',
'maxbytesafter',
'maxsleep',
'abort',
'resyncflags',
'resynclabels',
'syncacls',
'nossl2',
'nosyncacls',
'notls1',
'notls2',
'nouidexpunge2',
'nousecache',
'office2',
'testslive',
'debugmemory',
'exitwhenover',
'noid',
'noexpunge1',
'authmd51',
'logfile',
'proxyauth2',
'domain1',
'domain2',
'oauthaccesstoken1',
'oauthaccesstoken2',
'oauthdirect1',
'oauthdirect2',
'folder',
'folderrec',
'folderfirst',
'folderlast',
'nomixfolders',
'authmd52',
'debugfolders',
'nossl2',
'office1',
'office2',
'pidfile',
'pidfilelocking',
'prefix1',
'prefix2',
'proxyauth1',
'proxyauth2',
'resyncflags',
'resynclabels',
'search',
'search1',
'search2',
'sep1',
'sep2',
'showpasswords',
'skipemptyfolders',
'ssl2',
'sslargs1',
'sslargs2',
'subfolder1',
'subscribe',
'subscribed',
'syncacls',
'syncduplicates',
'syncinternaldates',
'synclabels',
'tests',
'testslive',
'testslive6',
'tls2',
'notls2',
'debugssl',
'notls1',
'inet4',
'inet6',
'log',
'showpasswords'
'truncmess',
'usecache',
'useheader',
'useuid'
),
'blacklist' => array(
'skipmess',

View File

@ -220,7 +220,7 @@ jQuery(function($){
if (value.score > 0) highlightClass = 'negative';
else if (value.score < 0) highlightClass = 'positive';
else highlightClass = 'neutral';
$('#qid_detail_symbols').append('<span data-bs-toggle="tooltip" class="rspamd-symbol ' + highlightClass + '" title="' + (value.options ? value.options.join(', ') : '') + '">' + value.name + ' (<span class="score">' + value.score + '</span>)</span>');
$('#qid_detail_symbols').append('<span data-bs-toggle="tooltip" class="rspamd-symbol ' + highlightClass + '" title="' + (value.options ? escapeHtml(value.options.join(', ')) : '') + '">' + value.name + ' (<span class="score">' + value.score + '</span>)</span>');
});
$('[data-bs-toggle="tooltip"]').tooltip();
}
@ -295,3 +295,7 @@ jQuery(function($){
$(".table_collapse_option").hide();
}
});

View File

@ -209,7 +209,7 @@
"include_exclude_info": "Ve výchozím nastavení (bez výběru), jsou adresovány <b>všechny mailové schránky</b>",
"includes": "Zahrnout tyto přijemce",
"ip_check": "Kontrola IP",
"ip_check_disabled": "Kontrola IP je zakázána. Můžete ji povolit v nabídce<br> <strong>Systém > Nastavení > Možnosti > Přizpůsobení</strong>.",
"ip_check_disabled": "Kontrola IP je vypnuta. Můžete ji zapnout v <br> <strong>System > Nastavení > Options > Přizpůsobení</strong>",
"ip_check_opt_in": "Přihlásit se k používání služby třetí strany <strong>ipv4.mailcow.email</strong> a <strong>ipv6.mailcow.email</strong> pro zjištění externích IP adres.",
"is_mx_based": "Na základě MX",
"last_applied": "Naposledy použité",
@ -343,9 +343,7 @@
"verify": "Ověřit",
"yes": "&#10003;",
"f2b_ban_time_increment": "Délka banu je prodlužována s každým dalším banem",
"f2b_max_ban_time": "Maximální délka banu (s)",
"ip_check": "Kontrola IP",
"ip_check_disabled": "Kontrola IP je vypnuta. Můžete ji zapnout v <br> <strong>System > Nastavení > Options > Přizpůsobení</strong>"
"f2b_max_ban_time": "Maximální délka banu (s)"
},
"danger": {
"access_denied": "Přístup odepřen nebo jsou neplatná data ve formuláři",

View File

@ -58,6 +58,7 @@
"domain": "Domain",
"domain_matches_hostname": "Domain %s darf nicht dem Hostnamen entsprechen",
"domain_quota_m": "Domain-Speicherplatz gesamt (MiB)",
"dry": "Synchronisation simulieren",
"enc_method": "Verschlüsselung",
"exclude": "Elemente ausschließen (Regex)",
"full_name": "Vor- und Nachname",

View File

@ -58,6 +58,7 @@
"domain": "Domain",
"domain_matches_hostname": "Domain %s matches hostname",
"domain_quota_m": "Total domain quota (MiB)",
"dry": "Simulate synchronization",
"enc_method": "Encryption method",
"exclude": "Exclude objects (regex)",
"full_name": "Full name",

View File

@ -326,7 +326,11 @@
"password_policy_lowerupper": "Doit contenir des caractères minuscules et majuscules",
"password_policy_numbers": "Doit contenir au moins un chiffre",
"ip_check": "Vérification IP",
"ip_check_disabled": "La vérification IP est désactivée. Vous pouvez l'activer sous<br> <strong>Système > Configuration > Options > Personnaliser</strong>"
"ip_check_disabled": "La vérification IP est désactivée. Vous pouvez l'activer sous<br> <strong>Système > Configuration > Options > Personnaliser</strong>",
"logo_normal_label": "Normal",
"logo_dark_label": "Inversé pour le mode sombre",
"allowed_methods": "Access-Control-Allow-Methods",
"allowed_origins": "Access-Control-Allow-Origin"
},
"danger": {
"access_denied": "Accès refusé ou données de formulaire non valides",

View File

@ -11,6 +11,7 @@
<input type="hidden" value="0" name="skipcrossduplicates">
<input type="hidden" value="0" name="active">
<input type="hidden" value="0" name="subscribeall">
<input type="hidden" value="0" name="dry">
<div class="row mb-2">
<label class="control-label col-sm-2" for="host1">{{ lang.edit.hostname }}</label>
<div class="col-sm-10">
@ -95,7 +96,7 @@
<div class="row mb-4">
<label class="control-label col-sm-2" for="custom_params">{{ lang.add.custom_params }}</label>
<div class="col-sm-10">
<input type="text" class="form-control" name="custom_params" id="custom_params" value="{{ result.custom_params }}" placeholder="--dry --some-param=xy --other-param=yx">
<input type="text" class="form-control" name="custom_params" id="custom_params" value="{{ result.custom_params }}" placeholder="--some-param=xy --other-param=yx">
<small class="text-muted">{{ lang.add.custom_params_hint }}</small>
</div>
</div>
@ -141,6 +142,13 @@
</div>
</div>
</div>
<div class="row mb-2">
<div class="offset-sm-2 col-sm-10">
<div class="form-check">
<label><input type="checkbox" class="form-check-input" value="1" name="dry"{% if result.dry == '1' %} checked{% endif %}> {{ lang.add.dry }} (--dry)</label>
</div>
</div>
</div>
<div class="row mb-4">
<div class="offset-sm-2 col-sm-10">
<div class="form-check">

View File

@ -955,7 +955,7 @@
<div class="row mb-4">
<label class="control-label col-sm-2 text-sm-end" for="custom_params">{{ lang.add.custom_params }}</label>
<div class="col-sm-10">
<input type="text" class="form-control" name="custom_params" placeholder="--dry --some-param=xy --other-param=yx">
<input type="text" class="form-control" name="custom_params" placeholder="--some-param=xy --other-param=yx">
<small class="text-muted">{{ lang.add.custom_params_hint }}</small>
</div>
</div>
@ -994,13 +994,20 @@
</div>
</div>
</div>
<div class="row mb-4">
<div class="row mb-2">
<div class="offset-sm-2 col-sm-10">
<div class="form-check">
<label><input type="checkbox" class="form-check-input" value="1" name="subscribeall" checked> {{ lang.add.subscribeall }} (--subscribeall)</label>
</div>
</div>
</div>
<div class="row mb-4">
<div class="offset-sm-2 col-sm-10">
<div class="form-check">
<label><input type="checkbox" class="form-check-input" value="1" name="dry"> {{ lang.add.dry }} (--dry)</label>
</div>
</div>
</div>
<div class="row mb-2">
<div class="offset-sm-2 col-sm-10">
<div class="form-check">

View File

@ -167,6 +167,13 @@
</div>
</div>
</div>
<div class="row mb-2">
<div class="offset-sm-2 col-sm-10">
<div class="form-check">
<label><input type="checkbox" class="form-check-input" value="1" name="dry" checked> {{ lang.add.dry }} (--dry)</label>
</div>
</div>
</div>
<div class="row mb-4">
<div class="offset-sm-2 col-sm-10">
<div class="form-check">

View File

@ -218,7 +218,7 @@ services:
- sogo
dovecot-mailcow:
image: mailcow/dovecot:1.25
image: mailcow/dovecot:1.26
depends_on:
- mysql-mailcow
dns:

View File

@ -0,0 +1,122 @@
#!/usr/bin/env python3
# Based on github.com/diafygi/acme-tiny, original copyright:
# Copyright Daniel Roesler, under MIT license, see LICENSE at github.com/diafygi/acme-tiny
import argparse, subprocess, json, os, sys, base64, binascii, time, hashlib, re, copy, textwrap, logging
try:
from urllib.request import urlopen, Request # Python 3
except ImportError: # pragma: no cover
from urllib2 import urlopen, Request # Python 2
DEFAULT_DIRECTORY_URL = "https://acme-v02.api.letsencrypt.org/directory"
LOGGER = logging.getLogger(__name__)
LOGGER.addHandler(logging.StreamHandler())
LOGGER.setLevel(logging.INFO)
def get_id(account_key, log=LOGGER, directory_url=DEFAULT_DIRECTORY_URL, contact=None):
directory, acct_headers, alg, jwk = None, None, None, None # global variables
# helper functions - base64 encode for jose spec
def _b64(b):
return base64.urlsafe_b64encode(b).decode('utf8').replace("=", "")
# helper function - run external commands
def _cmd(cmd_list, stdin=None, cmd_input=None, err_msg="Command Line Error"):
proc = subprocess.Popen(cmd_list, stdin=stdin, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out, err = proc.communicate(cmd_input)
if proc.returncode != 0:
raise IOError("{0}\n{1}".format(err_msg, err))
return out
# helper function - make request and automatically parse json response
def _do_request(url, data=None, err_msg="Error", depth=0):
try:
resp = urlopen(Request(url, data=data, headers={"Content-Type": "application/jose+json", "User-Agent": "acme-tiny"}))
resp_data, code, headers = resp.read().decode("utf8"), resp.getcode(), resp.headers
except IOError as e:
resp_data = e.read().decode("utf8") if hasattr(e, "read") else str(e)
code, headers = getattr(e, "code", None), {}
try:
resp_data = json.loads(resp_data) # try to parse json results
except ValueError:
pass # ignore json parsing errors
if depth < 100 and code == 400 and resp_data['type'] == "urn:ietf:params:acme:error:badNonce":
raise IndexError(resp_data) # allow 100 retrys for bad nonces
if code not in [200, 201, 204]:
raise ValueError("{0}:\nUrl: {1}\nData: {2}\nResponse Code: {3}\nResponse: {4}".format(err_msg, url, data, code, resp_data))
return resp_data, code, headers
# helper function - make signed requests
def _send_signed_request(url, payload, err_msg, depth=0):
payload64 = "" if payload is None else _b64(json.dumps(payload).encode('utf8'))
new_nonce = _do_request(directory['newNonce'])[2]['Replay-Nonce']
protected = {"url": url, "alg": alg, "nonce": new_nonce}
protected.update({"jwk": jwk} if acct_headers is None else {"kid": acct_headers['Location']})
protected64 = _b64(json.dumps(protected).encode('utf8'))
protected_input = "{0}.{1}".format(protected64, payload64).encode('utf8')
out = _cmd(["openssl", "dgst", "-sha256", "-sign", account_key], stdin=subprocess.PIPE, cmd_input=protected_input, err_msg="OpenSSL Error")
data = json.dumps({"protected": protected64, "payload": payload64, "signature": _b64(out)})
try:
return _do_request(url, data=data.encode('utf8'), err_msg=err_msg, depth=depth)
except IndexError: # retry bad nonces (they raise IndexError)
return _send_signed_request(url, payload, err_msg, depth=(depth + 1))
# helper function - poll until complete
def _poll_until_not(url, pending_statuses, err_msg):
result, t0 = None, time.time()
while result is None or result['status'] in pending_statuses:
assert (time.time() - t0 < 3600), "Polling timeout" # 1 hour timeout
time.sleep(0 if result is None else 2)
result, _, _ = _send_signed_request(url, None, err_msg)
return result
# parse account key to get public key
log.info("Parsing account key...")
out = _cmd(["openssl", "rsa", "-in", account_key, "-noout", "-text"], err_msg="OpenSSL Error")
pub_pattern = r"modulus:[\s]+?00:([a-f0-9\:\s]+?)\npublicExponent: ([0-9]+)"
pub_hex, pub_exp = re.search(pub_pattern, out.decode('utf8'), re.MULTILINE|re.DOTALL).groups()
pub_exp = "{0:x}".format(int(pub_exp))
pub_exp = "0{0}".format(pub_exp) if len(pub_exp) % 2 else pub_exp
alg, jwk = "RS256", {
"e": _b64(binascii.unhexlify(pub_exp.encode("utf-8"))),
"kty": "RSA",
"n": _b64(binascii.unhexlify(re.sub(r"(\s|:)", "", pub_hex).encode("utf-8"))),
}
accountkey_json = json.dumps(jwk, sort_keys=True, separators=(',', ':'))
thumbprint = _b64(hashlib.sha256(accountkey_json.encode('utf8')).digest())
# get the ACME directory of urls
log.info("Getting directory...")
directory, _, _ = _do_request(directory_url, err_msg="Error getting directory")
log.info("Directory found!")
# create account and get the global key identifier
log.info("Registering account...")
reg_payload = {"termsOfServiceAgreed": True} if contact is None else {"termsOfServiceAgreed": True, "contact": contact}
account, code, acct_headers = _send_signed_request(directory['newAccount'], reg_payload, "Error registering")
log.info("Registered!" if code == 201 else "Already registered!")
return acct_headers['Location']
def main(argv=None):
parser = argparse.ArgumentParser(
formatter_class=argparse.RawDescriptionHelpFormatter,
description=textwrap.dedent("""\
Generate a CAA record for Mailcow.
Example Usage: python mailcow_gencaa.py --account-key data/assets/ssl/acme/account.pem
""")
)
parser.add_argument("--account-key", required=True, help="path to your Let's Encrypt account private key")
parser.add_argument("--quiet", action="store_const", const=logging.ERROR, help="suppress output except for errors")
parser.add_argument("--directory-url", default=DEFAULT_DIRECTORY_URL, help="certificate authority directory url, default is Let's Encrypt")
parser.add_argument("--contact", metavar="CONTACT", default=None, nargs="*", help="Contact details (e.g. mailto:aaa@bbb.com) for your account-key")
args = parser.parse_args(argv)
LOGGER.setLevel(args.quiet or LOGGER.level)
id = get_id(args.account_key, log=LOGGER, directory_url=args.directory_url, contact=args.contact)
print("Use this as your CAA record:")
print('issue 128 "letsencrypt.org;accounturi={}"'.format(id))
if __name__ == "__main__": # pragma: no cover
main(sys.argv[1:])

View File

@ -106,6 +106,10 @@ elif [[ ${NC_UPDATE} == "y" ]]; then
exit 1
else
docker exec -it -u www-data $(docker ps -f name=php-fpm-mailcow -q) bash -c "php /web/nextcloud/updater/updater.phar"
NC_SUBD=$(docker exec -i -u www-data $(docker ps -f name=php-fpm-mailcow -q) /web/nextcloud/occ config:system:get overwritehost)
mv ./data/conf/nginx/nextcloud.conf ./data/conf/nginx/nextcloud.conf-$(date +%s).bak
cp ./data/assets/nextcloud/nextcloud.conf ./data/conf/nginx/
sed -i "s/NC_SUBD/${NC_SUBD}/g" ./data/conf/nginx/nextcloud.conf
fi
elif [[ ${NC_INSTALL} == "y" ]]; then