mirror of
https://github.com/webmin/webmin.git
synced 2026-04-07 17:50:24 +01:00
Compare commits
32 Commits
dev/no-log
...
2.630
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d2b4fa89c5 | ||
|
|
3a3b202a96 | ||
|
|
30f08f73fb | ||
|
|
e499b5b3a5 | ||
|
|
443cf449eb | ||
|
|
58d6308589 | ||
|
|
02ed8e8fbd | ||
|
|
3a0dea1d2c | ||
|
|
916d22b55b | ||
|
|
1f1a7e4562 | ||
|
|
ac68a0be0c | ||
|
|
263cc142a6 | ||
|
|
19fdea395b | ||
|
|
40f82d0df3 | ||
|
|
39ab2c5f02 | ||
|
|
f39a59bdce | ||
|
|
a4846f5f32 | ||
|
|
5558910722 | ||
|
|
bebd99d656 | ||
|
|
bad0d2f821 | ||
|
|
4797852f6f | ||
|
|
d467810076 | ||
|
|
6d1ec1a3e1 | ||
|
|
82ea895c81 | ||
|
|
36e699eb29 | ||
|
|
04e8df863a | ||
|
|
96c2312349 | ||
|
|
1d594e82f0 | ||
|
|
cbd96a4176 | ||
|
|
ed17ade510 | ||
|
|
dc63aa22a5 | ||
|
|
1b9b9ae21f |
10
CHANGELOG.md
10
CHANGELOG.md
@@ -1,5 +1,15 @@
|
|||||||
## Changelog
|
## Changelog
|
||||||
|
|
||||||
|
#### 2.630 (March 24, 2026)
|
||||||
|
* Add improvements to user input validation across all modules
|
||||||
|
* Update Authentic theme to the latest version with various improvements and fixes:
|
||||||
|
- Add a new airy button style to the light palette to match the dark one
|
||||||
|
- Fix to optimize stats server to reduce WebSocket memory usage
|
||||||
|
- Fix the real-time follow indicator when viewing the journal
|
||||||
|
- Fix regex-based match highlighting when viewing the journal
|
||||||
|
- Fix mail compose panel sizing in HTML mode on low-DPR screens
|
||||||
|
- Fix display of the 2FA QR code in the dark palette
|
||||||
|
|
||||||
#### 2.621 (January 25, 2026)
|
#### 2.621 (January 25, 2026)
|
||||||
* Fix to prevent NAT from dropping idle RPC sessions during long transfers
|
* Fix to prevent NAT from dropping idle RPC sessions during long transfers
|
||||||
* Fix to improve the message when socket authentication is used in the MySQL/MariaDB module
|
* Fix to improve the message when socket authentication is used in the MySQL/MariaDB module
|
||||||
|
|||||||
@@ -66,14 +66,18 @@ if ($in{'enable'}) {
|
|||||||
{ 'provider' => $user->{'twofactor_provider'},
|
{ 'provider' => $user->{'twofactor_provider'},
|
||||||
'id' => $user->{'twofactor_id'} });
|
'id' => $user->{'twofactor_id'} });
|
||||||
|
|
||||||
# Show a test form, so the user can validate
|
# Show a test form only when enrolling for yourself
|
||||||
print &ui_form_start("test_twofactor.cgi");
|
if ($user->{'name'} eq $base_remote_user) {
|
||||||
print $text{'twofactor_testdesc'},"<p>\n";
|
print &ui_form_start("test_twofactor.cgi");
|
||||||
print "$text{'twofactor_testfield'} \n",
|
print &ui_tag('p', $text{'twofactor_testdesc'});
|
||||||
&ui_textbox("test", undef, 12),"\n";
|
print &ui_tag('p', "$text{'twofactor_testfield'}".
|
||||||
print &ui_hidden("user", $in{'user'}) if ($in{'user'});
|
" ".
|
||||||
print "<p>\n";
|
&ui_textbox("test", undef, 12));
|
||||||
print &ui_form_end([ [ undef, $text{'twofactor_test'} ] ]);
|
print &ui_hidden("user", $in{'user'}) if ($in{'user'});
|
||||||
|
print &ui_tag('p');
|
||||||
|
print &ui_form_end([ [ undef,
|
||||||
|
$text{'twofactor_test'} ] ]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&ui_print_footer("", $text{'index_return'});
|
&ui_print_footer("", $text{'index_return'});
|
||||||
|
|||||||
@@ -60,12 +60,11 @@ print &ui_tabs_end_tab();
|
|||||||
|
|
||||||
# Show immediate form
|
# Show immediate form
|
||||||
print &ui_tabs_start_tab("tab", "backup");
|
print &ui_tabs_start_tab("tab", "backup");
|
||||||
my $filename = 'webmin-backup-config-on-';
|
my $hostname = &get_system_hostname() || "localhost";
|
||||||
my $hostname = &get_system_hostname();
|
|
||||||
$hostname =~ s/\./-/g;
|
$hostname =~ s/\./-/g;
|
||||||
$filename .= $hostname;
|
my $filename = $hostname."+configuration_backup-webmin-".
|
||||||
$filename .= "-".strftime("%Y-%m-%d-%H-%M", localtime);
|
strftime("%Y-%m-%d-%H-%M", localtime);
|
||||||
print &ui_form_start("backup.cgi/$filename.tgz", "post");
|
print &ui_form_start("backup.cgi/$filename.tar.gz", "post");
|
||||||
print &ui_table_start($text{'index_header'}, undef, 2);
|
print &ui_table_start($text{'index_header'}, undef, 2);
|
||||||
|
|
||||||
my @dmods = split(/\s+/, $config{'mods'} || "");
|
my @dmods = split(/\s+/, $config{'mods'} || "");
|
||||||
|
|||||||
@@ -700,7 +700,7 @@ if ($v && $v->{'members'}) {
|
|||||||
push(@av, join(" ", $av->{'name'}, @{$av->{'values'}}));
|
push(@av, join(" ", $av->{'name'}, @{$av->{'values'}}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ($_[3] == 0) {
|
if (!$_[3]) {
|
||||||
# text area
|
# text area
|
||||||
return &ui_table_row($_[0],
|
return &ui_table_row($_[0],
|
||||||
&ui_textarea($_[1], join("\n", @av), 3, 50));
|
&ui_textarea($_[1], join("\n", @av), 3, 50));
|
||||||
@@ -805,14 +805,14 @@ my $v = &find($_[1], $_[2]);
|
|||||||
my $n;
|
my $n;
|
||||||
($n = $_[1]) =~ s/[^A-Za-z0-9_]/_/g;
|
($n = $_[1]) =~ s/[^A-Za-z0-9_]/_/g;
|
||||||
return &ui_table_row($_[0],
|
return &ui_table_row($_[0],
|
||||||
&ui_opt_textbox($n, $v ? $v->{'value'} : "", $_[4], $_[3])." ".$_[5],
|
&ui_opt_textbox($n, $v ? $v->{'value'} : "", $_[4], $_[3])." ".($_[5] // ""),
|
||||||
$_[4] > 30 ? 3 : 1);
|
$_[4] > 30 ? 3 : 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub save_opt
|
sub save_opt
|
||||||
{
|
{
|
||||||
my ($dir, $n, $err);
|
my ($dir, $n, $err);
|
||||||
($n = $_[0]) =~ s/[^A-Za-z0-9_]/_/g;
|
($n = ($_[0] // "")) =~ s/[^A-Za-z0-9_]/_/g;
|
||||||
if ($in{"${n}_def"}) { &save_directive($_[2], $_[0], [ ], $_[3]); }
|
if ($in{"${n}_def"}) { &save_directive($_[2], $_[0], [ ], $_[3]); }
|
||||||
elsif ($err = &{$_[1]}($in{$n})) {
|
elsif ($err = &{$_[1]}($in{$n})) {
|
||||||
&error($err);
|
&error($err);
|
||||||
@@ -906,7 +906,7 @@ my ($fwdconf, $fwdfile, $fwdrec, $ipv6);
|
|||||||
# find forward domain
|
# find forward domain
|
||||||
my $host = $_[0]; $host =~ s/\.$//;
|
my $host = $_[0]; $host =~ s/\.$//;
|
||||||
my @zl = grep { $_->{'type'} ne 'view' } &list_zone_names();
|
my @zl = grep { $_->{'type'} ne 'view' } &list_zone_names();
|
||||||
if ($_[1] ne '' && $_[1] ne 'any') {
|
if ($_[1] && $_[1] ne 'any') {
|
||||||
@zl = grep { $_->{'view'} && $_->{'viewindex'} == $_[1] } @zl;
|
@zl = grep { $_->{'view'} && $_->{'viewindex'} == $_[1] } @zl;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -968,6 +968,7 @@ else {
|
|||||||
# Returns 1 if some zone can be edited
|
# Returns 1 if some zone can be edited
|
||||||
sub can_edit_zone
|
sub can_edit_zone
|
||||||
{
|
{
|
||||||
|
$access{'zones'} //= '*';
|
||||||
my %zcan;
|
my %zcan;
|
||||||
my ($zn, $vn, $file);
|
my ($zn, $vn, $file);
|
||||||
if ($_[0]->{'members'}) {
|
if ($_[0]->{'members'}) {
|
||||||
@@ -2405,7 +2406,7 @@ return undef;
|
|||||||
sub is_bind_running
|
sub is_bind_running
|
||||||
{
|
{
|
||||||
my $pidfile = &get_pid_file();
|
my $pidfile = &get_pid_file();
|
||||||
my $rv = &check_pid_file(&make_chroot($pidfile, 1));
|
my $rv = &check_pid_file(&make_chroot($pidfile, 1)) || 0;
|
||||||
if (!$rv && $gconfig{'os_type'} eq 'windows') {
|
if (!$rv && $gconfig{'os_type'} eq 'windows') {
|
||||||
# Fall back to checking for process
|
# Fall back to checking for process
|
||||||
$rv = &find_byname("named");
|
$rv = &find_byname("named");
|
||||||
@@ -2536,6 +2537,7 @@ if ($changed || !$znc{'version'} ||
|
|||||||
next if (!$type);
|
next if (!$type);
|
||||||
$type = lc($type);
|
$type = lc($type);
|
||||||
my $file = &find_value("file", $z->{'members'});
|
my $file = &find_value("file", $z->{'members'});
|
||||||
|
$file //= "";
|
||||||
my $up = &find("update-policy", $z->{'members'});
|
my $up = &find("update-policy", $z->{'members'});
|
||||||
my $au = &find("allow-update", $z->{'members'});
|
my $au = &find("allow-update", $z->{'members'});
|
||||||
my $dynamic = $up || $au || $gau ? 1 : 0;
|
my $dynamic = $up || $au || $gau ? 1 : 0;
|
||||||
@@ -3198,6 +3200,7 @@ else {
|
|||||||
$zonename = $zone->{'name'};
|
$zonename = $zone->{'name'};
|
||||||
$zonefile = $zone->{'file'};
|
$zonefile = $zone->{'file'};
|
||||||
}
|
}
|
||||||
|
return () if (!$zonename || !$zonefile);
|
||||||
my $out = &backquote_command(
|
my $out = &backquote_command(
|
||||||
$config{'checkzone'}." ".quotemeta($zonename)." ".
|
$config{'checkzone'}." ".quotemeta($zonename)." ".
|
||||||
quotemeta(&make_chroot(&absolute_path($zonefile)))." 2>&1 </dev/null");
|
quotemeta(&make_chroot(&absolute_path($zonefile)))." 2>&1 </dev/null");
|
||||||
@@ -3221,6 +3224,7 @@ else {
|
|||||||
$zonename = $zone->{'name'};
|
$zonename = $zone->{'name'};
|
||||||
$zonefile = $zone->{'file'};
|
$zonefile = $zone->{'file'};
|
||||||
}
|
}
|
||||||
|
return () if (!$zonename || !$zonefile);
|
||||||
my $absfile = &make_chroot(&absolute_path($zonefile));
|
my $absfile = &make_chroot(&absolute_path($zonefile));
|
||||||
my $out = &backquote_command(
|
my $out = &backquote_command(
|
||||||
$config{'checkzone'}." ".quotemeta($zonename)." ".
|
$config{'checkzone'}." ".quotemeta($zonename)." ".
|
||||||
@@ -3363,7 +3367,7 @@ if (!$access{'ro'} && $access{'apply'}) {
|
|||||||
if ($zone && ($access{'apply'} == 1 || $access{'apply'} == 2)) {
|
if ($zone && ($access{'apply'} == 1 || $access{'apply'} == 2)) {
|
||||||
# Apply this zone
|
# Apply this zone
|
||||||
my $link = "restart_zone.cgi?return=$r&".
|
my $link = "restart_zone.cgi?return=$r&".
|
||||||
"view=$zone->{'viewindex'}&".
|
"view=".($zone->{'viewindex'} // "")."&".
|
||||||
"zone=$zone->{'name'}";
|
"zone=$zone->{'name'}";
|
||||||
push(@rv, &ui_link($link, $text{'links_apply'}) );
|
push(@rv, &ui_link($link, $text{'links_apply'}) );
|
||||||
}
|
}
|
||||||
@@ -3934,7 +3938,7 @@ if (&find_byname("nscd")) {
|
|||||||
sub transfer_slave_records
|
sub transfer_slave_records
|
||||||
{
|
{
|
||||||
my ($dom, $masters, $file, $source, $sourceport) = @_;
|
my ($dom, $masters, $file, $source, $sourceport) = @_;
|
||||||
my $sourcearg;
|
my $sourcearg = "";
|
||||||
if ($source && $source ne "*") {
|
if ($source && $source ne "*") {
|
||||||
$sourcearg = "-t ".$source;
|
$sourcearg = "-t ".$source;
|
||||||
if ($sourceport) {
|
if ($sourceport) {
|
||||||
|
|||||||
@@ -28,12 +28,12 @@ for(my $i=0; $i<@servers; $i++) {
|
|||||||
my @cols = ( );
|
my @cols = ( );
|
||||||
push(@cols, &ui_textbox("ip_$i", $s->{'value'}, 30));
|
push(@cols, &ui_textbox("ip_$i", $s->{'value'}, 30));
|
||||||
|
|
||||||
my $bogus = &find_value("bogus", $s->{'members'});
|
my $bogus = &find_value("bogus", $s->{'members'}) // "";
|
||||||
push(@cols, &ui_radio("bogus_$i", lc($bogus) eq 'yes' ? 1 : 0,
|
push(@cols, &ui_radio("bogus_$i", lc($bogus) eq 'yes' ? 1 : 0,
|
||||||
[ [ 1, $text{'yes'} ],
|
[ [ 1, $text{'yes'} ],
|
||||||
[ 0, $text{'no'} ] ]));
|
[ 0, $text{'no'} ] ]));
|
||||||
|
|
||||||
my $format = &find_value("transfer-format", $s->{'members'});
|
my $format = &find_value("transfer-format", $s->{'members'}) // "";
|
||||||
push(@cols, &ui_radio("format_$i", lc($format),
|
push(@cols, &ui_radio("format_$i", lc($format),
|
||||||
[ [ 'one-answer', $text{'servers_one'} ],
|
[ [ 'one-answer', $text{'servers_one'} ],
|
||||||
[ 'many-answers', $text{'servers_many'} ],
|
[ 'many-answers', $text{'servers_many'} ],
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ else {
|
|||||||
|
|
||||||
my %bumpedrev;
|
my %bumpedrev;
|
||||||
my @delr;
|
my @delr;
|
||||||
foreach my $d (sort { $b <=> $a } @d) {
|
foreach my $d (sort { ($b =~ /^(\d+)/)[0] <=> ($a =~ /^(\d+)/)[0] } @d) {
|
||||||
my ($num, $id) = split(/\//, $d, 2);
|
my ($num, $id) = split(/\//, $d, 2);
|
||||||
my $r = &find_record_by_id(\@recs, $id, $num);
|
my $r = &find_record_by_id(\@recs, $id, $num);
|
||||||
next if (!$r);
|
next if (!$r);
|
||||||
@@ -77,7 +77,7 @@ else {
|
|||||||
# Delete the actual record
|
# Delete the actual record
|
||||||
&lock_file(&make_chroot($r->{'file'}));
|
&lock_file(&make_chroot($r->{'file'}));
|
||||||
&delete_record($r->{'file'}, $r);
|
&delete_record($r->{'file'}, $r);
|
||||||
splice(@recs, $d, 1);
|
splice(@recs, $num, 1);
|
||||||
push(@delr, $r);
|
push(@delr, $r);
|
||||||
}
|
}
|
||||||
&bump_soa_record($zone->{'file'}, \@recs);
|
&bump_soa_record($zone->{'file'}, \@recs);
|
||||||
|
|||||||
@@ -172,6 +172,7 @@ for(my $i=0; $i<@_; $i++) {
|
|||||||
else {
|
else {
|
||||||
$name = $r->{'name'};
|
$name = $r->{'name'};
|
||||||
}
|
}
|
||||||
|
$name //= "";
|
||||||
my @cols;
|
my @cols;
|
||||||
$name = &html_escape($name);
|
$name = &html_escape($name);
|
||||||
my $id = &record_id($r);
|
my $id = &record_id($r);
|
||||||
|
|||||||
@@ -488,6 +488,7 @@ else {
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
# For other record types, just save the lines
|
# For other record types, just save the lines
|
||||||
|
$in{'values'} //= "";
|
||||||
$in{'values'} =~ s/\r//g;
|
$in{'values'} =~ s/\r//g;
|
||||||
my @vlines = split(/\n/, $in{'values'});
|
my @vlines = split(/\n/, $in{'values'});
|
||||||
$vals = join(" ",map { $_ =~ /\s|;/ ? "\"$_\"" : $_ } @vlines);
|
$vals = join(" ",map { $_ =~ /\s|;/ ? "\"$_\"" : $_ } @vlines);
|
||||||
|
|||||||
@@ -918,7 +918,7 @@ if ($has_parted) {
|
|||||||
elsif ($tag eq "linux-swap") {
|
elsif ($tag eq "linux-swap") {
|
||||||
@rv = ( "swap" );
|
@rv = ( "swap" );
|
||||||
}
|
}
|
||||||
elsif ($tag eq "NTFS") {
|
elsif ($tag eq "NTFS" || $tag eq "ntfs") {
|
||||||
@rv = ( "ntfs" );
|
@rv = ( "ntfs" );
|
||||||
}
|
}
|
||||||
elsif ($tag eq "reiserfs") {
|
elsif ($tag eq "reiserfs") {
|
||||||
|
|||||||
@@ -4,6 +4,16 @@
|
|||||||
$no_acl_check++;
|
$no_acl_check++;
|
||||||
require './fsdump-lib.pl';
|
require './fsdump-lib.pl';
|
||||||
|
|
||||||
|
sub start_tls
|
||||||
|
{
|
||||||
|
my ($fh, $what) = @_;
|
||||||
|
eval { require IO::Socket::SSL; IO::Socket::SSL->import(); 1; } ||
|
||||||
|
&error_exit("FTP server requires TLS, but IO::Socket::SSL is not installed");
|
||||||
|
IO::Socket::SSL->start_SSL($fh, SSL_verify_mode => 0) ||
|
||||||
|
&error_exit("FTP $what TLS handshake failed : ".
|
||||||
|
IO::Socket::SSL::errstr());
|
||||||
|
}
|
||||||
|
|
||||||
# Parse args, and get password
|
# Parse args, and get password
|
||||||
select(STDERR); $| = 1; select(STDOUT);
|
select(STDERR); $| = 1; select(STDOUT);
|
||||||
$host = $ARGV[0];
|
$host = $ARGV[0];
|
||||||
@@ -36,6 +46,15 @@ while(1) {
|
|||||||
&error_exit("FTP connection failed : $err") if ($err);
|
&error_exit("FTP connection failed : $err") if ($err);
|
||||||
&ftp_command("", 2, \$err) ||
|
&ftp_command("", 2, \$err) ||
|
||||||
&error_exit("FTP prompt failed : $err");
|
&error_exit("FTP prompt failed : $err");
|
||||||
|
$ssl_enabled = 0;
|
||||||
|
if (&ftp_command("AUTH TLS", 2, \$err)) {
|
||||||
|
&start_tls(\*SOCK, "control");
|
||||||
|
&ftp_command("PBSZ 0", 2, \$err) ||
|
||||||
|
&error_exit("FTP TLS setup failed : $err");
|
||||||
|
&ftp_command("PROT P", 2, \$err) ||
|
||||||
|
&error_exit("FTP TLS setup failed : $err");
|
||||||
|
$ssl_enabled = 1;
|
||||||
|
}
|
||||||
|
|
||||||
# Login to server
|
# Login to server
|
||||||
@urv = &ftp_command("USER $user", [ 2, 3 ], \$err);
|
@urv = &ftp_command("USER $user", [ 2, 3 ], \$err);
|
||||||
@@ -174,5 +193,8 @@ elsif ($mode == 2) {
|
|||||||
else {
|
else {
|
||||||
$opened = 0;
|
$opened = 0;
|
||||||
}
|
}
|
||||||
|
if ($opened && $ssl_enabled) {
|
||||||
|
&start_tls(\*CON, "data");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -430,8 +430,8 @@ if (!$main::ui_hidden_start_donejs++) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# Build list of tab titles and names
|
# Build list of tab titles and names
|
||||||
my $tabnames = "[".join(",", map { "\""."e_escape($_->[0])."\"" } @$tabs)."]";
|
my $tabnames = &convert_to_json([map { $_->[0] } @$tabs]);
|
||||||
my $tabtitles = "[".join(",", map { "\""."e_escape($_->[1])."\"" } @$tabs)."]";
|
my $tabtitles = &convert_to_json([map { $_->[1] } @$tabs]);
|
||||||
$rv .= "<script>\n";
|
$rv .= "<script>\n";
|
||||||
$rv .= "document.${name}_tabnames = $tabnames;\n";
|
$rv .= "document.${name}_tabnames = $tabnames;\n";
|
||||||
$rv .= "document.${name}_tabtitles = $tabtitles;\n";
|
$rv .= "document.${name}_tabtitles = $tabtitles;\n";
|
||||||
@@ -649,7 +649,7 @@ if (!$main::WRAPPER_OPEN) { # If we're not already inside of a wrapper, wrap it
|
|||||||
}
|
}
|
||||||
$main::WRAPPER_OPEN++;
|
$main::WRAPPER_OPEN++;
|
||||||
my $colspan = 1;
|
my $colspan = 1;
|
||||||
$rv .= "<details class='ui_hidden_table_start'$opened>";
|
$rv .= "<details data-name='$name' class='ui_hidden_table_start'$opened $tabletags>";
|
||||||
$rv .= "<summary>$header $rheader</summary>\n";
|
$rv .= "<summary>$header $rheader</summary>\n";
|
||||||
$rv .= "<table width=100%>\n";
|
$rv .= "<table width=100%>\n";
|
||||||
$main::ui_table_cols = $cols || 4;
|
$main::ui_table_cols = $cols || 4;
|
||||||
|
|||||||
@@ -1175,6 +1175,47 @@ elsif ($init_mode eq "launchd") {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
=head2 activate_action(name)
|
||||||
|
|
||||||
|
Unmasks some action, enables it at boot time, and starts it if not running.
|
||||||
|
Returns 1 if the action exists, 0 if not.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
sub activate_action
|
||||||
|
{
|
||||||
|
my ($name) = @_;
|
||||||
|
my $st = &action_status($name);
|
||||||
|
return 0 if (!$st);
|
||||||
|
&unmask_action($name);
|
||||||
|
&enable_at_boot($name);
|
||||||
|
my $running = &status_action($name);
|
||||||
|
if ($running != 1) { # unknown or stopped
|
||||||
|
&start_action($name);
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
=head2 deactivate_action(name, [mask])
|
||||||
|
|
||||||
|
Stops some action if currently running, disables it at boot time, and masks it
|
||||||
|
on systemd systems. The optional mask flag can be set to 0 to skip masking.
|
||||||
|
Returns 1 if the action exists, 0 if not.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
sub deactivate_action
|
||||||
|
{
|
||||||
|
my ($name, $mask) = @_;
|
||||||
|
my $st = &action_status($name);
|
||||||
|
return 0 if (!$st);
|
||||||
|
my $running = &status_action($name);
|
||||||
|
if ($running != 0) { # unknown or running
|
||||||
|
&stop_action($name);
|
||||||
|
}
|
||||||
|
&disable_at_boot($name);
|
||||||
|
&mask_action($name) if (!defined($mask) || $mask);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
=head2 delete_at_boot(name)
|
=head2 delete_at_boot(name)
|
||||||
|
|
||||||
Delete the init script, RC script or whatever with some name
|
Delete the init script, RC script or whatever with some name
|
||||||
|
|||||||
@@ -221,12 +221,23 @@ else {
|
|||||||
print &ui_table_row($text{'index_email'}, $efield);
|
print &ui_table_row($text{'index_email'}, $efield);
|
||||||
|
|
||||||
# Install or just notify?
|
# Install or just notify?
|
||||||
print &ui_table_row($text{'index_action'},
|
$action_ui = &ui_select("action", int($config{'sched_action'}),
|
||||||
&ui_radio("action", int($config{'sched_action'}),
|
[ [ -1, $text{'index_action-1'} ],
|
||||||
[ [ -1, $text{'index_action-1'} ],
|
[ 0, $text{'index_action0'} ],
|
||||||
[ 0, $text{'index_action0'} ],
|
[ 1, $text{'index_action1'} ],
|
||||||
[ 1, $text{'index_action1'} ],
|
[ 2, $text{'index_action2'} ] ]);
|
||||||
[ 2, $text{'index_action2'} ] ]));
|
if (my @auto_updates = &list_enabled_auto_update_services()) {
|
||||||
|
# If any auto-update services are enabled, show option to disable them
|
||||||
|
$auto_update_names = join(", ", map { $_->{'name'} } @auto_updates);
|
||||||
|
$action_ui .= " ".
|
||||||
|
&ui_checkbox("disable_auto_updates", 1,
|
||||||
|
&text('index_action_disable',
|
||||||
|
$auto_update_names), 0);
|
||||||
|
$action_ui .= "<br>\n".
|
||||||
|
&ui_note(&text('index_action_note',
|
||||||
|
&ui_tag('tt', $auto_update_names)));
|
||||||
|
}
|
||||||
|
print &ui_table_row($text{'index_action'}, $action_ui);
|
||||||
|
|
||||||
print &ui_table_end();
|
print &ui_table_end();
|
||||||
print &ui_form_end([ [ "save", $text{'save'} ] ]);
|
print &ui_form_end([ [ "save", $text{'save'} ] ]);
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ index_action-1=Just notify for security updates
|
|||||||
index_action0=Just notify for any updates
|
index_action0=Just notify for any updates
|
||||||
index_action1=Install security updates
|
index_action1=Install security updates
|
||||||
index_action2=Install any updates
|
index_action2=Install any updates
|
||||||
|
index_action_note=Updates may also be installed by $1; disable external updates if you want Webmin to handle updates exclusively
|
||||||
|
index_action_disable=Disable external updates
|
||||||
index_err=Failed to fetch package list
|
index_err=Failed to fetch package list
|
||||||
index_refresh=Refresh Available Packages
|
index_refresh=Refresh Available Packages
|
||||||
index_noupdate=No update exists from version $1
|
index_noupdate=No update exists from version $1
|
||||||
|
|||||||
@@ -8,6 +8,16 @@ eval "use WebminCore;";
|
|||||||
&foreign_require("cron", "cron-lib.pl");
|
&foreign_require("cron", "cron-lib.pl");
|
||||||
&foreign_require("webmin", "webmin-lib.pl");
|
&foreign_require("webmin", "webmin-lib.pl");
|
||||||
|
|
||||||
|
# Known OS package auto-update services that can overlap with Webmin's
|
||||||
|
# scheduled package updates.
|
||||||
|
@auto_update_services = (
|
||||||
|
"unattended-upgrades",
|
||||||
|
"dnf-automatic.timer",
|
||||||
|
"dnf-automatic-install.timer",
|
||||||
|
"dnf-automatic-download.timer",
|
||||||
|
"dnf-automatic-notifyonly.timer",
|
||||||
|
);
|
||||||
|
|
||||||
$available_cache_file = &cache_file_path("available.cache");
|
$available_cache_file = &cache_file_path("available.cache");
|
||||||
$current_cache_file = &cache_file_path("current.cache");
|
$current_cache_file = &cache_file_path("current.cache");
|
||||||
$updates_cache_file = &cache_file_path("updates.cache");
|
$updates_cache_file = &cache_file_path("updates.cache");
|
||||||
@@ -19,6 +29,36 @@ $yum_changelog_cache_dir = &cache_file_path("yumchangelog");
|
|||||||
|
|
||||||
$update_progress_dir = "$module_var_directory/progress";
|
$update_progress_dir = "$module_var_directory/progress";
|
||||||
|
|
||||||
|
# list_enabled_auto_update_services()
|
||||||
|
# Returns known OS-level auto-update services that are currently enabled.
|
||||||
|
sub list_enabled_auto_update_services
|
||||||
|
{
|
||||||
|
return ( ) if (!&foreign_check("init"));
|
||||||
|
&foreign_require("init");
|
||||||
|
my @rv;
|
||||||
|
foreach my $service (@auto_update_services) {
|
||||||
|
next if (&init::action_status($service) != 2); # not enabled
|
||||||
|
my $service_name = $service;
|
||||||
|
$service_name =~ s/\.[^\.]+$//; # nice name
|
||||||
|
push(@rv, { 'service' => $service, 'name' => $service_name });
|
||||||
|
}
|
||||||
|
return @rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
# disable_enabled_auto_update_services()
|
||||||
|
# Stops, disables and masks known auto-update services that are enabled.
|
||||||
|
sub disable_enabled_auto_update_services
|
||||||
|
{
|
||||||
|
return ( ) if (!&foreign_check("init"));
|
||||||
|
&foreign_require("init");
|
||||||
|
my @services = map { $_->{'service'} } &list_enabled_auto_update_services();
|
||||||
|
my @rv;
|
||||||
|
foreach my $service (@services) {
|
||||||
|
push(@rv, $service) if (&init::deactivate_action($service, 0));
|
||||||
|
}
|
||||||
|
return @rv;
|
||||||
|
}
|
||||||
|
|
||||||
# cache_file_path(name)
|
# cache_file_path(name)
|
||||||
# Returns a path in the /var directory unless the file already exists under
|
# Returns a path in the /var directory unless the file already exists under
|
||||||
# /etc/webmin
|
# /etc/webmin
|
||||||
|
|||||||
@@ -51,6 +51,11 @@ else {
|
|||||||
$msg = $text{'sched_yes'};
|
$msg = $text{'sched_yes'};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Disable auto-update services if requested
|
||||||
|
if ($in{'disable_auto_updates'}) {
|
||||||
|
&disable_enabled_auto_update_services();
|
||||||
|
}
|
||||||
|
|
||||||
# Tell the user
|
# Tell the user
|
||||||
&ui_print_header(undef, $text{'sched_title'}, "");
|
&ui_print_header(undef, $text{'sched_title'}, "");
|
||||||
|
|
||||||
@@ -59,4 +64,3 @@ print "$msg<p>\n";
|
|||||||
&webmin_log("sched", undef, $in{'sched_def'} ? 0 : 1);
|
&webmin_log("sched", undef, $in{'sched_def'} ? 0 : 1);
|
||||||
&ui_print_footer("index.cgi?mode=$in{'mode'}&search=".
|
&ui_print_footer("index.cgi?mode=$in{'mode'}&search=".
|
||||||
&urlize($in{'search'}), $text{'index_return'});
|
&urlize($in{'search'}), $text{'index_return'});
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ index_medit=Manage
|
|||||||
index_manual=Edit Manually
|
index_manual=Edit Manually
|
||||||
index_anyfile=Edit other PHP configuration file
|
index_anyfile=Edit other PHP configuration file
|
||||||
index_return=configuration files
|
index_return=configuration files
|
||||||
index_pkgs=Manage PHP Packages
|
index_pkgs=Manage PHP Versions
|
||||||
index_pkgsdesc=Install and remove PHP versions from your system's software package repository, so that they can be configured here any used in Virtualmin.
|
index_pkgsdesc=Install and remove PHP versions from your system's software package repository, so that they can be configured here any used in Virtualmin.
|
||||||
|
|
||||||
file_global=Global PHP configuration
|
file_global=Global PHP configuration
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ if (&foreign_installed("package-updates")) {
|
|||||||
my @allpkgs = &extend_installable_php_packages(\@newpkgs);
|
my @allpkgs = &extend_installable_php_packages(\@newpkgs);
|
||||||
@allpkgs = sort { $b->{'ver'} cmp $a->{'ver'} } @allpkgs;
|
@allpkgs = sort { $b->{'ver'} cmp $a->{'ver'} } @allpkgs;
|
||||||
print &ui_select("u", undef,
|
print &ui_select("u", undef,
|
||||||
[ map { [ $_->{'name'}, "PHP $_->{'ver'}" ] } @allpkgs ]);
|
[ map { [ $_->{'name'}, $_->{'ver'} ] } @allpkgs ]);
|
||||||
print &ui_hidden(
|
print &ui_hidden(
|
||||||
"redir", &get_webprefix()."/$module_name/list_pkgs.cgi");
|
"redir", &get_webprefix()."/$module_name/list_pkgs.cgi");
|
||||||
print &ui_hidden("redirdesc", $text{'pkgs_title'});
|
print &ui_hidden("redirdesc", $text{'pkgs_title'});
|
||||||
|
|||||||
@@ -4,3 +4,5 @@ name=PostgreSQL
|
|||||||
longdesc=Manage databases, tables and users in your PostgreSQL database server.
|
longdesc=Manage databases, tables and users in your PostgreSQL database server.
|
||||||
readonly=1
|
readonly=1
|
||||||
cpan=1
|
cpan=1
|
||||||
|
rpm_recommends=perl-DBI perl-DBD-Pg
|
||||||
|
deb_recommends=libdbi-perl libdbd-pg-perl
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ require (-r 'sendmail-lib.pl' ? './sendmail-lib.pl' :
|
|||||||
-r 'qmail-lib.pl' ? './qmail-lib.pl' :
|
-r 'qmail-lib.pl' ? './qmail-lib.pl' :
|
||||||
'./postfix-lib.pl');
|
'./postfix-lib.pl');
|
||||||
&ReadParse();
|
&ReadParse();
|
||||||
if (substr($in{'file'}, 0, length($access{'apath'})) ne $access{'apath'}) {
|
if (!&is_under_directory($access{'apath'}, $in{'file'})) {
|
||||||
&error(&text('afile_efile', $in{'file'}));
|
&error(&text('afile_efile', $in{'file'}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ require (-r 'sendmail-lib.pl' ? './sendmail-lib.pl' :
|
|||||||
-r 'qmail-lib.pl' ? './qmail-lib.pl' :
|
-r 'qmail-lib.pl' ? './qmail-lib.pl' :
|
||||||
'./postfix-lib.pl');
|
'./postfix-lib.pl');
|
||||||
&ReadParse();
|
&ReadParse();
|
||||||
if (substr($in{'file'}, 0, length($access{'apath'})) ne $access{'apath'}) {
|
if (!&is_under_directory($access{'apath'}, $in{'file'})) {
|
||||||
&error(&text('ffile_efile', $in{'file'}));
|
&error(&text('ffile_efile', $in{'file'}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ require (-r 'sendmail-lib.pl' ? './sendmail-lib.pl' :
|
|||||||
-r 'qmail-lib.pl' ? './qmail-lib.pl' :
|
-r 'qmail-lib.pl' ? './qmail-lib.pl' :
|
||||||
'./postfix-lib.pl');
|
'./postfix-lib.pl');
|
||||||
&ReadParse();
|
&ReadParse();
|
||||||
if (substr($in{'file'}, 0, length($access{'apath'})) ne $access{'apath'}) {
|
if (!&is_under_directory($access{'apath'}, $in{'file'})) {
|
||||||
&error(&text('rfile_efile', $in{'file'}));
|
&error(&text('rfile_efile', $in{'file'}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ require (-r 'sendmail-lib.pl' ? './sendmail-lib.pl' :
|
|||||||
-r 'qmail-lib.pl' ? './qmail-lib.pl' :
|
-r 'qmail-lib.pl' ? './qmail-lib.pl' :
|
||||||
'./postfix-lib.pl');
|
'./postfix-lib.pl');
|
||||||
&ReadParseMime();
|
&ReadParseMime();
|
||||||
if (substr($in{'file'}, 0, length($access{'apath'})) ne $access{'apath'}) {
|
if (!&is_under_directory($access{'apath'}, $in{'file'})) {
|
||||||
&error(&text('afile_efile', $in{'file'}));
|
&error(&text('afile_efile', $in{'file'}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ require (-r 'sendmail-lib.pl' ? './sendmail-lib.pl' :
|
|||||||
'./postfix-lib.pl');
|
'./postfix-lib.pl');
|
||||||
&ReadParseMime();
|
&ReadParseMime();
|
||||||
&error_setup($text{'ffile_err'});
|
&error_setup($text{'ffile_err'});
|
||||||
if (substr($in{'file'}, 0, length($access{'apath'})) ne $access{'apath'}) {
|
if (!&is_under_directory($access{'apath'}, $in{'file'})) {
|
||||||
&error(&text('ffile_efile', $in{'file'}));
|
&error(&text('ffile_efile', $in{'file'}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ require (-r 'sendmail-lib.pl' ? './sendmail-lib.pl' :
|
|||||||
-r 'qmail-lib.pl' ? './qmail-lib.pl' :
|
-r 'qmail-lib.pl' ? './qmail-lib.pl' :
|
||||||
'./postfix-lib.pl');
|
'./postfix-lib.pl');
|
||||||
&ReadParseMime();
|
&ReadParseMime();
|
||||||
if (substr($in{'file'}, 0, length($access{'apath'})) ne $access{'apath'}) {
|
if (!&is_under_directory($access{'apath'}, $in{'file'})) {
|
||||||
&error(&text('rfile_efile', $in{'file'}));
|
&error(&text('rfile_efile', $in{'file'}));
|
||||||
}
|
}
|
||||||
$in{'replies_def'} || $in{'replies'} =~ /^\/\S+/ ||
|
$in{'replies_def'} || $in{'replies'} =~ /^\/\S+/ ||
|
||||||
|
|||||||
@@ -2114,8 +2114,8 @@ if (!$main::ui_hidden_start_donejs++) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# Build list of tab titles and names
|
# Build list of tab titles and names
|
||||||
my $tabnames = "[".join(",", map { "\""."e_escape($_->[0])."\"" } @$tabs)."]";
|
my $tabnames = &convert_to_json([map { $_->[0] } @$tabs]);
|
||||||
my $tabtitles = "[".join(",", map { "\""."e_escape($_->[1])."\"" } @$tabs)."]";
|
my $tabtitles = &convert_to_json([map { $_->[1] } @$tabs]);
|
||||||
$rv .= "<script type='text/javascript'>\n";
|
$rv .= "<script type='text/javascript'>\n";
|
||||||
$rv .= "document.${name}_tabnames = $tabnames;\n";
|
$rv .= "document.${name}_tabnames = $tabnames;\n";
|
||||||
$rv .= "document.${name}_tabtitles = $tabtitles;\n";
|
$rv .= "document.${name}_tabtitles = $tabtitles;\n";
|
||||||
|
|||||||
@@ -47,9 +47,9 @@ print &ui_table_row($text{'sendmail_login'},
|
|||||||
&ui_radio("login_def", $user ? 0 : 1,
|
&ui_radio("login_def", $user ? 0 : 1,
|
||||||
[ [ 1, $text{'sendmail_login1'}."<br>" ],
|
[ [ 1, $text{'sendmail_login1'}."<br>" ],
|
||||||
[ 0, $text{'sendmail_login0'} ] ])." ".
|
[ 0, $text{'sendmail_login0'} ] ])." ".
|
||||||
&ui_textbox("login_user", $user, 20)." ".
|
&ui_textbox("login_user", $user, 12)." ".
|
||||||
$text{'sendmail_pass'}." ".
|
$text{'sendmail_pass'}." ".
|
||||||
&ui_textbox("login_pass", $pass, 20));
|
&ui_textbox("login_pass", $pass, 12));
|
||||||
|
|
||||||
# Authentication method
|
# Authentication method
|
||||||
$auth = $mconfig{'smtp_auth'};
|
$auth = $mconfig{'smtp_auth'};
|
||||||
@@ -62,11 +62,12 @@ print &ui_table_row($text{'sendmail_auth'},
|
|||||||
$from = $mconfig{'webmin_from'};
|
$from = $mconfig{'webmin_from'};
|
||||||
$fromdef = "webmin-noreply\@".&mailboxes::get_from_domain();
|
$fromdef = "webmin-noreply\@".&mailboxes::get_from_domain();
|
||||||
print &ui_table_row($text{'sendmail_from'},
|
print &ui_table_row($text{'sendmail_from'},
|
||||||
&ui_opt_textbox("from", $from, 40,
|
&ui_radio_table("from_def", $from ? 0 : 1,
|
||||||
&text('sendmail_fromdef', $fromdef)."<br>",
|
[ [ 1, "", &text('sendmail_fromdef', $fromdef) ],
|
||||||
$text{'sendmail_fromaddr'})." ".
|
[ 0, "", $text{'sendmail_fromaddr'}." ".
|
||||||
$text{'sendmail_name'}." ".
|
&ui_textbox("from", $from, 40)."<br>\n".
|
||||||
&ui_textbox("from_name", $mconfig{'webmin_from_name'}, 30), 3);
|
$text{'sendmail_name'}." ".
|
||||||
|
&ui_textbox("from_name", $mconfig{'webmin_from_name'}, 30) ] ]), 3);
|
||||||
|
|
||||||
# Default to address for notifications
|
# Default to address for notifications
|
||||||
$to = $gconfig{'webmin_email_to'};
|
$to = $gconfig{'webmin_email_to'};
|
||||||
|
|||||||
@@ -93,6 +93,19 @@ sub list_secret_keys
|
|||||||
return grep { $_->{'secret'} } &list_keys();
|
return grep { $_->{'secret'} } &list_keys();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# key_matches_id(id, &key)
|
||||||
|
# Returns 1 if some GnuPG key ID or fingerprint refers to the given key
|
||||||
|
sub key_matches_id
|
||||||
|
{
|
||||||
|
my ($id, $key) = @_;
|
||||||
|
$id = lc($id);
|
||||||
|
return 1 if (lc($key->{'key'}) eq $id || lc($key->{'key'}) =~ /\Q$id\E$/);
|
||||||
|
foreach my $key2 (@{$key->{'key2'}}) {
|
||||||
|
return 1 if (lc($key2) eq $id || lc($key2) =~ /\Q$id\E$/);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
# key_fingerprint(&key)
|
# key_fingerprint(&key)
|
||||||
sub key_fingerprint
|
sub key_fingerprint
|
||||||
{
|
{
|
||||||
@@ -206,46 +219,66 @@ if ($key) {
|
|||||||
$pflag = "--batch --passphrase-file ".
|
$pflag = "--batch --passphrase-file ".
|
||||||
quotemeta(&get_passphrase_file($key));
|
quotemeta(&get_passphrase_file($key));
|
||||||
}
|
}
|
||||||
my $cmd = "$gpgpath $pflag --output ".quotemeta($dstfile).
|
|
||||||
" --decrypt ".quotemeta($srcfile);
|
|
||||||
my ($fh, $fpid) = &proc::pty_process_exec($cmd);
|
|
||||||
my ($error, $seen_pass, $keyid);
|
my ($error, $seen_pass, $keyid);
|
||||||
$wait_for_debug = 1;
|
my $retry = 0;
|
||||||
while(1) {
|
while(1) {
|
||||||
my $rv = &wait_for($fh, "passphrase:", "key,\\s+ID\\s+(\\S+),", "failed.*\\n", "error.*\\n", "invalid.*\\n", "signal caught.*\\n");
|
unlink($dstfile);
|
||||||
if ($rv == 0) {
|
my $cmd = "$gpgpath --pinentry-mode loopback $pflag".
|
||||||
# Only needed if caller didn't supply a key with passphrase
|
" --output ".quotemeta($dstfile).
|
||||||
last if ($seen_pass++);
|
" --decrypt ".quotemeta($srcfile);
|
||||||
sleep(1);
|
my ($fh, $fpid) = &proc::pty_process_exec($cmd);
|
||||||
syswrite($fh, "$pass\n", length("$pass\n"));
|
my $rerun = 0;
|
||||||
}
|
$error = $seen_pass = $keyid = undef;
|
||||||
elsif ($rv == 1) {
|
while(1) {
|
||||||
# Only needed if caller didn't supply a key
|
my $rv = &wait_for($fh, "passphrase:", "key,\\s+ID\\s+(\\S+),", "failed.*\\n", "error.*\\n", "invalid.*\\n", "signal caught.*\\n");
|
||||||
$keyid = $matches[1];
|
if ($rv == 0) {
|
||||||
my $rkey;
|
# Only needed if caller didn't supply a key with passphrase
|
||||||
($rkey) = grep { &indexof($matches[1], @{$_->{'key2'}}) >= 0 ||
|
last if ($seen_pass++);
|
||||||
$_->{'key'} eq $matches[1] }
|
sleep(1);
|
||||||
&list_secret_keys();
|
syswrite($fh, "$pass\n", length("$pass\n"));
|
||||||
if ($rkey && $key) {
|
|
||||||
# Does discovered key match?
|
|
||||||
return &text('gnupg_ecryptkey2', "<tt>$keyid</tt>")
|
|
||||||
if ($rkey->{'key'} ne $key->{'key'});
|
|
||||||
}
|
}
|
||||||
elsif ($rkey && !$key) {
|
elsif ($rv == 1) {
|
||||||
# Discovered the key to use
|
# Only needed if caller didn't supply a key
|
||||||
$pass = &get_passphrase($rkey);
|
$keyid = $matches[1];
|
||||||
$key = $rkey;
|
my $rkey;
|
||||||
|
($rkey) = grep { &key_matches_id($matches[1], $_) }
|
||||||
|
&list_secret_keys();
|
||||||
|
if ($rkey && $key) {
|
||||||
|
# Does discovered key match?
|
||||||
|
return &text('gnupg_ecryptkey2', "<tt>$keyid</tt>")
|
||||||
|
if (!&key_matches_id($keyid, $key));
|
||||||
|
}
|
||||||
|
elsif ($rkey && !$key) {
|
||||||
|
# Discovered the key to use. Retry with a stored
|
||||||
|
# passphrase file for newer GnuPG versions
|
||||||
|
$pass = &get_passphrase($rkey);
|
||||||
|
$key = $rkey;
|
||||||
|
if (defined($pass) && !$retry++) {
|
||||||
|
$pflag = "--batch --passphrase-file ".
|
||||||
|
quotemeta(
|
||||||
|
&get_passphrase_file(
|
||||||
|
$key));
|
||||||
|
$rerun++;
|
||||||
|
last;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
elsif ($rv > 1) {
|
||||||
|
$error++;
|
||||||
|
last;
|
||||||
|
}
|
||||||
|
elsif ($rv < 0) {
|
||||||
|
last;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
elsif ($rv > 1) {
|
if ($rerun) {
|
||||||
$error++;
|
kill('TERM', $fpid);
|
||||||
last;
|
close($fh);
|
||||||
}
|
next;
|
||||||
elsif ($rv < 0) {
|
|
||||||
last;
|
|
||||||
}
|
}
|
||||||
|
close($fh);
|
||||||
|
last;
|
||||||
}
|
}
|
||||||
close($fh);
|
|
||||||
&reset_environment();
|
&reset_environment();
|
||||||
unlink($srcfile);
|
unlink($srcfile);
|
||||||
my $dst = &read_file_contents($dstfile);
|
my $dst = &read_file_contents($dstfile);
|
||||||
@@ -283,7 +316,8 @@ if (!defined($pass)) {
|
|||||||
return $text{'gnupg_esignpass'}.". ".
|
return $text{'gnupg_esignpass'}.". ".
|
||||||
&text('gnupg_canset', "/gnupg/edit_key.cgi?key=$key->{'key'}").".";
|
&text('gnupg_canset', "/gnupg/edit_key.cgi?key=$key->{'key'}").".";
|
||||||
}
|
}
|
||||||
my $pflag = "--batch --passphrase-file ".quotemeta(&get_passphrase_file($key));
|
my $pflag = "--batch --pinentry-mode loopback --passphrase-file ".
|
||||||
|
quotemeta(&get_passphrase_file($key));
|
||||||
my $cmd;
|
my $cmd;
|
||||||
if ($mode == 0) {
|
if ($mode == 0) {
|
||||||
$cmd = "$gpgpath $pflag --output ".quotemeta($dstfile)." --default-key $key->{'key'} --sign ".quotemeta($srcfile);
|
$cmd = "$gpgpath $pflag --output ".quotemeta($dstfile)." --default-key $key->{'key'} --sign ".quotemeta($srcfile);
|
||||||
|
|||||||
@@ -224,24 +224,57 @@ sub message_twofactor_totp
|
|||||||
my ($user) = @_;
|
my ($user) = @_;
|
||||||
my $name = &get_display_hostname()." (".$user->{'name'}.")";
|
my $name = &get_display_hostname()." (".$user->{'name'}.")";
|
||||||
my $str = "otpauth://totp/".$name."?secret=".$user->{'twofactor_id'};
|
my $str = "otpauth://totp/".$name."?secret=".$user->{'twofactor_id'};
|
||||||
my $url;
|
my $qrcode = &ui_tag('p',
|
||||||
|
&text('twofactor_qrcode', "<tt>$user->{'twofactor_id'}</tt>"));
|
||||||
if (&can_generate_qr()) {
|
if (&can_generate_qr()) {
|
||||||
|
my $url;
|
||||||
if (&get_product_name() eq 'usermin') {
|
if (&get_product_name() eq 'usermin') {
|
||||||
$url = "qr.cgi?size=6&str=".&urlize($str);
|
$url = "qr.cgi?size=6";
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$url = "$gconfig{'webprefix'}/webmin/qr.cgi?".
|
$url = "$gconfig{'webprefix'}/webmin/qr.cgi?size=6";
|
||||||
"size=6&str=".&urlize($str);
|
|
||||||
}
|
}
|
||||||
|
my $id = "twofactor_qr_".int(time())."_".int(rand(1000000));
|
||||||
|
my $img = &ui_tag('img', undef,
|
||||||
|
{ 'id' => $id, 'border' => 0,
|
||||||
|
'style' => 'width:210px; height:210px; '.
|
||||||
|
'border:1px solid #444;',
|
||||||
|
'alt' => 'QR code' });
|
||||||
|
my $id_js = "e_javascript($id);
|
||||||
|
my $url_js = "e_javascript($url);
|
||||||
|
my $str_js = "e_javascript($str);
|
||||||
|
return <<EOF;
|
||||||
|
$qrcode$img
|
||||||
|
<script>
|
||||||
|
(function() {
|
||||||
|
const img = document.getElementById("$id_js"),
|
||||||
|
body = "str=" + encodeURIComponent("$str_js");
|
||||||
|
fetch("$url_js", {
|
||||||
|
method: "POST",
|
||||||
|
body: body
|
||||||
|
}).then(function(response) {
|
||||||
|
if (!response.ok) { return null; }
|
||||||
|
return response.blob();
|
||||||
|
}).then(function(blob) {
|
||||||
|
if (!blob) { return; }
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.onloadend = function() { img.src = reader.result; };
|
||||||
|
reader.readAsDataURL(blob);
|
||||||
|
}).catch(function() { });
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
<p>
|
||||||
|
EOF
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$url = "https://api.qrserver.com/v1/create-qr-code/?".
|
my $url = "https://api.qrserver.com/v1/create-qr-code/?".
|
||||||
"size=200x200&data=".&urlize($str);
|
"size=200x200&data=".&urlize($str);
|
||||||
|
my $img = &ui_tag('img', undef,
|
||||||
|
{ 'src' => $url, 'border' => 0, 'alt' => 'QR code' });
|
||||||
|
return <<EOF;
|
||||||
|
$qrcode$img<p>
|
||||||
|
EOF
|
||||||
}
|
}
|
||||||
my $rv;
|
|
||||||
$rv .= &text('twofactor_qrcode', "<tt>$user->{'twofactor_id'}</tt>")."<p>\n";
|
|
||||||
$rv .= "<img src='$url' border=0><p>\n";
|
|
||||||
return $rv;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# validate_twofactor_totp(id, token)
|
# validate_twofactor_totp(id, token)
|
||||||
|
|||||||
Reference in New Issue
Block a user