Compare commits

..

64 Commits

Author SHA1 Message Date
Ilia Ross
d694649872 Fix to safely get user hostname from URL 2025-10-03 03:19:54 +03:00
Ilia Ross
991795c296 Fix to redirect to HTTPS when we get the host from the browser URL 2025-10-02 15:52:14 +03:00
Ilia Ross
8932f3bd6a Fix to truly move option tag rather than recreating
*Note: It keep existing styles and using index from "add()" will also put moved options to the top of the select and make them clearly visible
2025-10-02 13:36:22 +03:00
Ilia Ross
b3ec083c7b Update changelog 2025-10-01 23:45:22 +03:00
Ilia Ross
da18dea780 Update changelog release date to today (night) 2025-10-01 20:19:58 +03:00
Ilia Ross
35f8061049 Fix to kill Webmin subprocesses during RC stop on FreeBSD and other systems 2025-10-01 20:12:54 +03:00
Ilia Ross
e6e79a7eb5 Fix to replace vmstat with /proc sampler for same output with much lower overhead 2025-10-01 18:35:20 +03:00
Ilia Ross
a4d1280ef7 Fix copy-paste artifact 2025-10-01 14:34:40 +03:00
Ilia Ross
cc2cc62717 Fix to query specific fields in FreeBSD memory stats collection
*Note: Replace `sysctl -a` with targeted queries for only the 5 needed values (hw.physmem, hw.pagesize, vm.stats.vm.v_*_count) instead of dumping thousands of kernel params. This reduces `get_memory_info()` overhead from 25% CPU to ~5% CPU when called by real-time monitoring every 1-3 seconds.
2025-10-01 14:33:22 +03:00
Ilia Ross
c59591e3de Update changelog 2025-10-01 01:15:54 +03:00
Ilia Ross
fc4e2751dc Fix broken redirect when there is no way to get FQDN
*Note: "$host = &get_socket_name(SOCK, $ipv6fhs{$s});" won't return FQDN if it can't be resolved (or if it isn't in /etc/hosts), breaking some redirects, most likely proxied ones that aren't using HTTPS in its config.
2025-10-01 01:03:45 +03:00
Ilia Ross
4dc64f5028 Fix recommended package name (again) 2025-09-30 23:51:27 +03:00
Ilia Ross
01d2323496 Fix to mention IO::Socket::INET6 too 2025-09-30 22:28:08 +03:00
Ilia Ross
4c8b0fe008 Update changelog for 2.520 2025-09-30 17:26:46 +03:00
Jamie Cameron
e44ec464eb Merge branch 'master' of github.com:webmin/webmin 2025-09-29 13:35:49 -07:00
Jamie Cameron
98c54fe3fb Include config files by default
https://github.com/webmin/webmin/issues/2562
2025-09-29 13:35:43 -07:00
Ilia Ross
86968bfc31 Fix support for other Raspberry Pi sensors #2545 2025-09-29 18:23:10 +03:00
Ilia Ross
956ad7ed0f Dev: Trigger rebuilt for testing purposes for all modules
[no-commit-check]
2025-09-28 02:05:04 +03:00
Ilia Ross
a0d99e0a31 Dev: Trigger rebuilt for testing purposes 2025-09-28 02:02:11 +03:00
Ilia Ross
f27b1415be Fix to remove extra space
*Note: Made to trigger a re-built for testing purposes
2025-09-28 01:27:54 +03:00
Ilia Ross
1f4b467ea8 Fix to never mess around with headers; no headers check log 2025-09-28 01:20:29 +03:00
Ilia Ross
95423c7425 Fix not to loose prefix 2025-09-27 20:09:52 +03:00
Ilia Ross
89d23c5aa8 Fix for tar builds have no release but consider edition
*Note: Release for tar files should exist, however edition that can be passed like .gpl or .pro should exist
2025-09-27 20:06:57 +03:00
Ilia Ross
be3fcb89b0 Fix to remove release number from tar builds as never applicable 2025-09-27 18:28:52 +03:00
Ilia Ross
25fa7c589d Fix to add prefix for tar builds too 2025-09-27 03:50:54 +03:00
Ilia Ross
c86c45b10a Fix to keep release number too in tar builds 2025-09-27 03:00:44 +03:00
Ilia Ross
f10540bd54 Fix to add a flag to copy tar build too 2025-09-27 02:08:45 +03:00
Jamie Cameron
388f51843f Merge pull request #2558 from webmin/dev/remove-bottleneck-of-shelling-out
Fix to remove significant bottleneck of shelling out
2025-09-25 20:28:21 -07:00
Ilia Ross
7a723719da Fix to recommend Sys::Syslog module #2557 2025-09-25 15:33:52 +03:00
Ilia Ross
a027ad5dd6 Fix variable names to avoid ambiguity
* Note: Discussed here:
https://github.com/webmin/webmin/pull/2553#issuecomment-3328436525
2025-09-25 15:23:06 +03:00
Ilia Ross
d99a24b045 Fix to remove significant bottleneck of shelling out 2025-09-25 14:48:14 +03:00
Jamie Cameron
f08ad4eb19 Merge branch 'master' of github.com:webmin/webmin 2025-09-24 20:32:47 -07:00
Jamie Cameron
df97b4a419 Gracefully handle monitor types that don't exist
https://forum.virtualmin.com/t/finally-upgraded-and-a-problem/135204
2025-09-24 20:32:40 -07:00
Jamie Cameron
1ef0914610 Merge pull request #2553 from webmin/dev/further-fixes-to-ssl-host
Add final fixes to address ongoing issue with checking remote host
2025-09-24 20:24:28 -07:00
Ilia Ross
97678653c6 Fix to prefer SSL_HOST over HTTP_HOST 2025-09-24 16:04:19 +03:00
Ilia Ross
3717dfb505 Revert "Fix to correctly pick remote host based on connection"
This reverts commit 55b5739287.
2025-09-24 15:11:26 +03:00
Ilia Ross
e5d6c5627d Add back SSL host cert check but only in SSL mode; show faked host (HTTP_HOST) first for clarity 2025-09-24 14:37:18 +03:00
Ilia Ross
e194e2d500 Revert "Fix to make sure SSL_HOST also has port set"
This reverts commit adf36a177d.
2025-09-24 14:28:54 +03:00
Jamie Cameron
d5a22a592f Cache reads of the same cert file 2025-09-23 17:10:55 -07:00
Jamie Cameron
890a4ffd3f Valid http host header against cert name, as it may be different from the SSL hostname 2025-09-23 17:05:32 -07:00
Ilia Ross
4e229d8adb Fix to show clear warning when trying to reset password over insecure connection 2025-09-24 00:15:12 +03:00
Ilia Ross
d1ee0a5ed6 Fix not to completely forbid password reset without SSL 2025-09-23 23:36:09 +03:00
Ilia Ross
cd489ccefc Fix not to limit to valid SSL certificate, as self-signed is also valid and safe enough 2025-09-23 23:11:44 +03:00
Ilia Ross
55b5739287 Fix to correctly pick remote host based on connection 2025-09-23 22:35:16 +03:00
Ilia Ross
adf36a177d Fix to make sure SSL_HOST also has port set 2025-09-23 22:21:08 +03:00
Ilia Ross
9393162b0e Add a complete overhaul of var_dump, which is now portable
*Note: Can now be easily used in "miniserv.pl" or anywhere else. Supports multiple passed params.
2025-09-23 18:58:08 +03:00
Jamie Cameron
287fb3cb81 Only allow forgotten password reset when in SSL mode and hostname is valid 2025-09-22 21:42:26 -07:00
Jamie Cameron
61b2603e06 Add check for same hostname 2025-09-22 21:14:07 -07:00
Jamie Cameron
eb02824bfc Detect if SSL hostname is valid for the cert being used, and pass it to an environment variable 2025-09-22 21:13:46 -07:00
Ilia Ross
6191a222ae Revert "Fix to make sure the mail URL uses a well-known host name"
This reverts commit e88a77d32a.
2025-09-23 02:22:17 +03:00
Ilia Ross
2ac82016aa Revert "Perfect previous code"
This reverts commit 8f987d21a9.
2025-09-23 02:22:06 +03:00
Ilia Ross
8f987d21a9 Perfect previous code 2025-09-21 22:54:25 +03:00
Ilia Ross
e88a77d32a Fix to make sure the mail URL uses a well-known host name 2025-09-21 21:47:12 +03:00
Ilia Ross
5231b31ddd Add classes for text colors 2025-09-20 21:22:35 +03:00
Ilia Ross
5c5d5fe699 Fix printing bottom button row
https://forum.virtualmin.com/t/clarifying-how-to-create-custom-links/135115?u=ilia
2025-09-17 22:07:57 +03:00
Ilia Ross
34fba22799 Revert "Dev: Testing build script issues for separate modules"
This reverts commit a708e5f6e9.
2025-09-17 04:30:23 +03:00
Ilia Ross
a708e5f6e9 Dev: Testing build script issues for separate modules 2025-09-17 01:01:17 +03:00
Ilia Ross
f7058f10b4 2.510 2025-09-16 20:11:26 +03:00
Jamie Cameron
a4df380e2e new version bump 2025-09-16 09:19:34 -07:00
Jamie Cameron
70d1c843fc Merge branch 'master' of github.com:webmin/webmin 2025-09-15 15:37:13 -07:00
Jamie Cameron
1b38d806fc Make sure re-signing period is less than 30 days
https://github.com/webmin/webmin/issues/2550
2025-09-15 15:37:06 -07:00
Ilia Ross
f8d7e8810a Fix new line
*Note: For devel purposes. I expect a separate package "webmin-custom" rebuilt.
2025-09-12 15:17:08 +03:00
Jamie Cameron
43d5355114 201 is a valid OK resoonse code 2025-09-11 18:07:46 -07:00
Jamie Cameron
87d39127ef Put back english
https://github.com/webmin/webmin/issues/2547
2025-09-10 21:13:55 -07:00
27 changed files with 581 additions and 136 deletions

View File

@@ -1,5 +1,36 @@
## Changelog
#### 2.520 (October 2, 2025)
* Fix to make sure the mail URL uses a well-known host name [security]
* Fix support for other Raspberry Pi sensors #2545
* Fix the printing of the bottom button row in the form column table
* Fix to recommend Perl `Sys::Syslog` module #2557
* Fix to avoid using short hostname in HTTPS redirects when an FQDN is available
* Fix to use _/proc_ sampler instead of `vmstat` for the same output with much lower overhead
* Fix to query specific fields in FreeBSD memory stats collection, cutting CPU use by 80%
* Fix to kill Webmin subprocesses during RC stop on FreeBSD and other systems
* Add a complete overhaul of `var_dump` subroutine, which is now fully portable
* Update the Authentic theme to the latest version with various fixes:
- Fix the text color when reading email in the Read User Mail module [webmin#2555](https://github.com/webmin/webmin/issues/2555)
- Fix to ensure the selected color palette is correctly stored when changed manually [webmin#2552](https://github.com/webmin/webmin/issues/2552)
- Fix a bug when the Webmin version label was missing when copying to clipboard system information from the dashboard
- Fix DNS query spike from network stats collection on FreeBSD [webmin#2556](https://github.com/webmin/webmin/issues/2556)
- Fix spinner color in toast messages for dark palette
- Fix other bugs and add various small improvements
#### 2.510 (September 16, 2025)
* Fix to ensure DNSSEC re-signing period is less than 30 days in the BIND DNS module
* Fix to treat 201 as a valid response code in the internal download function
* Update the Authentic theme to the latest version with various improvements and fixes:
- Add optimizations to dashboard graphs with dynamic trimming to prevent page lagging
- Add improvements to how the system cache for the dashboard is updated
- Add support to correctly reload the page in proxy mode
- Add an option to choose if default page should always load when switching navigation
- Fix to ensure the color palette is preserved for the user [webmin#2537](https://github.com/webmin/webmin/issues/2537)
- Fix algorithm for calculating rows per page in data table pagination
- Fix the alert info box text color for dark mode
- Fix critical lags and appearance of Custom Commands module
#### 2.501 (September 10, 2025)
* Add support for Raspberry Pi sensors #2539 #2517
* Add Squid 7 support

View File

@@ -15,6 +15,8 @@ if ($in{'new'}) {
$backup = { 'emode' => 0,
'email' => $gconfig{'webmin_email_to'},
'sched' => 1,
'configfile' => 1,
'nofiles' => 0,
'mins' => 0,
'hours' => 0,
'days' => '*',

View File

@@ -1207,6 +1207,7 @@ dnssec_secs=seconds
dnssec_desc=Zones signed with DNSSEC typically have two keys - a zone key which must be re-generated and signed regularly, and a key signing key which remains constant. This page allows you to configure Webmin to perform this re-signing automatically.
dnssec_err=Failed to save DNSSEC key re-signing
dnssec_eperiod=Missing or invalid number of days between re-signs
dnssec_eperiod30=Number of days between re-signs must be less than 30
dnssectools_title=DNSSEC-Tools Automation
dt_conf_title=DNSSEC-Tools Automation

View File

@@ -16,6 +16,7 @@ require './bind8-lib.pl';
$access{'defaults'} || &error($text{'dnssec_ecannot'});
$in{'period'} =~ /^[1-9]\d*$/ || &error($text{'dnssec_eperiod'});
$in{'period'} < 30 || &error($text{'dnssec_eperiod30'});
# Create or delete the cron job
my $job = &get_dnssec_cron_job();

View File

@@ -48,8 +48,10 @@ $in{'dt_zsklife'} =~ /(\b[0-9]+\b)/ ||
&error($text{'dt_conf_ezsklife'});
$nv{'zsklife'} = $1;
&save_dnssectools_directive($conf, \%nv);
$in{'period'} =~ /^[1-9]\d*$/ || &error($text{'dnssec_eperiod'});
$in{'period'} < 30 || &error($text{'dnssec_eperiod30'});
&save_dnssectools_directive($conf, \%nv);
&lock_file($module_config_file);
$config{'dnssec_period'} = $in{'period'};

View File

@@ -647,5 +647,4 @@ else {
}
}
1;

View File

@@ -1 +1,59 @@
Jedes Kommando hat eine Beschreibung, die auf der Schaltfläche auf der Hauptseite angezeigt wird, sowie ein auszuführendes Kommando. Kommandos werden von <tt>sh</tt> ausgeführt und können daher Shell-Metazeichen wie <tt>|</tt>, <tt>&gt;</tt> und <tt>&amp;</tt> enthalten. Ebenso können Parameter wie <tt>$foo</tt> verwendet werden, die vor der Ausführung vom/von der Benutzer abgefragt werden.<p>Diese Parameter können in die untenstehende Tabelle eingetragen werden. Für jeden Parameter müssen folgende Angaben gemacht werden:<dl><dt><b>Name</b><dd>Ein eindeutiger Code für den Parameter. Wenn der Name <tt>foo</tt> lautet, wird <tt>$foo</tt> durch diese Eingabe ersetzt, wenn das Kommando ausgeführt wird.<p><dt><b>Beschreibung</b><dd>Diese Beschreibung wird neben den Parametern auf der Hauptseite angezeigt.<p><dt><b>Typ</b><dd>Diese Option bestimmt, wie der Parameter eingegeben wird. Mögliche Optionen sind:<ul><li><b>Text</b><br>Freie Texteingabe<li><b>Benutzer</b><br>Ein:e Benutzer Ihres Systems<li><b>UID</b><br>Die Benutzer-ID eines/einer Benutzer Ihres Systems<li><b>Gruppe</b><br>Ein Gruppenname Ihres Systems<li><b>GID</b><br>Die Gruppen-ID einer Gruppe Ihres Systems<li><b>Datei</b><br>Der vollständige Pfad zu einer Datei<li><b>Verzeichnis</b><br>Der vollständige Pfad zu einem Verzeichnis<li><b>Option</b><br>Eine Ja/Nein-Eingabe, die den Parameter auf den Wert setzt, der im Feld daneben eingetragen ist aber nur, wenn <tt>JA</tt> ausgewählt wurde.<li><b>Passwort</b><br>Eine vollständig freie Texteingabe, wobei die Eingabe als „<tt>*</tt>“ maskiert wird.<li><b>Menü</b><br>Ein Dropdown-Menü mit Optionen, die aus der Datei entnommen werden, die im Feld daneben angegeben ist.</ul><p><dt><b>Parameter zitieren?</b><dd>Wenn <tt>Ja</tt> ausgewählt ist, werden die Parameter vor der Ausführung in Anführungszeichen (<tt>"</tt>) gesetzt. Dies ermöglicht die Verwendung von Parametern mit Leerzeichen.<p></dl><hr>
Each command has a description (displayed on the button on the main page),
and an actual command to execute. This command string can contain shell
operators like |, &gt; and ; for executing multiple commands and pipelines.
The string can also contain parameters like <tt>$foo</tt>, which are replaced
by user inputs when the command is run. <p>
These parameters can be entered into the table at the bottom of the page.
For each parameter you must enter :
<dl>
<dt><b>Name</b>
<dd>A unique code for this parameter. If the name is <tt>foo</tt>, then
<tt>$foo</tt> will be replaced by the parameter value when the command
is executed.<p>
<dt><b>Description</b>
<dd>The description next to this parameter on the main page.<p>
<dt><b>Type</b>
<dd>This option controls how the parameter is input. Available options are :
<ul>
<li><b>Text</b><br>
A totally free-text input.
<li><b>User</b><br>
A username from your system.
<li><b>UID</b><br>
The UID of a user from your system.
<li><b>Group</b><br>
A group name from your system.
<li><b>GID</b><br>
The GID of a group from your system.
<li><b>File</b><br>
The full path to a file.
<li><b>Directory</b><br>
The full path to a directory.
<li><b>Option</b><br>
A Yes/No input that will set the parameter to whatever is in
the field next to the type input only if Yes is chosen.
<li><b>Password</b><br>
A totally free-text input, but with the password replaced by *'s.
<li><b>Menu</b><br>
A drop-down menu of options, taken from the filename entered into
the text field to it. Or, instead of a filename you can enter a
command with an | at the end, whose output will be used to determine
the available options.
<li><b>Upload</b><br>
An input box for selecting a file on the client side, which will be
uploaded to the server when the command is run. This will be be
placed in a temporary file, and the path to that file will be the
value of this parameter when the command is run.
<li><b>Textbox</b><br>
A multi-line free text field. When the command is run, any newline
characters in the entered text will be converted into spaces.
</ul><p>
In most cases, the default value for the parameter will be whatever you
enter in the text box next to the parameter type menu.
<dt><b>Quote parameter?</b>
<dd>If Yes, the parameter will be quoted with " before substitution, allowing
the user to enter values containing whitespaces.<p>
</dl>
<hr>

View File

@@ -14,6 +14,12 @@ $trust_unknown_referers = 1;
$gconfig{'forgot_pass'} || &error($text{'forgot_ecannot'});
my $timeout = $gconfig{'passreset_timeout'} || 15;
$remote_user && &error($text{'forgot_elogin'});
$ENV{'HTTPS'} eq 'ON' || $gconfig{'forgot_pass'} == 2 ||
&error($text{'forgot_essl'});
$ENV{'SSL_CN_CERT'} == 1 ||
&error(&text('forgot_esslhost',
&html_escape($ENV{'HTTP_HOST'} || $ENV{'SSL_CN'})))
if ($ENV{'HTTPS'} eq 'ON');
# Check that the random ID is valid
$in{'id'} =~ /^[a-f0-9]+$/i || &error($text{'forgot_eid'});
@@ -23,6 +29,12 @@ my $linkfile = $main::forgot_password_link_dir."/".$in{'id'};
time() - $link{'time'} > 60*$timeout &&
&error(&text('forgot_etime', $timeout));
# Check that the hostname in the original email matches the current hostname
my ($basehost) = &parse_http_url(&get_webmin_email_url());
if ($basehost ne $link{'host'}) {
&error($text{'forgot_ehost'});
}
# Get the Webmin user
&foreign_require("acl");
my ($wuser) = grep { $_->{'name'} eq $link{'user'} } &acl::list_users();

View File

@@ -13,9 +13,16 @@ $trust_unknown_referers = 1;
&error_setup($text{'forgot_err'});
$gconfig{'forgot_pass'} || &error($text{'forgot_ecannot'});
$remote_user && &error($text{'forgot_elogin'});
$ENV{'HTTPS'} eq 'ON' || $gconfig{'forgot_pass'} == 2 ||
&error($text{'forgot_essl'});
$ENV{'SSL_CN_CERT'} == 1 ||
&error(&text('forgot_esslhost',
&html_escape($ENV{'HTTP_HOST'} || $ENV{'SSL_CN'})))
if ($ENV{'HTTPS'} eq 'ON');
&ui_print_header(undef, $text{'forgot_title'}, "", undef, undef, 1, 1);
print &ui_alert_box("<b> ⚠ ".$text{'forgot_nossl_warn'}, 'warn')
if ($gconfig{'forgot_pass'} == 2 && $ENV{'HTTPS'} ne 'ON');
print "<center>\n";
print $text{'forgot_desc'},"<p>\n";
print &ui_form_start("forgot_send.cgi", "post");

View File

@@ -12,6 +12,12 @@ $no_acl_check++;
&error_setup($text{'forgot_err'});
$gconfig{'forgot_pass'} || &error($text{'forgot_ecannot'});
$remote_user && &error($text{'forgot_elogin'});
$ENV{'HTTPS'} eq 'ON' || $gconfig{'forgot_pass'} == 2 ||
&error($text{'forgot_essl'});
$ENV{'SSL_CN_CERT'} == 1 ||
&error(&text('forgot_esslhost',
&html_escape($ENV{'HTTP_HOST'} || $ENV{'SSL_CN'})))
if ($ENV{'HTTPS'} eq 'ON');
# Lookup the Webmin user
&foreign_require("acl");
@@ -103,8 +109,11 @@ sleep($maxtries);
$wuser->{'pass'} eq '*LK*' && &error($text{'forgot_elock'});
# Generate a random ID and tracking file for this password reset
my $baseurl = &get_webmin_email_url();
my ($basehost) = &parse_http_url($baseurl);
my %link = ( 'id' => &acl::generate_random_id(),
'remote' => $ENV{'REMOTE_ADDR'},
'host' => $basehost,
'time' => $now,
'user' => $wuser->{'name'},
'uuser' => $uuser ? $uuser->{'user'} : undef,
@@ -114,7 +123,6 @@ my $linkfile = $main::forgot_password_link_dir."/".$link{'id'};
&lock_file($linkfile);
&write_file($linkfile, \%link);
&unlock_file($linkfile);
my $baseurl = &get_webmin_email_url();
my $url = $baseurl.'/forgot.cgi?id='.&urlize($link{'id'});
my $username = $muser ? $muser->{'user'} :
$uuser ? $uuser->{'user'} : $wuser->{'name'};

View File

@@ -891,3 +891,15 @@ body > .mode > b[data-mode="server-manager"] > a > .ff-cloudmin {
field-sizing: content !important;
min-width: 40px !important;
}
.text-danger {
color: #bc0303;
}
.text-success {
color: #3c763d;
}
.text-warning {
color: #b58900;
}
.text-info {
color: #108eda;
}

View File

@@ -159,6 +159,7 @@ forgot_erandom=Failed to generate random ID!
forgot_eid=Missing or invalid-looking reset ID!
forgot_eid2=Reset ID is not valid!
forgot_etime=Password reset email is more than $1 minutes old.
forgot_ehost=Hostname mismatch in reset email
forgot_newpass=New password for user $1
forgot_newpass2=New password again
forgot_passok=Change Password
@@ -182,6 +183,10 @@ forgot_eunixlock=User user's password is locked!
forgot_elogin=Forgotten password pages cannot be used when you are already logged in to Webmin!
forgot_erate=Too many password reset attempts for $1! Please try again later.
forgot_eremote=Webmin server on this system is not running or is not configured to allow forgotten password recovery.
forgot_essl=Forgotten password recovery can only be used over an SSL connection unless explicitly allowed
forgot_nossl=Yes, and allow over insecure connection
forgot_esslhost=Forgotten password recovery cannot be used with invalid SSL hostname $1
forgot_nossl_warn=Warning: This password reset is being sent over an insecure, not-encrypted connection and is vulnerable to man-in-the-middle (MITM) and header-injection attacks.
pam_header=Login to Webmin
pam_mesg=You must respond to the question below to login to Webmin server on $1.

View File

@@ -109,7 +109,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", "html2text", "qrencode", "libdbi-perl", "libdbd-mysql-perl", "libjson-xs-perl" );
@recommends = ( "libdatetime-perl", "libdatetime-timezone-perl", "libdatetime-locale-perl", "libtime-piece-perl", "libencode-detect-perl", "libtime-hires-perl", "libsocket6-perl", "html2text", "qrencode", "libdbi-perl", "libdbd-mysql-perl", "libjson-xs-perl", "libsys-syslog-perl" );
$recommends = join(", ", @recommends);
open(CONTROL, ">$control_file");
print CONTROL <<EOF;

View File

@@ -34,7 +34,7 @@ my $allow_overwrite = 0;
my ($force_theme, $no_prefix, $set_prefix,
$obsolete_wbm, $vendor, $url, $force_usermin, $final_mod, $sign, $keyname,
$epoch, $dir, $ver, @exclude,
$epoch, $dir, $ver, @exclude, $copy_tar,
$rpmdepends, $norpmdepends, $rpmrecommends, $norpmrecommends,
$no_requires, $no_recommends, $no_suggests, $no_conflicts, $no_provides,
@@ -136,6 +136,9 @@ while(@ARGV) {
elsif ($a eq "--mod-list") {
$mod_list = shift(@ARGV);
}
elsif ($a eq "--copy-tar") {
$copy_tar = 1;
}
elsif ($a =~ /^\-\-/) {
print STDERR "Unknown option $a\n";
exit(1);
@@ -556,6 +559,15 @@ fi
EOF
close($SPEC);
# Put the tar in /tmp too if tar package is requested
my $tar_release_file;
if ($copy_tar) {
my $tar_release = $release;
$tar_release =~ s/^[0-9]+//;
$tar_release_file = "$prefix$mod-$ver$tar_release.tar.gz";
system("cp $rpm_source_dir/$mod.tar.gz /tmp/$tar_release_file");
}
# Build the actual RPM
my $cmd = -x "/usr/bin/rpmbuild" ? "/usr/bin/rpmbuild" : "/bin/rpm";
system("$cmd -ba $spec_dir/$prefix$mod.spec") && exit;
@@ -570,10 +582,20 @@ if ($sign) {
if ($target_dir =~ /:/) {
# scp to dest
system("scp $rpm_dir/$prefix$mod-$ver-$release.noarch.rpm $target_dir/$prefix$mod-$ver-$release.noarch.rpm");
# scp the tar too if requested
if ($copy_tar) {
system("scp /tmp/$tar_release_file $target_dir/$tar_release_file");
unlink("/tmp/$tar_release_file");
}
}
elsif ($rpm_dir ne $target_dir) {
# Just copy
system("/bin/cp $rpm_dir/$prefix$mod-$ver-$release.noarch.rpm $target_dir/$prefix$mod-$ver-$release.noarch.rpm");
# Copy the tar too if requested
if ($copy_tar) {
system("/bin/cp /tmp/$tar_release_file $target_dir/$tar_release_file");
unlink("/tmp/$tar_release_file");
}
}
# read_file(file, &assoc, [&order], [lowercase])

View File

@@ -88,7 +88,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) html2text shared-mime-info perl-File-Basename perl-File-Path perl-JSON-XS qrencode perl(DBI) perl(DBD::mysql)
Recommends: perl(DateTime) perl(DateTime::TimeZone) perl(DateTime::Locale) perl(Time::Piece) perl(Encode::Detect) perl(Time::HiRes) perl(Socket6) perl(Sys::Syslog) html2text shared-mime-info perl-File-Basename perl-File-Path perl-JSON-XS qrencode perl(DBI) perl(DBD::mysql)
AutoReq: 0
License: BSD-3-Clause
Group: System/Tools

View File

@@ -941,7 +941,9 @@ while(1) {
(ord($byte) & 0x80))) {
($ssl_con,
$ssl_certfile,
$ssl_keyfile) =
$ssl_keyfile,
$ssl_cn,
$ssl_alts) =
&ssl_connection_for_ip(
SOCK, $ipv6fhs{$s});
print DEBUG "ssl_con returned ".
@@ -1379,26 +1381,48 @@ $method = $page = $request_uri = undef;
print DEBUG "handle_request reqline=$reqline\n";
alarm(0);
if (!$use_ssl && $config{'ssl'} && $config{'ssl_enforce'}) {
# This is an http request when https must be enforced
local $urlhost = $config{'musthost'} || $host;
$urlhost = "[".$urlhost."]" if (&check_ip6address($urlhost));
local $wantport = $port;
if ($wantport == 80 &&
&indexof(443, @listening_on_ports) >= 0) {
# Connection was to port 80, but since we are also
# accepting on port 443, redirect to that
$wantport = 443;
# This is an HTTP request when HTTPS should be enforced
my $musthost = $config{'musthost'};
my $hostheader;
if (!$musthost) {
# Read host HTTP header because we want one earlier
alarm(10);
local $SIG{'ALRM'} = sub { die "timeout" };
while (defined(my $line = &read_line())) {
$line =~ s/\r|\n//g;
last if $line eq '';
if ($line =~ /^host:\s*(.*)$/i) {
$hostheader = "https://$1";
last;
}
}
alarm(0);
}
local $url = $wantport == 443
? "https://$urlhost/"
: "https://$urlhost:$wantport/";
# Host header must already contain full URL
my $url = $hostheader;
if (!$url) {
# No host header
local $urlhost = $musthost || $host;
$urlhost = "[".$urlhost."]" if (&check_ip6address($urlhost));
local $wantport = $port;
if ($wantport == 80 &&
&indexof(443, @listening_on_ports) >= 0) {
# Connection was to port 80, but since we are also
# accepting on port 443, redirect to that
$wantport = 443;
}
$url = $wantport == 443
? "https://$urlhost/"
: "https://$urlhost:$wantport/";
}
# Enforce HTTPS
&write_data("HTTP/1.0 302 Moved Temporarily\r\n");
&write_data("Date: $datestr\r\n");
&write_data("Server: @{[&server_info()]}\r\n");
&write_data("Location: $url\r\n");
&write_keep_alive(0);
&write_data("\r\n");
&log_error("Redirecting HTTP request to HTTPS for $acptip");
&log_error("Redirecting HTTP request to $url");
&log_request($loghost, $authuser, $reqline, 302, 0);
return 0;
}
@@ -2500,6 +2524,11 @@ if (&get_type($full) eq "internal/cgi" && $validated != 4) {
$ENV{"MINISERV_CONFIG"} = $config_file;
$ENV{"HTTPS"} = $use_ssl ? "ON" : "";
$ENV{"SSL_HSTS"} = $config{"ssl_hsts"};
if ($use_ssl) {
$ENV{"SSL_CN"} = $ssl_cn;
$ENV{"SSL_CN_CERT"} =
&ssl_hostname_match($header{'host'}, $ssl_alts);
}
$ENV{"MINISERV_PID"} = $miniserv_main_pid;
if ($use_ssl) {
$ENV{"MINISERV_CERTFILE"} = $ssl_certfile;
@@ -4776,11 +4805,18 @@ if ($config{'ssl_honorcipherorder'}) {
eval 'Net::SSLeay::CTX_set_options($ssl_ctx,
&Net::SSLeay::OP_NO_RENEGOTIATION)';
# Get the hostnames each cert is valid for
my $info = &cert_names($certfile);
my @hosts;
push(@hosts, $info->{'cn'}) if ($info->{'cn'});
push(@hosts, @{$info->{'alt'}}) if ($info->{'alt'});
return { 'keyfile' => $keyfile,
'keytime' => $kst[9],
'certfile' => $certfile,
'certtime' => $cst[9],
'extracas' => $extracas,
'hosts' => \@hosts,
'ctx' => $ssl_ctx };
}
@@ -4816,8 +4852,9 @@ alarm(0);
return undef if (!$ok);
# Check for a per-hostname SSL context and use that instead
my $h;
if (defined(&Net::SSLeay::get_servername)) {
my $h = Net::SSLeay::get_servername($ssl_con);
$h = Net::SSLeay::get_servername($ssl_con);
if ($h) {
my $c = $ssl_contexts{$h} ||
$h =~ /^[^\.]+\.(.*)$/ && $ssl_contexts{"*.$1"};
@@ -4826,7 +4863,8 @@ if (defined(&Net::SSLeay::get_servername)) {
}
}
}
return ($ssl_con, $ssl_ctx->{'certfile'}, $ssl_ctx->{'keyfile'});
return ($ssl_con, $ssl_ctx->{'certfile'}, $ssl_ctx->{'keyfile'}, $h,
$ssl_ctx->{'hosts'});
}
# parse_websockets_config()
@@ -4873,8 +4911,7 @@ print DEBUG "in reload_config_file\n";
&build_config_mappings();
&read_webmin_crons();
&precache_files();
&setup_ssl_contexts()
if ($use_ssl);
&setup_ssl_contexts() if ($use_ssl);
&parse_websockets_config();
if ($config{'session'}) {
dbmclose(%sessiondb);
@@ -5507,6 +5544,9 @@ foreach my $pe (split(/\t+/, $config{'expires_paths'})) {
# Reset cache of sudo checks
undef(%sudocache);
# Reset cache of cert files
undef(%cert_names_cache);
}
# is_group_member(&uinfo, groupname)
@@ -6995,3 +7035,75 @@ if (!$sig) {
}
return $sig;
}
=head2 cert_names($file)
Extract Common Name and Subject Alternative Names from an X.509 certificate
file. Supports both PEM and DER certificates. Returns undef if file cannot be
read or parsed. Cache results for speed.
=cut
sub cert_names
{
my ($file) = @_;
return $cert_names_cache{$file} if ($cert_names_cache{$file});
return undef if (!$file || !-r $file);
my %rv;
my $cert;
# Try PEM first
my $bio = Net::SSLeay::BIO_new_file($file, 'r');
if ($bio) {
$cert = Net::SSLeay::PEM_read_bio_X509($bio);
Net::SSLeay::BIO_free($bio);
}
# Try DER if PEM failed
if (!$cert) {
my $bio = Net::SSLeay::BIO_new_file($file, 'rb');
if ($bio) {
$cert = Net::SSLeay::d2i_X509_bio($bio);
Net::SSLeay::BIO_free($bio);
}
}
# Certificate not found
return undef if !$cert;
# Subject
my $subject = Net::SSLeay::X509_get_subject_name($cert);
if ($subject) {
# commonName
my $cn = Net::SSLeay::X509_NAME_get_text_by_NID($subject, 13);
$rv{cn} = $cn if defined $cn && $cn ne '' && $cn ne '-1';
}
# subjectAltName
my @alts = Net::SSLeay::X509_get_subjectAltNames($cert);
if (@alts) {
my @dns;
while (my ($type, $val) = splice(@alts, 0, 2)) {
push @dns, $val if $type == 2;
}
$rv{alt} = \@dns if @dns;
}
Net::SSLeay::X509_free($cert);
$cert_names_cache{$file} = \%rv;
return \%rv;
}
# ssl_hostname_match(hostname, &hosts-list)
# Does a hostname match a list of hostnames for an SSL cert?
sub ssl_hostname_match
{
my ($h, $hosts) = @_;
$h =~ s/:\d+$//;
foreach my $p (@$hosts) {
return 1 if (lc($p) eq lc($h));
return 1 if ($p =~ /^\*\.(\S+)$/ &&
(lc($h) eq lc($1) || $h =~ /^([^\.]+)\.\Q$1\E$/i));
return 2 if ($p eq "*");
}
return 0;
}

View File

@@ -101,7 +101,16 @@ ioctl($ttyfh, 536900705, 0);
sub get_memory_info
{
my $sysctl = {};
my $sysctl_output = &backquote_command("/sbin/sysctl -a 2>/dev/null");
# Get only the specific values we need not all with -a
my @needed = qw(
hw.physmem
hw.pagesize
vm.stats.vm.v_inactive_count
vm.stats.vm.v_cache_count
vm.stats.vm.v_free_count
);
my $sysctl_output = &backquote_command("/sbin/sysctl " .
join(" ", @needed) . " 2>/dev/null");
return ( ) if ($?);
foreach my $line (split(/\n/, $sysctl_output)) {
if ($line =~ m/^([^:]+):\s+(.+)\s*$/s) {

View File

@@ -700,14 +700,14 @@ if (!@fans && @cpu && @fans_all &&
}
# Fall back logic for CPU temperature and fans spread over multiple
# devices like Raspberry Pi #2517 and #2539
# devices like Raspberry Pi #2517 and #2539 #2545
if (@cpu || !@fans) {
# - Look for least two ISA voltage rails anywhere
# - See a CPU temp under cpu_thermal
# - Optionally grab a fan RPM under pwmfan-isa-*
# - Optionally grab a fan RPM under *fan-isa-*
my $can_fallback =
(!@cpu && (grep { /^\s*cpu_thermal/i } @sensors)) ||
(@cpu && !@fans && (grep { /^\s*pwmfan-isa-/i } @sensors));
(@cpu && !@fans && (grep { /fan-isa-\d+/i } @sensors));
return (\@cpu, \@fans) if (!$can_fallback);
my ($chip, $bus); # isa|pci|platform|virtual
my $isa_volt;
@@ -735,7 +735,7 @@ if (@cpu || !@fans) {
}
# Fan RPM
if (defined $chip && $chip =~ /^pwmfan/i &&
if (defined $chip && $chip =~ /fan$/i &&
/\b(?:cpu[_ ]?fan(?:\s*\d+)?|fan\d+)\s*:\s*(\d+)\s*rpm\b/i) {
my $rpm = $1 + 0;
$fan_rpm = $rpm if (!$fan_rpm || $rpm > $fan_rpm);
@@ -754,31 +754,65 @@ return (\@cpu, \@fans);
}
# get_cpu_io_usage()
# Returns a list containing CPU user, kernel, idle, io and VM time, and IO
# blocks in and out
# Returns a list of "us", "sy", "id", "wa", "st", "bi", "bo" that match `vmstat`
# output by using /proc with much lower overhead
sub get_cpu_io_usage
{
my ($nodelay) = @_;
my $interval;
$interval = " 1 2"
if (!$nodelay);
my ($out, @lines, @w);
if (&has_command("vmstat")) {
$out = &backquote_command("vmstat$interval 2>/dev/null");
@lines = split(/\r?\n/, $out);
@w = split(/\s+/, $lines[$#lines]);
shift(@w) if ($w[0] eq '');
if ($w[8] =~ /^\d+$/ && $w[9] =~ /^\d+$/) {
return ( @w[12..16], $w[8], $w[9] );
}
} elsif (&has_command("dstat")) {
$out = &backquote_command("dstat 1 1 2>/dev/null");
@lines = split(/\r?\n/, $out);
@w = split(/[\s|]+/, $lines[$#lines]);
shift(@w) if ($w[0] eq '');
return( @w[0..4], @w[6..7]);
}
return undef;
# Read CPU counters from /proc/stat, the first "cpu" line
my $read_cpu = sub {
open(my $fh, '<', '/proc/stat') or return;
my $line = <$fh>;
close($fh);
return unless defined $line && $line =~ /^cpu\s+/;
my @v = split /\s+/, $line; shift @v;
push @v, (0) x (8-@v) if @v < 8;
return @v[0..7];
};
# Read pgpgin/pgpgout from /proc/vmstat (in KB). Note, that all modern kernels,
# /proc/vmstat pgpgin/pgpgout are KiB (scale=1); not as *pages*, so no need to
# convert deltas
my $read_io = sub {
open(my $fh, '<', '/proc/vmstat') or return (undef, undef);
my ($in, $out);
while (my $l = <$fh>) {
$in = $1 if !defined $in && $l =~ /^pgpgin\s+(\d+)/;
$out = $1 if !defined $out && $l =~ /^pgpgout\s+(\d+)/;
last if defined $in && defined $out;
}
close($fh);
return ($in, $out); # KB on kernels 2.6.18+; no conversion needed
};
# Sample A
my @cb = $read_cpu->() or return;
my ($pgin_b, $pgout_b) = $read_io->();
# Sleep half a second
select(undef, undef, undef, 0.5);
# Sample B
my @ca = $read_cpu->() or return;
my ($pgin_a, $pgout_a) = $read_io->();
# CPU percentages as in vmstat
my @d = map { $ca[$_] - $cb[$_] } 0..7;
for (@d) { $_ = 0 if $_ < 0 }
my $tot = 0; $tot += $_ for @d[0..7];
return if !$tot;
# Calculate percentages
my $us = int( (($d[0] + $d[1]) / $tot) * 100 ); # user+nice
my $sy = int( (($d[2] + $d[5] + $d[6]) / $tot) * 100 ); # system+irq+softirq
my $id = int( ( $d[3] / $tot ) * 100 ); # idle
my $wa = int( ( $d[4] / $tot ) * 100 ); # iowait
my $st = int( ( $d[7] / $tot ) * 100 ); # steal
# Calculate bi/bo in KiB/s over half a second
my ($bi, $bo) = (0, 0);
$bi = int( (($pgin_a // 0) - ($pgin_b // 0)) / 0.5 );
$bo = int( (($pgout_a // 0) - ($pgout_b // 0)) / 0.5 );
return ($us, $sy, $id, $wa, $st, $bi, $bo);
}
# has_disk_stats()

View File

@@ -108,7 +108,7 @@ if ($out =~ /Primary\s+memory\s+available:\s+([0-9\.]+)\s+g/i) {
$rv[0] = $1 * 1024 * 1024;
}
else {
my $out = &backquote_command("sysctl -a hw.physmem 2>/dev/null");
my $out = &backquote_command("sysctl hw.physmem 2>/dev/null");
if ($out =~ /:\s*(\d+)/) {
$rv[0] = $1 / 1024;
}
@@ -129,7 +129,7 @@ if ($usage > $rv[0]) {
$rv[1] = $rv[0] - $usage;
# Get swap usage
$out = &backquote_command("sysctl -a vm.swapusage 2>/dev/null");
$out = &backquote_command("sysctl vm.swapusage 2>/dev/null");
if ($out =~ /total\s*=\s*([0-9\.]+)([KMGT]).*free\s*=\s*([0-9\.]+)([KMGT])/) {
$rv[2] = $1*($2 eq "K" ? 1 :
$2 eq "M" ? 1024 :
@@ -155,7 +155,7 @@ my $out = &backquote_command("uptime 2>&1");
my @rv = $out =~ /average(s)?:\s+([0-9\.]+),?\s+([0-9\.]+),?\s+([0-9\.]+)/i ?
( $2, $3, $4 ) : ( undef, undef, undef );
$out = &backquote_command("sysctl -a machdep.cpu.brand_string");
$out = &backquote_command("sysctl machdep.cpu.brand_string");
if ($out =~ /:\s*(\S.*)/) {
$rv[4] = $1;
if ($rv[4] =~ s/\s*\@\s*([0-9\.]+)(GHz|MHz)//i) {
@@ -169,7 +169,7 @@ if (!$?) {
$rv[5] = $out;
}
else {
$out = &backquote_command("sysctl -a machdep.cpu.vendor");
$out = &backquote_command("sysctl machdep.cpu.vendor");
if ($out =~ /:\s*(\S.*)/) {
$rv[5] = $1;
}
@@ -180,7 +180,7 @@ if ($out =~ /:\s*(\d+)/) {
$rv[6] = $1 * 1024;
}
$out = &backquote_command("sysctl -a machdep.cpu.core_count");
$out = &backquote_command("sysctl machdep.cpu.core_count");
if ($out =~ /:\s*(\d+)/) {
$rv[7] = $1;
}

View File

@@ -616,6 +616,13 @@ else {
print STOP "else\n";
print STOP " echo Stopping Webmin server in $wadir\n";
print STOP "fi\n";
print STOP "pids=\`ps axww -o pid= -o command= | awk -v wd=\"$wadir/\" '\$0 ~ wd && \$0 !~ /miniserv\\.pl/ {print \$1}'\`\n";
print STOP "[ -n \"\$pids\" ] && kill \$pids 2>/dev/null || true\n";
print STOP "if [ \"\$1\" = \"--kill\" ]; then\n";
print STOP " sleep 1\n";
print STOP " pids=\`ps axww -o pid= -o command= | awk -v wd=\"$wadir/\" '\$0 ~ wd && \$0 !~ /miniserv\\.pl/ {print \$1}'\`\n";
print STOP " [ -n \"\$pids\" ] && kill -KILL \$pids 2>/dev/null || true\n";
print STOP "fi\n";
print STOP "pidfile=\`grep \"^pidfile=\" $config_directory/miniserv.conf | sed -e 's/pidfile=//g'\`\n";
print STOP "pid=\`cat \$pidfile 2>/dev/null\`\n";
print STOP "if [ \"\$pid\" != \"\" ]; then\n";
@@ -971,7 +978,7 @@ if (!$ENV{'nostart'}) {
print " - DateTime, DateTime::Locale, DateTime::TimeZone, Data::Dumper,\n";
print " - Digest::MD5, Digest::SHA, Encode::Detect, File::Basename,\n";
print " - File::Path, Net::SSLeay, Time::HiRes, Time::Local, Time::Piece,\n";
print " - JSON::XS, lib, open\n";
print " - Socket6, Sys::Syslog, JSON::XS, lib, open\n";
print " Packages:\n";
print " - openssl - Cryptography library with TLS implementation\n";
print " - shared-mime-info - Shared MIME information database\n";

View File

@@ -695,6 +695,13 @@ echo " echo Force stopping Webmin server in $wadir" >>$config_dir/.stop-init
echo "else" >>$config_dir/.stop-init
echo " echo Stopping Webmin server in $wadir" >>$config_dir/.stop-init
echo "fi" >>$config_dir/.stop-init
echo "pids=\`ps axww -o pid= -o command= | awk -v wd=\"$wadir/\" '\$0 ~ wd && \$0 !~ /miniserv\\.pl/ {print \$1}'\`" >>$config_dir/.stop-init
echo "[ -n \"\$pids\" ] && kill \$pids 2>/dev/null || true" >>$config_dir/.stop-init
echo "if [ \"\$1\" = \"--kill\" ]; then" >>$config_dir/.stop-init
echo " sleep 1" >>$config_dir/.stop-init
echo " pids=\`ps axww -o pid= -o command= | awk -v wd=\"$wadir/\" '\$0 ~ wd && \$0 !~ /miniserv\\.pl/ {print \$1}'\`" >>$config_dir/.stop-init
echo " [ -n \"\$pids\" ] && kill -KILL \$pids 2>/dev/null || true" >>$config_dir/.stop-init
echo "fi" >>$config_dir/.stop-init
echo "pidfile=\`grep \"^pidfile=\" $config_dir/miniserv.conf | sed -e 's/pidfile=//g'\`" >>$config_dir/.stop-init
echo "pid=\`cat \$pidfile 2>/dev/null\`" >>$config_dir/.stop-init
echo "if [ \"\$pid\" != \"\" ]; then" >>$config_dir/.stop-init
@@ -1063,7 +1070,7 @@ if [ "$nostart" = "" ]; then
echo " - DateTime, DateTime::Locale, DateTime::TimeZone, Data::Dumper,"
echo " - Digest::MD5, Digest::SHA, Encode::Detect, File::Basename,"
echo " - File::Path, Net::SSLeay, Time::HiRes, Time::Local, Time::Piece,"
echo " - JSON::XS, lib, open"
echo " - Socket6, Sys::Syslog, JSON::XS, lib, open"
echo " Packages:"
echo " - openssl - Cryptography library with TLS implementation"
echo " - shared-mime-info - Shared MIME information database"

View File

@@ -197,9 +197,14 @@ foreach $r (&expand_remotes($serv)) {
# Just include and use the local monitor library
do "${t}-monitor.pl" if (!$done_monitor{$t}++);
my $func = "get_${t}_status";
$rv = &$func($serv,
if (defined(&$func)) {
$rv = &$func($serv,
$serv->{'clone'} ? $serv->{'clone'} : $t,
$fromcgi);
}
else {
$rv = { 'up' => -1 };
}
}
alarm(0);
};

View File

@@ -673,7 +673,7 @@ $rv .= &ui_columns_table($heads, $width, $data, $types, $nosort, $title,
$emptymsg);
# Add the bottom links unless excluded
if ($selectall && $selectall != 2) {
if ($selectall != 2) {
$rv .= $links;
}
@@ -1108,9 +1108,8 @@ if (dir == 1 && opts_idx >= 0) {
for(var i=0; i<opts.options.length; i++) {
var o = opts.options[i];
if (o.selected) {
vals.options[vals.options.length] =
new Option(o.text, o.value);
opts.remove(i);
o.selected = false;
vals.add(o, 0);
i--;
}
}
@@ -1120,9 +1119,8 @@ else if (dir == 0 && vals_idx >= 0) {
for(var i=0; i<vals.options.length; i++) {
var o = vals.options[i];
if (o.selected) {
opts.options[opts.options.length] =
new Option(o.text, o.value);
vals.remove(i);
o.selected = false;
opts.add(o, 0);
i--;
}
}

View File

@@ -39,7 +39,10 @@ print &ui_table_row("",
# Enable forgotten password recovery
print &ui_table_row($text{'session_forgot'},
&ui_yesno_radio("forgot", $uconfig{'forgot_pass'}));
&ui_radio("forgot", $uconfig{'forgot_pass'},
[ [ 0, $text{'no'}."<br>" ],
[ 1, $text{'yes'}."<br>" ],
[ 2, $text{'forgot_nossl'} ] ]));
# Log to syslog
eval "use Sys::Syslog qw(:DEFAULT setlogsock)";

View File

@@ -1 +1 @@
2.501
2.510

View File

@@ -3090,7 +3090,7 @@ local ($line, %header, @headers, $s);
$timeout = 60 if (!defined($timeout));
alarm($timeout) if ($timeout);
($line = &read_http_connection($h)) =~ tr/\r\n//d;
if ($line !~ /^HTTP\/1\..\s+(200|30[0-9]|400)(\s+|$)/) {
if ($line !~ /^HTTP\/1\..\s+(20[0-9]|30[0-9]|400)(\s+|$)/) {
$line ||= "Failed to read HTTP response line";
alarm(0) if ($timeout);
&close_http_connection($h);
@@ -7477,84 +7477,189 @@ if ($gconfig{'logfiles'} && !&get_module_variable('$no_log_file_changes')) {
}
}
=head2 var_dump(objref, [filename|no-html], [pid-to-filename])
=head2 var_dump(@data)
Prints to UI or dumps to a file content of array/hash
ref or variable. For internal debug use only.
Dump any Perl values to a log file and, when running in a
Webmin web context, also print them to the UI.
Examples to print output to UI:
var_dump(\@array_ref, 0); # No HTML, standard output
var_dump(\%hash_ref); # HTML output (newlines and spaces replaced with <br> and &nbsp;)
=item * Call with one or more arguments.
Examples to dump content to a file (under Webmin tempdir):
var_dump(\@array_ref, 'array_ref_name', 'add-process-pid-to-filename');
var_dump(\%hash_ref, 'hash_ref_name');
Use a reference for composites to avoid list flattening.
var_dump(\@array);
var_dump(\%hash);
var_dump($hashref);
var_dump($scalar);
var_dump($scalar, \%hash, \@array);
=item * Log file
Prints by appending to the "/var/webmin/webmin.dump" file.
=item * Header
Each entry starts with:
===== @ Tue Sep 21 12:00:00.123 2025 pid 1234 =====
Millisecond precision is used when Time::HiRes is available.
=item * UI (web context)
If the dump is also printed to the browser, it is HTML-escaped with preserved
formatting. A single header is sent once per process if a header hasn't already
been sent.
Long output is collapsed after the first 5 lines, with the rest shown inside an
expandable/collapsible accordion toggle.
=item * Return
Returns 0 on success, or an "Error: ..." string. If Data::Dumper is missing,
the error is written to the log and shown in the UI on the web.
=back
=cut
sub var_dump
{
my ($objref, $filename, $pidtofilename) = @_;
my $pid;
$pid = "-" . $$ if ($pidtofilename);
my $filename_ = "$pid";
my @args = @_;
if ($filename && $filename_) {
$filename =~ tr/A-Za-z0-9\_\-//cd;
$filename = "$filename--";
# Output target
my $dir = $main::var_dir || $main::var_directory || '/var/webmin';
my $file = "$dir/webmin.dump";
# Create directory if missing
mkdir $dir, 0750 if (!-d $dir);
# Timestamp with milliseconds if possible
my ($sec, $usec);
{
eval 'use Time::HiRes qw(gettimeofday)';
if ($@) { $sec = time; $usec = 0; }
else { ($sec, $usec) = Time::HiRes::gettimeofday(); }
}
my $ts = scalar localtime($sec);
my $ms = int($usec / 1000);
$ts =~ s/(\d{2}:\d{2}:\d{2})/$1 . sprintf(".%03d", $ms)/e;
# Open log file ealier to log open errors too
my $fh;
my $opened = open($fh, '>>', $file);
if ($opened) {
eval { flock($fh, 2); };
print {$fh} "===== @ $ts pid $$ =====\n";
}
state $check_header;
my $check_headers = sub {
if (!$check_header++ && !$main::done_webmin_header) {
print "Content-type: text/html\n\n";
}
};
# Load Data::Dumper dynamically
my $dumper_err;
eval 'use Data::Dumper';
if (!$@) {
$Data::Dumper::Indent = 1;
$Data::Dumper::Terse = 1;
$Data::Dumper::Deepcopy = 1;
$Data::Dumper::Sortkeys = 1;
if ($@) {
$dumper_err = "Error: The Data::Dumper Perl module is not available ".
"on your system";
print {$fh} "$dumper_err\n" if $opened;
}
# Write file
if ($filename) {
my ($seconds, $microseconds);
eval 'use Time::Piece';
if (!$@) {
eval 'use Time::HiRes';
if (!$@) {
($seconds, $microseconds) = Time::HiRes::gettimeofday;
$microseconds = "$seconds$microseconds-";
# Build dump or error text
my $dump_txt = $dumper_err || '';
if (!$dumper_err) {
local $Data::Dumper::Indent = 2;
local $Data::Dumper::Terse = 1;
local $Data::Dumper::Sortkeys = 1;
local $Data::Dumper::Quotekeys = 0;
local $Data::Dumper::Useqq = 0;
for (my $i = 0; $i < @args; $i++) {
$dump_txt .= "----- param #".($i+1)." -----\n";
$dump_txt .= Data::Dumper::Dumper($args[$i]);
}
}
# Write to file or show error if can't open
$dump_txt = "Error: open($file) failed: $!\n" . ($dump_txt // '') if (!$opened);
if ($opened) {
print {$fh} $dump_txt, "\n\n";
eval { flock($fh, 8); };
close $fh or return "Error: close($file): $!";
}
# Optionally print to UI (web scripts only) only if header already sent
if ($main::done_webmin_header && $main::webmin_script_type eq 'web') {
# Limit long dumps by wrapping parameter chunks between accordion lines
my $out = $dump_txt;
# Add header
$out = "===== @ $ts pid $$ =====\n" . $out;
# Split into chunks
my @chunks;
{
my $param_re = qr/^-{3,}\s*param\s*#\d+\s*-{3,}\s*$/i;
my @lines = split /\n/, $out, -1;
my ($cur_header, @cur_lines);
my $push_chunk = sub {
push @chunks, {
header => $cur_header,
lines => [ @cur_lines ] }
if defined($cur_header) || @cur_lines;
($cur_header, @cur_lines) = (undef);
};
for my $ln (@lines) {
if ($ln =~ $param_re) {
$push_chunk->();
$cur_header = $ln;
}
else {
push @cur_lines, $ln;
}
}
write_file_contents(tempname("${microseconds}${filename}${filename_}"), Dumper($objref));
}
# Print on screen
else {
my $dumped_data = Dumper($objref);
# If print to UI, escape HTML and
# replace new lines and spaces
if ($main::webmin_script_type eq 'web') {
$dumped_data = &html_escape($dumped_data);
$dumped_data =~ s/\n/<br>/g;
$dumped_data =~ s/\s/&nbsp;/g;
$push_chunk->();
}
# Render each chunk and wrap only param chunks that have more than the
# limit lines
my $limit = 5;
my $esc = sub {
my ($s) = @_;
$s =~ s/&/&amp;/g;
$s =~ s/</&lt;/g;
$s =~ s/>/&gt;/g;
$s =~ s/"/&quot;/g;
$s =~ s/'/&apos;/g;
$s =~ s/ /&nbsp;/g;
return $s;
};
my @rendered;
my $first = 1;
for my $c (@chunks) {
my $piece = '';
$piece .= "<br>\n" unless $first;
$first = 0;
my $hdr = defined $c->{header} ? $esc->($c->{header}) : undef;
my @elines = map { $esc->($_) } @{$c->{lines}};
$piece .= "$hdr<br>\n" if defined $hdr;
if (defined($hdr) && @elines > $limit) {
# Param chunk with wrapping
my $head = join '<br>', @elines[0 .. $limit-1];
my $tail = join '<br>', @elines[$limit .. $#elines];
$piece .= $head
. "<br>\n"
. "<details class=\"inline\">"
. "<summary>&nbsp;&nbsp;...</summary>\n"
. $tail . "\n</details>\n";
}
&$check_headers();
$objref && print $dumped_data;
}
}
else {
my $dumpererr = "Error: The Data::Dumper Perl module is not available on your system";
# Write file
if ($filename) {
write_file_contents(tempname("${filename_}_error"), $dumpererr);
}
# Print on screen
else {
&$check_headers();
$objref && print Dumper($dumpererr);
else {
# Non-param chunk or short param with no wrapping
$piece .= join '<br>', @elines;
}
push @rendered, $piece;
}
$out = join('', @rendered);
$out .= "<br>\n";
print "<tt>$out</tt>";
}
# Return result
return $dumper_err || 0;
}
=head2 webmin_debug_log(type, message)
@@ -13150,10 +13255,12 @@ if (!$def && $gconfig{'webmin_email_url'}) {
# From a config option
$url = $gconfig{'webmin_email_url'};
}
elsif ($ENV{'HTTP_HOST'}) {
elsif ($ENV{'HTTP_HOST'} || $ENV{'SSL_CN'}) {
# From this HTTP request
my $host = $ENV{'HTTP_HOST'};
my $port = $ENV{'SERVER_PORT'} || 80;
my $host = $ENV{'SSL_CN'}
? "$ENV{'SSL_CN'}:$port"
: $ENV{'HTTP_HOST'};
if ($host =~ s/:(\d+)$//) {
$port = $1;
}

View File

@@ -42,7 +42,10 @@ print &ui_table_row("",
# Enable forgotten password recovery
print &ui_table_row($text{'session_forgot'},
&ui_yesno_radio("forgot", $gconfig{'forgot_pass'}));
&ui_radio("forgot", $gconfig{'forgot_pass'},
[ [ 0, $text{'no'}."<br>" ],
[ 1, $text{'yes'}."<br>" ],
[ 2, $text{'forgot_nossl'} ] ]));
# Block bad password requests
$gconfig{'passreset_failures'} //= 3;