Merge branch 'master' of github.com:webmin/webmin

This commit is contained in:
Jamie Cameron
2023-06-09 14:56:20 -07:00
19 changed files with 217 additions and 52 deletions

File diff suppressed because one or more lines are too long

View File

@@ -207,7 +207,7 @@ if ($access{'lang'}) {
if ($access{'locale'}) {
# Current locale
eval "use DateTime; use DateTime::Locale; use DateTime::TimeZone;";
if (!$@) {
if (!$@ && $] > 5.011) {
my $locales = &list_locales();
my %localesrev = reverse %{$locales};
my $locale_auto = &parse_accepted_language();

View File

@@ -59,9 +59,9 @@ if ($access{'lang'}) {
# Old datetime format or a new locale
if ($access{'locale'}) {
eval "use DateTime; use DateTime::Locale; use DateTime::TimeZone;";
&foreign_require('webmin');
if (!$@) {
eval "use DateTime; use DateTime::Locale; use DateTime::TimeZone;";
if (!$@ && $] > 5.011) {
my $locales = &list_locales();
my %localesrev = reverse %{$locales};
my $locale = $locale_auto || $gconfig{'locale'} || &get_default_system_locale();

View File

@@ -261,7 +261,8 @@ else {
# Generate the header
local (@hcols, @tds);
push(@hcols, "", $text{'index_action'});
push(@tds, "width=5", "width=30% nowrap");
push(@tds, "width=5", "width=10% nowrap",
"width=70%", "nowrap", "nowrap");
if ($config{'view_condition'}) {
push(@hcols, $text{'index_desc'});
push(@tds, "nowrap");

View File

@@ -1480,7 +1480,7 @@ if ($u) {
my $locale;
if ($userconfig{'date_fmt'} eq 'auto' || $config{'date_fmt'} eq 'auto') {
eval "use DateTime; use DateTime::Locale; use DateTime::TimeZone;";
if (!$@) {
if (!$@ && $] > 5.011) {
$locale++;
return &make_date($u, undef, $fmt);
}

View File

@@ -16,6 +16,7 @@ fwd_mode=0
delete_warn=y
top_buttons=2
view_html=2
view_images=3
mail_usermin=mail
sync_create=0
sync_modify=1

View File

@@ -10,6 +10,7 @@ show_sent=Show number of messages in sent mail folder?,1,1-Yes,0-No
fwd_mode=Forward messages with quoting?,1,0-Yes,1-No
delete_warn=Ask for confirmation before deleting?,10,y-Yes,n-No,For mbox files larger than
view_html=Show message body as,4,0-Always plain text,1-Text if possible, HTML otherwise,2-HTML if possible, text otherwise,3-Convert HTML to plain text
view_images=Show inline images by default?,1,0-Yes, client fetched,3-Yes, server fetched,1-No external images,2-No
html_edit=Use HTML editor for composing?,4,2-Always,1-When replying to HTML email,0-Never
html_quote=HTML quoting mode,1,1-Message below <hr>,0-Message inside <blockquote>
log_read=Record the reading of mail in the Webmin Actions Log?,1,1-Yes,0-No

View File

@@ -2786,17 +2786,24 @@ while($html =~ /^([\000-\377]*?)(<\s*img[^>]*src=('[^']*'|"[^"]*"|\S+)[^>]*>)([\
my ($before, $allimg, $img, $after) = ($1, $2, $3, $4);
$img =~ s/^'(.*)'$/$1/ || $img =~ s/^"(.*)"$/$1/;
push(@$urls, $img) if ($urls);
if ($dis == 0) {
if ($dis == 3) {
# Let server load it in async mode
my $imgcont = $allimg;
$imgcont =~ s/src=/data-presrc=/g;
$newhtml .= $before.$imgcont;
$masked_img++;
}
elsif ($dis == 0) {
# Don't harm image
$newhtml .= $before.$allimg;
}
elsif ($dis == 1) {
# Don't touch unless offsite
if ($img =~ /^(http|https|ftp):/) {
$masked_img++;
my $imgcont = $allimg;
$imgcont =~ s/src=/data-nosrc=/g;
$newhtml .= $before.$imgcont;
$masked_img++;
}
else {
$newhtml .= $before.$allimg;
@@ -2811,16 +2818,16 @@ while($html =~ /^([\000-\377]*?)(<\s*img[^>]*src=('[^']*'|"[^"]*"|\S+)[^>]*>)([\
$newhtml .= $html;
if ($masked_img) {
my $masked_img_style =
"<style>".
"img[data-nosrc]
"<style>
img[data-nosrc]
{
border-radius: 0 !important;
background: #e1567833 !important;
border-color: transparent !important;
min-width: 16px;
min-height: 16px;
}".
"</style>";
}
</style>";
$masked_img_style =~ s/[\n\r\s]+/ /g;
$masked_img_style = &trim($masked_img_style);
if ($newhtml =~ /<\/body>/) {
@@ -2851,6 +2858,8 @@ if ($body =~ /<\/body>/) {
}
$body = &trim(&quote_escape($body, '"'));
# Email iframe stuff
my $webprefix = &get_webprefix();
my $image_mode = int(defined($in{'images'}) ? $in{'images'} : $userconfig{'view_images'});
my $iframe_body = <<EOF;
<div id="mail-iframe-spinner"></div>
<style>
@@ -2884,18 +2893,43 @@ my $iframe_body = <<EOF;
return;
}
const iframe_spinner = document.querySelector('#mail-iframe-spinner'),
iframe_height_bound = iframe.contentWindow.document.body.getBoundingClientRect().bottom,
iframe_scroll_height = iframe.contentWindow.document.body.scrollHeight,
iframe_height =
iframe_height_bound > iframe_scroll_height ?
iframe_height_bound : iframe_scroll_height;
iframe.style.height = Math.ceil(iframe_height) + "px";
iframe_resize = function() {
const iframeobj = document.querySelector('#mail-iframe'),
iframe_height_bound = iframeobj.contentWindow.document.body.getBoundingClientRect().bottom,
iframe_scroll_height = iframeobj.contentWindow.document.body.scrollHeight,
iframe_height =
iframe_height_bound > iframe_scroll_height ?
iframe_height_bound : iframe_scroll_height;
iframeobj.style.height = Math.ceil(iframe_height) + "px";
};
iframe_spinner && iframe_spinner.remove();
iframe.classList.add("loaded");
setTimeout(iframe_resize);
setTimeout(function() {
const imgPresrc = iframe.contentWindow.document.querySelectorAll('img[data-presrc]');
imgPresrc.forEach(function(img) {
(async function() {
try {
const response = await fetch("$webprefix/$module_name/xhr.cgi?action=fetch&type=download&subtype=blob&url=" + encodeURIComponent(img.dataset.presrc) + "");
response.blob().then(function(blob) {
try {
const urlBlob = URL.createObjectURL(blob);
img.removeAttribute('data-presrc');
img.src = urlBlob;
img.addEventListener('load', iframe_resize, { once: true });
} catch(error) {
console.warn(\`Cannot load image: \$\{error.message\}\`);
}
});
} catch (e) {}
})();
});
}, 99);
}
</script>
<iframe
id="mail-iframe"
class="mail-iframe mode-$image_mode"
onload="mail_iframe_onload(this)"
sandbox="allow-same-origin allow-popups allow-popups-to-escape-sandbox"
src="about:blank" srcdoc="$body">

View File

@@ -158,7 +158,7 @@ if ($body && $body->{'data'} =~ /\S/) {
# Attempt to show HTML
$bodycontents = $body->{'data'};
my @imageurls;
my $image_mode = defined($in{'images'}) ? $in{'images'} : 1;
my $image_mode = int(defined($in{'images'}) ? $in{'images'} : $config{'view_images'});
$bodycontents = &disable_html_images($bodycontents, $image_mode, \@imageurls);
$bodycontents = &fix_cids($bodycontents, \@attach,
"detach.cgi?user=$uuser&idx=$in{'idx'}&folder=$in{'folder'}$subs");
@@ -166,9 +166,9 @@ if ($body && $body->{'data'} =~ /\S/) {
push(@bodyright,
"<a href='$hbase&body=1'>$text{'view_astext'}</a>");
}
if (@imageurls && $image_mode) {
if (@imageurls && $image_mode && $image_mode != 3) {
# Link to show images
push(@bodyright, "<a href='$hbase&body=$in{'body'}&headers=$in{'headers'}&images=0'>$text{'view_images'}</a>");
push(@bodyright, "<a href='$hbase&body=$in{'body'}&headers=$in{'headers'}&images=3'>$text{'view_images'}</a>");
}
}
$bodycontents = &iframe_body($bodycontents)
@@ -177,7 +177,7 @@ if ($body && $body->{'data'} =~ /\S/) {
if ($bodycontents) {
print &ui_table_start($text{'view_body'}, "width=100%", 1,
undef, &ui_links_row(\@bodyright));
print &ui_table_row(undef, $bodycontents);
print &ui_table_row(undef, $bodycontents, undef, undef, ["data-contents='email'"]);
print &ui_table_end();
}
else {

63
mailboxes/xhr-lib.pl Normal file
View File

@@ -0,0 +1,63 @@
#!/usr/local/bin/perl
# XHR related routines
use strict;
our (%in, %gconfig, $root_directory, $remote_user, $current_theme);
sub xhr
{
my %data = ();
my $output_json = sub {
my ($data) = @_;
print "x-no-links: 1\n";
print_json($data);
};
my $error = sub {
my ($err) = @_;
$data{'error'} = $err;
&$output_json(\%data);
exit;
};
# Fetch actions
if ($in{'action'} eq "fetch") {
# Download types
if ($in{'type'} eq "download") {
# Format Blob format
if ($in{'subtype'} eq "blob") {
# Download using giving URL
my $url = $in{'url'};
if ($url) {
# Unescape possibly HTML escaped
# image URL (LinkedIn and other)
$url = &html_unescape($url);
my ($host, $port, $page, $ssl) = &parse_http_url($url);
my ($img, $err, $response_headers);
&http_download($host, $port, $page, \$img, \$err, undef,
$ssl, undef, undef, 10, undef, undef,
undef, \$response_headers);
# Check if download worked
&$error("File download failed : $err")
if ($err);
# Get MIME content type
my $mime_type = $response_headers->{'content-type'};
print "x-no-links: 1\n";
print "Content-type: $mime_type;\n\n";
print $img;
exit;
}
&$error("File URL is missing")
}
&$error("Downloading file failed")
}
&$error("Fetching file failed");
}
else {
&$error("Unknown request");
}
&$output_json(\%data);
}
1;

15
mailboxes/xhr.cgi Executable file
View File

@@ -0,0 +1,15 @@
#!/usr/local/bin/perl
# A caller for loading XHR related routines
use strict;
our ($root_directory);
BEGIN { push(@INC, "."); };
use WebminCore;
&init_config();
&ReadParse();
&switch_to_remote_user();
do "./xhr-lib.pl";
xhr();

View File

@@ -123,7 +123,7 @@ else {
}
print &text($msg, "<tt>".&html_escape(join(" ", @pkgnames))."</tt>"),
"<br>\n";
print "<ul>\n";
print "<ul data-package-updates='1'>\n";
@got = &package_install_multiple(
\@pkgnames, $pkgsystem, $in{'mode'} eq 'new');
print "</ul><br>\n";
@@ -134,7 +134,7 @@ else {
($p, $s) = split(/\//, $ps);
next if ($donedep{$p});
print &text($msg, "<tt>@{[&html_escape($p)]}</tt>"),"<br>\n";
print "<ul>\n";
print "<ul data-package-updates='2'>\n";
@pgot = &package_install(
$p, $s, $in{'mode'} eq 'new');
foreach $g (@pgot) {

View File

@@ -59,7 +59,7 @@ if (!$hba_conf_file && &is_postgresql_local()) {
if ($r == 0) {
# Not running .. need to start it
&main_header(1);
print "<b>$text{'index_notrun'}</b> <p>\n";
print &ui_alert_box($text{'index_notrun'}, 'danger');
if (&is_postgresql_local()) {
print &ui_hr();
@@ -76,18 +76,20 @@ if ($r == 0) {
}
print &ui_buttons_end();
}
$lerr++;
}
elsif ($r == -1 && $access{'user'} && 0) {
# Running, but the user's password is wrong
&main_header(1);
print "<b>",&text('index_nouser', "<tt>$access{'user'}</tt>"),
"</b><p>\n";
print &text('index_emsg', "<tt>$rout</tt>"),"<p>\n";
print &ui_alert_box(&text('index_nouser', "<tt>$access{'user'}</tt>"), 'danger');
print &ui_alert_box(&text('index_emsg', "<tt>$rout</tt>"), 'info');
$lerr++;
}
elsif ($r == -1) {
# Running, but webmin doesn't know the login/password
&main_header(1);
print "<b>$text{'index_nopass'}</b> <p>\n";
print &ui_alert_box($text{'index_nopass'}, 'danger');
print &ui_alert_box(&text('index_emsg', "<tt>$rout</tt>"), 'info');
print &ui_form_start("login.cgi", "post");
print &ui_table_start($text{'index_ltitle'}, undef, 2);
@@ -105,13 +107,12 @@ elsif ($r == -1) {
print &ui_table_end();
print &ui_form_end([ [ undef, $text{'save'} ] ]);
print &text('index_emsg', "<tt>$rout</tt>"),"<p>\n";
# Button to edit user permissions
if ($access{'users'}) {
print &ui_form_start("list_hosts.cgi");
print &ui_form_end([ [ undef, $text{'index_edithosts'} ] ]);
}
$lerr++;
}
elsif ($r == -2) {
# Looks like a shared library problem
@@ -120,8 +121,9 @@ elsif ($r == -2) {
"@{[&get_webprefix()]}/config.cgi?$module_name"),"<p>\n";
print &text('index_ldpath', "<tt>$ENV{$gconfig{'ld_env'}}</tt>",
"<tt>$config{'psql'}</tt>"),"<br>\n";
print "<pre>",&html_escape($out),"</pre>\n";
print &text('index_emsg', "<tt>$rout</tt>"),"<p>\n";
print "<pre>",&html_escape($out),"</pre><p>\n";
print &ui_alert_box(&text('index_emsg', "<tt>$rout</tt>"), 'info');
$lerr++;
}
else {
# Running .. check version
@@ -263,8 +265,10 @@ else {
}
}
print &ui_hr();
print &ui_buttons_start();
if (!$lerr) {
print &ui_hr();
print &ui_buttons_start();
}
# Show stop server button
if ($access{'stop'} && &is_postgresql_local() && $r != 0) {

View File

@@ -1,5 +1,5 @@
index_title=PostgreSQL Database Server
index_notrun=PostgreSQL is not running on your system - database list could not be retrieved.
index_notrun=PostgreSQL is not running on your system!
index_start=Start PostgreSQL Server
index_startmsg2=Click this button to start the PostgreSQL database server on your system. This Webmin module cannot administer the database until it is started.
index_nopass=Webmin needs to know your PostgreSQL administration login and password in order to manage your database. Please enter your administration username and password below.

View File

@@ -6,6 +6,7 @@
webmin_download="https://download.webmin.com"
webmin_key="developers-key.asc"
webmin_key_download="$webmin_download/$webmin_key"
webmin_key_suffix="webmin-developers"
debian_repo_file="/etc/apt/sources.list.d/webmin.list"
rhel_repo_file="/etc/yum.repos.d/webmin.repo"
download_wget="/usr/bin/wget"
@@ -61,6 +62,11 @@ fi
osid_debian_like=$(echo "$osid" | grep "debian\|ubuntu")
osid_rhel_like=$(echo "$osid" | grep "rhel\|fedora\|centos")
repoid_debian_like=debian
if [ -n "${ID}" ]; then
repoid_debian_like="${ID}"
fi
# Setup OS dependent
if [ -n "$osid_debian_like" ]; then
package_type=deb
@@ -138,7 +144,7 @@ rpm)
# Install our keys
echo " Installing Webmin key .."
rpm --import $webmin_key
cp -f $webmin_key /etc/pki/rpm-gpg/RPM-GPG-KEY-webmin
cp -f $webmin_key /etc/pki/rpm-gpg/RPM-GPG-KEY-$webmin_key_suffix
echo " .. done"
# Create repo file
echo " Setting up Webmin repository .."
@@ -146,7 +152,7 @@ rpm)
echo "name=Webmin - noarch" >>$rhel_repo_file
echo "baseurl=$webmin_download/download/newkey/yum" >>$rhel_repo_file
echo "enabled=1" >>$rhel_repo_file
echo "gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-webmin" >>$rhel_repo_file
echo "gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-$webmin_key_suffix" >>$rhel_repo_file
echo "gpgcheck=1" >>$rhel_repo_file
echo " .. done"
;;
@@ -154,11 +160,11 @@ deb)
# Install our keys
echo " Installing Webmin key .."
gpg --import $webmin_key 1>/dev/null 2>&1
cat $webmin_key | gpg --dearmor > /usr/share/keyrings/debian-webmin.gpg
cat $webmin_key | gpg --dearmor > "/usr/share/keyrings/$repoid_debian_like-$webmin_key_suffix.gpg"
echo " .. done"
# Create repo file
echo " Setting up Webmin repository .."
echo "deb [signed-by=/usr/share/keyrings/debian-webmin.gpg] $webmin_download/download/newkey/repository stable contrib" >$debian_repo_file
echo "deb [signed-by=/usr/share/keyrings/$repoid_debian_like-$webmin_key_suffix.gpg] $webmin_download/download/newkey/repository stable contrib" >$debian_repo_file
echo " .. done"
# Clean meta
echo " Cleaning repository metadata .."

View File

@@ -83,7 +83,7 @@ if (&show_section('host')) {
# System time
my $tm = localtime(time());
eval "use DateTime; use DateTime::Locale; use DateTime::TimeZone;";
if (!$@) {
if (!$@ && $] > 5.011) {
$tm = make_date(time(), {get => 'complete'});
}
if (&foreign_available("time")) {

View File

@@ -21,7 +21,7 @@ print &ui_table_row($text{'lang_lang'},
# Old datetime format or a new locale
eval "use DateTime; use DateTime::Locale; use DateTime::TimeZone;";
if (!$@) {
if (!$@ && $] > 5.011) {
my $locales = &list_locales();
my %localesrev = reverse %{$locales};
my $locale_auto = &parse_accepted_language(\%uconfig);

View File

@@ -252,7 +252,7 @@ sub html_escape
{
my ($tmp) = @_;
if (!defined $tmp) {
return ''; # empty string
return ''; # empty string
};
# Before escaping ampersand use negative lookahead to see if occurrence
# is not an HTML entity already to prevent double escaping
@@ -266,6 +266,31 @@ $tmp =~ s/=/&#61;/g;
return $tmp;
}
=head2 html_unescape(string)
Converts HTML entities to the corresponding character
=cut
sub html_unescape
{
my ($str) = @_;
if (!defined $str) {
return ''; # empty string
};
$str =~ s/&amp;/&/g;
$str =~ s/&lt;/</g;
$str =~ s/&gt;/>/g;
$str =~ s/&quot;/"/g;
$str =~ s/&#39;/'/g;
$str =~ s/&#61;/=/g;
$str =~ s/&nbsp;/ /g;
eval "use HTML::Entities";
if (!$@) {
$str = decode_entities($str);
}
return $str;
}
=head2 html_strip(string, replacement)
Removes any HTML from a string, replacing with nothing or given chars
@@ -1993,7 +2018,7 @@ sub make_date
my ($secs, $only, $fmt) = @_;
$secs ||= 0;
eval "use DateTime; use DateTime::Locale; use DateTime::TimeZone;";
if (!$@) {
if (!$@ && $] > 5.011) {
my $opts = ref($only) ? $only : {};
my $locale_default = &get_default_system_locale();
my $locale_auto = &parse_accepted_language();
@@ -2704,11 +2729,13 @@ Downloads data from a HTTP url to a local file or string. The parameters are :
=item headers - If set to a hash ref of additional HTTP headers, they will be added to the request.
=item response_headers - If set returns a hash ref of response HTTP headers.
=cut
sub http_download
{
my ($host, $port, $page, $dest, $error, $cbfunc, $ssl, $user, $pass,
$timeout, $osdn, $nocache, $headers) = @_;
$timeout, $osdn, $nocache, $headers, $response_headers) = @_;
if ($gconfig{'debug_what_net'}) {
&webmin_debug_log('HTTP', "host=$host port=$port page=$page ssl=$ssl".
($user ? " user=$user pass=$pass" : "").
@@ -2769,13 +2796,14 @@ if (!ref($h)) {
else { &error(&html_escape($h)); }
}
&complete_http_download($h, $dest, $error, $cbfunc, $osdn, $host, $port,
$headers, $ssl, $nocache, $timeout);
$headers, $ssl, $nocache, $timeout,
defined($response_headers) ? $response_headers : undef);
if ((!$error || !$$error) && !$nocache) {
&write_to_http_cache($url, $dest);
}
}
=head2 complete_http_download(handle, destfile, [&error], [&callback], [osdn], [oldhost], [oldport], [&send-headers], [old-ssl], [no-cache], [timeout])
=head2 complete_http_download(handle, destfile, [&error], [&callback], [osdn], [oldhost], [oldport], [&send-headers], [old-ssl], [no-cache], [timeout], [response-header])
Do a HTTP download, after the headers have been sent. For internal use only,
typically called by http_download.
@@ -2784,9 +2812,10 @@ typically called by http_download.
sub complete_http_download
{
my ($h, $destfile, $error, $cbfunc, $osdn, $oldhost, $oldport, $headers,
$oldssl, $nocache, $timeout) = @_;
local ($line, %header, @headers, $s); # Kept local so that callback funcs
# can access them.
$oldssl, $nocache, $timeout, $response_headers) = @_;
# Kept local so that callback funcs # can access them.
local ($line, %header, @headers, $s);
# read headers
$timeout = 60 if (!defined($timeout));
@@ -2809,6 +2838,8 @@ while(1) {
$header{lc($1)} = $2;
push(@headers, [ lc($1), $2 ]);
}
$$response_headers = \%header
if (defined($response_headers));
alarm(0) if ($timeout);
if ($main::download_timed_out) {
&close_http_connection($h);
@@ -7125,7 +7156,16 @@ if (!$@) {
# Write file
if ($filename) {
write_file_contents(tempname("${filename}${filename_}"), Dumper($objref));
my ($seconds, $microseconds);
eval 'use Time::Piece';
if (!$@) {
eval 'use Time::HiRes';
if (!$@) {
($seconds, $microseconds) = Time::HiRes::gettimeofday;
$microseconds = "$seconds$microseconds-";
}
}
write_file_contents(tempname("${microseconds}${filename}${filename_}"), Dumper($objref));
}
# Print on screen
else {

View File

@@ -22,7 +22,7 @@ print &ui_table_row($text{'lang_lang'},
# Old datetime format or a new locale
eval "use DateTime; use DateTime::Locale; use DateTime::TimeZone;";
if (!$@) {
if (!$@ && $] > 5.011) {
my $locales = &list_locales();
my %localesrev = reverse %{$locales};
my $locale_auto = &parse_accepted_language();