mirror of
https://github.com/webmin/webmin.git
synced 2026-02-05 07:02:14 +00:00
Compare commits
58 Commits
dev/embed-
...
dev/patch-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7507433bf1 | ||
|
|
248cb719c0 | ||
|
|
94b7fdf0ec | ||
|
|
d89f6411b6 | ||
|
|
01d08a3605 | ||
|
|
3d9497ff45 | ||
|
|
a6832450d1 | ||
|
|
e41037388c | ||
|
|
a7b3af534b | ||
|
|
87e006ceeb | ||
|
|
fbee8f0588 | ||
|
|
e077b4da94 | ||
|
|
38efad8265 | ||
|
|
4d2a1fa084 | ||
|
|
13b2eca3b1 | ||
|
|
aa4c3b1de6 | ||
|
|
edaab4fd6f | ||
|
|
6fded0862c | ||
|
|
4006b0454e | ||
|
|
5d4ab58baa | ||
|
|
e6c7a60fe6 | ||
|
|
a75781d61a | ||
|
|
45348f5b02 | ||
|
|
548c078813 | ||
|
|
76ccb2b7ed | ||
|
|
61d2081371 | ||
|
|
458916b4d8 | ||
|
|
37451ad905 | ||
|
|
9eec58a1f1 | ||
|
|
178f527afa | ||
|
|
cd1555b3e1 | ||
|
|
2d7cfca67f | ||
|
|
e92a35b3ed | ||
|
|
e35efd0f00 | ||
|
|
a12f385a5b | ||
|
|
5dc7cfafd7 | ||
|
|
35aee74311 | ||
|
|
d04dfdf157 | ||
|
|
41f1adf0df | ||
|
|
1ae01bed8d | ||
|
|
4a6f5d9a6a | ||
|
|
bb3a4bc1e8 | ||
|
|
084c117547 | ||
|
|
20d481e96b | ||
|
|
6af5ed6e2f | ||
|
|
3fa687e716 | ||
|
|
d21188c2ad | ||
|
|
ba3be31335 | ||
|
|
adb3d0bb67 | ||
|
|
cd9bc8b7c4 | ||
|
|
cce8911f0f | ||
|
|
cde548ada3 | ||
|
|
00d0a89630 | ||
|
|
9c971202b3 | ||
|
|
2aa3bce4da | ||
|
|
4103177d1e | ||
|
|
31b6cdc82b | ||
|
|
fd8c634277 |
File diff suppressed because one or more lines are too long
153
bin/patch
Executable file
153
bin/patch
Executable file
@@ -0,0 +1,153 @@
|
||||
#!/usr/bin/env perl
|
||||
# patch - Apply a patch to Webmin core or its modules from GitHub or a local file
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use 5.010;
|
||||
|
||||
use Getopt::Long qw(:config permute pass_through);
|
||||
use Pod::Usage;
|
||||
use File::Basename;
|
||||
use Cwd qw(cwd);
|
||||
|
||||
my %opt;
|
||||
GetOptions(
|
||||
'help|h' => \$opt{'help'},
|
||||
'config|c=s' => \$opt{'config'},
|
||||
);
|
||||
pod2usage(0) if ($opt{'help'});
|
||||
|
||||
# Get Webmin path
|
||||
my $path = cwd;
|
||||
my $lib = "web-lib-funcs.pl";
|
||||
if (!-r "$path/$lib") {
|
||||
$path = dirname(dirname($0));
|
||||
if (!-r "$path/$lib") {
|
||||
$path = $path = Cwd::realpath('..');
|
||||
}
|
||||
}
|
||||
|
||||
# Init core
|
||||
my $config_dir = $opt{'config'} || '/etc/webmin';
|
||||
$ENV{'WEBMIN_CONFIG'} = $config_dir;
|
||||
push(@INC, $path);
|
||||
eval 'use WebminCore';
|
||||
init_config();
|
||||
|
||||
# Check if curl is installed
|
||||
if (!has_command('curl')) {
|
||||
print "curl is not installed\n";
|
||||
exit 1;
|
||||
}
|
||||
|
||||
# Check if git is installed
|
||||
if (!has_command('git')) {
|
||||
print "git is not installed\n";
|
||||
exit 1;
|
||||
}
|
||||
|
||||
# Get patch URL or file
|
||||
my $patch = $ARGV[0];
|
||||
|
||||
# Params check
|
||||
if (!$patch) {
|
||||
pod2usage(0);
|
||||
exit 1;
|
||||
}
|
||||
|
||||
# Patch check
|
||||
if ($patch !~ /^https?:\/\//) {
|
||||
if (!-r $patch) {
|
||||
print "Patch file $patch doesn't exist\n";
|
||||
exit 1;
|
||||
}
|
||||
}
|
||||
elsif ($patch =~ /^https?:\/\/(github|gitlab)\.com/ &&
|
||||
$patch !~ /\.patch$/ && $patch !~ /\.diff$/) {
|
||||
$patch .= '.patch';
|
||||
}
|
||||
|
||||
# Parse module name from URL
|
||||
my $module = "";
|
||||
if ($patch =~ m{https://(github|gitlab)\.com/[^/]+/([^/]+)/commit/[^/]+}) {
|
||||
$module = $2;
|
||||
$module = "" if ($2 eq 'webmin');
|
||||
# Special handling for some modules
|
||||
$module = $module =~ /^virtualmin-pro$/ ?
|
||||
'virtual-server/pro' :
|
||||
'virtual-server'
|
||||
if $module =~ /^virtualmin-(gpl|pro)$/;
|
||||
}
|
||||
|
||||
# Check if module exists
|
||||
if (!-d "$path/$module") {
|
||||
print "Module $module doesn't exist\n";
|
||||
exit 1;
|
||||
}
|
||||
|
||||
# Download command or cat patch file
|
||||
my $cmd;
|
||||
if ($patch =~ /^https?:\/\//) {
|
||||
$cmd = "curl -s @{[quotemeta($patch)]}";
|
||||
chdir "$path/$module";
|
||||
}
|
||||
else {
|
||||
$cmd = "cat @{[quotemeta($patch)]}";
|
||||
}
|
||||
|
||||
# Apply patch using Git
|
||||
my $output = `$cmd 2>&1 | git apply --reject --verbose --whitespace=fix 2>&1`;
|
||||
if ($output !~ /applied patch.*?cleanly/i) {
|
||||
print "Patch failed: $output\n";
|
||||
exit 1;
|
||||
}
|
||||
print "Patch applied successfully to:\n";
|
||||
print " $1\n" while $output =~ /^Applied patch\s+(\S+)/mg;
|
||||
system("$config_dir/restart");
|
||||
|
||||
=pod
|
||||
|
||||
=head1 NAME
|
||||
|
||||
patch
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Apply a patch to Webmin core or its modules from GitHub or a local file.
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
webmin patch patch-url/file
|
||||
|
||||
=head1 OPTIONS
|
||||
|
||||
=over
|
||||
|
||||
=item --help, -h
|
||||
|
||||
Give this help list.
|
||||
|
||||
=item --config, -c
|
||||
|
||||
Specify the full path to the Webmin configuration directory. Defaults to
|
||||
C</etc/webmin>
|
||||
|
||||
Examples of usage:
|
||||
|
||||
Apply a patch from a URL.
|
||||
|
||||
- webmin patch https://github.com/webmin/webmin/commit/e6a2bb15b0.patch
|
||||
|
||||
- webmin patch https://github.com/virtualmin/virtualmin-gpl/commit/f4433153d
|
||||
|
||||
Apply a patch from local file.
|
||||
|
||||
- cd /usr/libexec/webmin/virtual-server/pro &&
|
||||
webmin patch /root/virtualmin-pro/patches/patch-1.patch
|
||||
|
||||
=back
|
||||
|
||||
=head1 LICENSE AND COPYRIGHT
|
||||
|
||||
Copyright 2024 Ilia Ross <ilia@virtualmin.com>
|
||||
@@ -1988,6 +1988,8 @@ if (@transfer) {
|
||||
}
|
||||
}
|
||||
if (@notify) {
|
||||
my %done;
|
||||
@notify = grep { !$done{$_->{'name'}}++ } @notify;
|
||||
my $also = { 'name' => 'also-notify',
|
||||
'type' => 1,
|
||||
'members' => \@notify};
|
||||
@@ -1996,6 +1998,8 @@ if (@notify) {
|
||||
'values' => [ 'yes' ] });
|
||||
}
|
||||
if (@transfer) {
|
||||
my %done;
|
||||
@transfer = grep { !$done{$_->{'name'}}++ } @transfer;
|
||||
my $allow = { 'name' => 'allow-transfer',
|
||||
'type' => 1,
|
||||
'members' => \@transfer };
|
||||
|
||||
@@ -40,6 +40,7 @@ if (@keyrecs) {
|
||||
my $kt = $key->{'ksk'} ? 'ksk' : 'zone';
|
||||
my ($keyrec) = grep { $_->{'values'}->[0] ==
|
||||
($key->{'ksk'} ? 257 : 256) } @keyrecs;
|
||||
next if (!$keyrec);
|
||||
my $keyline = format_dnssec_public_key(
|
||||
join(" ", $keyrec->{'name'}, $keyrec->{'class'},
|
||||
$keyrec->{'type'}, @{$keyrec->{'values'}}));
|
||||
|
||||
@@ -21,7 +21,7 @@ if ($in{'source'} == 0) {
|
||||
if (!$in{'local'})
|
||||
{ &install_error($text{'download_elocal'}); }
|
||||
if (!-r $in{'local'})
|
||||
{ &install_error(&text('download_elocal2', $in{'local'})); }
|
||||
{ &install_error(&text('download_elocal2', &html_escape($in{'local'}))); }
|
||||
$source = $in{'local'};
|
||||
@pfile = ( $in{'local'} );
|
||||
$need_unlink = 0;
|
||||
@@ -91,8 +91,9 @@ elsif ($in{'source'} == 3) {
|
||||
$i = 0;
|
||||
@fallback = ( );
|
||||
foreach $yum (@cpanyum) {
|
||||
print &text('download_yum', "<tt>$cpan[$i]</tt>",
|
||||
"<tt>$yum->{'package'}</tt>"),"<br>\n";
|
||||
print &text('download_yum',
|
||||
"<tt>".&html_escape($cpan[$i])."</tt>",
|
||||
"<tt>".&html_escape($yum->{'package'})."</tt>"),"<br>\n";
|
||||
print "<ul>\n";
|
||||
@got = &software::update_system_install(
|
||||
$yum->{'package'});
|
||||
@@ -154,7 +155,8 @@ elsif ($in{'source'} == 3) {
|
||||
|
||||
# Fail if any modules are missing from CPAN
|
||||
for($i=0; $i<@cpan; $i++) {
|
||||
push(@missing, "<tt>$cpan[$i]</tt>") if (!$source[$i]);
|
||||
push(@missing, "<tt>".&html_escape($cpan[$i])."</tt>")
|
||||
if (!$source[$i]);
|
||||
}
|
||||
|
||||
if ($in{'missingok'}) {
|
||||
@@ -167,11 +169,12 @@ elsif ($in{'source'} == 3) {
|
||||
}
|
||||
}
|
||||
@cpan || &install_error(&text('download_ecpan',
|
||||
join(" ", @missing)));
|
||||
&html_escape(join(" ", @missing))));
|
||||
}
|
||||
elsif (@missing) {
|
||||
# Fail due to missing modules
|
||||
&install_error(&text('download_ecpan', join(" ", @missing)));
|
||||
&install_error(&text('download_ecpan',
|
||||
&html_escape(join(" ", @missing))));
|
||||
}
|
||||
$source = join("<br>", @source);
|
||||
|
||||
@@ -192,14 +195,16 @@ elsif ($in{'source'} == 3) {
|
||||
&ftp_download($host, $file, $pfile, \$error,
|
||||
\&progress_callback);
|
||||
}
|
||||
else { &install_error(&text('download_eurl', $m)); }
|
||||
else {
|
||||
&install_error(&text('download_eurl',&html_escape($m)));
|
||||
}
|
||||
&install_error($error) if ($error);
|
||||
push(@pfile, $pfile);
|
||||
}
|
||||
$need_unlink = 1;
|
||||
}
|
||||
else {
|
||||
&error("Unknown source mode $in{'source'}");
|
||||
&error("Unknown source mode ".&html_escape($in{'source'}));
|
||||
}
|
||||
|
||||
# Check if the file looks like a perl module
|
||||
@@ -287,7 +292,7 @@ foreach $d (@dirs) {
|
||||
close(MAKEFILE);
|
||||
push(@allreqs, @prereqs);
|
||||
}
|
||||
system("rm -rf $mtemp");
|
||||
&unlink_file($mtemp);
|
||||
|
||||
# Work out which pre-requesites are missing
|
||||
@allreqs = &unique(@allreqs);
|
||||
|
||||
@@ -350,7 +350,7 @@ if ($fh6) {
|
||||
while(1) {
|
||||
$$port++;
|
||||
if ($$port < 0 || $$port > 65535) {
|
||||
return "Failed to allocate a free port number: $port";
|
||||
return "Failed to allocate a free port number: $$port";
|
||||
}
|
||||
$pack = pack_sockaddr_in($$port, INADDR_ANY);
|
||||
next if (!bind($fh, $pack));
|
||||
|
||||
@@ -1,7 +1 @@
|
||||
body blockquote:not([style*="border-left"]) {
|
||||
border-left: 1px solid #ccc;
|
||||
margin-left: 6px;
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
padding-left: 12px;
|
||||
}
|
||||
body blockquote:not([style*="border-left"]){border-left:1px solid #ccc;margin-left:6px;margin-top:0;margin-bottom:0;padding-left:12px;}pre{white-space:break-spaces;margin:0 0 4px 0}
|
||||
@@ -29,9 +29,6 @@ table.ui_table thead td {
|
||||
table.sortable tbody td {
|
||||
padding: 2px;
|
||||
}
|
||||
table.ui_table td * {
|
||||
line-height:1.5em;
|
||||
}
|
||||
table.ui_table td textarea {line-height:normal; font-family:monospace;}
|
||||
table.ui_table td div.barchart * {
|
||||
margin: 0;
|
||||
@@ -55,9 +52,16 @@ div.ui_form_end_buttons {
|
||||
padding: 6px 3px;
|
||||
}
|
||||
.ui_form_end_buttons input {
|
||||
padding: 2px;
|
||||
padding: 1px;
|
||||
}
|
||||
table.ui_grid_table td { padding: 2px 4px; }
|
||||
.ui_form_value td > b > input+label+tt,
|
||||
.ui_form_value td > b > input+label {
|
||||
font-weight: normal !important;
|
||||
}
|
||||
table.ui_grid_table td {
|
||||
padding: 2px 4px;
|
||||
}
|
||||
select,
|
||||
input {
|
||||
padding: 1px;
|
||||
}
|
||||
|
||||
@@ -2371,6 +2371,8 @@ if (ref($opts)) {
|
||||
&print_tempfile(CFILE, "ExecReload=$kill -HUP \$MAINPID\n") if ($opts->{'reload'} eq '0');
|
||||
&print_tempfile(CFILE, "ExecStop=$opts->{'stop'}\n") if ($opts->{'stop'});
|
||||
&print_tempfile(CFILE, "ExecReload=$opts->{'reload'}\n") if ($opts->{'reload'});
|
||||
&print_tempfile(CFILE, "ExecStartPre=$opts->{'startpre'}\n") if ($opts->{'startpre'});
|
||||
&print_tempfile(CFILE, "ExecStartPost=$opts->{'startpost'}\n") if ($opts->{'startpost'});
|
||||
&print_tempfile(CFILE, "Type=$opts->{'type'}\n") if ($opts->{'type'});
|
||||
&print_tempfile(CFILE, "Environment=\"$opts->{'env'}\"\n") if ($opts->{'env'});
|
||||
&print_tempfile(CFILE, "User=$opts->{'user'}\n") if ($opts->{'user'});
|
||||
@@ -2381,8 +2383,8 @@ if (ref($opts)) {
|
||||
&print_tempfile(CFILE, "RestartSec=$opts->{'restartsec'}\n") if ($opts->{'restartsec'});
|
||||
&print_tempfile(CFILE, "TimeoutSec=$opts->{'timeout'}\n") if ($opts->{'timeout'});
|
||||
&print_tempfile(CFILE, "TimeoutStopSec=$opts->{'timeoutstopsec'}\n") if ($opts->{'timeoutstopsec'});
|
||||
&print_tempfile(CFILE, "StandardOutput=file:$opts->{'logstd'}\n") if ($opts->{'logstd'});
|
||||
&print_tempfile(CFILE, "StandardError=file:$opts->{'logerr'}\n") if ($opts->{'logerr'});
|
||||
&print_tempfile(CFILE, "StandardOutput=".($opts->{'logstd'} =~ /^\// ? 'file:' : '')."$opts->{'logstd'}\n") if ($opts->{'logstd'});
|
||||
&print_tempfile(CFILE, "StandardError=".($opts->{'logerr'} =~ /^\// ? 'file:' : '')."$opts->{'logerr'}\n") if ($opts->{'logerr'});
|
||||
}
|
||||
|
||||
&print_tempfile(CFILE, "\n");
|
||||
|
||||
@@ -16,7 +16,7 @@ fwd_mode=0
|
||||
delete_warn=y
|
||||
top_buttons=2
|
||||
view_html=2
|
||||
view_images=3
|
||||
view_images=1
|
||||
mail_usermin=mail
|
||||
sync_create=0
|
||||
sync_modify=1
|
||||
|
||||
@@ -2947,7 +2947,8 @@ my $iframe_body = <<EOF;
|
||||
theme_mail_iframe_onload(iframe);
|
||||
return;
|
||||
}
|
||||
const iframe_spinner = document.querySelector('#mail-iframe-spinner'),
|
||||
const iframeDoc = iframe.contentDocument || iframe.contentWindow.document,
|
||||
iframe_spinner = document.querySelector('#mail-iframe-spinner'),
|
||||
iframe_resize = function() {
|
||||
const iframeobj = document.querySelector('#mail-iframe'),
|
||||
iframe_height_bound = iframeobj.contentWindow.document.body.getBoundingClientRect().bottom,
|
||||
@@ -2955,8 +2956,11 @@ my $iframe_body = <<EOF;
|
||||
iframe_height =
|
||||
iframe_height_bound > iframe_scroll_height ?
|
||||
iframe_height_bound : iframe_scroll_height;
|
||||
iframeobj.style.height = Math.ceil(iframe_height) + "px";
|
||||
iframeobj.style.height = Math.ceil(iframe_height - 1) + "px";
|
||||
};
|
||||
iframeDoc.body.style.removeProperty('width');
|
||||
iframeDoc.body.style.margin = '4px';
|
||||
iframeDoc.body.style.padding = '0';
|
||||
iframe_spinner && iframe_spinner.remove();
|
||||
iframe.classList.add("loaded");
|
||||
setTimeout(iframe_resize);
|
||||
@@ -2980,6 +2984,15 @@ my $iframe_body = <<EOF;
|
||||
})();
|
||||
});
|
||||
}, 99);
|
||||
iframeDoc.addEventListener('click', function(event) {
|
||||
if (event.target.tagName.toLowerCase() === 'summary' &&
|
||||
event.target.dataset.resize === 'iframe') {
|
||||
setTimeout(iframe_resize);
|
||||
}
|
||||
});
|
||||
iframe.contentWindow.addEventListener('resize', function() {
|
||||
setTimeout(iframe_resize);
|
||||
});
|
||||
}
|
||||
</script>
|
||||
<iframe
|
||||
@@ -4399,10 +4412,10 @@ elsif ($line =~ /^END:VEVENT/) {
|
||||
# Try to add local 'when (period)'
|
||||
my $dtstart_local_obj =
|
||||
$event{'_obj_dtstart_local_time'} =
|
||||
make_date($event{'dtstart_local_timestamp'}, { _ });
|
||||
make_date($event{'dtstart_local_timestamp'}, { });
|
||||
my $dtend_local_obj =
|
||||
$event{'_obj_dtend_local_time'} =
|
||||
make_date($event{'dtend_local_timestamp'}, { _ });
|
||||
make_date($event{'dtend_local_timestamp'}, { });
|
||||
# Build when local, e.g.:
|
||||
# Tue Jun 04, 2024 04:30 PM – 05:15
|
||||
# PM (Asia/Nicosia +0300)
|
||||
@@ -4579,6 +4592,12 @@ my ($calendars) = @_;
|
||||
my @calendars = @{$calendars};
|
||||
$calendars = { };
|
||||
if (@calendars) {
|
||||
# Fonts for our HTML
|
||||
$calendars->{'html'} .= &theme_fonts()
|
||||
if (defined(&theme_fonts));
|
||||
my $theme_css_inline;
|
||||
$theme_css_inline = &theme_css_inline('calendar')
|
||||
if (defined(&theme_css_inline));
|
||||
# CSS for HTML version
|
||||
$calendars->{'html'} .= <<STYLE;
|
||||
<style>
|
||||
@@ -4588,6 +4607,7 @@ if (@calendars) {
|
||||
border-collapse: collapse;
|
||||
border: 1px solid #99999933;
|
||||
margin-bottom: 4px;
|
||||
font-family: 'RobotoLocal',arial,helvetica,clean,sans-serif !important;
|
||||
}
|
||||
.calendar-table-inner {
|
||||
table-layout: fixed;
|
||||
@@ -4610,44 +4630,50 @@ if (@calendars) {
|
||||
font-weight: bold;
|
||||
}
|
||||
.calendar-month {
|
||||
font-size: 21px;
|
||||
font-size: 19px;
|
||||
color: #1d72ff;
|
||||
text-align: center;
|
||||
padding: 2px 8px;
|
||||
}
|
||||
.calendar-day {
|
||||
font-size: 24px;
|
||||
font-size: 19px;
|
||||
text-align: center;
|
||||
padding: 4px 8px;
|
||||
}
|
||||
.calendar-week {
|
||||
font-size: 16px;
|
||||
font-size: 13px;
|
||||
border-top: 1px dotted #999999aa;
|
||||
padding: 6px;
|
||||
display: inline-block;
|
||||
}
|
||||
.calendar-details h2 {
|
||||
margin: 0;
|
||||
font-size: 18px;
|
||||
font-size: 15px;
|
||||
}
|
||||
.calendar-details p {
|
||||
margin: 4px 0;
|
||||
}
|
||||
.calendar-details .title {
|
||||
font-size: 20px;
|
||||
font-size: 16px;
|
||||
}
|
||||
.calendar-details .detail strong {
|
||||
opacity: 0.66;
|
||||
opacity: 0.83;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.calendar-details .detail + .desc p:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
details.calendar-details {
|
||||
font-size: 90%;
|
||||
font-size: 87%;
|
||||
display: inline-block;
|
||||
margin-left: 9px;
|
||||
}
|
||||
.calendar-details > .calendar-table-inner .detail:has(strong),
|
||||
.calendar-details > .calendar-table-inner .detail strong,
|
||||
.calendar-details > .calendar-table-inner .detail + td {
|
||||
font-size: 13px;
|
||||
line-height: 1.2;
|
||||
}
|
||||
details.calendar-details summary {
|
||||
cursor: help;
|
||||
}
|
||||
@@ -4655,6 +4681,7 @@ if (@calendars) {
|
||||
.calendar-details tr:has(>.detail+td:empty) {
|
||||
display: none;
|
||||
}
|
||||
$theme_css_inline
|
||||
</style>
|
||||
STYLE
|
||||
foreach my $calendar (@calendars) {
|
||||
|
||||
@@ -332,6 +332,7 @@ folder_inbox=Inbox
|
||||
folder_sent=Sent
|
||||
folder_drafts=Drafts
|
||||
folder_trash=Trash
|
||||
folder_spam=Spam
|
||||
|
||||
detach_err=Failed to detach file
|
||||
detach_edir=No file or directory to save to entered
|
||||
|
||||
@@ -330,7 +330,7 @@ Corvus Latinux 8.0 redhat-linux 7.1 `cat /etc/latinux-release 2>/dev/null` =~
|
||||
Immunix Linux $1 redhat-linux $1 $etc_issue =~ /Immunix.*\s([0-9\.]+)/i || `cat /etc/immunix-release 2>/dev/null` =~ /([0-9\.]+)/
|
||||
|
||||
# All versions of Gentoo (which don't appear to have version numbers)
|
||||
Gentoo Linux Any version gentoo-linux * -d "/usr/portage"
|
||||
Gentoo Linux Any version gentoo-linux * -d "/usr/portage" || `cat /etc/os-release 2>/dev/null` =~ /gentoo/
|
||||
|
||||
# Secure Linux (now called Trustix?)
|
||||
Secure Linux 1.0 redhat-linux 7.2 `cat /etc/securelinux-release 2>/dev/null` =~ /SecureLinux.*1\.0/i
|
||||
|
||||
@@ -56,7 +56,7 @@ if ($in{'action'} eq $text{'action_sync'}) {
|
||||
if (defined($in{'sync_service_name'}) &&
|
||||
defined($in{'sync_service_status'})) {
|
||||
my $service_name = $in{'sync_service_name'};
|
||||
if ($service_name !~ /^(chronyd|systemd-timesyncd)$/) {
|
||||
if ($service_name !~ /^(chronyd|chrony|systemd-timesyncd)$/) {
|
||||
&error(&text('error_serviceunknown', &html_escape($service_name)));
|
||||
}
|
||||
my $service_status = int($in{'sync_service_status'});
|
||||
|
||||
@@ -5,7 +5,8 @@ use warnings;
|
||||
no warnings 'redefine';
|
||||
no warnings 'uninitialized';
|
||||
require './time-lib.pl';
|
||||
our (%in, %text, %config, %access, $base_remote_user, $get_hardware_time_error);
|
||||
our (%in, %text, %config, %access, $base_remote_user,
|
||||
$get_hardware_time_error, $cronyd_name);
|
||||
|
||||
my ($rawdate, $rawhwdate, %system_date, $rawtime, %hw_date, $txt);
|
||||
$txt = "";
|
||||
@@ -174,13 +175,14 @@ if ( ( !$access{ 'sysdate' } && &has_command( "date" ) || !$access{ 'hwdate' } &
|
||||
$config{'timeserver_hardware'}));
|
||||
}
|
||||
}
|
||||
if (&foreign_require('init') && &init::action_status('chronyd') > 0 && &has_command("chronyc")) {
|
||||
my $chronyd_running = &init::status_action('chronyd');
|
||||
my $chronyd_run_atboot = &init::action_status('chronyd');
|
||||
my $ui_hiddens = &ui_hidden("sync_service_name", "chronyd");
|
||||
if (&foreign_require('init') &&
|
||||
&init::action_status($cronyd_name) > 0 && &has_command("chronyc")) {
|
||||
my $chronyd_running = &init::status_action($cronyd_name);
|
||||
my $chronyd_run_atboot = &init::action_status($cronyd_name);
|
||||
my $ui_hiddens = &ui_hidden("sync_service_name", $cronyd_name);
|
||||
$ui_hiddens .= &ui_hidden("timeserver", $config{'timeserver'})
|
||||
if (!$ntp_support);
|
||||
print &ui_table_row(&text('index_tabsync2', 'chronyd'),
|
||||
print &ui_table_row(&text('index_tabsync2', $cronyd_name),
|
||||
$ui_hiddens.
|
||||
&ui_radio("sync_service_status",
|
||||
(($chronyd_run_atboot == 2 && $chronyd_running) ? 2 :
|
||||
|
||||
@@ -13,6 +13,7 @@ our ($timezones_file, $currentzone_link, $currentzone_file, $timezones_dir,
|
||||
$sysclock_file);
|
||||
our ($get_hardware_time_error);
|
||||
our $cron_cmd = "$module_config_directory/sync.pl";
|
||||
our $cronyd_name = $gconfig{'os_type'} eq 'debian-linux' ? 'chrony' : 'chronyd';
|
||||
our $rawtime;
|
||||
if ($config{'zone_style'}) {
|
||||
do "$config{'zone_style'}-lib.pl";
|
||||
@@ -49,13 +50,13 @@ if (&has_command("ntpdate")) {
|
||||
elsif (&has_command("sntp")) {
|
||||
$out = &backquote_logged("sntp -s $servs 2>&1");
|
||||
}
|
||||
elsif (&foreign_require('init') && &init::action_status('chronyd') > 0 && &has_command("chronyc")) {
|
||||
my $chronyd_running = &init::status_action('chronyd');
|
||||
$out = &backquote_logged("systemctl restart chronyd 2>&1");
|
||||
elsif (&foreign_require('init') && &init::action_status($cronyd_name) > 0 && &has_command("chronyc")) {
|
||||
my $chronyd_running = &init::status_action($cronyd_name);
|
||||
$out = &backquote_logged("systemctl restart $cronyd_name 2>&1");
|
||||
$out .= &backquote_logged("chronyc makestep 2>&1");
|
||||
sleep ($chronyd_running ? 5 : 15);
|
||||
if (!$chronyd_running) {
|
||||
&backquote_logged("systemctl stop chronyd 2>&1");
|
||||
&backquote_logged("systemctl stop $cronyd_name 2>&1");
|
||||
}
|
||||
}
|
||||
elsif (&foreign_require('init') && &init::action_status('systemd-timesyncd') > 0) {
|
||||
|
||||
@@ -21,7 +21,8 @@ return 0;
|
||||
sub open_last_command
|
||||
{
|
||||
local ($fh, $user) = @_;
|
||||
open($fh, "last $user |");
|
||||
local $quser = quotemeta($user);
|
||||
open($fh, "(last -w $quser || last $quser) |");
|
||||
}
|
||||
|
||||
# read_last_line(handle)
|
||||
@@ -34,7 +35,9 @@ while(1) {
|
||||
chop($line = <$fh>);
|
||||
if (!$line) { return (); }
|
||||
if ($line =~ /^(reboot|shutdown)/) { next; }
|
||||
if ($line =~ /^(\S+)\s+(\S+)\s+(\S+)?\s+(\S+\s+\S+\s+\d+\s+\d+:\d+)\s+\-\s+(\S+)\s+\((\d+:\d+)\)/) {
|
||||
if ($line =~ /^(\S+)\s+(\S+)\s+(\S+)?\s+(\S+\s+\S+\s+\d+\s+\d+:\d+)\s+\-\s+(\S+)\s+\((.*?\d+:\d+.*?)\)/) {
|
||||
# root pts/0 10.211.55.2 Tue Nov 22 21:06 - 23:16 (02:10:00)
|
||||
# root pts/1 10.211.55.2 Wed Jun 29 13:13 - shutdown (7+00:01:20)
|
||||
return ($1, $2, $3, $4, $5 eq "shutdown" ? "Shutdown" :
|
||||
$5 eq "crash" ? "Crash" : $5, $6);
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ sub open_last_command
|
||||
{
|
||||
local ($fh, $user) = @_;
|
||||
local $quser = quotemeta($user);
|
||||
open($fh, "(last -F $quser || last $quser) |");
|
||||
open($fh, "(last -F -w $quser || last -w $quser) |");
|
||||
}
|
||||
|
||||
# read_last_line(handle)
|
||||
@@ -39,12 +39,16 @@ while(1) {
|
||||
# jcameron pts/0 fudu Thu Feb 22 09:47 - 10:15
|
||||
return ($1, $2, $3, $4, $5 eq "down" ? "Shutdown" : $5, $6);
|
||||
}
|
||||
elsif ($line =~ /^(\S+)\s+(\S+)\s+(\S+)?\s+(\S+\s+\S+\s+\d+\s+\d+:\d+:\d+\s+\d+)\s+\-\s+(\S+\s+\S+\s+\d+\s+\d+:\d+:\d+\s+\d+)\s+\((\d+:\d+)\)/) {
|
||||
elsif ($line =~ /^(\S+)\s+(\S+)\s+(\S+)?\s+(\S+\s+\S+\s+\d+\s+\d+:\d+:\d+\s+\d+)\s+\-\s+(\S+\s+\S+\s+\d+\s+\d+:\d+:\d+\s+\d+)\s+\((.*?\d+:\d+)\)/) {
|
||||
# jcameron pts/0 fudu Thu Feb 22 09:47 - 10:15
|
||||
# jcameron pts/0 fudu Sun Feb 4 02:26:28 2024 - Wed Feb 7 18:25:09 2024 (3+15:58)
|
||||
return ($1, $2, $3, $4, $5 eq "down" ? "Shutdown" : $5, $6);
|
||||
}
|
||||
elsif ($line =~ /^(\S+)\s+(\S+)\s+(\S+)?\s+(\S+\s+\S+\s+\d+\s+\d+:\d+)\s+still/) {
|
||||
# root pts/0 fudu Fri Feb 23 18:46 still logged in
|
||||
elsif ($line =~ /^(\S+)\s+(\S+)\s+(\S+)?\s+(\S+\s+\S+\s+\d+\s+\d+:\d+)\s+(still|gone)/ ||
|
||||
$line =~ /^(\S+)\s+(\S+)\s+(\S+)?\s+(\S+\s+\S+\s+\d+\s+\d+:\d+:\d+\s+\d+)\s+(still|gone)/) {
|
||||
# root pts/0 fudu Fri Feb 23 18:46 still logged in
|
||||
# root ftpd10 fudu Thu Jun 20 11:19 gone - no logout
|
||||
# root pts/0 fudu Tue Jun 18 23:10:30 2024 still logged in
|
||||
return ($1, $2, $3, $4);
|
||||
}
|
||||
}
|
||||
|
||||
199
web-lib-funcs.pl
199
web-lib-funcs.pl
@@ -2098,10 +2098,10 @@ formatted like dd/mmm/yyyy hh:mm:ss. Parameters are :
|
||||
|
||||
=item seconds - Unix time is seconds to convert.
|
||||
|
||||
=item date-only-or-opts - If set to 1, exclude the time from the returned string.
|
||||
=item date-only-or-opts - If set to 1 exclude the time from the returned string.
|
||||
|
||||
In case this param is a hash reference use it for options in a new DateTime::Locale
|
||||
code or preserve the original, old logic
|
||||
In case this param is a hash reference use it for options in a new
|
||||
DateTime::Locale code or preserve the original, old logic
|
||||
|
||||
=item fmt - Optional, one of dd/mon/yyyy, dd/mm/yyyy, mm/dd/yyyy or yyyy/mm/dd
|
||||
|
||||
@@ -2116,27 +2116,31 @@ if (!$@ && $] > 5.011) {
|
||||
my $opts = ref($only) ? $only : {};
|
||||
my $locale_default = &get_default_system_locale();
|
||||
my $locale_auto = &parse_accepted_language();
|
||||
my $locale_name = $opts->{'locale'} || $gconfig{'locale_'.$base_remote_user} ||
|
||||
my $locale_name = $opts->{'locale'} ||
|
||||
$gconfig{'locale_'.$base_remote_user} ||
|
||||
$gconfig{'locale_'.$remote_user} || $locale_auto ||
|
||||
$gconfig{'locale'} || &get_default_system_locale();
|
||||
my $tz = $opts->{'tz'};
|
||||
if (!$tz) {
|
||||
eval {
|
||||
$tz =
|
||||
DateTime::TimeZone->new(name => strftime("%z", localtime()))->name(); # +0200
|
||||
$tz = DateTime::TimeZone->new(
|
||||
name => strftime("%z", localtime()))->name(); # +0200
|
||||
};
|
||||
if ($@) {
|
||||
eval {
|
||||
$tz = DateTime::TimeZone->new(name => 'local')->name(); # Asia/Nicosia
|
||||
$tz = DateTime::TimeZone->new(
|
||||
name => 'local')->name(); # Asia/Nicosia
|
||||
};
|
||||
if ($@) {
|
||||
$tz = DateTime::TimeZone->new(name => 'UTC')->name(); # UTC
|
||||
$tz = DateTime::TimeZone->new(
|
||||
name => 'UTC')->name(); # UTC
|
||||
}
|
||||
}
|
||||
}
|
||||
# Pre-process time locale
|
||||
my $locale_military_status = sub {
|
||||
return ($locale_military_name && $locale_military_name =~ /[a-z]/i) ? 2 :
|
||||
return ($locale_military_name &&
|
||||
$locale_military_name =~ /[a-z]/i) ? 2 :
|
||||
($locale_military_name == 1) ? 1 : 0;
|
||||
};
|
||||
# Allow locales with military time (in 24h format)
|
||||
@@ -2152,15 +2156,18 @@ if (!$@ && $] > 5.011) {
|
||||
my $locale = DateTime::Locale->load($locale_name_loaded);
|
||||
# Create a new locale out of base locale
|
||||
if (&$locale_military_status() == 1) {
|
||||
my %locale_data = $locale->locale_data;
|
||||
$locale_data{'code'} = $locale_name_initial;
|
||||
my %locale_data = $locale->locale_data;
|
||||
$locale_data{'code'} = $locale_name_initial;
|
||||
|
||||
# Force 24h time
|
||||
$locale_data{'glibc_date_1_format'} = '%a %b %e %H:%M:%S %Z %Y';
|
||||
$locale_data{'glibc_date_1_format'} = '%a %b %e %H:%M:%S %Z %Y';
|
||||
$locale_data{'glibc_datetime_format'} = '%a %d %b %Y %T %Z';
|
||||
$locale_data{'glibc_time_format'} = '%T';
|
||||
$locale_data{'glibc_time_format'} = '%T';
|
||||
DateTime::Locale->register_from_data(%locale_data);
|
||||
|
||||
# Load newly cloned locale in 24h time format
|
||||
$locale_military_name = $locale_name_loaded = $locale_name_initial;
|
||||
$locale_military_name = $locale_name_loaded =
|
||||
$locale_name_initial;
|
||||
$locale = DateTime::Locale->load($locale_name_loaded);
|
||||
}
|
||||
my $locale_format_full_tz = $locale->glibc_date_1_format; # Sat 20 Nov 2286 17:46:39 UTC
|
||||
@@ -2176,7 +2183,7 @@ if (!$@ && $] > 5.011) {
|
||||
}
|
||||
|
||||
# Return fully detailed object
|
||||
if (%{$opts}) {
|
||||
if (ref($only)) {
|
||||
# Can we get ago time
|
||||
my $ago;
|
||||
my $ago_secs = time() - $secs;
|
||||
@@ -2194,18 +2201,31 @@ if (!$@ && $] > 5.011) {
|
||||
"pretty" => $ago_obj->pretty
|
||||
};
|
||||
}
|
||||
# my $xxxx = $locale->full_date_format;
|
||||
my $data = {
|
||||
# Wed Feb 8 05:09:39 PM UTC 2023
|
||||
'full-tz-utc' => DateTime->from_epoch(locale => $locale_name_loaded, epoch => $secs)->strftime($locale_format_full_tz),
|
||||
'full-tz-utc' => DateTime->from_epoch(
|
||||
locale => $locale_name_loaded,
|
||||
epoch => $secs)->strftime($locale_format_full_tz),
|
||||
# Wed Feb 8 07:10:01 PM EET 2023
|
||||
'full-tz' => DateTime->from_epoch(locale => $locale_name_loaded, epoch => $secs, time_zone => $tz)->strftime($locale_format_full_tz),
|
||||
'full-tz' => DateTime->from_epoch(
|
||||
locale => $locale_name_loaded,
|
||||
epoch => $secs,
|
||||
time_zone => $tz)->strftime($locale_format_full_tz),
|
||||
# Wed 08 Feb 2023 07:11:26 PM EET
|
||||
'full' => DateTime->from_epoch(locale => $locale_name_loaded, epoch => $secs, time_zone => $tz)->strftime($locale_format_full),
|
||||
'full' => DateTime->from_epoch(
|
||||
locale => $locale_name_loaded,
|
||||
epoch => $secs,
|
||||
time_zone => $tz)->strftime($locale_format_full),
|
||||
# 02/08/2023
|
||||
'short' => DateTime->from_epoch(locale => $locale_name_loaded, epoch => $secs, time_zone => $tz)->strftime($locale_format_short),
|
||||
'short' => DateTime->from_epoch(
|
||||
locale => $locale_name_loaded,
|
||||
epoch => $secs,
|
||||
time_zone => $tz)->strftime($locale_format_short),
|
||||
# 07:12:07 PM
|
||||
'time' => DateTime->from_epoch(locale => $locale_name_loaded, epoch => $secs, time_zone => $tz)->strftime($locale_format_time),
|
||||
'time' => DateTime->from_epoch(
|
||||
locale => $locale_name_loaded,
|
||||
epoch => $secs,
|
||||
time_zone => $tz)->strftime($locale_format_time),
|
||||
'ago' => $ago,
|
||||
'tz' => $tz,
|
||||
'delimiter' => $locale_format_delimiter,
|
||||
@@ -2216,10 +2236,16 @@ if (!$@ && $] > 5.011) {
|
||||
$data->{'timeshort'} = $data->{'time'};
|
||||
$data->{'timeshort'} =~ s/(\d+):(\d+):(\d+)(.*?)/$1:$2$4/;
|
||||
|
||||
# %c alternative with full week and month and no seconds in time (complete)
|
||||
# Wednesday, February 8, 2023, 8:18 PM or 星期三, 2023年2月8日 20:18 or miércoles, 8 febrero 2023, 20:28
|
||||
$data->{'monthfull'} = DateTime->from_epoch(locale => $locale_name_loaded, epoch => $secs, time_zone => $tz)->strftime("%B");
|
||||
foreach (split(/\s+/, DateTime->from_epoch(locale => $locale_name_loaded, epoch => $secs, time_zone => $tz)->strftime("%A, %c"))) {
|
||||
# %c alternative with full week and month and no seconds
|
||||
# in time (complete)
|
||||
# Wednesday, February 8, 2023, 8:18 PM or 星期三,
|
||||
# 2023年2月8日 20:18 or miércoles, 8 febrero 2023, 20:28
|
||||
$data->{'monthfull'} = DateTime->from_epoch(
|
||||
locale => $locale_name_loaded, epoch => $secs,
|
||||
time_zone => $tz)->strftime("%B");
|
||||
foreach (split(/\s+/, DateTime->from_epoch(
|
||||
locale => $locale_name_loaded, epoch => $secs,
|
||||
time_zone => $tz)->strftime("%A, %c"))) {
|
||||
if ($data->{'monthfull'} =~ /^$_/) {
|
||||
$data->{'complete'} .= "$data->{'monthfull'} "
|
||||
}
|
||||
@@ -2227,9 +2253,24 @@ if (!$@ && $] > 5.011) {
|
||||
$data->{'complete'} .= "$_ "
|
||||
}
|
||||
};
|
||||
$data->{'year'} = DateTime->from_epoch(locale => $locale_name_loaded, epoch => $secs, time_zone => $tz)->strftime("%Y");
|
||||
$data->{'year'} = DateTime->from_epoch(
|
||||
locale => $locale_name_loaded, epoch => $secs,
|
||||
time_zone => $tz)->strftime("%Y");
|
||||
$data->{'day'} = DateTime->from_epoch(
|
||||
locale => $locale_name_loaded, epoch => $secs,
|
||||
time_zone => $tz)->strftime("%d");
|
||||
$data->{'week'} = DateTime->from_epoch(
|
||||
locale => $locale_name_loaded, epoch => $secs,
|
||||
time_zone => $tz)->strftime("%a");
|
||||
$data->{'weekfull'} = DateTime->from_epoch(
|
||||
locale => $locale_name_loaded, epoch => $secs,
|
||||
time_zone => $tz)->strftime("%A");
|
||||
$data->{'month'} = DateTime->from_epoch(
|
||||
locale => $locale_name_loaded, epoch => $secs,
|
||||
time_zone => $tz)->strftime("%b");
|
||||
$data->{'complete'} =~ s/(\d+):(\d+):(\d+)(.*?)/$1:$2$4/;
|
||||
($data->{'complete_short'} = $data->{'complete'}) =~ s/(.*?)([\s\,]*\Q$data->{'year'}\E.*)/$1/;
|
||||
($data->{'complete_short'} = $data->{'complete'}) =~
|
||||
s/(.*?)([\s\,]*\Q$data->{'year'}\E.*)/$1/;
|
||||
|
||||
if ($opts->{'get'}) {
|
||||
return $data->{$opts->{'get'}};
|
||||
@@ -7969,6 +8010,100 @@ return ref($s) && $s->{'host'} && $s->{'port'} ?
|
||||
ref($s) ? "" : "$s.$$";
|
||||
}
|
||||
|
||||
=head2 verify_session_id(session-id, [sessiondb], [miniserv])
|
||||
|
||||
Returns the username (or an array with user, last login, and IP address) for the
|
||||
given session (or session hash) ID, or undefined if session ID is invalid.
|
||||
|
||||
Args:
|
||||
|
||||
$sid: The session ID to verify
|
||||
|
||||
$sessiondb: A reference to the session database hash (optional)
|
||||
|
||||
$miniserv: A reference to the miniserv configuration hash (optional)
|
||||
|
||||
Returns:
|
||||
|
||||
In list context: A list containing the user, last activity time, and IP
|
||||
address associated with the session ID.
|
||||
|
||||
In scalar context: The user associated with the session ID.
|
||||
|
||||
If the session ID is not found, returns an empty list in list context
|
||||
or undef in scalar context.
|
||||
|
||||
Usage:
|
||||
|
||||
Retrieve the username associated with the session ID or undef if
|
||||
session is invalid.
|
||||
|
||||
my $user = verify_session_id($main::session_id);
|
||||
Example of return:
|
||||
root
|
||||
|
||||
Retrieve array containing the user, last login, and IP address
|
||||
for given session hash ID or undef if session is invalid.
|
||||
|
||||
my (@user) = verify_session_id('BSRxr6wpF25lqeRinQ/sv0');
|
||||
Example of return:
|
||||
[
|
||||
'root',
|
||||
'1719401071',
|
||||
'10.211.55.2'
|
||||
]
|
||||
|
||||
Retrieve the username associated with the session ID, using given
|
||||
session DB or undef if session is invalid.
|
||||
|
||||
my %sessiondb;
|
||||
dbmopen(%sessiondb, "$var_directory/sessiondb", 0400);
|
||||
my $user = verify_session_id($main::session_id, \%sessiondb);
|
||||
dbmclose(%sessiondb);
|
||||
Example of return:
|
||||
someuser1
|
||||
|
||||
=cut
|
||||
sub verify_session_id
|
||||
{
|
||||
my ($sid, $sessiondb, $miniserv) = @_;
|
||||
my $hashsessionidfunc = \&miniserv::hash_session_id;
|
||||
my %miniserv;
|
||||
if ($miniserv) {
|
||||
# Use provided miniserv configuration
|
||||
%miniserv = %{$miniserv};
|
||||
}
|
||||
else {
|
||||
# Load miniserv configuration if not provided
|
||||
&get_miniserv_config(\%miniserv);
|
||||
}
|
||||
my %sessiondb_;
|
||||
if ($sessiondb) {
|
||||
# Use provided session database
|
||||
%sessiondb_ = %{$sessiondb};
|
||||
}
|
||||
elsif (&foreign_available('acl')) {
|
||||
# Use session database using ACL module API
|
||||
&foreign_require("acl");
|
||||
&acl::open_session_db(\%miniserv);
|
||||
$hashsessionidfunc = \&acl::hash_session_id;
|
||||
%sessiondb_ = %acl::sessiondb;
|
||||
}
|
||||
else {
|
||||
return wantarray ? ( ) : undef;
|
||||
}
|
||||
# Verify given session (hash) ID against the session database
|
||||
foreach my $k (grep { $sessiondb_{$_} } keys %sessiondb_) {
|
||||
if ($k eq $sid ||
|
||||
(defined($hashsessionidfunc) && $k eq $hashsessionidfunc->($sid))) {
|
||||
my ($user, $last, $ip) = split(/\s+/, $sessiondb_{$k});
|
||||
return wantarray ? ($user, $last, $ip) : $user;
|
||||
}
|
||||
}
|
||||
# Return an empty list or undef if session ID is not found
|
||||
return wantarray ? ( ) : undef;
|
||||
}
|
||||
|
||||
=head2 remote_foreign_require(server, module, file)
|
||||
|
||||
Connects to rpc.cgi on a remote webmin server and have it open a session
|
||||
@@ -13569,12 +13704,22 @@ my $ws_proto = lc($ENV{'HTTPS'}) eq 'on' ? 'wss' : 'ws';
|
||||
my %miniserv;
|
||||
&get_miniserv_config(\%miniserv);
|
||||
my $http_host_conf = &trim($miniserv{'websocket_host'} || $host);
|
||||
# Pass as defined
|
||||
if ($http_host_conf) {
|
||||
if ($http_host_conf !~ /^wss?:\/\//) {
|
||||
$http_host_conf = "$ws_proto://$http_host_conf";
|
||||
}
|
||||
$http_host_conf =~ s/[\/]+$//g;
|
||||
}
|
||||
# Try to rely on the proxy
|
||||
if (!defined($http_host_conf)) {
|
||||
my $forwarded_host = $ENV{'HTTP_X_FORWARDED_HOST'};
|
||||
if ($forwarded_host) {
|
||||
$http_host_conf = "$ws_proto://$forwarded_host".
|
||||
&get_webprefix();
|
||||
$http_host_conf =~ s/\/$//;
|
||||
}
|
||||
}
|
||||
my $http_host = $http_host_conf || "$ws_proto://$ENV{'HTTP_HOST'}";
|
||||
return "$http_host/$module/ws-$port";
|
||||
}
|
||||
|
||||
@@ -277,7 +277,9 @@ else {
|
||||
my @doms = $config{'letsencrypt_doms'} ?
|
||||
split(/\s+/, $config{'letsencrypt_doms'}) : ( $host );
|
||||
print &ui_table_row($text{'ssl_letsdoms'},
|
||||
&ui_textarea("dom", join("\n", @doms), 5, 40));
|
||||
&ui_textarea("dom", join("\n", @doms), 5, 40)."<br>\n".
|
||||
&ui_checkbox("subset", 1, $text{'ssl_subset'},
|
||||
$config{'letsencrypt_subset'}));
|
||||
|
||||
# Apache vhost or other path
|
||||
my @opts;
|
||||
|
||||
@@ -424,6 +424,7 @@ ssl_letserr2=Alternately, check the <a href='$1'>module configuration</a> page t
|
||||
ssl_letsdesc2=This page can be used to request a new certificate, which will overwrite any other currently have configured in Webmin. However, the Let's Encrypt service requires that your ownership of the certificate domain be validated by checking that this system hosts the website for the domain. This is done by placing a small temporary file in the website's document directory.
|
||||
ssl_letsheader=Options for new SSL certificate
|
||||
ssl_letsdoms=Hostnames for certificate
|
||||
ssl_subset=Skip unverifiable hostnames?
|
||||
ssl_letsmode=Let's Encrypt validation method
|
||||
ssl_letsmode0=Apache virtual host matching hostname
|
||||
ssl_letsmode1=Selected Apache virtual host
|
||||
|
||||
@@ -58,14 +58,15 @@ return &software::missing_install_link(
|
||||
|
||||
# request_letsencrypt_cert(domain|&domains, webroot, [email], [keysize],
|
||||
# [request-mode], [use-staging], [account-email],
|
||||
# [reuse-key], [server-url, server-key, server-hmac])
|
||||
# [reuse-key], [server-url, server-key, server-hmac],
|
||||
# [allow-subset])
|
||||
# Attempt to request a cert using a generated key with the Let's Encrypt client
|
||||
# command, and write it to the given path. Returns a status flag, and either
|
||||
# an error message or the paths to cert, key and chain files.
|
||||
sub request_letsencrypt_cert
|
||||
{
|
||||
my ($dom, $webroot, $email, $size, $mode, $staging, $account_email,
|
||||
$key_type, $reuse_key, $server, $server_key, $server_hmac) = @_;
|
||||
$key_type, $reuse_key, $server, $server_key, $server_hmac, $subset) = @_;
|
||||
my @doms = ref($dom) ? @$dom : ($dom);
|
||||
$email ||= "root\@$doms[0]";
|
||||
$mode ||= "web";
|
||||
@@ -179,6 +180,7 @@ if ($letsencrypt_cmd) {
|
||||
my $new_flags = "";
|
||||
my $reuse_flags = "";
|
||||
my $server_flags = "";
|
||||
my $subset_flags = "";
|
||||
$key_type ||= $config{'letsencrypt_algo'} || 'rsa';
|
||||
if (&compare_version_numbers($cmd_ver, 1.11) < 0) {
|
||||
$old_flags = " --manual-public-ip-logging-ok";
|
||||
@@ -192,6 +194,9 @@ if ($letsencrypt_cmd) {
|
||||
else {
|
||||
$reuse_flags = " --no-reuse-key";
|
||||
}
|
||||
if ($subset) {
|
||||
$subset_flags = " --allow-subset-of-names";
|
||||
}
|
||||
$reuse_flags = "" if ($reuse_key && $reuse_key == -1);
|
||||
if ($server) {
|
||||
$server_flags = " --server ".quotemeta($server);
|
||||
@@ -227,6 +232,7 @@ if ($letsencrypt_cmd) {
|
||||
$old_flags.
|
||||
$server_flags.
|
||||
$new_flags.
|
||||
$subset_flags.
|
||||
" 2>&1)");
|
||||
&reset_environment();
|
||||
}
|
||||
@@ -245,6 +251,7 @@ if ($letsencrypt_cmd) {
|
||||
$old_flags.
|
||||
$server_flags.
|
||||
$new_flags.
|
||||
$subset_flags.
|
||||
" 2>&1)");
|
||||
&reset_environment();
|
||||
}
|
||||
@@ -260,6 +267,7 @@ if ($letsencrypt_cmd) {
|
||||
$old_flags.
|
||||
$server_flags.
|
||||
$new_flags.
|
||||
$subset_flags.
|
||||
" 2>&1)");
|
||||
&reset_environment();
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ else {
|
||||
|
||||
if ($in{'save'}) {
|
||||
# Just update renewal
|
||||
&save_renewal_only(\@doms, $webroot, $mode);
|
||||
&save_renewal_only(\@doms, $webroot, $mode, $size, $in{'subset'});
|
||||
&redirect("edit_ssl.cgi");
|
||||
}
|
||||
else {
|
||||
@@ -88,7 +88,9 @@ else {
|
||||
'letsencrypt_doing',
|
||||
"<tt>".&html_escape(join(", ", @doms))."</tt>",
|
||||
"<tt>".&html_escape($webroot)."</tt>"),"<p>\n";
|
||||
my ($ok, $cert, $key, $chain) = &request_letsencrypt_cert(\@doms, $webroot, undef, $size, $mode, $in{'staging'});
|
||||
my ($ok, $cert, $key, $chain) = &request_letsencrypt_cert(
|
||||
\@doms, $webroot, undef, $size, $mode, $in{'staging'},
|
||||
undef, 0, undef, undef, undef, $in{'subset'});
|
||||
if (!$ok) {
|
||||
print &text('letsencrypt_failed', $cert),"<p>\n";
|
||||
}
|
||||
@@ -148,15 +150,16 @@ else {
|
||||
&ui_print_footer("", $text{'index_return'});
|
||||
}
|
||||
|
||||
# save_renewal_only(&doms, webroot, mode)
|
||||
# save_renewal_only(&doms, webroot, mode, size, subset-mode)
|
||||
# Save for future renewals
|
||||
sub save_renewal_only
|
||||
{
|
||||
my ($doms, $webroot, $mode) = @_;
|
||||
my ($doms, $webroot, $mode, $size, $subset) = @_;
|
||||
$config{'letsencrypt_doms'} = join(" ", @$doms);
|
||||
$config{'letsencrypt_webroot'} = $webroot;
|
||||
$config{'letsencrypt_mode'} = $mode;
|
||||
$config{'letsencrypt_size'} = $size;
|
||||
$config{'letsencrypt_subset'} = $subset;
|
||||
&save_module_config();
|
||||
if (&foreign_check("webmincron")) {
|
||||
my $job = &find_letsencrypt_cron_job();
|
||||
|
||||
@@ -128,7 +128,7 @@ if (!$eol_data->{'_eol_timestamp'}) {
|
||||
&error_stderr("The provided data is not a valid EOL data hash reference");
|
||||
return undef;
|
||||
}
|
||||
my $eol_date = &make_date($eol_data->{'_eol_timestamp'}, { '_' => 1 });
|
||||
my $eol_date = &make_date($eol_data->{'_eol_timestamp'}, { });
|
||||
if (ref($eol_date)) {
|
||||
my $eol_in = sub {
|
||||
my $eol_date = shift;
|
||||
|
||||
@@ -10,6 +10,10 @@ my ($o) = @_;
|
||||
print &ui_table_row($text{'acl_user'},
|
||||
&ui_opt_textbox("user", $o->{'user'} eq '*' ? undef : $o->{'user'},
|
||||
20, $text{'acl_sameuser'}));
|
||||
|
||||
print &ui_table_row($text{'acl_sudoenforce'},
|
||||
&ui_yesno_radio("sudoenforce",
|
||||
$o->{'sudoenforce'} == 1 ? 1 : 0));
|
||||
}
|
||||
|
||||
sub acl_security_save
|
||||
@@ -17,4 +21,5 @@ sub acl_security_save
|
||||
my ($o) = @_;
|
||||
|
||||
$o->{'user'} = $in{'user_def'} ? '*' : $in{'user'};
|
||||
$o->{'sudoenforce'} = $in{'sudoenforce'} ? 1 : 0;
|
||||
}
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
user=root
|
||||
sudoenforce=1
|
||||
|
||||
@@ -168,13 +168,21 @@ EOF
|
||||
print "<div data-label=\"$text{'index_connecting'}\" id=\"terminal\"></div>\n";
|
||||
|
||||
# Get a free port that can be used for the socket
|
||||
my $port = &allocate_miniserv_websocket();
|
||||
my $port = &allocate_miniserv_websocket($module_name);
|
||||
|
||||
# Check permissions for user to run as
|
||||
my $user = $access{'user'};
|
||||
if ($user eq "*") {
|
||||
$user = $remote_user;
|
||||
}
|
||||
elsif ($user eq "root" && $remote_user ne $user && !$in{'user'} &&
|
||||
$access{'sudoenforce'} ne '0') {
|
||||
# If possible, start with a sudo-capable user
|
||||
my @uinfo = getpwnam($remote_user);
|
||||
if (@uinfo && $uinfo[7]) {
|
||||
$user = $remote_user;
|
||||
}
|
||||
}
|
||||
$user = $config{'user'} if ($user eq 'root' && $config{'user'});
|
||||
|
||||
# Switch to given user
|
||||
@@ -200,7 +208,7 @@ $ENV{'SESSION_ID'} = $main::session_id;
|
||||
" >$module_var_directory/websocket-connection-$port.out 2>&1 </dev/null");
|
||||
|
||||
# Open the terminal
|
||||
my $url = &get_miniserv_websocket_url($port, $config{'host'});
|
||||
my $url = &get_miniserv_websocket_url($port, $config{'host'}, $module_name);
|
||||
my $canvasAddon = $termlinks->{'js'}[3];
|
||||
my $webGLAddon = $termlinks->{'js'}[4];
|
||||
my $term_script = <<EOF;
|
||||
|
||||
@@ -9,3 +9,4 @@ index_eproxy=The Terminal module cannot be used when accessing Webmin via anothe
|
||||
|
||||
acl_user=Run shell as Unix user
|
||||
acl_sameuser=Same as Webmin login
|
||||
acl_sudoenforce=Enforce <em>sudo</em>-only privileges
|
||||
|
||||
@@ -14,7 +14,7 @@ my @uinfo = getpwnam($user);
|
||||
my ($uid, $gid);
|
||||
if ($user ne "root" && !$<) {
|
||||
if (!@uinfo) {
|
||||
&remove_miniserv_websocket($port);
|
||||
&remove_miniserv_websocket($port, $module_name);
|
||||
die "User $user does not exist!";
|
||||
}
|
||||
$uid = $uinfo[2];
|
||||
@@ -83,7 +83,7 @@ my ($shellfh, $pid) = &proc::pty_process_exec($shellexec, $uid, $gid, $shelllogi
|
||||
&reset_environment();
|
||||
my $shcmd = "'$shellexec".($shelllogin ? " $shelllogin" : "")."'";
|
||||
if (!$pid) {
|
||||
&remove_miniserv_websocket($port);
|
||||
&remove_miniserv_websocket($port, $module_name);
|
||||
die "Failed to run shell with $shcmd\n";
|
||||
}
|
||||
else {
|
||||
@@ -99,7 +99,7 @@ close(STDIN);
|
||||
|
||||
# Clean up when socket is terminated
|
||||
$SIG{'ALRM'} = sub {
|
||||
&remove_miniserv_websocket($port);
|
||||
&remove_miniserv_websocket($port, $module_name);
|
||||
die "timeout waiting for connection";
|
||||
};
|
||||
alarm(60);
|
||||
@@ -154,13 +154,13 @@ Net::WebSocket::Server->new(
|
||||
}
|
||||
if (!syswrite($shellfh, $msg, length($msg))) {
|
||||
&error_stderr("Write to shell failed : $!");
|
||||
&remove_miniserv_websocket($port);
|
||||
&remove_miniserv_websocket($port, $module_name);
|
||||
exit(1);
|
||||
}
|
||||
},
|
||||
disconnect => sub {
|
||||
&error_stderr("WebSocket connection closed");
|
||||
&remove_miniserv_websocket($port);
|
||||
&remove_miniserv_websocket($port, $module_name);
|
||||
kill('KILL', $pid) if ($pid);
|
||||
exit(0);
|
||||
}
|
||||
@@ -173,7 +173,7 @@ Net::WebSocket::Server->new(
|
||||
my $ok = sysread($shellfh, $buf, 1024);
|
||||
if ($ok <= 0) {
|
||||
&error_stderr("End of output from shell");
|
||||
&remove_miniserv_websocket($port);
|
||||
&remove_miniserv_websocket($port, $module_name);
|
||||
exit(0);
|
||||
}
|
||||
if ($wsconn) {
|
||||
@@ -186,5 +186,5 @@ Net::WebSocket::Server->new(
|
||||
],
|
||||
)->start;
|
||||
&error_stderr("Exited WebSocket server");
|
||||
&remove_miniserv_websocket($port);
|
||||
&cleanup_miniserv_websockets([$port]);
|
||||
&remove_miniserv_websocket($port, $module_name);
|
||||
&cleanup_miniserv_websockets([$port], $module_name);
|
||||
Reference in New Issue
Block a user