Compare commits

..

74 Commits

Author SHA1 Message Date
Jamie Cameron
2751224d4d Really update version 2024-07-21 10:21:20 -07:00
Jamie Cameron
6fce9fa491 New version bump 2024-07-21 10:20:43 -07:00
Jamie Cameron
41fdb5dac2 Fix renumbering when adding a domain 2024-07-20 21:53:49 -07:00
Jamie Cameron
4ffca4597c Clean up group_to_dn function 2024-07-20 21:30:46 -07:00
Jamie Cameron
ecfc06d9c6 Preserve firstname and surname 2024-07-20 21:28:45 -07:00
Jamie Cameron
db55dde7ce Merge branch 'master' of github.com:webmin/webmin 2024-07-20 21:23:16 -07:00
Jamie Cameron
c6edd4b97d Code cleanups 2024-07-20 21:19:01 -07:00
Ilia Ross
8cd2dbae96 Fix to redirect to tabs only inside same module
https://forum.virtualmin.com/t/small-upgrade-error-maybe-wrong-url-on-button/127999?u=ilia
2024-07-21 02:37:32 +03:00
Jamie Cameron
8676a3fb21 Merge branch 'master' of github.com:webmin/webmin 2024-07-20 16:18:25 -07:00
Jamie Cameron
27339eb1bf Use my instead of local 2024-07-20 16:18:16 -07:00
Ilia Ross
e014926854 Update CHANGELOG.md for 2.200 2024-07-21 02:00:01 +03:00
Ilia Ross
a721f60f9c Fix to allow service-worker.js in unauth [build] 2024-07-20 15:57:40 +03:00
Jamie Cameron
46c76e13f9 Merge branch 'master' of github.com:webmin/webmin 2024-07-19 16:52:29 -07:00
Jamie Cameron
f72058306b COnfig option to show hostname and comment https://github.com/webmin/webmin.com/issues/18 2024-07-19 16:52:23 -07:00
Ilia Ross
a15446d3b1 Fix to simplify the code and use no globals [build] 2024-07-19 13:40:14 +03:00
Ilia Ross
fc9ce7f3dd Fix comment 2024-07-19 13:30:01 +03:00
Ilia Ross
72cd50a054 Fix to consider "F" as new theme name "Framed" not "Gray" 2024-07-19 13:13:53 +03:00
Ilia Ross
e307fb4dcd Fix to drop unnecessary option 2024-07-19 12:36:52 +03:00
Jamie Cameron
d631929194 Merge branch 'master' of github.com:webmin/webmin 2024-07-16 16:08:06 -07:00
Jamie Cameron
db9628e7eb Update MIME types for javascript and gzipped files 2024-07-16 16:08:01 -07:00
Jamie Cameron
2c04c04ce7 Merge pull request #2217 from webmin/dev/package-updates-always-show-sec
Fix to always show security updates button
2024-07-16 11:51:16 -07:00
Ilia Ross
a5301245d3 Fix to always show security updates button 2024-07-16 17:38:14 +03:00
Jamie Cameron
165af690c7 Log automatic package updates 2024-07-13 17:07:07 -07:00
Jamie Cameron
29da8ea3d0 Clarify what kind of update it is https://forum.virtualmin.com/t/security-updates-being-auto-installed-regardles-of-software-scheduled-upgrades-set-to-just-notify/127852 2024-07-12 17:22:19 -07:00
Jamie Cameron
73b7e62f13 Merge branch 'master' of github.com:webmin/webmin 2024-07-09 08:30:51 -07:00
Ilia Ross
76141ce22f Fix to use larger QR code [build] 2024-07-09 15:53:18 +03:00
Jamie Cameron
4b575b8168 Clarify comment 2024-07-08 22:55:44 -07:00
Ilia Ross
2b28521297 Fix to display correct return button 2024-07-08 21:03:40 +03:00
Ilia Ross
be767951ca Fix to have no block elements in header 2024-07-08 19:33:52 +03:00
Jamie Cameron
9960d6011f Update comment to match reality 2024-07-07 11:12:47 -07:00
Jamie Cameron
461bd30e2a Save last size and subset mode 2024-07-06 15:26:57 -07:00
Jamie Cameron
2f88a4eefb Follow default reuse option 2024-07-06 15:23:17 -07:00
Jamie Cameron
c9f368d264 Properly check all ServerAlias directives and ports 2024-07-06 15:12:08 -07:00
Jamie Cameron
0e24e8ac61 Merge branch 'master' of github.com:webmin/webmin 2024-07-05 10:32:21 -07:00
Jamie Cameron
1d0d25efac Don't add tab param twice https://sourceforge.net/p/webadmin/bugs/5648/ 2024-07-05 10:01:55 -07:00
Jamie Cameron
1c5d2d2bd7 Merge pull request #2211 from webmin/dev/preserve-file-acls
Add ability to preserve original file ACLs
2024-07-03 15:43:43 -07:00
Ilia Ross
77e809166c Fix to use backquote_command instead 2024-07-03 10:40:20 +03:00
Jamie Cameron
927a2c32d8 Show queue state in list 2024-07-02 21:36:05 -07:00
Jamie Cameron
4a3c6c4854 Merge branch 'master' of github.com:webmin/webmin 2024-07-02 21:21:27 -07:00
Jamie Cameron
37beab77ba Show mail queue directory 2024-07-02 21:21:21 -07:00
Ilia Ross
82f5284ffc Fix to use pipe handling data directly in memory
https://github.com/webmin/webmin/pull/2211#discussion_r1663042881
2024-07-02 22:48:40 +03:00
Ilia Ross
5f579e8ded Fix to invalidate OS EOL data on upgrade 2024-07-02 22:24:09 +03:00
Ilia Ross
4b66ac0be5 Add ability to preserve original file ACLs
https://github.com/webmin/authentic-theme/discussions/1511#discussioncomment-9913902
2024-07-02 20:50:19 +03:00
Jamie Cameron
e69fb75c8e Show the mail queue path 2024-07-01 21:33:22 -07:00
Jamie Cameron
ff8781c112 Merge branch 'master' of github.com:webmin/webmin 2024-07-01 21:15:04 -07:00
Jamie Cameron
0dd75db8d8 Don't wrap header lines 2024-07-01 21:09:17 -07:00
Jamie Cameron
05d01aeef3 Merge pull request #2207 from webmin/dev/patch-api
Add apply patch CLI API
2024-06-30 11:05:00 -07:00
Jamie Cameron
11f2bc20eb Merge branch 'master' of github.com:webmin/webmin 2024-06-30 10:07:08 -07:00
Jamie Cameron
52c3178b92 Add API to delete sections 2024-06-30 10:07:01 -07:00
Jamie Cameron
e3dec2222d Merge pull request #2209 from webmin/dev/separate-temps-collection-2208
Fix to separate drive and CPU temperatures collection #2208
2024-06-30 08:24:26 -07:00
Ilia Ross
05752faec0 Fix to separate drive and CPU temperatures collection #2208 2024-06-30 15:54:47 +03:00
Ilia Ross
7507433bf1 Fix to drop dependency from Term::ANSIColor 2024-06-30 11:41:41 +03:00
Ilia Ross
248cb719c0 Fix restart command depend on config dir 2024-06-29 23:32:15 +03:00
Ilia Ross
94b7fdf0ec Fix to escape params 2024-06-29 23:29:55 +03:00
Ilia Ross
d89f6411b6 Fix to use has_command API 2024-06-29 20:34:29 +03:00
Ilia Ross
01d08a3605 Fix to make patch API work directly from URL 2024-06-28 19:48:51 +03:00
Jamie Cameron
1cf3813fb6 Merge branch 'master' of github.com:webmin/webmin 2024-06-27 19:45:29 -07:00
Jamie Cameron
1216ae709b Stop showing and editing dnssec-lookaside https://github.com/webmin/webmin/issues/617 2024-06-27 19:45:21 -07:00
Ilia Ross
3d9497ff45 Fix command example 2024-06-28 01:51:02 +03:00
Ilia Ross
a6832450d1 Fix to restart Webmin when done 2024-06-28 01:38:28 +03:00
Ilia Ross
e41037388c Apply a patch to Webmin core or its modules from GitHub or a local file 2024-06-28 01:35:10 +03:00
Ilia Ross
a7b3af534b Fix to drop Threads module from recommended packages 2024-06-26 18:12:47 +03:00
Ilia Ross
87e006ceeb Add new API to verify session id 2024-06-26 14:39:23 +03:00
Ilia Ross
fbee8f0588 Add logging for active FTP conns 2024-06-20 20:04:19 +03:00
Jamie Cameron
e077b4da94 Start of work on support for requesting only a subset of hostnames 2024-06-19 22:05:22 -07:00
Jamie Cameron
38efad8265 Escape some inputs 2024-06-19 20:37:25 -07:00
Jamie Cameron
4d2a1fa084 Add spam folder name 2024-06-19 17:19:51 -07:00
Jamie Cameron
13b2eca3b1 Follow webmin code standards 2024-06-19 16:48:41 -07:00
Ilia Ross
aa4c3b1de6 Fix embedded styles margin 2024-06-19 16:55:25 +03:00
Ilia Ross
edaab4fd6f Fix embedded styles 2024-06-19 16:52:04 +03:00
Jamie Cameron
6fded0862c Merge pull request #2201 from webmin/dev/fix-last-command-match-freebsd
Fix listing last logins in FreeBSD
2024-06-18 16:16:37 -07:00
Ilia Ross
4006b0454e Fix to escape and not to truncate username 2024-06-18 23:51:19 +03:00
Ilia Ross
5d4ab58baa Fix the regex for last command on FreeBSD 2024-06-18 23:50:12 +03:00
Jamie Cameron
e6c7a60fe6 Merge pull request #2200 from webmin/dev/fix-last-command-regexes
Fix the regex to correctly match the output of the `last` command on contemporary systems
2024-06-18 13:38:21 -07:00
55 changed files with 697 additions and 322 deletions

View File

@@ -1,5 +1,29 @@
## Changelog
#### 2.200 (July 21, 2024)
* Add support for blocking a given IP temporarily or permanently in the FirewallD module
* Add support for parsing iCalendar event files in the Mailbox module
* Add support for tailing logs in real time in System Logs module
* Add ability to preserve original file ACLs when writing files [webmin/authentic-theme#1511](https://github.com/webmin/authentic-theme/discussions/1511#discussioncomment-9913902)
* Add a `patch` sub-command to the `webmin` command for easy application of patches
* Add a config option to display hostname and comment in the DHCP Server module [#2221](https://github.com/webmin/webmin/issues/2221)
* Add support for ED25519 and ED448 algorithms in BIND DNS module for DNSSEC
* Add support for larger ranger of authentication methods in Dovecot module
* Add improved support for displaying last logins in the Users and Groups module
* Fix to prevent duplicate `also-notify` and `allow-transfer` IPs in the BIND DNS module
* Fix issues with Terminal module to correct text display problems in editor mode
* Fix to allow disabling the newly introduced enforcement of _sudo_-capable logins in the Terminal module
* Fix to store Terminal module logs in the `/var/webmin` directory
* Fix to display the Spam folder nicely in the Mailbox module
* Fix how modules are loaded in ProFTPd module
* Fix support for the Chrony service on Debian systems in the System Time module
* Fix to use static routes to set the default gateway in Network Configuration module
* Fix to correctly invalidate EOL cache on re-checks [#2139](https://github.com/webmin/webmin/issues/2139)
* Fix to change default monitor name based on database used MariaDB vs MySQL [#2139](https://github.com/virtualmin/virtualmin-gpl/issues/798)
* Fix to disable manual upgrades for systems installed from the repository
* Fix to preserve Webmin service state during package upgrades [#2133](https://github.com/webmin/webmin/issues/2133)
* Rename "System Logs" module to "System Logs RS" and "System Logs Viewer" to "System Logs" for clarity
#### 2.111 (April 16, 2024)
* Fix EOL detection for unreleased Linux distributions

File diff suppressed because one or more lines are too long

153
bin/patch Executable file
View File

@@ -0,0 +1,153 @@
#!/usr/bin/env perl
# patch - Apply a patch to Webmin core or its modules from GitHub or a local file
use strict;
use warnings;
use 5.010;
use Getopt::Long qw(:config permute pass_through);
use Pod::Usage;
use File::Basename;
use Cwd qw(cwd);
my %opt;
GetOptions(
'help|h' => \$opt{'help'},
'config|c=s' => \$opt{'config'},
);
pod2usage(0) if ($opt{'help'});
# Get Webmin path
my $path = cwd;
my $lib = "web-lib-funcs.pl";
if (!-r "$path/$lib") {
$path = dirname(dirname($0));
if (!-r "$path/$lib") {
$path = $path = Cwd::realpath('..');
}
}
# Init core
my $config_dir = $opt{'config'} || '/etc/webmin';
$ENV{'WEBMIN_CONFIG'} = $config_dir;
push(@INC, $path);
eval 'use WebminCore';
init_config();
# Check if curl is installed
if (!has_command('curl')) {
print "curl is not installed\n";
exit 1;
}
# Check if git is installed
if (!has_command('git')) {
print "git is not installed\n";
exit 1;
}
# Get patch URL or file
my $patch = $ARGV[0];
# Params check
if (!$patch) {
pod2usage(0);
exit 1;
}
# Patch check
if ($patch !~ /^https?:\/\//) {
if (!-r $patch) {
print "Patch file $patch doesn't exist\n";
exit 1;
}
}
elsif ($patch =~ /^https?:\/\/(github|gitlab)\.com/ &&
$patch !~ /\.patch$/ && $patch !~ /\.diff$/) {
$patch .= '.patch';
}
# Parse module name from URL
my $module = "";
if ($patch =~ m{https://(github|gitlab)\.com/[^/]+/([^/]+)/commit/[^/]+}) {
$module = $2;
$module = "" if ($2 eq 'webmin');
# Special handling for some modules
$module = $module =~ /^virtualmin-pro$/ ?
'virtual-server/pro' :
'virtual-server'
if $module =~ /^virtualmin-(gpl|pro)$/;
}
# Check if module exists
if (!-d "$path/$module") {
print "Module $module doesn't exist\n";
exit 1;
}
# Download command or cat patch file
my $cmd;
if ($patch =~ /^https?:\/\//) {
$cmd = "curl -s @{[quotemeta($patch)]}";
chdir "$path/$module";
}
else {
$cmd = "cat @{[quotemeta($patch)]}";
}
# Apply patch using Git
my $output = `$cmd 2>&1 | git apply --reject --verbose --whitespace=fix 2>&1`;
if ($output !~ /applied patch.*?cleanly/i) {
print "Patch failed: $output\n";
exit 1;
}
print "Patch applied successfully to:\n";
print " $1\n" while $output =~ /^Applied patch\s+(\S+)/mg;
system("$config_dir/restart");
=pod
=head1 NAME
patch
=head1 DESCRIPTION
Apply a patch to Webmin core or its modules from GitHub or a local file.
=head1 SYNOPSIS
webmin patch patch-url/file
=head1 OPTIONS
=over
=item --help, -h
Give this help list.
=item --config, -c
Specify the full path to the Webmin configuration directory. Defaults to
C</etc/webmin>
Examples of usage:
Apply a patch from a URL.
- webmin patch https://github.com/webmin/webmin/commit/e6a2bb15b0.patch
- webmin patch https://github.com/virtualmin/virtualmin-gpl/commit/f4433153d
Apply a patch from local file.
- cd /usr/libexec/webmin/virtual-server/pro &&
webmin patch /root/virtualmin-pro/patches/patch-1.patch
=back
=head1 LICENSE AND COPYRIGHT
Copyright 2024 Ilia Ross <ilia@virtualmin.com>

View File

@@ -17,7 +17,6 @@ $access{'defaults'} || &error($text{'trusted_ecannot'});
my $conf = &get_config();
my $options = &find("options", $conf);
my $mems = $options->{'members'};
my @dlv = &find("dnssec-lookaside", $mems);
my $tkeys = &find("trusted-keys", $conf);
$tkeys ||= { 'members' => [ ] };
@@ -38,33 +37,6 @@ if (&supports_dnssec_client() == 2) {
$text{'default'}, undef);
}
# Trusted DLVs (obsolete)
if (@dlv) {
my @dtable = ( );
my $i = 0;
foreach my $d (@dlv, { 'values' => [ '.' ] }) {
my $dlv = $d->{'values'}->[0];
$dlv = "" if ($dlv eq ".");
push(@dtable, [
&ui_opt_textbox("anchor_$i", $d->{'values'}->[2],
30, $text{'trusted_none'}),
&ui_opt_textbox("dlv_$i", $dlv, 20,
$text{'trusted_root'}) ]);
$i++;
}
print &ui_table_row($text{'trusted_dlvs'},
&ui_radio("dlv_auto",
@dlv == 0 ? 2 :
@dlv == 1 && $dlv[0]->{'values'}->[0] eq 'auto' ? 1 : 0,
[ [ 1, $text{'trusted_dlvs1'} ],
[ 2, $text{'trusted_dlvs2'} ],
[ 0, $text{'trusted_dlvs0'} ] ])."<br>\n".
&ui_columns_table([ $text{'trusted_anchor'},
$text{'trusted_dlv'} ],
undef,
\@dtable), 3);
}
# Trusted keys
if (@{$tkeys->{'members'}}) {
my @ktable = ( );

View File

@@ -24,46 +24,6 @@ if (&supports_dnssec_client() == 2) {
&save_choice("dnssec-validation", $options, 1);
}
# Save DLV zones
if (defined($in{'dlv_auto'})) {
my @dlvs = ( );
if ($in{'dlv_auto'} == 1) {
# Automatic mode
push(@dlvs, { 'name' => 'dnssec-lookaside',
'values' => [ 'auto' ] });
}
elsif ($in{'dlv_auto'} == 0) {
# Listed zones
my $dlv;
for(my $i=0; defined($in{"anchor_$i"}); $i++) {
if (!$in{"anchor_${i}_def"}) {
$in{"anchor_$i"} =~ /^[a-z0-9\.\-\_]+$/ ||
&error(&text('trusted_eanchor', $i+1));
$in{"anchor_$i"} .= "."
if ($in{"anchor_$i"} !~ /\.$/);
if ($in{"dlv_${i}_def"}) {
$dlv = ".";
}
else {
$in{"dlv_$i"} =~ /^[a-z0-9\.\-\_]+$/ ||
&error(&text('trusted_edlv', $i+1));
$dlv = $in{"dlv_$i"};
$dlv .= "." if ($dlv !~ /\.$/);
}
push(@dlvs, { 'name' => 'dnssec-lookaside',
'values' => [
$dlv, "trust-anchor",
$in{"anchor_$i"} ] });
}
}
}
elsif ($in{'dlv_auto'} == 2) {
# None
@dlvs = ( );
}
&save_directive($options, "dnssec-lookaside", \@dlvs, 1);
}
# Save trusted keys
if (defined($in{'zone_0'})) {
my @keys = ( );

View File

@@ -21,7 +21,7 @@ if ($in{'source'} == 0) {
if (!$in{'local'})
{ &install_error($text{'download_elocal'}); }
if (!-r $in{'local'})
{ &install_error(&text('download_elocal2', $in{'local'})); }
{ &install_error(&text('download_elocal2', &html_escape($in{'local'}))); }
$source = $in{'local'};
@pfile = ( $in{'local'} );
$need_unlink = 0;
@@ -91,8 +91,9 @@ elsif ($in{'source'} == 3) {
$i = 0;
@fallback = ( );
foreach $yum (@cpanyum) {
print &text('download_yum', "<tt>$cpan[$i]</tt>",
"<tt>$yum->{'package'}</tt>"),"<br>\n";
print &text('download_yum',
"<tt>".&html_escape($cpan[$i])."</tt>",
"<tt>".&html_escape($yum->{'package'})."</tt>"),"<br>\n";
print "<ul>\n";
@got = &software::update_system_install(
$yum->{'package'});
@@ -154,7 +155,8 @@ elsif ($in{'source'} == 3) {
# Fail if any modules are missing from CPAN
for($i=0; $i<@cpan; $i++) {
push(@missing, "<tt>$cpan[$i]</tt>") if (!$source[$i]);
push(@missing, "<tt>".&html_escape($cpan[$i])."</tt>")
if (!$source[$i]);
}
if ($in{'missingok'}) {
@@ -167,11 +169,12 @@ elsif ($in{'source'} == 3) {
}
}
@cpan || &install_error(&text('download_ecpan',
join(" ", @missing)));
&html_escape(join(" ", @missing))));
}
elsif (@missing) {
# Fail due to missing modules
&install_error(&text('download_ecpan', join(" ", @missing)));
&install_error(&text('download_ecpan',
&html_escape(join(" ", @missing))));
}
$source = join("<br>", @source);
@@ -192,14 +195,16 @@ elsif ($in{'source'} == 3) {
&ftp_download($host, $file, $pfile, \$error,
\&progress_callback);
}
else { &install_error(&text('download_eurl', $m)); }
else {
&install_error(&text('download_eurl',&html_escape($m)));
}
&install_error($error) if ($error);
push(@pfile, $pfile);
}
$need_unlink = 1;
}
else {
&error("Unknown source mode $in{'source'}");
&error("Unknown source mode ".&html_escape($in{'source'}));
}
# Check if the file looks like a perl module
@@ -287,7 +292,7 @@ foreach $d (@dirs) {
close(MAKEFILE);
push(@allreqs, @prereqs);
}
system("rm -rf $mtemp");
&unlink_file($mtemp);
# Work out which pre-requesites are missing
@allreqs = &unique(@allreqs);

View File

@@ -8,7 +8,7 @@ lease_refresh=Seconds between refreshing lease list,3,Never
show_ip=Show IP addresses for hosts?,1,1-Yes,0-No
show_mac=Show MAC addresses for hosts?,1,1-Yes,0-No
group_name=Show group names as,1,1-<tt>domain&#45;name</tt> option,0-Name or member count,2-Description
desc_name=Show other object descriptions instead of names?,1,1-Yes,0-No
desc_name=Show descriptions instead of names?,1,1-Description only,0-Name only,2-Both name and description
display_max=Maximum number of subnets and hosts to display,3,Unlimited
add_file=Add new subnets&#44; hosts and groups to file,3,Main configuration file
line2=System configuration,11

View File

@@ -7,7 +7,6 @@ lease_refresh=Segons entre refrescs de la llista de préstecs,3,Mai
show_ip=Mostra les adreces IP dels hosts,1,1-Sí,0-No
show_mac=Mostra les adreces MAC dels hosts,1,1-Sí,0-No
group_name=Mostra els noms de grup com,1,1-Opció <tt>domini&#45;nom</tt>,0-Nom o recompte de membres,2-Descripció
desc_name=Mostra les descripcions dels altres objectes en lloc dels noms,1,1-Sí,0-No
display_max=Nombre màxim de subxarxes i hosts a mostrar,3,Il·limitat
add_file=Afegeix subxarxes noves&#44; hosts i grups al fitxer,3,Fitxer de configuració principal
line2=Configuració del sistema,11

View File

@@ -6,7 +6,6 @@ lease_tz=Zobrazit dobu pronájmu v,1,0-GMT,1-Lokálním čase
show_ip=Zobrazit IP adresy pro hosty?,1,1-ano,0-ne
show_mac=Zobrazit MAC adresy hostů?,1,1-Ano,0-Ne
group_name=Zobrazení jmen skupin jako,1,1-<tt>doménová jména</tt>,0-Jména nebo počtu členů,2-Popisu
desc_name=Ukázat popisy objektů místo jejich názvů?,1,1-Ano,0-Ne
display_max=Počet maximálně zobrazených podsítí a hostů,3,Bezomezení
line2=Konfigurace systému,11
dhcpd_conf=Konfigurační soubor DHCP serveru,0

View File

@@ -6,7 +6,6 @@ lease_tz=Vis lease tider i,1,0-GMT,1-Lokal tid
show_ip=Vis IP adresser for hosts?,1,1-Ja,0-Nej
show_mac=Vis MAC adresser for hosts?,1,1-Ja,0-Nej
group_name=Vis gruppenavne som,1,1-<tt>domæne&#45;navne</tt> indstilling,0-Navn eller medlemsantal,2-Beskrivelse
desc_name=Vis andre objektbeskrivelse istedet for navne?,1,1-Ja,0-Nej
display_max=Maksimalt antal hosts og subnet der skal vises,3,Ubegrænset
line2=Systemkonfiguration
dhcpd_conf=DHCP server config fil,0

View File

@@ -7,7 +7,6 @@ lease_refresh=Sekunden zwischen dem Aktualisieren der Leasing-Liste,3,Niemals
show_ip=Zeige IP-Adressen für Hosts?,1,1-Ja,0-Nein
show_mac=Zeige MAC-Adressen für Hosts?,1,1-Ja,0-Nein
group_name=Zeige Gruppennamen als,1,1-<tt>domain&#45;name</tt> Option,0-Name oder Mitglied,2-Beschreibung
desc_name=Zeige andere Objektbeschreibungen statt Namen?,1,1-Ja,0-Nein
display_max=Maximale Anzahl an Subnetzen und Hosts zum Anzeigen,3,Unbegrenzt
add_file=Füge neue Subnetze&#44; Hosts und Gruppen in einer Datei hinzu,3,Haupt-Konfigurationsdatei
line2=Systemkonfiguration,11

View File

@@ -6,7 +6,6 @@ lease_tz=نمايش زمان انتساب داده شده‌ها براساس,1,
show_ip=آيا نشانيهاي IP براي ميزبانها نشان داده شود؟,1,1-بله,0-خير
show_mac=آيا نشانيهاي MACبراي ميزبانها نشان داده شود؟,1,1-بله,0-خير
group_name=نشان‌دادن نامهاي گروه به‌صورت,1,1- گزينه<tt> دامنه/نام </tt>,0-نام يا تعداد اعضا,2-شرح
desc_name=آيا ساير شرحها به جاي نام نشان داده شود؟,1,1-بله,0-خير
display_max=حداکثر تعداد زيرشبکه‌ها و ميزبانها جهت نمايش,3,نامحدود
line2=پيکربندي سيستم,11
dhcpd_conf=پرونده پيکربندي کارساز DHCP,0

View File

@@ -6,7 +6,6 @@ lease_tz=Afficher les temps de bail en,1,0-GMT,1-Temps local
show_ip=Montrer les adresses IP des postes ?,1,1-Oui,0-Non
show_mac=Montrer les adresses MAC des postes ?,1,1-Oui,0-Non
group_name=Montrer les noms de groupe comme,1,1-Nom de domaine,0-Nom ou numero de membre
desc_name=Montrer les descriptions au lieu des noms ?,1,1-Oui,0-Non
display_max=Nombre maximum de sous-reseaux et de postes a afficher,3,Illimite
line2=Configuration du systeme,11
dhcpd_conf=Fichier de configuration du serveur DHCP,0

View File

@@ -6,7 +6,6 @@ lease_tz=リース時間の表示形式,1,0-GMT,1-ローカル時間
show_ip=ホストのIPアドレスを表示しますか?,1,1-はい,0-いいえ
show_mac=ホストのMACアドレスを表示しますか?,1,1-はい,0-いいえ
group_name=グループ名の表示方法,1,1-<tt>ドメイン&#45;名</tt> オプション,0-名前またはメンバー数
desc_name=名称の替わりにオブジェクトの説明を表示しますか?,1,1-はい,0-いいえ
display_max=サブネットとホストの表示する最大数,3,無制限
line2=システム設定,11
dhcpd_conf=DHCPサーバ 設定ファイル,0

View File

@@ -7,7 +7,6 @@ lease_refresh=Seconden tussen verversen van lease lijst,3,Nooit
show_ip=Laat IP adressen zien voor hosts?,1,1-Ja,0-Nee
show_mac=Laat MAC adressen zien voor hosts?,1,1-Ja,0-Nee
group_name=Laat groep namen zien als,1,1-<tt>domein&#45;naam</tt> optie,0-Naam of Lid van,2-Omschrijving
desc_name=Laat andere object omschrijvingen zien in plaats van namen?,1,1-Ja,0-Nee
display_max=Maximum aantal subnets en hosts om te laten zien,3,Ongelimiteerd
add_file=Toevoegen nieuwe subnetten&#44; host en groepen aan file,3,Hoofd configuratie file
line2=Systeem configuratie,11

View File

@@ -7,7 +7,6 @@ lease_refresh=Sekunder mellom oppfrisking av liste med leieavtaler,3,Aldri
show_ip=Vis IP adresser for verter?,1,1-Ja,0-Nei
show_mac=Vis MAC adresser for verter?,1,1-Ja,0-Nei
group_name=Vis gruppenavn som,1,1-<tt>domene&#45;navn</tt> alternativ,0-Navn eller antall medlemmer,2-Beskrivelse
desc_name=Vis andre objektbeskrivelser i stedet for navn,1,1-Ja,0-Nei
display_max=Maks antall subnett og verter som skal vises,3,Ubegrenset
add_file=Legg til nye subnett&#44; verter og grupper i filen,3,Hoved konfigurasjonsfil
line2=System konfigurasjon,11

View File

@@ -7,7 +7,6 @@ lease_refresh=Sekund pomiędzy odświeżaniem listy dzierżawy,3,Nigdy
show_ip=Wyświetlać adres IP hostów?,1,1-Tak,0-Nie
show_mac=Wyświetlać adres MAC hostów?,1,1-Tak,0-Nie
group_name=Wyświetl nazwy grupy jako,1,1-<tt>domain&#45;name</tt>,0-Ilość lub nazwa użytkowników,2-Opis
desc_name=Wyświetlać inne opisy obiektów zamiast nazw,1,1-Tak,0-Nie
display_max=Maksymalna liczba wyświetlanych podsieci i hostów,3,Nieograniczona
add_file=Dodaj nowe podsieci&#44; hosty i grupy do pliku,3,Główny plik konfiguracyjny
line2=Opcje systemowe,11

View File

@@ -6,7 +6,6 @@ lease_tz=Display leases times in,1,0-GMT,1-Local time
show_ip=Mostrar endereço IP para computadores?,1,1-Sim,0-Não
show_mac=Mostrar endereço MAC para computadores?,1,1-Sim,0-Não
group_name=Mostrar nomes de grupo como,1,1-<tt>nome de&#45;domínio</tt>,0-Nome ou membro
desc_name=Mostrar descrição de objetos ao invés de nomes?,1,1-Sim,0-Não
display_max=Número máximo de subredes e computadores a serem
add_file=Acrescentar novas subredes&#44; hosts e grupos para arquivo,3,Arquivo principal de configuração
line2=Configuração do sistema,11

View File

@@ -6,7 +6,6 @@ lease_tz=Zobraziť časy pridelených adries v,1,0-GMT,1-Lokálnom čase
show_ip=Zobraziť IP adresy pre počítače?,1,1-Áno,0-Nie
show_mac=Zobraziť MAC adresy pre počítače?,1,1-Áno,0-Nie
group_name=Zobraziť skupinové mená ako ,1,1-<tt>doménové mená</tt> ,0-Meno alebo počet členov,2-Popis
desc_name=Ukázať popisy objektov namiesto ich názvov?,1,1-Áno,0-Nie
display_max=Maximálny počet subnetov a hostiteľov pre zobrazenie,3,Bez limitu
line2=Systémové nastavenia,11
dhcpd_conf=Konfiguračný súbor DHCP servera,0

View File

@@ -211,14 +211,18 @@ foreach $u (@subn) {
push(@sicons, $i = "images/shared.gif");
push(@checkboxids, $u->{'index'});
}
if ($config{'desc_name'} && $u->{'comment'}) {
push(@utitles, $t = &html_escape($u->{'comment'}));
if ($config{'desc_name'} == 0) {
$t = $u->{'values'}->[0];
}
elsif ($config{'desc_name'} == 1) {
$t = $u->{'comment'} || $u->{'values'}->[0];
}
else {
push(@utitles, $t = &html_escape($u->{'values'}->[0]));
$t = $u->{'values'}->[0].($u->{'comment'} ? " ($u->{'comment'})" : "");
}
push(@utitles, &html_escape($t));
push(@uslinks, $l); # so that ordering is preserved
push(@ustitles, $t);
push(@ustitles, &html_escape($t));
push(@usicons, $i);
}
@checkboxes = map { &ui_checkbox("d", $_) } @checkboxids;
@@ -336,12 +340,16 @@ foreach $h (@host) {
(defined($subnet{$h}) ? "&uidx=$subnet{$h}" : "").
(defined($shared{$h}) ? "&sidx=$shared{$h}" : "") :
undef);
if ($config{'desc_name'} && $h->{'comment'}) {
push(@htitles, &html_escape($h->{'comment'}));
if ($config{'desc_name'} == 0) {
$t = $h->{'values'}->[0];
}
elsif ($config{'desc_name'} == 1) {
$t = $h->{'comment'} || $h->{'values'}->[0];
}
else {
push(@htitles, &html_escape($h->{'values'}->[0]));
$t = $h->{'values'}->[0].($h->{'comment'} ? " ($h->{'comment'})" : "");
}
push(@htitles, &html_escape($t));
if ($config{'show_ip'}) {
$fv = &fixedaddr($h);
$htitles[$#htitles] .= "<br>".$fv if ($fv);

View File

@@ -95,7 +95,7 @@ foreach (@lines) {
}
}
elsif (/^(\s*)(#?)([a-z0-9\_]+)\s+=\s*(.*)/) {
# A directive inside a section
# A directive which may or may not be inside a section
local $dir = { 'name' => $3,
'value' => $4,
'enabled' => !$2,
@@ -376,9 +376,9 @@ else {
}
}
splice(@$lref, $section->{'line'}, 0, @newlines);
$section->{'eline'} = $section->{'line'} + scalar(@newlines) - 1;
&renumber($conf, $section->{'eline'}, $section->{'file'},
scalar(@newlines)-$oldlen);
$section->{'eline'} = $section->{'line'} + scalar(@newlines) - 1;
$section->{'file'} = $file;
my $i = 1;
foreach my $m (@{$section->{'members'}}) {
@@ -391,6 +391,17 @@ foreach my $m (@{$section->{'members'}}) {
}
}
# delete_section(&conf, &section)
# Remove a section and all it's members from the config file
sub delete_section
{
my ($conf, $section) = @_;
my $lref = &read_file_lines($section->{'file'});
my $len = $section->{'eline'} - $section->{'line'} + 1;
splice(@$lref, $section->{'line'}, $len);
&renumber($conf, $section->{'line'}, $section->{'file'}, -$len);
}
# renumber(&conf, line, file, offset)
sub renumber
{

View File

@@ -55,7 +55,7 @@ if (@jails) {
$col =~ s/\s/_/g;
if ($col !~ /journal_matches/ &&
$col !~ /file_list/) {
push(@head, "<div $tdc>".$text{"status_head_$col"}."</div>");
push(@head, "<span $tdc>".$text{"status_head_$col"}."</span>");
if ($col =~ /banned_ip_list/) {
$jips = $val;
my @ips = split(/\s+/, $val);

View File

@@ -1,7 +1 @@
body blockquote:not([style*="border-left"]) {
border-left: 1px solid #ccc;
margin-left: 6px;
margin-top: 0;
margin-bottom: 0;
padding-left: 12px;
}
body blockquote:not([style*="border-left"]){border-left:1px solid #ccc;margin-left:6px;margin-top:0;margin-bottom:0;padding-left:12px;}pre{white-space:break-spaces;margin:0 0 4px 0}

View File

@@ -230,31 +230,32 @@ return @list_users_cache;
# it to the LDAP database
sub create_user
{
local $ldap = &ldap_connect();
local $base = &get_user_base();
$_[0]->{'dn'} = "uid=$_[0]->{'user'},$base";
local @classes = ( &def_user_obj_class(), "shadowAccount",
my ($user) = @_;
my $ldap = &ldap_connect();
my $base = &get_user_base();
$user->{'dn'} = "uid=$user->{'user'},$base";
my @classes = ( &def_user_obj_class(), "shadowAccount",
split(/\s+/, $config{'other_class'}),
@{$_[0]->{'ldap_class'}} );
local $schema = $ldap->schema();
@{$user->{'ldap_class'}} );
my $schema = $ldap->schema();
if ($schema->objectclass("person") && $config{'person'}) {
push(@classes, "person");
}
@classes = &uniquelc(@classes);
@classes = grep { /\S/ } @classes; # Remove empty
local @attrs = &user_to_dn($_[0]);
push(@attrs, &split_props($config{'props'}, $_[0]));
push(@attrs, @{$_[0]->{'ldap_attrs'}});
my @attrs = &user_to_dn($user);
push(@attrs, &split_props($config{'props'}, $user));
push(@attrs, @{$user->{'ldap_attrs'}});
push(@attrs, "objectClass" => \@classes);
if (&indexoflc("person", @classes) >= 0 && !&in_props(\@attrs, "sn")) {
# Person needs 'sn'
push(@attrs, "sn", &in_props(\@attrs, "cn"));
}
local $rv = $ldap->add($_[0]->{'dn'}, attr => \@attrs);
my $rv = $ldap->add($user->{'dn'}, attr => \@attrs);
if ($rv->code) {
&error(&text('usave_eadd', $rv->error));
}
push(@list_users_cache, $_[0]) if (scalar(@list_users_cache));
push(@list_users_cache, $user) if (scalar(@list_users_cache));
$ldap->unbind();
&useradmin::refresh_nscd() if (!$batch_mode);
}
@@ -264,8 +265,9 @@ $ldap->unbind();
# it from the LDAP database
sub delete_user
{
local $ldap = &ldap_connect();
local $rv = $ldap->delete($_[0]->{'dn'});
my ($user) = @_;
my $ldap = &ldap_connect();
my $rv = $ldap->delete($user->{'dn'});
if ($rv->code) {
my $err = $rv->error;
if ($err !~ /No such object/i) {
@@ -273,7 +275,7 @@ if ($rv->code) {
}
}
$ldap->unbind();
@list_users_cache = grep { $_ ne $_[0] } @list_users_cache
@list_users_cache = grep { $_ ne $user } @list_users_cache
if (scalar(@list_users_cache));
&useradmin::refresh_nscd() if (!$batch_mode);
}
@@ -281,52 +283,53 @@ $ldap->unbind();
# modify_user(&olduser, &newuser)
sub modify_user
{
local $ldap = &ldap_connect();
local $base = &get_user_base();
local @attrs = &user_to_dn($_[1]);
push(@attrs, &split_props($config{'mod_props'}, $_[1]));
push(@attrs, @{$_[1]->{'ldap_attrs'}});
if ($_[1]->{'ldap_class'} &&
(!ref($_[1]->{'ldap_class'}) || @{$_[1]->{'ldap_class'}})) {
push(@attrs, "objectClass" => $_[1]->{'ldap_class'});
my ($olduser, $user) = @_;
my $ldap = &ldap_connect();
my $base = &get_user_base();
my @attrs = &user_to_dn($user);
push(@attrs, &split_props($config{'mod_props'}, $user));
push(@attrs, @{$user->{'ldap_attrs'}});
if ($user->{'ldap_class'} &&
(!ref($user->{'ldap_class'}) || @{$user->{'ldap_class'}})) {
push(@attrs, "objectClass" => $user->{'ldap_class'});
}
if (&indexoflc("person", @{$_[1]->{'ldap_class'}}) >= 0 &&
if (&indexoflc("person", @{$user->{'ldap_class'}}) >= 0 &&
!&in_props(\@attrs, "sn")) {
# Person needs 'sn'
push(@attrs, "sn", &in_props(\@attrs, "cn"));
}
local %replace;
my %replace;
for(my $i=0; $i<@attrs; $i+=2) {
$replace{$attrs[$i]} ||= [ ];
local $v = $attrs[$i+1];
my $v = $attrs[$i+1];
push(@{$replace{$attrs[$i]}}, ref($v) ? @$v : $v);
}
if ($_[0]->{'pass'} eq $_[1]->{'pass'}) {
if ($olduser->{'pass'} eq $user->{'pass'}) {
# Don't change password attribute if not change
delete($replace{'userPassword'});
}
# Do rename to new DN first
if ($_[0]->{'user'} ne $_[1]->{'user'}) {
local $newdn = $_[0]->{'dn'};
if ($newdn !~ s/^uid=$_[0]->{'user'},/uid=$_[1]->{'user'},/) {
$newdn = "uid=$_[1]->{'user'},$base";
if ($olduser->{'user'} ne $user->{'user'}) {
my $newdn = $olduser->{'dn'};
if ($newdn !~ s/^uid=$olduser->{'user'},/uid=$user->{'user'},/) {
$newdn = "uid=$user->{'user'},$base";
}
if (!&same_dn($newdn, $_[0]->{'dn'})) {
$rv = $ldap->moddn($_[0]->{'dn'},
newrdn => "uid=$_[1]->{'user'}");
if (!&same_dn($newdn, $olduser->{'dn'})) {
$rv = $ldap->moddn($olduser->{'dn'},
newrdn => "uid=$user->{'user'}");
if ($rv->code) {
&error(&text('usave_emoddn', $rv->error));
}
$_[1]->{'dn'} = $newdn;
$user->{'dn'} = $newdn;
}
}
local $rv = $ldap->modify($_[1]->{'dn'}, replace => \%replace);
my $rv = $ldap->modify($user->{'dn'}, replace => \%replace);
if ($rv->code) {
&error(&text('usave_emod', $rv->error));
}
if ($_[0] ne $_[1] && &indexof($_[0], @list_users_cache) != -1) {
if ($olduser ne $user && &indexof($olduser, @list_users_cache) != -1) {
# Update old object in cache
%{$_[0]} = %{$_[1]};
%{$olduser} = %{$user};
}
$ldap->unbind();
&useradmin::refresh_nscd() if (!$batch_mode);
@@ -431,37 +434,42 @@ $ldap->unbind();
# in the same format uses by the useradmin module
sub dn_to_hash
{
if ($_[0]->get_value("uid")) {
local %user = ( 'dn' => $_[0]->dn(),
'user' => $_[0]->get_value("uid"),
'uid' => $_[0]->get_value("uidNumber"),
'gid' => $_[0]->get_value("gidNumber"),
'real' => $_[0]->get_value("cn"),
'home' => $_[0]->get_value("homeDirectory"),
'shell' => $_[0]->get_value("loginShell"),
'pass' => $_[0]->get_value("userPassword"),
'change' => $_[0]->get_value("shadowLastChange") || "",
'expire' => $_[0]->get_value("shadowExpire") || "",
'min' => $_[0]->get_value("shadowMin") || "",
'max' => $_[0]->get_value("shadowMax") || "",
'warn' => $_[0]->get_value("shadowWarning") || "",
'inactive' => $_[0]->get_value("shadowInactive") || "",
);
my ($obj) = @_;
if ($obj->get_value("uid")) {
my %user = ( 'dn' => $obj->dn(),
'user' => $obj->get_value("uid"),
'uid' => $obj->get_value("uidNumber"),
'gid' => $obj->get_value("gidNumber"),
'real' => $obj->get_value("cn"),
'home' => $obj->get_value("homeDirectory"),
'shell' => $obj->get_value("loginShell"),
'pass' => $obj->get_value("userPassword"),
'change' => $obj->get_value("shadowLastChange") || "",
'expire' => $obj->get_value("shadowExpire") || "",
'min' => $obj->get_value("shadowMin") || "",
'max' => $obj->get_value("shadowMax") || "",
'warn' => $obj->get_value("shadowWarning") || "",
'inactive' => $obj->get_value("shadowInactive") || "",
);
if ($config{'given'}) {
$user{'firstname'} = $obj->get_value("givenName");
$user{'surname'} = $obj->get_value("sn");
}
$user{'pass'} =~ s/^(\!?)\{[a-z0-9]+\}/$1/i;
$user{'all_ldap_attrs'} = { map { lc($_), scalar($_[0]->get_value($_)) }
$_[0]->attributes() };
$user{'ldap_class'} = [ $_[0]->get_value('objectClass') ];
$user{'all_ldap_attrs'} = { map { lc($_), scalar($obj->get_value($_)) }
$obj->attributes() };
$user{'ldap_class'} = [ $obj->get_value('objectClass') ];
return %user;
}
else {
local @members = $_[0]->get_value('memberUid');
local %group = ( 'dn' => $_[0]->dn(),
'group' => $_[0]->get_value("cn"),
'gid' => $_[0]->get_value("gidNumber"),
'pass' => $_[0]->get_value("userPassword") || "",
'members' => join(",", @members) || "",
'desc' => $_[0]->get_value("description"),
);
my @members = $obj->get_value('memberUid');
my %group = ( 'dn' => $obj->dn(),
'group' => $obj->get_value("cn"),
'gid' => $obj->get_value("gidNumber"),
'pass' => $obj->get_value("userPassword") || "",
'members' => join(",", @members) || "",
'desc' => $obj->get_value("description"),
);
return %group;
}
}
@@ -470,48 +478,56 @@ else {
# Given a useradmin-style user hash, returns a list of properties
sub user_to_dn
{
local $pfx = $_[0]->{'pass'} =~ /^\{[a-z0-9]+\}/i ? undef :
$_[0]->{'pass'} =~ /^\$1\$/ ? "{md5}" :
$_[0]->{'pass'} =~ /^[a-zA-Z0-9\.\/]{13}$/ ? "{crypt}" :
$config{'md5'} == 1 || $config{'md5'} == 3 ? "{md5}" :
$config{'md5'} == 4 ? "{ssha}" :
$config{'md5'} == 0 ? "{crypt}" : "";
local $pass = $_[0]->{'pass'};
local $disabled;
my ($user) = @_;
my $pfx = $user->{'pass'} =~ /^\{[a-z0-9]+\}/i ? undef :
$user->{'pass'} =~ /^\$1\$/ ? "{md5}" :
$user->{'pass'} =~ /^[a-zA-Z0-9\.\/]{13}$/ ? "{crypt}" :
$config{'md5'} == 1 || $config{'md5'} == 3 ? "{md5}" :
$config{'md5'} == 4 ? "{ssha}" :
$config{'md5'} == 0 ? "{crypt}" : "";
my $pass = $user->{'pass'};
my $disabled;
if ($pass =~ s/^\!//) {
$disabled = "!";
}
$cn = $_[0]->{'real'} eq '' ? $_[0]->{'user'} : $_[0]->{'real'};
my $cn = $user->{'real'} eq '' ? $user->{'user'} : $user->{'real'};
return ( "cn" => $cn,
"uid" => $_[0]->{'user'},
"uidNumber" => $_[0]->{'uid'},
"loginShell" => $_[0]->{'shell'},
"homeDirectory" => $_[0]->{'home'},
"gidNumber" => $_[0]->{'gid'},
"uid" => $user->{'user'},
"uidNumber" => $user->{'uid'},
"loginShell" => $user->{'shell'},
"homeDirectory" => $user->{'home'},
"gidNumber" => $user->{'gid'},
"userPassword" => $disabled.$pfx.$pass,
$_[0]->{'change'} eq '' ? ( ) :
( "shadowLastChange" => $_[0]->{'change'} ),
$_[0]->{'expire'} eq '' ? ( ) :
( "shadowExpire" => $_[0]->{'expire'} ),
$_[0]->{'min'} eq '' ? ( ) :
( "shadowMin" => $_[0]->{'min'} ),
$_[0]->{'max'} eq '' ? ( ) :
( "shadowMax" => $_[0]->{'max'} ),
$_[0]->{'warn'} eq '' ? ( ) :
( "shadowWarning" => $_[0]->{'warn'} ),
$_[0]->{'inactive'} eq '' ? ( ) :
( "shadowInactive" => $_[0]->{'inactive'} )
$user->{'change'} eq '' ? ( ) :
( "shadowLastChange" => $user->{'change'} ),
$user->{'expire'} eq '' ? ( ) :
( "shadowExpire" => $user->{'expire'} ),
$user->{'min'} eq '' ? ( ) :
( "shadowMin" => $user->{'min'} ),
$user->{'max'} eq '' ? ( ) :
( "shadowMax" => $user->{'max'} ),
$user->{'warn'} eq '' ? ( ) :
( "shadowWarning" => $user->{'warn'} ),
$user->{'inactive'} eq '' ? ( ) :
( "shadowInactive" => $user->{'inactive'} ),
$user->{'firstname'} eq '' ? ( ) :
( "givenName" => $user->{'firstname'} ),
$user->{'surname'} eq '' ? ( ) :
( "sn" => $user->{'surname'} ),
);
}
# group_to_dn(&group)
# Given a useradmin-style group hash, returns a list of properties
sub group_to_dn
{
local @members = split(/,/, $_[0]->{'members'});
return ( "cn" => $_[0]->{'group'},
"gidNumber" => $_[0]->{'gid'},
"userPassword" => $_[0]->{'pass'},
my ($group) = @_;
my @members = split(/,/, $group->{'members'});
return ( "cn" => $group->{'group'},
"gidNumber" => $group->{'gid'},
"userPassword" => $group->{'pass'},
@members ? ( "memberUid" => \@members ) : ( ),
defined($_[0]->{'desc'}) ? ( "description" => $_[0]->{'desc'} ) : ( ),
defined($group->{'desc'}) ? ( "description" => $group->{'desc'} ) : ( ),
);
}

View File

@@ -332,6 +332,7 @@ folder_inbox=Inbox
folder_sent=Sent
folder_drafts=Drafts
folder_trash=Trash
folder_spam=Spam
detach_err=Failed to detach file
detach_edir=No file or directory to save to entered

View File

@@ -107,7 +107,7 @@ if ($product eq "webmin") {
$size = int(`du -sk $tmp_dir`);
@deps = ( "perl", "libnet-ssleay-perl", "openssl", "libauthen-pam-perl", "libpam-runtime", "libio-pty-perl", "unzip", "shared-mime-info", "tar", "libdigest-sha-perl", "libdigest-md5-perl", "gzip" );
$deps = join(", ", @deps);
@recommends = ( "libdatetime-perl", "libdatetime-timezone-perl", "libdatetime-locale-perl", "libtime-piece-perl", "libencode-detect-perl", "libtime-hires-perl", "libsocket6-perl", "libthreads-perl", "libthreads-shared-perl", "lynx", "qrencode" );
@recommends = ( "libdatetime-perl", "libdatetime-timezone-perl", "libdatetime-locale-perl", "libtime-piece-perl", "libencode-detect-perl", "libtime-hires-perl", "libsocket6-perl", "lynx", "qrencode" );
$recommends = join(", ", @recommends);
open(CONTROL, ">$control_file");
print CONTROL <<EOF;

View File

@@ -87,7 +87,7 @@ Release: $rel
Provides: %{name}-%{version} perl(WebminCore)
Requires(pre): /usr/bin/perl
Requires: /bin/sh /usr/bin/perl perl(lib) perl(open) perl(Net::SSLeay) perl(Time::Local) perl(Data::Dumper) perl(File::Path) perl(File::Basename) perl(Digest::SHA) perl(Digest::MD5) openssl unzip tar gzip
Recommends: perl(DateTime) perl(DateTime::TimeZone) perl(DateTime::Locale) perl(Time::Piece) perl(Encode::Detect) perl(Time::HiRes) perl(Socket6) perl(threads) perl(threads::shared) lynx shared-mime-info perl-File-Basename perl-File-Path qrencode
Recommends: perl(DateTime) perl(DateTime::TimeZone) perl(DateTime::Locale) perl(Time::Piece) perl(Encode::Detect) perl(Time::HiRes) perl(Socket6) lynx shared-mime-info perl-File-Basename perl-File-Path qrencode
AutoReq: 0
License: BSD-3-clause
Group: System/Tools

View File

@@ -269,9 +269,9 @@ application/x-director dcr dir dxr
application/x-dvi dvi # DVI
application/x-futuresplash spl
application/x-gtar gtar
application/x-gzip gz tgz # Gzipped data
application/gzip gz tgz # Gzipped data
application/x-hdf hdf
application/x-javascript js # Javascript source
application/javascript js # Javascript source
application/x-koan skp skd skt skm
application/x-latex latex # LaTeX source
application/x-netcdf nc cdf

View File

@@ -4894,7 +4894,7 @@ my %vital = ("port", 80,
"listen_delay", 5,
"pam", "webmin",
"sidname", "sid",
"unauth", "^/unauthenticated/ ^/robots.txt\$ ^[A-Za-z0-9\\-/_]+\\.jar\$ ^[A-Za-z0-9\\-/_]+\\.class\$ ^[A-Za-z0-9\\-/_]+\\.gif\$ ^[A-Za-z0-9\\-/_]+\\.png\$ ^[A-Za-z0-9\\-/_]+\\.conf\$ ^[A-Za-z0-9\\-/_]+\\.ico\$ ^/robots.txt\$",
"unauth", "^/unauthenticated/ ^/robots.txt\$ ^[A-Za-z0-9\\-/_]+\\.jar\$ ^[A-Za-z0-9\\-/_]+\\.class\$ ^[A-Za-z0-9\\-/_]+\\.gif\$ ^[A-Za-z0-9\\-/_]+\\.png\$ ^[A-Za-z0-9\\-/_]+\\.conf\$ ^[A-Za-z0-9\\-/_]+\\.ico\$ ^/robots.txt\$ ^/service-worker.js\$",
"max_post", 10000,
"expires", 7*24*60*60,
"pam_test_user", "root",

View File

@@ -20,12 +20,10 @@ print &ui_tabs_start([ [ 'pkgs', $text{'index_tabpkgs'} ],
# See if any security updates exist
$in{'mode'} ||= 'updates';
@avail = &list_for_mode($in{'mode'}, 0);
($sec) = grep { $_->{'security'} } @avail;
# Show mode selector (all, updates only, updates and new)
@grid = ( );
foreach $m ('current', 'updates', 'new',
$sec || $in{'mode'} eq 'security' ? ( 'security' ) : ( )) {
foreach $m ('current', 'updates', 'security', 'new') {
$mmsg = $text{'index_mode_'.$m};
if ($in{'mode'} eq $m) {
push(@mlinks, "<b>$mmsg</b>");

View File

@@ -103,6 +103,7 @@ sched_yes=Scheduled checking for updates is now active.
sched_no=Scheduled checking for updates has been disabled.
log_update=Installed $1 updated packages
log_sched=Background installed $1 updated packages
log_sched=Enabled scheduled updates
log_unsched=Disabled scheduled updates
log_refresh=Refreshed available packages

View File

@@ -18,6 +18,9 @@ elsif ($type eq 'repo') {
elsif ($action eq 'update') {
return &text('log_update', $object);
}
elsif ($action eq 'sched') {
return &text('log_sched', $object);
}
elsif ($action eq 'sched') {
return $text{$object ? 'log_sched' : 'log_unsched'};
}

View File

@@ -15,10 +15,10 @@ else {
$redir = "index.cgi?mode=".&urlize($in{'mode'}).
"&search=".&urlize($in{'search'});
$redirdesc = $text{'index_return'};
$redir = $redir =~ /tab=/ ? $redir :
$redir =~ /\?/ ? "$redir&tab=pkgs" : "$redir?tab=pkgs";
}
$redir = ($redir =~ /\?/) ? "$redir&tab=pkgs" : "$redir?tab=pkgs";
if ($in{'refresh'} || $in{'refresh_top'}) {
&ui_print_unbuffered_header(undef, $text{'refresh_title'}, "");

View File

@@ -17,19 +17,22 @@ if ($ARGV[0] eq "--debug" || $ARGV[0] eq "-debug") {
$tellcount = 0;
%already = ( );
&start_update_progress([ map { $_->{'name'} } @todo ]);
$icount = 0;
foreach $t (@todo) {
next if ($already{$t->{'update'}});
my $umsg = $t->{'security'} ? "security update" : "update";
if ($config{'sched_action'} == 2 ||
$config{'sched_action'} == 1 && $t->{'security'}) {
# Can install
$body .= "An update to $t->{'name'} from $t->{'oldversion'} to $t->{'version'} is needed.\n";
$body .= "An $umsg to $t->{'name'} from $t->{'oldversion'} to $t->{'version'} is needed.\n";
$icount++;
($out, $done) = &capture_function_output(
\&package_install, $t->{'update'});
if (@$done) {
$body .= "This update has been successfully installed.\n\n";
$body .= "This $umsg has been successfully installed.\n\n";
}
else {
$body .= "However, this update could not be installed! Try the update manually\nusing the Package Updates module.\n\n";
$body .= "However, this $usmg could not be installed! Try the update manually\nusing the Package Updates module.\n\n";
}
foreach $p (@$done) {
$already{$p}++;
@@ -39,7 +42,7 @@ foreach $t (@todo) {
$config{'sched_action'} == 0 ||
$config{'sched_action'} == -1 && $t->{'security'}) {
# Just tell the user about it
$body .= "An update to $t->{'name'} from $t->{'oldversion'} to $t->{'version'} is available.\n\n";
$body .= "An $umsg to $t->{'name'} from $t->{'oldversion'} to $t->{'version'} is available.\n\n";
$tellcount++;
}
}
@@ -69,3 +72,7 @@ if ($config{'sched_email'} && $body) {
}
}
# Log the update, if anything was installed
if ($icount) {
&webmin_log("sched", "packages", $icount);
}

View File

@@ -16,5 +16,11 @@ print &ui_hidden("oneini", $in{'oneini'}),"\n";
print &ui_textarea("data", &read_file_contents_as_user($in{'file'}), 20, 80);
print &ui_form_end([ [ "save", $text{'save'} ] ]);
&ui_print_footer("", $text{'index_return'});
if ($in{'oneini'}) {
&ui_print_footer("list_ini.cgi?file=".&urlize($in{'file'}),
$text{'list_return'});
}
else {
&ui_print_footer("", $text{'index_return'});
}

View File

@@ -512,6 +512,15 @@ mail_forward=Forward
mail_rfc=From line
mail_move=Move to:
mail_eexists=Message no longer exists!
mail_qfile=Mail queue file
mail_qlast=Last retried at
mail_qdir=Mail queue status
mail_qdir_active=Active
mail_qdir_incoming=Incoming
mail_qdir_deferred=Deferred
mail_qdir_corrupt=Corrupt
mail_qdir_hold=On hold
mail_qdir_maildrop=Mail drop
view_title=Read Email
view_desc=Message $1 in $2
@@ -622,6 +631,7 @@ mailq_active=Active
mailq_deferred=Deferred
mailq_hold=Hold Selected
mailq_unhold=Un-Hold Selected
mailq_dir=Queue state
flushq_title=Flush Queue
flushq_desc=Forcing the attempted delivery of mail with the command $1 ..

View File

@@ -1449,6 +1449,15 @@ foreach my $l (split(/\r?\n/, $out)) {
$q->{'time'} = $t;
}
}
foreach my $dir (&list_mailq_directories()) {
my $f = substr($q->{'id'}, 0, 1);
my $path = "$config{'mailq_dir'}/$dir/$f/$q->{'id'}";
if (-r $path) {
$q->{'dir'} = $dir;
$q->{'file'} = $file;
last;
}
}
push(@qfiles, $q);
}
elsif ($l =~ /\((.*)\)/ && @qfiles) {
@@ -1461,18 +1470,18 @@ foreach my $l (split(/\r?\n/, $out)) {
return @qfiles;
}
sub list_mailq_directories
{
return ("active", "incoming", "deferred", "corrupt", "hold", "maildrop");
}
# parse_queue_file(id)
# Parses a postfix mail queue file into a standard mail structure
sub parse_queue_file
{
local @qfiles = ( &recurse_files("$config{'mailq_dir'}/active"),
&recurse_files("$config{'mailq_dir'}/incoming"),
&recurse_files("$config{'mailq_dir'}/deferred"),
&recurse_files("$config{'mailq_dir'}/corrupt"),
&recurse_files("$config{'mailq_dir'}/hold"),
&recurse_files("$config{'mailq_dir'}/maildrop"),
);
local $f = $_[0];
local ($f) = @_;
local @qfiles = map { &recurse_files("$config{'mailq_dir'}/$_") }
&list_mailq_directories();
local ($file) = grep { $_ =~ /\/$f$/ } @qfiles;
return undef if (!$file);
local $mode = 0;
@@ -1504,6 +1513,12 @@ $mail->{'headers'} = \@headers;
foreach $h (@headers) {
$mail->{'header'}->{lc($h->[0])} = $h->[1];
}
$mail->{'file'} = $file;
my @st = stat($file);
$mail->{'last_retry'} = $st[9];
if ($file =~ /^\Q$config{'mailq_dir'}\E\/([^\/]+)\//) {
$mail->{'dir'} = $1;
}
return $mail;
}
@@ -1856,11 +1871,12 @@ foreach my $q (@$qfiles) {
'value' => $q->{'id'} });
push(@cols, &ui_link("view_mailq.cgi?id=$q->{'id'}",$q->{'id'}));
local $size = &nice_size($q->{'size'});
push(@cols, "<font size=1>$q->{'date'}</font>");
push(@cols, "<font size=1>".&html_escape($q->{'from'})."</font>");
push(@cols, "<font size=1>".&html_escape($q->{'to'})."</font>");
push(@cols, "<font size=1>$size</font>");
push(@cols, "<font size=1>".&html_escape($q->{'status'})."</font>");
push(@cols, $q->{'date'});
push(@cols, &html_escape($q->{'from'}));
push(@cols, &html_escape($q->{'to'}));
push(@cols, $size);
push(@cols, $text{'mailq_'.$q->{'dir'}} || $q->{'dir'});
push(@cols, &html_escape($q->{'status'}));
push(@table, \@cols);
}
@@ -1877,7 +1893,8 @@ print &ui_form_columns_table("delete_queues.cgi",
undef,
undef,
[ "", $text{'mailq_id'}, $text{'mailq_date'}, $text{'mailq_from'},
$text{'mailq_to'}, $text{'mailq_size'}, $text{'mailq_status'} ],
$text{'mailq_to'}, $text{'mailq_size'}, $text{'mailq_dir'},
$text{'mailq_status'} ],
100,
\@table);
}

View File

@@ -51,7 +51,11 @@ if ($in{'headers'}) {
}
foreach $h (@{$mail->{'headers'}}) {
print &ui_table_row($h->[0],
&html_escape(&decode_mimewords($h->[1])));
&html_escape(&decode_mimewords($h->[1])), 1, [ "nowrap" ]);
}
if ($mail->{'file'}) {
print &ui_table_row($text{'mail_qfile'},
"<tt>".&html_escape($mail->{'file'})."</tt>");
}
}
else {
@@ -68,6 +72,14 @@ else {
print &ui_table_row($text{'mail_subject'},
&html_escape($mail->{'header'}->{'subject'}));
}
if ($mail->{'last_retry'}) {
print &ui_table_row($text{'mail_qlast'},
&make_date($mail->{'last_retry'}));
}
if ($mail->{'dir'}) {
print &ui_table_row($text{'mail_qdir'},
$text{'mailq_'.$mail->{'dir'}} || $mail->{'dir'});
}
print &ui_table_end();
# Find body attachment

View File

@@ -61,7 +61,7 @@ if ($in{'headers'}) {
}
foreach $h (@{$mail->{'headers'}}) {
print &ui_table_row($h->[0],
&html_escape(&decode_mimewords($h->[1])));
&html_escape(&decode_mimewords($h->[1])), 1, [ "nowrap" ]);
}
}
else {

View File

@@ -12,10 +12,7 @@ my $err = sub {
print("<tt>Cannot change theme : $_[0]</tt>\n");
exit(1);
};
# Check if in debug mode
&$err("Debug mode is not enabled!")
if (!$gconfig{'debug_enabled'} &&
!$gconfig{'debug_theme_switcher'});
# Check if allowed to change theme,
# otherwise throw an error
if (!&foreign_available('theme') &&

View File

@@ -74,7 +74,7 @@ if (&foreign_installed("package-updates") && $config{'collect_pkgs'}) {
}
# CPU and drive temps
if (!$config{'collect_notemp'} && defined(&proc::get_current_cpu_data)) {
if (!$config{'collect_notemp2'} && defined(&proc::get_current_cpu_data)) {
my ($cpu, $fans) = &proc::get_current_cpu_data();
$info->{'cputemps'} = $cpu if (ref($cpu) && @{$cpu} >= 1);
$info->{'cpufans'} = $fans if (ref($fans) && @{$fans} >= 1);

View File

@@ -2349,11 +2349,45 @@ Hotkeys are:
sub ui_switch_theme_javascript
{
return &theme_ui_switch_theme_javascript(@_) if (defined(&theme_ui_switch_theme_javascript));
return "" if (!$gconfig{'debug_enabled'} && !$gconfig{'debug_theme_switcher'});
my $switch_script = "<script>const __webmin_webprefix__ = '@{[&get_webprefix()]}';</script>\n";
my $webmin_version = &get_webmin_version();
$webmin_version =~ s/\.//g;
$switch_script .= "<script type=\"text/javascript\" src=\"@{[&get_webprefix()]}/unauthenticated/switch_theme.js?$webmin_version\"></script>\n";
my $webprefix = &get_webprefix();
my $switch_script .= <<EOF;
<script type="text/javascript">
(function () {
let firstCombinationPressed = false;
document.addEventListener("keydown", function (event) {
// Check for Ctrl+Alt+T or Control+Option+T
if (event.ctrlKey && event.altKey && event.keyCode === 84) {
firstCombinationPressed = true;
// Set a timeout to reset the state after a short period (e.g., 1 seconds)
setTimeout(() => {
firstCombinationPressed = false;
}, 1000);
}
if (firstCombinationPressed && event.shiftKey &&
(event.keyCode === 65 ||
event.keyCode === 70 || event.keyCode === 71 ||
event.keyCode === 76)) {
const theme =
// Shift + A : Authentic theme
event.keyCode === 65 ? 1 :
// Shift + F / Shift + G : Framed theme / Gray theme
(event.keyCode === 70 || event.keyCode === 71) ? 2 :
// Shift + L : Legacy theme
event.keyCode === 76 ? 3 : null;
firstCombinationPressed = false;
try {
top.document.documentElement.style.filter = 'grayscale(100%) blur(0.5px) brightness(0.75) opacity(0.5)';
top.document.documentElement.style.cursor = 'wait';
top.document.documentElement.style.pointerEvents = 'none';
} catch (error) {}
top.location.href = "$webprefix/switch_theme.cgi?theme=" + theme + "";
}
});
})();
document.currentScript.remove();
</script>
EOF
return $switch_script;
}

View File

@@ -1,31 +0,0 @@
(function () {
let firstCombinationPressed = false;
document.addEventListener("keydown", function (event) {
// Check for Ctrl+Alt+T or Control+Option+T
if (event.ctrlKey && event.altKey && event.keyCode === 84) {
firstCombinationPressed = true;
// Set a timeout to reset the state after a short period (e.g., 1 seconds)
setTimeout(() => {
firstCombinationPressed = false;
}, 1000);
}
if (firstCombinationPressed && event.shiftKey &&
(event.keyCode === 65 || event.keyCode === 71 || event.keyCode === 76)) {
const theme =
// Shift + A : Authentic theme
event.keyCode === 65 ? 1 :
// Shift + G : Gray theme
event.keyCode === 71 ? 2 :
// Shift + L : Legacy theme.
event.keyCode === 76 ? 3 : null;
firstCombinationPressed = false;
try {
top.document.documentElement.style.filter = 'grayscale(100%) blur(0.5px) brightness(0.75) opacity(0.5)';
top.document.documentElement.style.cursor = 'wait';
top.document.documentElement.style.pointerEvents = 'none';
} catch (error) {}
top.location.href = __webmin_webprefix__ + "/switch_theme.cgi?theme=" + theme + "";
}
});
})();

View File

@@ -21,7 +21,8 @@ return 0;
sub open_last_command
{
local ($fh, $user) = @_;
open($fh, "last $user |");
local $quser = quotemeta($user);
open($fh, "(last -w $quser || last $quser) |");
}
# read_last_line(handle)
@@ -34,7 +35,9 @@ while(1) {
chop($line = <$fh>);
if (!$line) { return (); }
if ($line =~ /^(reboot|shutdown)/) { next; }
if ($line =~ /^(\S+)\s+(\S+)\s+(\S+)?\s+(\S+\s+\S+\s+\d+\s+\d+:\d+)\s+\-\s+(\S+)\s+\((\d+:\d+)\)/) {
if ($line =~ /^(\S+)\s+(\S+)\s+(\S+)?\s+(\S+\s+\S+\s+\d+\s+\d+:\d+)\s+\-\s+(\S+)\s+\((.*?\d+:\d+.*?)\)/) {
# root pts/0 10.211.55.2 Tue Nov 22 21:06 - 23:16 (02:10:00)
# root pts/1 10.211.55.2 Wed Jun 29 13:13 - shutdown (7+00:01:20)
return ($1, $2, $3, $4, $5 eq "shutdown" ? "Shutdown" :
$5 eq "crash" ? "Crash" : $5, $6);
}

View File

@@ -44,9 +44,10 @@ while(1) {
# jcameron pts/0 fudu Sun Feb 4 02:26:28 2024 - Wed Feb 7 18:25:09 2024 (3+15:58)
return ($1, $2, $3, $4, $5 eq "down" ? "Shutdown" : $5, $6);
}
elsif ($line =~ /^(\S+)\s+(\S+)\s+(\S+)?\s+(\S+\s+\S+\s+\d+\s+\d+:\d+)\s+still/ ||
$line =~ /^(\S+)\s+(\S+)\s+(\S+)?\s+(\S+\s+\S+\s+\d+\s+\d+:\d+:\d+\s+\d+)\s+still/) {
elsif ($line =~ /^(\S+)\s+(\S+)\s+(\S+)?\s+(\S+\s+\S+\s+\d+\s+\d+:\d+)\s+(still|gone)/ ||
$line =~ /^(\S+)\s+(\S+)\s+(\S+)?\s+(\S+\s+\S+\s+\d+\s+\d+:\d+:\d+\s+\d+)\s+(still|gone)/) {
# root pts/0 fudu Fri Feb 23 18:46 still logged in
# root ftpd10 fudu Thu Jun 20 11:19 gone - no logout
# root pts/0 fudu Tue Jun 18 23:10:30 2024 still logged in
return ($1, $2, $3, $4);
}

View File

@@ -1 +1 @@
2.111
2.200

View File

@@ -2098,10 +2098,10 @@ formatted like dd/mmm/yyyy hh:mm:ss. Parameters are :
=item seconds - Unix time is seconds to convert.
=item date-only-or-opts - If set to 1, exclude the time from the returned string.
=item date-only-or-opts - If set to 1 exclude the time from the returned string.
In case this param is a hash reference use it for options in a new DateTime::Locale
code or preserve the original, old logic
In case this param is a hash reference use it for options in a new
DateTime::Locale code or preserve the original, old logic
=item fmt - Optional, one of dd/mon/yyyy, dd/mm/yyyy, mm/dd/yyyy or yyyy/mm/dd
@@ -2116,27 +2116,31 @@ if (!$@ && $] > 5.011) {
my $opts = ref($only) ? $only : {};
my $locale_default = &get_default_system_locale();
my $locale_auto = &parse_accepted_language();
my $locale_name = $opts->{'locale'} || $gconfig{'locale_'.$base_remote_user} ||
my $locale_name = $opts->{'locale'} ||
$gconfig{'locale_'.$base_remote_user} ||
$gconfig{'locale_'.$remote_user} || $locale_auto ||
$gconfig{'locale'} || &get_default_system_locale();
my $tz = $opts->{'tz'};
if (!$tz) {
eval {
$tz =
DateTime::TimeZone->new(name => strftime("%z", localtime()))->name(); # +0200
$tz = DateTime::TimeZone->new(
name => strftime("%z", localtime()))->name(); # +0200
};
if ($@) {
eval {
$tz = DateTime::TimeZone->new(name => 'local')->name(); # Asia/Nicosia
$tz = DateTime::TimeZone->new(
name => 'local')->name(); # Asia/Nicosia
};
if ($@) {
$tz = DateTime::TimeZone->new(name => 'UTC')->name(); # UTC
$tz = DateTime::TimeZone->new(
name => 'UTC')->name(); # UTC
}
}
}
# Pre-process time locale
my $locale_military_status = sub {
return ($locale_military_name && $locale_military_name =~ /[a-z]/i) ? 2 :
return ($locale_military_name &&
$locale_military_name =~ /[a-z]/i) ? 2 :
($locale_military_name == 1) ? 1 : 0;
};
# Allow locales with military time (in 24h format)
@@ -2152,15 +2156,18 @@ if (!$@ && $] > 5.011) {
my $locale = DateTime::Locale->load($locale_name_loaded);
# Create a new locale out of base locale
if (&$locale_military_status() == 1) {
my %locale_data = $locale->locale_data;
$locale_data{'code'} = $locale_name_initial;
my %locale_data = $locale->locale_data;
$locale_data{'code'} = $locale_name_initial;
# Force 24h time
$locale_data{'glibc_date_1_format'} = '%a %b %e %H:%M:%S %Z %Y';
$locale_data{'glibc_date_1_format'} = '%a %b %e %H:%M:%S %Z %Y';
$locale_data{'glibc_datetime_format'} = '%a %d %b %Y %T %Z';
$locale_data{'glibc_time_format'} = '%T';
$locale_data{'glibc_time_format'} = '%T';
DateTime::Locale->register_from_data(%locale_data);
# Load newly cloned locale in 24h time format
$locale_military_name = $locale_name_loaded = $locale_name_initial;
$locale_military_name = $locale_name_loaded =
$locale_name_initial;
$locale = DateTime::Locale->load($locale_name_loaded);
}
my $locale_format_full_tz = $locale->glibc_date_1_format; # Sat 20 Nov 2286 17:46:39 UTC
@@ -2194,18 +2201,31 @@ if (!$@ && $] > 5.011) {
"pretty" => $ago_obj->pretty
};
}
# my $xxxx = $locale->full_date_format;
my $data = {
# Wed Feb 8 05:09:39 PM UTC 2023
'full-tz-utc' => DateTime->from_epoch(locale => $locale_name_loaded, epoch => $secs)->strftime($locale_format_full_tz),
'full-tz-utc' => DateTime->from_epoch(
locale => $locale_name_loaded,
epoch => $secs)->strftime($locale_format_full_tz),
# Wed Feb 8 07:10:01 PM EET 2023
'full-tz' => DateTime->from_epoch(locale => $locale_name_loaded, epoch => $secs, time_zone => $tz)->strftime($locale_format_full_tz),
'full-tz' => DateTime->from_epoch(
locale => $locale_name_loaded,
epoch => $secs,
time_zone => $tz)->strftime($locale_format_full_tz),
# Wed 08 Feb 2023 07:11:26 PM EET
'full' => DateTime->from_epoch(locale => $locale_name_loaded, epoch => $secs, time_zone => $tz)->strftime($locale_format_full),
'full' => DateTime->from_epoch(
locale => $locale_name_loaded,
epoch => $secs,
time_zone => $tz)->strftime($locale_format_full),
# 02/08/2023
'short' => DateTime->from_epoch(locale => $locale_name_loaded, epoch => $secs, time_zone => $tz)->strftime($locale_format_short),
'short' => DateTime->from_epoch(
locale => $locale_name_loaded,
epoch => $secs,
time_zone => $tz)->strftime($locale_format_short),
# 07:12:07 PM
'time' => DateTime->from_epoch(locale => $locale_name_loaded, epoch => $secs, time_zone => $tz)->strftime($locale_format_time),
'time' => DateTime->from_epoch(
locale => $locale_name_loaded,
epoch => $secs,
time_zone => $tz)->strftime($locale_format_time),
'ago' => $ago,
'tz' => $tz,
'delimiter' => $locale_format_delimiter,
@@ -2216,10 +2236,16 @@ if (!$@ && $] > 5.011) {
$data->{'timeshort'} = $data->{'time'};
$data->{'timeshort'} =~ s/(\d+):(\d+):(\d+)(.*?)/$1:$2$4/;
# %c alternative with full week and month and no seconds in time (complete)
# Wednesday, February 8, 2023, 8:18 PM or 星期三, 2023年2月8日 20:18 or miércoles, 8 febrero 2023, 20:28
$data->{'monthfull'} = DateTime->from_epoch(locale => $locale_name_loaded, epoch => $secs, time_zone => $tz)->strftime("%B");
foreach (split(/\s+/, DateTime->from_epoch(locale => $locale_name_loaded, epoch => $secs, time_zone => $tz)->strftime("%A, %c"))) {
# %c alternative with full week and month and no seconds
# in time (complete)
# Wednesday, February 8, 2023, 8:18 PM or 星期三,
# 2023年2月8日 20:18 or miércoles, 8 febrero 2023, 20:28
$data->{'monthfull'} = DateTime->from_epoch(
locale => $locale_name_loaded, epoch => $secs,
time_zone => $tz)->strftime("%B");
foreach (split(/\s+/, DateTime->from_epoch(
locale => $locale_name_loaded, epoch => $secs,
time_zone => $tz)->strftime("%A, %c"))) {
if ($data->{'monthfull'} =~ /^$_/) {
$data->{'complete'} .= "$data->{'monthfull'} "
}
@@ -2227,13 +2253,24 @@ if (!$@ && $] > 5.011) {
$data->{'complete'} .= "$_ "
}
};
$data->{'year'} = DateTime->from_epoch(locale => $locale_name_loaded, epoch => $secs, time_zone => $tz)->strftime("%Y");
$data->{'day'} = DateTime->from_epoch(locale => $locale_name_loaded, epoch => $secs, time_zone => $tz)->strftime("%d");
$data->{'week'} = DateTime->from_epoch(locale => $locale_name_loaded, epoch => $secs, time_zone => $tz)->strftime("%a");
$data->{'weekfull'} = DateTime->from_epoch(locale => $locale_name_loaded, epoch => $secs, time_zone => $tz)->strftime("%A");
$data->{'month'} = DateTime->from_epoch(locale => $locale_name_loaded, epoch => $secs, time_zone => $tz)->strftime("%b");
$data->{'year'} = DateTime->from_epoch(
locale => $locale_name_loaded, epoch => $secs,
time_zone => $tz)->strftime("%Y");
$data->{'day'} = DateTime->from_epoch(
locale => $locale_name_loaded, epoch => $secs,
time_zone => $tz)->strftime("%d");
$data->{'week'} = DateTime->from_epoch(
locale => $locale_name_loaded, epoch => $secs,
time_zone => $tz)->strftime("%a");
$data->{'weekfull'} = DateTime->from_epoch(
locale => $locale_name_loaded, epoch => $secs,
time_zone => $tz)->strftime("%A");
$data->{'month'} = DateTime->from_epoch(
locale => $locale_name_loaded, epoch => $secs,
time_zone => $tz)->strftime("%b");
$data->{'complete'} =~ s/(\d+):(\d+):(\d+)(.*?)/$1:$2$4/;
($data->{'complete_short'} = $data->{'complete'}) =~ s/(.*?)([\s\,]*\Q$data->{'year'}\E.*)/$1/;
($data->{'complete_short'} = $data->{'complete'}) =~
s/(.*?)([\s\,]*\Q$data->{'year'}\E.*)/$1/;
if ($opts->{'get'}) {
return $data->{$opts->{'get'}};
@@ -7973,6 +8010,100 @@ return ref($s) && $s->{'host'} && $s->{'port'} ?
ref($s) ? "" : "$s.$$";
}
=head2 verify_session_id(session-id, [sessiondb], [miniserv])
Returns the username (or an array with user, last login, and IP address) for the
given session (or session hash) ID, or undefined if session ID is invalid.
Args:
$sid: The session ID to verify
$sessiondb: A reference to the session database hash (optional)
$miniserv: A reference to the miniserv configuration hash (optional)
Returns:
In list context: A list containing the user, last activity time, and IP
address associated with the session ID.
In scalar context: The user associated with the session ID.
If the session ID is not found, returns an empty list in list context
or undef in scalar context.
Usage:
Retrieve the username associated with the session ID or undef if
session is invalid.
my $user = verify_session_id($main::session_id);
Example of return:
root
Retrieve array containing the user, last login, and IP address
for given session hash ID or undef if session is invalid.
my (@user) = verify_session_id('BSRxr6wpF25lqeRinQ/sv0');
Example of return:
[
'root',
'1719401071',
'10.211.55.2'
]
Retrieve the username associated with the session ID, using given
session DB or undef if session is invalid.
my %sessiondb;
dbmopen(%sessiondb, "$var_directory/sessiondb", 0400);
my $user = verify_session_id($main::session_id, \%sessiondb);
dbmclose(%sessiondb);
Example of return:
someuser1
=cut
sub verify_session_id
{
my ($sid, $sessiondb, $miniserv) = @_;
my $hashsessionidfunc = \&miniserv::hash_session_id;
my %miniserv;
if ($miniserv) {
# Use provided miniserv configuration
%miniserv = %{$miniserv};
}
else {
# Load miniserv configuration if not provided
&get_miniserv_config(\%miniserv);
}
my %sessiondb_;
if ($sessiondb) {
# Use provided session database
%sessiondb_ = %{$sessiondb};
}
elsif (&foreign_available('acl')) {
# Use session database using ACL module API
&foreign_require("acl");
&acl::open_session_db(\%miniserv);
$hashsessionidfunc = \&acl::hash_session_id;
%sessiondb_ = %acl::sessiondb;
}
else {
return wantarray ? ( ) : undef;
}
# Verify given session (hash) ID against the session database
foreach my $k (grep { $sessiondb_{$_} } keys %sessiondb_) {
if ($k eq $sid ||
(defined($hashsessionidfunc) && $k eq $hashsessionidfunc->($sid))) {
my ($user, $last, $ip) = split(/\s+/, $sessiondb_{$k});
return wantarray ? ($user, $last, $ip) : $user;
}
}
# Return an empty list or undef if session ID is not found
return wantarray ? ( ) : undef;
}
=head2 remote_foreign_require(server, module, file)
Connects to rpc.cgi on a remote webmin server and have it open a session
@@ -10545,6 +10676,16 @@ elsif (defined($main::open_tempfiles{$_[0]})) {
# Closing a file
my $noerror = $main::open_tempfiles_noerror{$_[0]};
&webmin_debug_log("CLOSE", $_[0]) if ($gconfig{'debug_what_write'});
my $getfacl = &has_command("getfacl");
my $setfacl = &has_command("setfacl");
my $file_acls;
if ($getfacl && $setfacl) {
# Set original ACLs
my $qaclfile = quotemeta($_[0]);
$file_acls = &backquote_command(
"$getfacl --absolute-names $qaclfile 2>/dev/null");
}
# Get status info for a file
my @st = stat($_[0]);
if (&is_selinux_enabled() && &has_command("chcon")) {
# Set original security context
@@ -10566,6 +10707,12 @@ elsif (defined($main::open_tempfiles{$_[0]})) {
chmod($st[2], $_[0]);
chown($st[4], $st[5], $_[0]);
}
if ($file_acls) {
# Set original ACLs
open(my $pipe, '|-', "$setfacl --restore=-");
print($pipe $file_acls);
close($pipe);
}
&reset_file_attributes($_[0], \@old_attributes);
delete($main::open_tempfiles{$_[0]});
delete($main::open_tempfiles_noerror{$_[0]});

View File

@@ -18,6 +18,7 @@ else {
# Save collection options
$system_status::config{'collect_pkgs'} = $in{'pkgs'};
$system_status::config{'collect_notemp2'} = !$in{'temp2'};
$system_status::config{'collect_notemp'} = !$in{'temp'};
$system_status::config{'collect_units'} = $in{'units'};

View File

@@ -277,7 +277,9 @@ else {
my @doms = $config{'letsencrypt_doms'} ?
split(/\s+/, $config{'letsencrypt_doms'}) : ( $host );
print &ui_table_row($text{'ssl_letsdoms'},
&ui_textarea("dom", join("\n", @doms), 5, 40));
&ui_textarea("dom", join("\n", @doms), 5, 40)."<br>\n".
&ui_checkbox("subset", 1, $text{'ssl_subset'},
$config{'letsencrypt_subset'}));
# Apache vhost or other path
my @opts;

View File

@@ -19,6 +19,10 @@ print &ui_table_row($text{'status_interval'},
print &ui_table_row($text{'status_pkgs'},
&ui_yesno_radio("pkgs", $system_status::config{'collect_pkgs'}));
# Collect CPU temerature?
print &ui_table_row($text{'status_temp2'},
&ui_yesno_radio("temp2", !$system_status::config{'collect_notemp2'}));
# Collect drive temps?
print &ui_table_row($text{'status_temp'},
&ui_yesno_radio("temp", !$system_status::config{'collect_notemp'}));

View File

@@ -424,6 +424,7 @@ ssl_letserr2=Alternately, check the <a href='$1'>module configuration</a> page t
ssl_letsdesc2=This page can be used to request a new certificate, which will overwrite any other currently have configured in Webmin. However, the Let's Encrypt service requires that your ownership of the certificate domain be validated by checking that this system hosts the website for the domain. This is done by placing a small temporary file in the website's document directory.
ssl_letsheader=Options for new SSL certificate
ssl_letsdoms=Hostnames for certificate
ssl_subset=Skip unverifiable hostnames?
ssl_letsmode=Let's Encrypt validation method
ssl_letsmode0=Apache virtual host matching hostname
ssl_letsmode1=Selected Apache virtual host
@@ -1060,6 +1061,7 @@ status_interval0=Every
status_mins=minutes
status_pkgs=Collect available package updates?
status_temp=Collect drive temperatures?
status_temp2=Collect CPU temperatures and fans speed?
status_units=Units for temperatures
status_celsius=Celsius
status_fahrenheit=Fahrenheit

View File

@@ -58,14 +58,16 @@ return &software::missing_install_link(
# request_letsencrypt_cert(domain|&domains, webroot, [email], [keysize],
# [request-mode], [use-staging], [account-email],
# [reuse-key], [server-url, server-key, server-hmac])
# [key-type], [reuse-key],
# [server-url, server-key, server-hmac],
# [allow-subset])
# Attempt to request a cert using a generated key with the Let's Encrypt client
# command, and write it to the given path. Returns a status flag, and either
# an error message or the paths to cert, key and chain files.
sub request_letsencrypt_cert
{
my ($dom, $webroot, $email, $size, $mode, $staging, $account_email,
$key_type, $reuse_key, $server, $server_key, $server_hmac) = @_;
$key_type, $reuse_key, $server, $server_key, $server_hmac, $subset) = @_;
my @doms = ref($dom) ? @$dom : ($dom);
$email ||= "root\@$doms[0]";
$mode ||= "web";
@@ -179,6 +181,7 @@ if ($letsencrypt_cmd) {
my $new_flags = "";
my $reuse_flags = "";
my $server_flags = "";
my $subset_flags = "";
$key_type ||= $config{'letsencrypt_algo'} || 'rsa';
if (&compare_version_numbers($cmd_ver, 1.11) < 0) {
$old_flags = " --manual-public-ip-logging-ok";
@@ -192,6 +195,9 @@ if ($letsencrypt_cmd) {
else {
$reuse_flags = " --no-reuse-key";
}
if ($subset) {
$subset_flags = " --allow-subset-of-names";
}
$reuse_flags = "" if ($reuse_key && $reuse_key == -1);
if ($server) {
$server_flags = " --server ".quotemeta($server);
@@ -227,6 +233,7 @@ if ($letsencrypt_cmd) {
$old_flags.
$server_flags.
$new_flags.
$subset_flags.
" 2>&1)");
&reset_environment();
}
@@ -245,6 +252,7 @@ if ($letsencrypt_cmd) {
$old_flags.
$server_flags.
$new_flags.
$subset_flags.
" 2>&1)");
&reset_environment();
}
@@ -260,6 +268,7 @@ if ($letsencrypt_cmd) {
$old_flags.
$server_flags.
$new_flags.
$subset_flags.
" 2>&1)");
&reset_environment();
}

View File

@@ -54,8 +54,11 @@ else {
"VirtualHost", $conf)) {
my $sn = &apache::find_directive(
"ServerName", $virt->{'members'});
my @sa = &apache::find_directive(
"ServerAlias", $virt->{'members'});
my $match = 0;
if ($in{'webroot_mode'} == 0 && $sn eq $doms[0]) {
if ($in{'webroot_mode'} == 0 &&
&indexof($doms[0], $sn, @sa) >= 0) {
# Based on domain name
$match = 1;
}
@@ -63,7 +66,16 @@ else {
# Specifically selected domain
$match = 1;
}
if ($match) {
my @ports;
foreach my $w (@{$virt->{'words'}}) {
if ($w =~ /:(\d+)$/) {
push(@ports, $1);
}
else {
push(@ports, 80);
}
}
if ($match && &indexof(80, @ports) >= 0) {
# Get document root
$webroot = &apache::find_directive(
"DocumentRoot", $virt->{'members'}, 1);
@@ -76,7 +88,7 @@ else {
if ($in{'save'}) {
# Just update renewal
&save_renewal_only(\@doms, $webroot, $mode);
&save_renewal_only(\@doms, $webroot, $mode, $size, $in{'subset'});
&redirect("edit_ssl.cgi");
}
else {
@@ -88,7 +100,9 @@ else {
'letsencrypt_doing',
"<tt>".&html_escape(join(", ", @doms))."</tt>",
"<tt>".&html_escape($webroot)."</tt>"),"<p>\n";
my ($ok, $cert, $key, $chain) = &request_letsencrypt_cert(\@doms, $webroot, undef, $size, $mode, $in{'staging'});
my ($ok, $cert, $key, $chain) = &request_letsencrypt_cert(
\@doms, $webroot, undef, $size, $mode, $in{'staging'},
undef, undef, undef, undef, undef, undef, $in{'subset'});
if (!$ok) {
print &text('letsencrypt_failed', $cert),"<p>\n";
}
@@ -127,7 +141,8 @@ else {
&put_miniserv_config(\%miniserv);
&unlock_file($ENV{'MINISERV_CONFIG'});
&save_renewal_only(\@doms, $webroot, $mode);
&save_renewal_only(\@doms, $webroot, $mode,
$size, $in{'subset'});
&webmin_log("letsencrypt");
&restart_miniserv(1);
@@ -148,15 +163,16 @@ else {
&ui_print_footer("", $text{'index_return'});
}
# save_renewal_only(&doms, webroot, mode)
# save_renewal_only(&doms, webroot, mode, size, subset-mode)
# Save for future renewals
sub save_renewal_only
{
my ($doms, $webroot, $mode) = @_;
my ($doms, $webroot, $mode, $size, $subset) = @_;
$config{'letsencrypt_doms'} = join(" ", @$doms);
$config{'letsencrypt_webroot'} = $webroot;
$config{'letsencrypt_mode'} = $mode;
$config{'letsencrypt_size'} = $size;
$config{'letsencrypt_subset'} = $subset;
&save_module_config();
if (&foreign_check("webmincron")) {
my $job = &find_letsencrypt_cron_job();

View File

@@ -234,7 +234,7 @@ my $str = "otpauth://totp/".$name."?secret=".$user->{'twofactor_id'};
my $url;
if (&can_generate_qr()) {
$url = "$gconfig{'webprefix'}/webmin/qr.cgi?".
"size=4&str=".&urlize($str);
"size=6&str=".&urlize($str);
}
else {
$url = "https://chart.googleapis.com/chart".

View File

@@ -2638,6 +2638,10 @@ $gconfig{'real_os_type'} = $osinfo{'real_os_type'};
$gconfig{'real_os_version'} = $osinfo{'real_os_version'};
$gconfig{'os_type'} = $osinfo{'os_type'};
$gconfig{'os_version'} = $osinfo{'os_version'};
foreach my $key ('os_eol_db', 'os_eol_expired',
'os_eol_expiring') {
delete($gconfig{$key});
}
&write_file("$config_directory/config", \%gconfig);
&unlock_file("$config_directory/config");
@@ -2869,7 +2873,7 @@ if (!$@) {
my $out;
Image::PNG::QRCode::qrpng(
text => $str,
scale => $size || 3,
scale => $size || 6,
out => \$out,
);
return ($out, "image/png");