Compare commits

..

22 Commits

Author SHA1 Message Date
Jamie Cameron
534c529705 Revert "Fix to use universal upload tracking directory"
Some checks failed
webmin.dev: webmin/webmin / build (push) Has been cancelled
This reverts commit e8e804ddca.
2026-01-25 21:17:14 -08:00
Ilia Ross
2d0063129c Update changelog
Some checks failed
webmin.dev: webmin/webmin / build (push) Has been cancelled
2026-01-25 13:29:02 +02:00
Ilia Ross
ad37eabdfe Update Xterm.js to the latest version
Some checks failed
webmin.dev: webmin/webmin / build (push) Has been cancelled
2026-01-25 01:04:44 +02:00
Ilia Ross
6d3da61b95 Update changelog
Some checks failed
webmin.dev: webmin/webmin / build (push) Has been cancelled
2026-01-24 00:49:57 +02:00
Ilia Ross
4d05e8a2d0 Merge branch 'master' of github.com:webmin/webmin 2026-01-24 00:38:18 +02:00
Ilia Ross
e8e804ddca Fix to use universal upload tracking directory
https://forum.virtualmin.com/t/upload-progress-bar-not-showing/136374?u=ilia
2026-01-24 00:37:52 +02:00
Ilia Ross
202a1b0b78 Add API to pick a writable system-wide temp directory 2026-01-24 00:34:06 +02:00
Jamie Cameron
3c1c327530 Show the full version properly 2026-01-23 13:46:21 -08:00
Jamie Cameron
2d7900d550 Fix comment 2026-01-23 13:42:41 -08:00
Ilia Ross
e9586fb2d8 Fix to use fixed PHP 5.6 label in the dropdown
Some checks failed
webmin.dev: webmin/webmin / build (push) Has been cancelled
https://github.com/webmin/webmin/issues/2622#issuecomment-3787051348
2026-01-23 14:04:39 +02:00
Jamie Cameron
9d95b5c977 Merge branch 'master' of github.com:webmin/webmin
Some checks failed
webmin.dev: webmin/webmin / build (push) Has been cancelled
2026-01-22 15:33:27 -08:00
Jamie Cameron
d9c651d06d Add extra protection against packets somehow arriving before handshake is called 2026-01-22 15:33:02 -08:00
Ilia Ross
c70bae48aa Fix release date in changelog
Some checks failed
webmin.dev: webmin/webmin / build (push) Has been cancelled
[rebuild-all-modules]
2026-01-22 12:16:26 +02:00
Jamie Cameron
83abdc8858 Logrotate requires 0644 or 0444 permissions
https://github.com/webmin/webmin/issues/2396
2026-01-13 15:24:54 -08:00
Ilia Ross
2833450b48 Update changelog (ongoing) 2026-01-10 20:47:43 +02:00
Ilia Ross
a0c023637f Fix to improve the message when socket auth is used 2026-01-10 20:44:59 +02:00
Ilia Ross
6721e13a6b Fix MariaDB version 10.3 already supports plugins 2026-01-10 20:31:47 +02:00
Ilia Ross
2b2814fdf4 Update changelog for 2.621 2026-01-09 18:51:04 +02:00
Ilia Ross
4dacdc31f6 Fix to prevents NAT from dropping idle connections
https://forum.virtualmin.com/t/problem-with-backup-of-large-virtual-servers-to-a-remote-webmin-server/136186/46
2026-01-09 18:14:45 +02:00
Ilia Ross
2627ba289e Add better logging for RPC 2026-01-09 18:12:37 +02:00
Ilia Ross
b4a67a0d90 Fix dates consistency in the changelog 2026-01-09 14:28:12 +02:00
Ilia Ross
63af275296 Update changelog for 2.620 2026-01-09 14:27:46 +02:00
17 changed files with 199 additions and 79 deletions

View File

@@ -1,5 +1,35 @@
## Changelog
#### 2.621 (January 25, 2026)
* 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 make upload tracking work correctly in all situations and on all systems
* Fix to correctly display the PHP version in the PHP Configuration module when managing packages
* Update Xterm.js to the latest version with lots of improvements and fixes
* Update Authentic theme to the latest version with various improvements and fixes:
* Fix the support for the cloned Terminal module
* Fix error handling for file uploads when the user is out of quota or the system is out of disk space in the File Manager module
* Fix to stop loading full file into memory for upload check to prevent memory leak on large uploads in the File Manager module
* Fix to permanently save the state of the navigation menu and right-side slider when toggled
#### 2.620 (January 9, 2026)
* Add ability to use correct driver depending on the database in MySQL/MariaDB module
* Add improvements to BIND DNS module for better key management
* Add support for Ubuntu 26.04 development preview
* Add a config option to increase the RPC timeout
* Add support for EC SSL certificate and key in the ProFTPd module
* Add support for using `gpart` in FreeBSD disk management module
* Add support for Ed25519 public key in User and Groups module
* Fix RPC session timeout during large file transfers
* Fix selection and configuration of TLS certificate and key in the ProFTPd module
* Update Authentic theme to the latest version with various improvements and fixes:
* Add support for multiple scrollable tabs in the File Manager
* Fix displaying of the right-side toolbar in File Manager when using Safari
* Fix to print menu separator when no virtual servers are added yet in Virtualmin
* Fix bugs in white palette
* Fix exported file name in data tables
#### 2.610 (November 23, 2025)
* Fix to drop dependency on `IO::Pty` Perl module
* Fix `virtual-server` module server-side search to work correctly
@@ -270,7 +300,7 @@
* Fix to using the `qrencode` command to generate QR codes locally instead of the remote Google Chart API
* Fix a number of various other issues
#### 2.105 (November 09, 2023)
#### 2.105 (November 9, 2023)
* Fix param to read only headers [sourceforge.net/usermin-bugs#501](https://sourceforge.net/p/webadmin/usermin-bugs/501/)
* Fix not to set `reuse` flag on initial Let's Encrypt request
* Fix to correctly escape mail file names upon deletion
@@ -285,7 +315,7 @@
* Fix the absent init script for legacy systems after the initial installation
* Update the Authentic theme to the latest version with various fixes and improvements
#### 2.103 (October 08, 2023)
#### 2.103 (October 8, 2023)
* Add support for hostname detection using `hostnamectl` command
* Add support for other ACME services
* Add ability to hide dotfiles in File Manager [#1578](https://github.com/webmin/authentic-theme/issues/1578)
@@ -334,7 +364,7 @@
* Fix clearing packages caches before checking for updates in status collection #1863
* Update the Authentic theme to the latest version
#### 2.020 (March 08, 2023)
#### 2.020 (March 8, 2023)
* Add full locale support
* Add slave zone file format option in BIND DNS module
* Add support for editing ACLs in File Manager
@@ -504,10 +534,10 @@ This release updates the built-in Let's Encrypt client, adds support for creatin
#### Version 1.930 (August 18, 2019)
These updates fix a [security vulnerability](http://webmin.com/security.html) and should be installed IMMEDIATELY by all users. Although it is not exploitable in a Webmin install with the default configuration, upgrading is strongly recommended.
#### Version 1.920 (July 04, 2019)
#### Version 1.920 (July 4, 2019)
This update includes the latest theme version, translation updates, the ability to disable hosts file entries, easier monitoring of bootup actions, and a bunch of bugfixes.
#### Version 1.910 (May 09, 2019)
#### Version 1.910 (May 9, 2019)
This release includes theme and translation updates, a page for editing package repositories, cron and status module improvements, and a bunch of other bugfixes and small improvements.
#### Version 1.900 (November 19, 2018)
@@ -519,7 +549,7 @@ This version includes Ubuntu 18 network config support, translation updates, mul
#### Version 1.880 (March 16, 2018)
This version includes German, Catalan and Bulgarian translation updates, a new version of the Authentic theme, support for directly editing the MySQL and PostgreSQL config files, Let's Encrypt bugfixes, more control over system status email notifications, and more.
#### Version 1.870 (December 08, 2018)
#### Version 1.870 (December 8, 2018)
This release includes many translation updates, fixes for Let's Encrypt support, UI cleanups, and most importantly a new major version of the Authentic theme.
#### Version 1.860 (October 10, 2017)
@@ -528,7 +558,7 @@ This release includes Let's Encrypt DNS fixes, Majordomo module improvements, XS
#### Version 1.850 (June 28, 2017)
This release includes Let's Encrypt fixes, Majordomo module improvements, FirewallD forwarding support, translation updates, an update to the Authentic theme, and a bunch of other bugfixes.
#### Version 1.840 (May 08, 2017)
#### Version 1.840 (May 8, 2017)
This major release includes a large theme update, XSS security fixes, per-domain SSL cert support, thin-provisioned LVM support, Let's Encrypt improvements, translation updates, and the usual gang of bugfixes. Also available is Usermin 1.710, which contains many of the same updates.
#### Version 1.830 (December 29, 2016)

File diff suppressed because one or more lines are too long

View File

@@ -78,7 +78,7 @@ if ($use_ipv6) {
}
$sel = select($rmask, undef, undef, $config_rpc_timeout);
if ($sel <= 0) {
print STDERR "fastrpc: accept timed out\n"
print STDERR "fastrpc[$$]: accept timed out\n"
if ($gconfig{'rpcdebug'});
exit;
}
@@ -115,8 +115,15 @@ while(1) {
if ($sel <= 0) {
# Don't kill the control session while a tcpwrite/tcpread is
# running
next if (keys %xfer_kids);
print STDERR "fastrpc: session timed out\n"
my $nk = scalar(keys %xfer_kids);
if ($nk) {
print STDERR "fastrpc[$$]: idle timeout, but $nk ".
"transfer(s) active: ".
join(",", sort keys %xfer_kids)."\n"
if ($gconfig{'rpcdebug'});
next;
}
print STDERR "fastrpc[$$]: session timed out\n"
if ($gconfig{'rpcdebug'});
last;
}
@@ -132,7 +139,7 @@ while(1) {
exit if ($rv <= 0);
$rawarg .= $got;
}
print STDERR "fastrpc: raw $rawarg\n" if ($gconfig{'rpcdebug'});
print STDERR "fastrpc[$$]: raw $rawarg\n" if ($gconfig{'rpcdebug'});
my $dumper = substr($rawarg, 0, 5) eq '$VAR1' ? 1 : 0;
my $arg = &unserialise_variable($rawarg);
@@ -140,19 +147,19 @@ while(1) {
my $rv;
if ($arg->{'action'} eq 'ping') {
# Just respond with an OK
print STDERR "fastrpc: ping\n" if ($gconfig{'rpcdebug'});
print STDERR "fastrpc[$$]: ping\n" if ($gconfig{'rpcdebug'});
$rv = { 'status' => 1 };
}
elsif ($arg->{'action'} eq 'check') {
# Check if some module is supported
print STDERR "fastrpc: check $arg->{'module'}\n" if ($gconfig{'rpcdebug'});
print STDERR "fastrpc[$$]: check $arg->{'module'}\n" if ($gconfig{'rpcdebug'});
$rv = { 'status' => 1,
'rv' => &foreign_check($arg->{'module'}, undef, undef,
$arg->{'api'}) };
}
elsif ($arg->{'action'} eq 'config') {
# Get the config for some module
print STDERR "fastrpc: config $arg->{'module'}\n" if ($gconfig{'rpcdebug'});
print STDERR "fastrpc[$$]: config $arg->{'module'}\n" if ($gconfig{'rpcdebug'});
my %config = &foreign_config($arg->{'module'});
$rv = { 'status' => 1, 'rv' => \%config };
}
@@ -161,7 +168,7 @@ while(1) {
my $file = $arg->{'file'} ? $arg->{'file'} :
$arg->{'name'} ? &tempname($arg->{'name'}) :
&tempname();
print STDERR "fastrpc: write $file\n" if ($gconfig{'rpcdebug'});
print STDERR "fastrpc[$$]: write $file\n" if ($gconfig{'rpcdebug'});
open(FILE, ">$file");
binmode(FILE);
print FILE $arg->{'data'};
@@ -173,7 +180,7 @@ while(1) {
my $file = $arg->{'file'} ? $arg->{'file'} :
$arg->{'name'} ? &tempname($arg->{'name'}) :
&tempname();
print STDERR "fastrpc: tcpwrite $file\n" if ($gconfig{'rpcdebug'});
print STDERR "fastrpc[$$]: tcpwrite $file\n" if ($gconfig{'rpcdebug'});
my $tsock = time().$$;
my $tsock6 = $use_ipv6 ? time().$$."v6" : undef;
my $tport = $port + 1;
@@ -187,7 +194,7 @@ while(1) {
close(MAIN);
close(MAIN6) if ($use_ipv6);
# Accept connection in separate process
print STDERR "fastrpc: tcpwrite $file port $tport\n"
print STDERR "fastrpc[$$]: tcpwrite $file port $tport\n"
if ($gconfig{'rpcdebug'});
my $rmask;
vec($rmask, fileno($tsock), 1) = 1;
@@ -202,12 +209,12 @@ while(1) {
elsif ($use_ipv6 && vec($rmask, fileno($tsock6), 1)) {
accept(TRANS, $tsock6) || exit;
}
print STDERR "fastrpc: tcpwrite $file accepted\n" if ($gconfig{'rpcdebug'});
print STDERR "fastrpc[$$]: tcpwrite $file accepted\n" if ($gconfig{'rpcdebug'});
my $buf;
my $err;
if (open(FILE, ">$file")) {
binmode(FILE);
print STDERR "fastrpc: tcpwrite $file writing\n" if ($gconfig{'rpcdebug'});
print STDERR "fastrpc[$$]: tcpwrite $file writing\n" if ($gconfig{'rpcdebug'});
my $bs = &get_buffer_size();
while(read(TRANS, $buf, $bs) > 0) {
my $ok = (print FILE $buf);
@@ -217,10 +224,10 @@ while(1) {
}
}
close(FILE);
print STDERR "fastrpc: tcpwrite $file written\n" if ($gconfig{'rpcdebug'});
print STDERR "fastrpc[$$]: tcpwrite $file written\n" if ($gconfig{'rpcdebug'});
}
else {
print STDERR "fastrpc: tcpwrite $file open failed $!\n" if ($gconfig{'rpcdebug'});
print STDERR "fastrpc[$$]: tcpwrite $file open failed $!\n" if ($gconfig{'rpcdebug'});
$err = "Failed to open $file : $!";
}
print TRANS $err ? "$err\n" : "OK\n";
@@ -229,7 +236,7 @@ while(1) {
}
else {
$xfer_kids{$cpid} = 1;
print STDERR "fastrpc: tcpwrite $file started\n"
print STDERR "fastrpc[$$]: tcpwrite $file started\n"
if ($gconfig{'rpcdebug'});
$rv = { 'status' => 1, 'rv' => [ $file, $tport ] };
}
@@ -238,7 +245,7 @@ while(1) {
}
elsif ($arg->{'action'} eq 'read') {
# Transfer data from a file
print STDERR "fastrpc: read $arg->{'file'}\n" if ($gconfig{'rpcdebug'});
print STDERR "fastrpc[$$]: read $arg->{'file'}\n" if ($gconfig{'rpcdebug'});
my ($data, $got);
open(FILE, "<$arg->{'file'}");
binmode(FILE);
@@ -251,7 +258,7 @@ while(1) {
}
elsif ($arg->{'action'} eq 'tcpread') {
# Transfer data from a file over TCP connection
print STDERR "fastrpc: tcpread $arg->{'file'}\n" if ($gconfig{'rpcdebug'});
print STDERR "fastrpc[$$]: tcpread $arg->{'file'}\n" if ($gconfig{'rpcdebug'});
if (-d $arg->{'file'}) {
$rv = { 'status' => 1, 'rv' => [ undef, "$arg->{'file'} is a directory" ] };
}
@@ -296,7 +303,7 @@ while(1) {
}
else {
$xfer_kids{$cpid} = 1;
print STDERR "fastrpc: tcpread $arg->{'file'} ".
print STDERR "fastrpc[$$]: tcpread $arg->{'file'} ".
"started\n"
if ($gconfig{'rpcdebug'});
$rv = { 'status' => 1,
@@ -309,23 +316,23 @@ while(1) {
}
elsif ($arg->{'action'} eq 'require') {
# require a library
print STDERR "fastrpc: require $arg->{'module'}/$arg->{'file'}\n" if ($gconfig{'rpcdebug'});
print STDERR "fastrpc[$$]: require $arg->{'module'}/$arg->{'file'}\n" if ($gconfig{'rpcdebug'});
eval {
&foreign_require($arg->{'module'},
$arg->{'file'});
};
if ($@) {
print STDERR "fastrpc: require error $@\n" if ($gconfig{'rpcdebug'});
print STDERR "fastrpc[$$]: require error $@\n" if ($gconfig{'rpcdebug'});
$rv = { 'status' => 0, 'rv' => $@ };
}
else {
print STDERR "fastrpc: require done\n" if ($gconfig{'rpcdebug'});
print STDERR "fastrpc[$$]: require done\n" if ($gconfig{'rpcdebug'});
$rv = { 'status' => 1 };
}
}
elsif ($arg->{'action'} eq 'call') {
# execute a function
print STDERR "fastrpc: call $arg->{'module'}::$arg->{'func'}(",join(",", @{$arg->{'args'}}),")\n" if ($gconfig{'rpcdebug'});
print STDERR "fastrpc[$$]: call $arg->{'module'}::$arg->{'func'}(",join(",", @{$arg->{'args'}}),")\n" if ($gconfig{'rpcdebug'});
my @rv;
eval {
local $main::error_must_die = 1;
@@ -334,7 +341,7 @@ while(1) {
@{$arg->{'args'}});
};
if ($@) {
print STDERR "fastrpc: call error $@\n" if ($gconfig{'rpcdebug'});
print STDERR "fastrpc[$$]: call error $@\n" if ($gconfig{'rpcdebug'});
$rv = { 'status' => 0, 'rv' => $@ };
}
elsif (@rv == 1) {
@@ -343,11 +350,11 @@ while(1) {
else {
$rv = { 'status' => 1, 'arv' => \@rv };
}
print STDERR "fastrpc: call $arg->{'module'}::$arg->{'func'} done = ",join(",", @rv),"\n" if ($gconfig{'rpcdebug'});
print STDERR "fastrpc[$$]: call $arg->{'module'}::$arg->{'func'} done = ",join(",", @rv),"\n" if ($gconfig{'rpcdebug'});
}
elsif ($arg->{'action'} eq 'eval') {
# eval some perl code
print STDERR "fastrpc: eval $arg->{'module'} $arg->{'code'}\n" if ($gconfig{'rpcdebug'});
print STDERR "fastrpc[$$]: eval $arg->{'module'} $arg->{'code'}\n" if ($gconfig{'rpcdebug'});
my $erv;
if ($arg->{'module'}) {
my $pkg = $arg->{'module'};
@@ -358,7 +365,7 @@ while(1) {
else {
$erv = eval $arg->{'code'};
}
print STDERR "fastrpc: eval $arg->{'module'} $arg->{'code'} done = $rv error = $@\n" if ($gconfig{'rpcdebug'});
print STDERR "fastrpc[$$]: eval $arg->{'module'} $arg->{'code'} done = $rv error = $@\n" if ($gconfig{'rpcdebug'});
if ($@) {
$rv = { 'status' => 0, 'rv' => $@ };
}
@@ -367,11 +374,11 @@ while(1) {
}
}
elsif ($arg->{'action'} eq 'quit') {
print STDERR "fastrpc: quit\n" if ($gconfig{'rpcdebug'});
print STDERR "fastrpc[$$]: quit\n" if ($gconfig{'rpcdebug'});
$rv = { 'status' => 1 };
}
else {
print STDERR "fastrpc: unknown $arg->{'action'}\n" if ($gconfig{'rpcdebug'});
print STDERR "fastrpc[$$]: unknown $arg->{'action'}\n" if ($gconfig{'rpcdebug'});
$rv = { 'status' => 0 };
}
$rawrv = &serialise_variable($rv, $dumper);

View File

@@ -344,7 +344,7 @@ foreach $c (@$conf) {
}
print TEMP map { "$_\n" } &directive_lines($_[0]);
close(TEMP);
&set_ownership_permissions(undef, undef, 0700, $temp);
&set_ownership_permissions(undef, undef, 0644, $temp);
local $out = &backquote_logged("$config{'logrotate'} -f $temp 2>&1");
return ($?, $out);
}

View File

@@ -885,7 +885,7 @@ root_epass1=No new password entered
root_epass2=Passwords do not match
root_none=No password!
root_auto=Automatic (typically <tt>root</tt>)
root_socket=The MySQL <tt>$1</tt> user is using Unix socket authentication, so no password is needed and the password cannot be changed.
root_socket=The MySQL <tt>$1</tt> user uses Unix socket authentication, so no password is needed. However, you can switch to password authentication on the <a href='list_users.cgi'>User Permissions</a> page.
mysqlpass_err=MySQL safe mode
mysqlpass_esafecmd=The command $1 needed to start MySQL with authentication disabled was not found

View File

@@ -2248,7 +2248,7 @@ return $rv->{'data'}->[0]->[0] =~ /unix_socket/i ? 'socket' : 'password';
sub list_authentication_plugins
{
my ($ver, $variant) = &get_remote_mysql_variant();
if ($variant eq "mariadb" && &compare_version_numbers($ver, "10.4") >= 0 ||
if ($variant eq "mariadb" && &compare_version_numbers($ver, "10.3") >= 0 ||
$variant eq "mysql" && &compare_version_numbers($ver, "5.7.6") >= 0) {
my $rv = &execute_sql($master_db, "show plugins");
my @plugins = map { $_->[0] } grep { $_->[1] eq 'ACTIVE' &&

View File

@@ -61,8 +61,7 @@ if (&foreign_installed("package-updates")) {
my @allpkgs = &extend_installable_php_packages(\@newpkgs);
@allpkgs = sort { $b->{'ver'} cmp $a->{'ver'} } @allpkgs;
print &ui_select("u", undef,
[ map { [ $_->{'name'},
"PHP $_->{'ver'}" ] } @allpkgs ]);
[ map { [ $_->{'name'}, "PHP $_->{'ver'}" ] } @allpkgs ]);
print &ui_hidden(
"redir", &get_webprefix()."/$module_name/list_pkgs.cgi");
print &ui_hidden("redirdesc", $text{'pkgs_title'});

View File

@@ -1047,7 +1047,8 @@ my @pkgs;
my @extra = ('cli', 'fpm');
foreach my $pkg (@{$pkgs}) {
my $p = { 'name' => $pkg->{'name'},
'ver' => $pkg->{'shortver'} };
'shortver' => $pkg->{'shortver'},
'ver' => $pkg->{'ver'} };
$p->{'name'} .= ' '.join(' ', map { "$1-$_" } @extra)
if ($p->{'name'} =~ /^(.*)-common$/);
push(@pkgs, $p);

View File

@@ -388,6 +388,58 @@ $str =~ s/["'<>&\\]/sprintf('\x%02x', ord $&)/ge;
return $str;
}
=head2 tempname_dir_sys()
Returns a shared base directory for system-wide temporary files. This directory
does not depend on the current user. The directory is chosen once per process
and verified by creating a temporary probe file.
The result is cached for the lifetime of the current Perl process.
=cut
sub tempname_dir_sys
{
# Cache per module
state %base;
my @can_dirs;
# Return cached value if already determined
my $mod = &get_module_name() || '';
return $base{$mod} if (defined $base{$mod});
# Check configured system temp dirs (module override first)
my $modk = $mod ? "tempdir_sys_$mod" : undef;
push(@can_dirs, $gconfig{$modk}) if ($modk && $gconfig{$modk});
push(@can_dirs, $gconfig{'tempdir_sys'}) if ($gconfig{'tempdir_sys'});
# Common fallbacks
push(@can_dirs, "/dev/shm", "/tmp", "/var/tmp", "/usr/tmp");
# Remove empty and duplicate entries, which can happen when both configured
# dirs are set to the same path, or when a configured path matches one of
# the built-in defaults
@can_dirs = &unique(grep { $_ } @can_dirs);
# Test each candidate in turn
for my $dir (@can_dirs) {
next if (!-d $dir);
next if (!-w $dir);
# Confirm we can actually create a file
my $tmp = "$dir/.webmin_${$}_".int(rand(1e16));
if (open(my $fh, ">", $tmp)) {
close($fh);
unlink($tmp);
$base{$mod} = $dir; # Success, cache and return
return $base{$mod};
}
}
my $keys = ($modk && $gconfig{$modk}) ? "$modk or tempdir_sys" : "tempdir_sys";
&error("No usable system temp directory found. Set $keys to a writable ".
"directory in $config_directory/config and try again.");
}
=head2 tempname_dir()
Returns the base directory under which temp files can be created.
@@ -8542,8 +8594,19 @@ else {
open(FILE, "<".$localfile) ||
return &$main::remote_error_handler("Failed to open $localfile : $!");
my $bs = &get_buffer_size();
my $last_ping = time();
my $ping_every = int(($gconfig{'rpc_timeout'} || 60) / 2);
$ping_every = 30 if ($ping_every < 30);
while(read(FILE, $got, $bs) > 0) {
print TWRITE $got;
if (time() - $last_ping >= $ping_every) {
eval {
local $main::remote_error_handler =
sub { return undef; };
&remote_rpc_call($host, { 'action' => 'ping' });
};
$last_ping = time();
}
}
close(FILE);
shutdown(TWRITE, 1);
@@ -8587,8 +8650,19 @@ else {
open(FILE, ">$localfile") ||
return &$main::remote_error_handler("Failed to open $localfile : $!");
my $bs = &get_buffer_size();
my $last_ping = time();
my $ping_every = int(($gconfig{'rpc_timeout'} || 60) / 2);
$ping_every = 30 if ($ping_every < 30);
while(read(TREAD, $got, $bs) > 0) {
print FILE $got;
if (time() - $last_ping >= $ping_every) {
eval {
local $main::remote_error_handler =
sub { return undef; };
&remote_rpc_call($host, { 'action' => 'ping' });
};
$last_ping = time();
}
}
close(FILE);
close(TREAD);
@@ -10847,7 +10921,7 @@ else {
&webmin_debug_log($1 eq ">" ? "WRITE" :
$1 eq ">>" ? "APPEND" : "READ", "nul") if ($db);
}
elsif ($file =~ /^(>|>>)(\/dev\/.*)/ || lc($file) eq "nul") {
elsif ($file =~ /^(>|>>)(\/dev\/(?!shm\/).*)/ || lc($file) eq "nul") {
# Writes to /dev/null or TTYs don't need to be handled
&webmin_debug_log($1 eq ">" ? "WRITE" : "APPEND", $2) if ($db);
return open($fh, "<".$file);
@@ -13997,8 +14071,7 @@ return $dir;
}
# allocate_miniserv_websocket([module], [base-remote-user])
# Allocate a new websocket and
# stores it miniserv.conf file
# Allocate a new websocket and stores it miniserv.conf file
sub allocate_miniserv_websocket
{
my ($module, $buser) = @_;

View File

@@ -46,7 +46,6 @@ my $termlinks =
'js' => ["xterm.js?$wver",
"xterm-addon-attach.js?$wver",
"xterm-addon-fit.js?$wver",
"xterm-addon-canvas.js?$wver",
"xterm-addon-webgl.js?$wver"] };
# Pre-process options
@@ -77,6 +76,7 @@ if ($conf_cols_n && $conf_rows_n && !$xmlhr) {
my $conf_screen_reader = $config{'screen_reader'} eq 'true' ? 'true' : 'false';
$termjs_opts{'Options'} = "{ cols: $env_cols, rows: $env_rows, ".
"screenReaderMode: $conf_screen_reader, ".
"overviewRuler: { width: 9 }, ".
"fontSize: $font_size }";
my $term_size = "
@@ -212,8 +212,7 @@ $ENV{'SESSION_ID'} = $main::session_id;
# Open the terminal
my $url = &get_miniserv_websocket_url($port, $config{'host'}, $module_name);
my $canvasAddon = $termlinks->{'js'}[3];
my $webGLAddon = $termlinks->{'js'}[4];
my $webGLAddon = $termlinks->{'js'}[3];
my $term_script = <<EOF;
(function() {
@@ -222,11 +221,10 @@ my $term_script = <<EOF;
err_conn_cannot = 'Cannot connect to the socket $url',
err_conn_lost = 'Connection to the socket $url lost',
webGLAddonLink = '$webGLAddon',
canvasAddonLink = '$canvasAddon',
detectWebGLContext = (function() {
const canvas = document.createElement("canvas"),
gl = canvas.getContext("webgl") ||
canvas.getContext("experimental-webgl");
gl = canvas.getContext("webgl") ||
canvas.getContext("experimental-webgl");
return gl instanceof WebGLRenderingContext ? true : false;
})();
socket.onopen = function() {
@@ -234,27 +232,31 @@ my $term_script = <<EOF;
attachAddon = new AttachAddon.AttachAddon(this),
fitAddon = new FitAddon.FitAddon(),
renderScript = document.createElement('script');
renderScript.src = detectWebGLContext ? webGLAddonLink : canvasAddonLink;
renderScript.src = webGLAddonLink;
renderScript.async = false;
document.body.appendChild(renderScript);
// Wait to load requested render addon
renderScript.addEventListener('load', function() {
const rendererAddon = detectWebGLContext ?
new WebglAddon.WebglAddon() :
new CanvasAddon.CanvasAddon();
let rendererAddon;
if (detectWebGLContext && typeof WebglAddon === 'object') {
rendererAddon = new WebglAddon.WebglAddon();
term.loadAddon(rendererAddon);
// Handle case of dropping WebGL context
if (rendererAddon.onContextLoss) {
rendererAddon.onContextLoss(function() {
rendererAddon.dispose();
});
}
}
term.loadAddon(attachAddon);
term.loadAddon(fitAddon);
term.loadAddon(rendererAddon);
term.open(termcont);
setTimeout(function() {term.focus()}, 6e2);
// Handle case of dropping WebGL context
if (typeof WebglAddon === 'object') {
rendererAddon.onContextLoss(function() {
rendererAddon.dispose();
});
}
setTimeout(function() { term.focus() }, 6e2);
// On resize event triggered by fit()
term.onResize(function(e) {

View File

@@ -114,6 +114,7 @@ my $server_socket = IO::Socket::INET->new(
ReuseAddr => 1,
);
$server_socket || die "failed to listen on port $port";
our $connected = 0;
Net::WebSocket::Server->new(
listen => $server_socket,
on_connect => sub {
@@ -129,6 +130,7 @@ Net::WebSocket::Server->new(
$conn->on(
handshake => sub {
# Is the key valid for this Webmin session?
&error_stderr("WebSocket handshake received");
my ($conn, $handshake) = @_;
my $key = $handshake->req->fields->{'sec-websocket-key'};
my $dsess = &encode_base64($main::session_id);
@@ -138,13 +140,18 @@ Net::WebSocket::Server->new(
&error_stderr("Key $key does not match session ID $dsess");
$conn->disconnect();
}
else {
$connected = 1;
}
},
ready => sub {
my ($conn) = @_;
$connected || die "Not connected yet in ready";
$conn->send_binary($shellbuf) if ($shellbuf);
},
utf8 => sub {
my ($conn, $msg) = @_;
$connected || die "Not connected yet in utf8";
utf8::encode($msg) if (utf8::is_utf8($msg));
# Check for resize escape sequence explicitly
if ($msg =~ /^\\033\[8;\((\d+)\);\((\d+)\)t$/) {
@@ -168,6 +175,7 @@ Net::WebSocket::Server->new(
}
},
disconnect => sub {
$connected = 0;
&error_stderr("WebSocket connection closed");
&remove_miniserv_websocket($port, $module_name);
kill('KILL', $pid) if ($pid);

View File

@@ -1,2 +1,2 @@
// 0.11.0
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.AttachAddon=t():e.AttachAddon=t()}(self,(()=>(()=>{"use strict";var e={};return(()=>{var t=e;function s(e,t,s){return e.addEventListener(t,s),{dispose:()=>{s&&e.removeEventListener(t,s)}}}Object.defineProperty(t,"__esModule",{value:!0}),t.AttachAddon=void 0,t.AttachAddon=class{constructor(e,t){this._disposables=[],this._socket=e,this._socket.binaryType="arraybuffer",this._bidirectional=!(t&&!1===t.bidirectional)}activate(e){this._disposables.push(s(this._socket,"message",(t=>{const s=t.data;e.write("string"==typeof s?s:new Uint8Array(s))}))),this._bidirectional&&(this._disposables.push(e.onData((e=>this._sendData(e)))),this._disposables.push(e.onBinary((e=>this._sendBinary(e))))),this._disposables.push(s(this._socket,"close",(()=>this.dispose()))),this._disposables.push(s(this._socket,"error",(()=>this.dispose())))}dispose(){for(const e of this._disposables)e.dispose()}_sendData(e){this._checkOpenSocket()&&this._socket.send(e)}_sendBinary(e){if(!this._checkOpenSocket())return;const t=new Uint8Array(e.length);for(let s=0;s<e.length;++s)t[s]=255&e.charCodeAt(s);this._socket.send(t)}_checkOpenSocket(){switch(this._socket.readyState){case WebSocket.OPEN:return!0;case WebSocket.CONNECTING:throw new Error("Attach addon was loaded before socket was open");case WebSocket.CLOSING:return console.warn("Attach addon socket is closing"),!1;case WebSocket.CLOSED:throw new Error("Attach addon socket is closed");default:throw new Error("Unexpected socket state")}}}})(),e})()));
// 0.12.0
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.AttachAddon=t():e.AttachAddon=t()}(globalThis,(()=>(()=>{"use strict";var e={};return(()=>{var t=e;function s(e,t,s){return e.addEventListener(t,s),{dispose:()=>{s&&e.removeEventListener(t,s)}}}Object.defineProperty(t,"__esModule",{value:!0}),t.AttachAddon=void 0,t.AttachAddon=class{constructor(e,t){this._disposables=[],this._socket=e,this._socket.binaryType="arraybuffer",this._bidirectional=!(t&&!1===t.bidirectional)}activate(e){this._disposables.push(s(this._socket,"message",(t=>{const s=t.data;e.write("string"==typeof s?s:new Uint8Array(s))}))),this._bidirectional&&(this._disposables.push(e.onData((e=>this._sendData(e)))),this._disposables.push(e.onBinary((e=>this._sendBinary(e))))),this._disposables.push(s(this._socket,"close",(()=>this.dispose()))),this._disposables.push(s(this._socket,"error",(()=>this.dispose())))}dispose(){for(const e of this._disposables)e.dispose()}_sendData(e){this._checkOpenSocket()&&this._socket.send(e)}_sendBinary(e){if(!this._checkOpenSocket())return;const t=new Uint8Array(e.length);for(let s=0;s<e.length;++s)t[s]=255&e.charCodeAt(s);this._socket.send(t)}_checkOpenSocket(){switch(this._socket.readyState){case WebSocket.OPEN:return!0;case WebSocket.CONNECTING:throw new Error("Attach addon was loaded before socket was open");case WebSocket.CLOSING:return console.warn("Attach addon socket is closing"),!1;case WebSocket.CLOSED:throw new Error("Attach addon socket is closed");default:throw new Error("Unexpected socket state")}}}})(),e})()));

File diff suppressed because one or more lines are too long

View File

@@ -1,2 +1,2 @@
// 0.10.0
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.FitAddon=t():e.FitAddon=t()}(self,(()=>(()=>{"use strict";var e={};return(()=>{var t=e;Object.defineProperty(t,"__esModule",{value:!0}),t.FitAddon=void 0,t.FitAddon=class{activate(e){this._terminal=e}dispose(){}fit(){const e=this.proposeDimensions();if(!e||!this._terminal||isNaN(e.cols)||isNaN(e.rows))return;const t=this._terminal._core;this._terminal.rows===e.rows&&this._terminal.cols===e.cols||(t._renderService.clear(),this._terminal.resize(e.cols,e.rows))}proposeDimensions(){if(!this._terminal)return;if(!this._terminal.element||!this._terminal.element.parentElement)return;const e=this._terminal._core,t=e._renderService.dimensions;if(0===t.css.cell.width||0===t.css.cell.height)return;const r=0===this._terminal.options.scrollback?0:e.viewport.scrollBarWidth,i=window.getComputedStyle(this._terminal.element.parentElement),o=parseInt(i.getPropertyValue("height")),s=Math.max(0,parseInt(i.getPropertyValue("width"))),n=window.getComputedStyle(this._terminal.element),l=o-(parseInt(n.getPropertyValue("padding-top"))+parseInt(n.getPropertyValue("padding-bottom"))),a=s-(parseInt(n.getPropertyValue("padding-right"))+parseInt(n.getPropertyValue("padding-left")))-r;return{cols:Math.max(2,Math.floor(a/t.css.cell.width)),rows:Math.max(1,Math.floor(l/t.css.cell.height))}}}})(),e})()));
// 0.11.0
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.FitAddon=t():e.FitAddon=t()}(globalThis,(()=>(()=>{"use strict";var e={};return(()=>{var t=e;Object.defineProperty(t,"__esModule",{value:!0}),t.FitAddon=void 0,t.FitAddon=class{activate(e){this._terminal=e}dispose(){}fit(){const e=this.proposeDimensions();if(!e||!this._terminal||isNaN(e.cols)||isNaN(e.rows))return;const t=this._terminal._core;this._terminal.rows===e.rows&&this._terminal.cols===e.cols||(t._renderService.clear(),this._terminal.resize(e.cols,e.rows))}proposeDimensions(){if(!this._terminal)return;if(!this._terminal.element||!this._terminal.element.parentElement)return;const e=this._terminal._core._renderService.dimensions;if(0===e.css.cell.width||0===e.css.cell.height)return;const t=0===this._terminal.options.scrollback?0:this._terminal.options.overviewRuler?.width||14,r=window.getComputedStyle(this._terminal.element.parentElement),i=parseInt(r.getPropertyValue("height")),o=Math.max(0,parseInt(r.getPropertyValue("width"))),s=window.getComputedStyle(this._terminal.element),n=i-(parseInt(s.getPropertyValue("padding-top"))+parseInt(s.getPropertyValue("padding-bottom"))),l=o-(parseInt(s.getPropertyValue("padding-right"))+parseInt(s.getPropertyValue("padding-left")))-t;return{cols:Math.max(2,Math.floor(l/e.css.cell.width)),rows:Math.max(1,Math.floor(n/e.css.cell.height))}}}})(),e})()));

File diff suppressed because one or more lines are too long

View File

@@ -1,8 +1,10 @@
/*!
* Xterm.js v5.5.0 (https://github.com/xtermjs/xterm.js)
* Copyright (c) 2017-2023, The xterm.js authors (https://github.com/xtermjs/xterm.js)
* Xterm.js v6.0.0 (https://github.com/xtermjs/xterm.js)
* Copyright (c) 2017-2026, The xterm.js authors (https://github.com/xtermjs/xterm.js)
* Copyright (c) 2014-2016, SourceLair Private Company (https://www.sourcelair.com)
* Copyright (c) 2012-2013, Christopher Jeffrey (https://github.com/chjj/)
* Licensed under MIT (https://github.com/xtermjs/xterm.js/blob/master/LICENSE)
*/
.xterm{cursor:text;position:relative;user-select:none;-ms-user-select:none;-webkit-user-select:none}.xterm.focus,.xterm:focus{outline:none}.xterm .xterm-helpers{position:absolute;top:0;z-index:5}.xterm .xterm-helper-textarea{padding:0;border:0;margin:0;position:absolute;opacity:0;left:-9999em;top:0;width:0;height:0;z-index:-5;white-space:nowrap;overflow:hidden;resize:none}.xterm .composition-view{background:#000;color:#FFF;display:none;position:absolute;white-space:nowrap;z-index:1}.xterm .composition-view.active{display:block}.xterm .xterm-viewport{background-color:#000;overflow-y:scroll;cursor:default;position:absolute;right:0;left:0;top:0;bottom:0}.xterm .xterm-screen{position:relative}.xterm .xterm-screen canvas{position:absolute;left:0;top:0}.xterm .xterm-scroll-area{visibility:hidden}.xterm-char-measure-element{display:inline-block;visibility:hidden;position:absolute;top:0;left:-9999em;line-height:normal}.xterm.enable-mouse-events{cursor:default}.xterm.xterm-cursor-pointer,.xterm .xterm-cursor-pointer{cursor:pointer}.xterm.column-select.focus{cursor:crosshair}.xterm .xterm-accessibility:not(.debug),.xterm .xterm-message{position:absolute;left:0;top:0;bottom:0;right:0;z-index:10;color:transparent;pointer-events:none}.xterm .xterm-accessibility-tree:not(.debug) *::selection{color:transparent}.xterm .xterm-accessibility-tree{user-select:text;white-space:pre}.xterm .live-region{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}.xterm-dim{opacity:1!important}.xterm-underline-1{text-decoration:underline}.xterm-underline-2{text-decoration:double underline}.xterm-underline-3{text-decoration:wavy underline}.xterm-underline-4{text-decoration:dotted underline}.xterm-underline-5{text-decoration:dashed underline}.xterm-overline{text-decoration:overline}.xterm-overline.xterm-underline-1{text-decoration:overline underline}.xterm-overline.xterm-underline-2{text-decoration:overline double underline}.xterm-overline.xterm-underline-3{text-decoration:overline wavy underline}.xterm-overline.xterm-underline-4{text-decoration:overline dotted underline}.xterm-overline.xterm-underline-5{text-decoration:overline dashed underline}.xterm-strikethrough{text-decoration:line-through}.xterm-screen .xterm-decoration-container .xterm-decoration{z-index:6;position:absolute}.xterm-screen .xterm-decoration-container .xterm-decoration.xterm-decoration-top-layer{z-index:7}.xterm-decoration-overview-ruler{z-index:8;position:absolute;top:0;right:0;pointer-events:none}.xterm-decoration-top{z-index:2;position:relative}
.xterm{cursor:text;position:relative;user-select:none;-ms-user-select:none;-webkit-user-select:none;}.xterm.focus,.xterm:focus{outline:none;}.xterm .xterm-helpers{position:absolute;top:0;z-index:5;}.xterm .xterm-helper-textarea{padding:0;border:0;margin:0;position:absolute;opacity:0;left:-9999em;top:0;width:0;height:0;z-index:-5;white-space:nowrap;overflow:hidden;resize:none;}.xterm .composition-view{background:#000;color:#FFF;display:none;position:absolute;white-space:nowrap;z-index:1;}.xterm .composition-view.active{display:block;}.xterm .xterm-viewport{background-color:#000;overflow-y:scroll;cursor:default;position:absolute;right:0;left:0;top:0;bottom:0;}.xterm .xterm-screen{position:relative;}.xterm .xterm-screen canvas{position:absolute;left:0;top:0;}.xterm-char-measure-element{display:inline-block;visibility:hidden;position:absolute;top:0;left:-9999em;line-height:normal;}.xterm.enable-mouse-events{cursor:default;}.xterm.xterm-cursor-pointer,.xterm .xterm-cursor-pointer{cursor:pointer;}.xterm.column-select.focus{cursor:crosshair;}.xterm .xterm-accessibility:not(.debug),.xterm .xterm-message{position:absolute;left:0;top:0;bottom:0;right:0;z-index:10;color:transparent;pointer-events:none;}.xterm .xterm-accessibility-tree:not(.debug) *::selection{color:transparent;}.xterm .xterm-accessibility-tree{font-family:monospace;user-select:text;white-space:pre;}.xterm .xterm-accessibility-tree>div{transform-origin:left;width:fit-content;}.xterm .live-region{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden;}.xterm-dim{opacity:1!important;}.xterm-underline-1{text-decoration:underline}.xterm-underline-2{text-decoration:double underline}.xterm-underline-3{text-decoration:wavy underline}.xterm-underline-4{text-decoration:dotted underline}.xterm-underline-5{text-decoration:dashed underline}.xterm-overline{text-decoration:overline;}.xterm-overline.xterm-underline-1{text-decoration:overline underline}.xterm-overline.xterm-underline-2{text-decoration:overline double underline}.xterm-overline.xterm-underline-3{text-decoration:overline wavy underline}.xterm-overline.xterm-underline-4{text-decoration:overline dotted underline}.xterm-overline.xterm-underline-5{text-decoration:overline dashed underline}.xterm-strikethrough{text-decoration:line-through;}.xterm-screen .xterm-decoration-container .xterm-decoration{z-index:6;position:absolute;}.xterm-screen .xterm-decoration-container .xterm-decoration.xterm-decoration-top-layer{z-index:7;}.xterm-decoration-overview-ruler{z-index:8;position:absolute;top:0;right:0;pointer-events:none;}.xterm-decoration-top{z-index:2;position:relative;}.xterm .xterm-scrollable-element>.scrollbar{cursor:default;}.xterm .xterm-scrollable-element>.scrollbar>.scra{cursor:pointer;font-size:11px!important;}.xterm .xterm-scrollable-element>.visible{opacity:1;background:rgba(0,0,0,0);transition:opacity 100ms linear;z-index:11;}.xterm .xterm-scrollable-element>.invisible{opacity:0;pointer-events:none;}.xterm .xterm-scrollable-element>.invisible.fade{transition:opacity 800ms linear;}.xterm .xterm-scrollable-element>.shadow{position:absolute;display:none;}.xterm .xterm-scrollable-element>.shadow.top{display:block;top:0;left:3px;height:3px;width:100%;box-shadow:var(--vscode-scrollbar-shadow,#000) 0 6px 6px -6px inset;}.xterm .xterm-scrollable-element>.shadow.left{display:block;top:3px;left:0;height:100%;width:3px;box-shadow:var(--vscode-scrollbar-shadow,#000) 6px 0 6px -6px inset;}.xterm .xterm-scrollable-element>.shadow.top-left-corner{display:block;top:0;left:0;height:3px;width:3px;}.xterm .xterm-scrollable-element>.shadow.top.left{box-shadow:var(--vscode-scrollbar-shadow,#000) 6px 0 6px -6px inset;}
/* custom */
.xterm .xterm-decoration-overview-ruler { display: none; }

File diff suppressed because one or more lines are too long