mirror of
https://github.com/webmin/webmin.git
synced 2026-02-03 14:13:29 +00:00
Compare commits
21 Commits
dev/fix-la
...
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 |
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>
|
||||
@@ -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);
|
||||
|
||||
@@ -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}
|
||||
@@ -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
|
||||
|
||||
@@ -107,7 +107,7 @@ if ($product eq "webmin") {
|
||||
$size = int(`du -sk $tmp_dir`);
|
||||
@deps = ( "perl", "libnet-ssleay-perl", "openssl", "libauthen-pam-perl", "libpam-runtime", "libio-pty-perl", "unzip", "shared-mime-info", "tar", "libdigest-sha-perl", "libdigest-md5-perl", "gzip" );
|
||||
$deps = join(", ", @deps);
|
||||
@recommends = ( "libdatetime-perl", "libdatetime-timezone-perl", "libdatetime-locale-perl", "libtime-piece-perl", "libencode-detect-perl", "libtime-hires-perl", "libsocket6-perl", "libthreads-perl", "libthreads-shared-perl", "lynx", "qrencode" );
|
||||
@recommends = ( "libdatetime-perl", "libdatetime-timezone-perl", "libdatetime-locale-perl", "libtime-piece-perl", "libencode-detect-perl", "libtime-hires-perl", "libsocket6-perl", "lynx", "qrencode" );
|
||||
$recommends = join(", ", @recommends);
|
||||
open(CONTROL, ">$control_file");
|
||||
print CONTROL <<EOF;
|
||||
|
||||
@@ -87,7 +87,7 @@ Release: $rel
|
||||
Provides: %{name}-%{version} perl(WebminCore)
|
||||
Requires(pre): /usr/bin/perl
|
||||
Requires: /bin/sh /usr/bin/perl perl(lib) perl(open) perl(Net::SSLeay) perl(Time::Local) perl(Data::Dumper) perl(File::Path) perl(File::Basename) perl(Digest::SHA) perl(Digest::MD5) openssl unzip tar gzip
|
||||
Recommends: perl(DateTime) perl(DateTime::TimeZone) perl(DateTime::Locale) perl(Time::Piece) perl(Encode::Detect) perl(Time::HiRes) perl(Socket6) perl(threads) perl(threads::shared) lynx shared-mime-info perl-File-Basename perl-File-Path qrencode
|
||||
Recommends: perl(DateTime) perl(DateTime::TimeZone) perl(DateTime::Locale) perl(Time::Piece) perl(Encode::Detect) perl(Time::HiRes) perl(Socket6) lynx shared-mime-info perl-File-Basename perl-File-Path qrencode
|
||||
AutoReq: 0
|
||||
License: BSD-3-clause
|
||||
Group: System/Tools
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -44,9 +44,10 @@ while(1) {
|
||||
# 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/ ||
|
||||
$line =~ /^(\S+)\s+(\S+)\s+(\S+)?\s+(\S+\s+\S+\s+\d+\s+\d+:\d+:\d+\s+\d+)\s+still/) {
|
||||
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);
|
||||
}
|
||||
|
||||
191
web-lib-funcs.pl
191
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
|
||||
@@ -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,13 +2253,24 @@ if (!$@ && $] > 5.011) {
|
||||
$data->{'complete'} .= "$_ "
|
||||
}
|
||||
};
|
||||
$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->{'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'}};
|
||||
@@ -7973,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
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user