chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 13:12:17 +08:00
commit cf8edb9f21
44281 changed files with 1655134 additions and 0 deletions
+31
View File
@@ -0,0 +1,31 @@
use strict;
use warnings;
no warnings 'uninitialized';
require 'xterm-lib.pl'; ## no critic
our (%in, %text);
# acl_security_form(&options)
# Output HTML for editing security options for the xterm module
sub acl_security_form
{
my ($o) = @_;
print ui_table_row($text{'acl_user'},
ui_opt_textbox("user", $o->{'user'} eq '*' ? undef : $o->{'user'},
20, $text{'acl_sameuser'}));
print ui_table_row($text{'acl_sudoenforce'},
ui_yesno_radio("sudoenforce",
$o->{'sudoenforce'} == 1 ? 1 : 0));
}
sub acl_security_save
{
my ($o) = @_;
$o->{'user'} = $in{'user_def'} ? '*' : $in{'user'};
$o->{'sudoenforce'} = $in{'sudoenforce'} ? 1 : 0;
}
1;
+6
View File
@@ -0,0 +1,6 @@
xterm=xterm-256color
fontsize=14
base_port=555
rcfile=0
locale=0
screen_reader=false
+7
View File
@@ -0,0 +1,7 @@
xterm=Set <tt>TERM</tt> environmental variable to,4,xterm+256color-xterm&#45;256color,xterm+16color-xterm&#45;16color,xterm-xterm,vt102-vt102,vt100-vt100,vt52-vt52,rxvt-rxvt,nsterm-nsterm,dtterm-dtterm,ansi-ansi
size=Terminal width and height in characters,3,Automatic,5,,,Static (80x24)
fontsize=Adjust font size,4,0-Auto,12-Small,14-Normal,16-Large,18-Huge
locale=Set shell character encoding,10,0-Shell default,1-<tt>en_US.UTF&#45;8</tt>,Custom
rcfile=Execute initialization commands from file,10,0-Shell default,1-Module default,Custom
screen_reader=Enable screen reader mode,1,true-Yes,false-No
user=Run terminal as Unix user,3,root
+2
View File
@@ -0,0 +1,2 @@
user=root
sudoenforce=1
Binary file not shown.

After

Width:  |  Height:  |  Size: 275 B

+304
View File
@@ -0,0 +1,304 @@
#!/usr/local/bin/perl
# Show a terminal that is connected to a Websockets server via Webmin proxying
use strict;
use warnings;
no warnings 'uninitialized';
our $unsafe_index_cgi = 1;
require './xterm-lib.pl'; ## no critic
our (%in, %text, %config, %gconfig, %access, %module_info,
$module_name, $module_config_directory, $module_var_directory,
$remote_user, $session_id);
ReadParse();
# Check for needed modules
my @modload = (
['Digest::SHA', sub { eval { require Digest::SHA; Digest::SHA->import; 1 } }],
['Digest::MD5', sub { eval { require Digest::MD5; Digest::MD5->import; 1 } }],
['IO::Select', sub { eval { require IO::Select; IO::Select->import; 1 } }],
['Time::HiRes', sub { eval { require Time::HiRes; Time::HiRes->import; 1 } }],
['Net::WebSocket::Server', sub { eval { require Net::WebSocket::Server; Net::WebSocket::Server->import; 1 } }],
);
foreach my $m (@modload) {
my ($modname, $loader) = @$m;
next if ($loader->());
ui_print_header(undef, $text{'index_title'}, "", undef, 1, 1, 0);
my $missinglink = text('index_cpan', "<tt>$modname</tt>",
"../cpan/download.cgi?source=3&cpan=$modname&mode=2&return=/$module_name/&returndesc=".urlize($module_info{'desc'}));
if ($gconfig{'os_type'} eq 'redhat-linux') {
$missinglink .= " ".
text('index_epel',
'https://docs.fedoraproject.org/en-US/epel');
}
elsif ($gconfig{'os_type'} eq 'suse-linux') {
$missinglink =
text('index_suse', "<tt>$modname</tt>",
'https://software.opensuse.org/download/package?package=perl-IO-Tty&project=devel%3Alanguages%3Aperl');
}
if (get_product_name() eq 'usermin') {
print text('index_missing', $modname) ."<p>\n";
}
else {
print $missinglink ."<p>\n";
}
ui_print_footer("/", $text{'index'});
exit;
}
# Get Webmin current version for links serial
my $wver = get_webmin_version();
$wver =~ s/\.//;
# Build Xterm dependency links
my $termlinks =
{ 'css' => ["xterm.css?$wver"],
'js' => ["xterm.js?$wver",
"xterm-addon-attach.js?$wver",
"xterm-addon-fit.js?$wver",
"xterm-addon-webgl.js?$wver"] };
# Pre-process options
my $conf_size_str = $config{'size'};
my $def_cols_n = 80;
my $def_rows_n = 24;
my $xmlhr = ($ENV{'HTTP_X_REQUESTED_WITH'} || '') eq "XMLHttpRequest";
my $font_size = $config{'fontsize'} || 14;
# Parse module config
my ($conf_cols_n, $conf_rows_n) = (($conf_size_str || '') =~ /([\d]+)X([\d]+)/i);
$conf_cols_n = int($conf_cols_n);
$conf_rows_n = int($conf_rows_n);
# Set columns and rows vars
my $env_cols = $conf_cols_n || $def_cols_n;
my $env_rows = $conf_rows_n || $def_rows_n;
# Set columns and rows environment vars only
# in fixed mode, and only for old themes
if ($conf_cols_n && $conf_rows_n && !$xmlhr) {
$ENV{'COLUMNS'} = $conf_cols_n;
$ENV{'LINES'} = $conf_rows_n;
}
# Define columns and rows
my $conf_screen_reader = $config{'screen_reader'} eq 'true' ? 'true' : 'false';
my %term_opts;
$term_opts{'Options'} = "{ cols: $env_cols, rows: $env_rows, ".
"screenReaderMode: $conf_screen_reader, ".
"overviewRuler: { width: 9 }, ".
"fontSize: $font_size }";
my $term_size = "
min-width: ".($conf_cols_n ? "".($conf_cols_n * 9)."px" : "calc(100vw - 22px)").";
max-width: ".($conf_cols_n ? "".($conf_cols_n * 9)."px" : "calc(100vw - 22px)").";
min-height: ".($conf_rows_n ? "".($conf_rows_n * 18)."px" : "calc(100vh - 55px)").";
max-height: ".($conf_rows_n ? "".($conf_rows_n * 18)."px" : "calc(100vh - 55px)").";";
# Tweak old themes inline
my $styles_inline = <<EOF;
body[style='height:100%'] {
height: 97% !important;
}
#headln2l a {
white-space: nowrap;
}
#terminal {
border: 1px solid #000;
background-color: #000;
padding: 2px;
margin: 0 auto;
$term_size
}
#terminal:empty:before {
display: block;
content: " ";
overflow: hidden;
width: 12px;
height: 12px;
margin-top: 4px;
margin-left: 4px;
border-radius: 50%;
box-sizing: border-box;
border: 1px solid transparent;
border-top-color: rgba(255, 255, 255, 0.8);
border-bottom-color: rgba(255, 255, 255, 0.8);
animation: jumping-spinner 1s ease infinite;
}
#terminal:empty:after {
display: block;
content: attr(data-label);
margin-left: 24px;
margin-top: -16px;
font-weight: 100;
color: rgba(255, 255, 255, 0.8);
font-family: "Lucida Console", Courier, monospace;
font-size: 14px;
text-transform: uppercase;
}
\@keyframes jumping-spinner {
to {
transform: rotate(360deg);
}
}
#terminal + script ~ * {
display: none
}
#terminal > .terminal {
visibility: hidden;
animation: .15s fadeIn;
animation-fill-mode: forwards;
}
\@keyframes fadeIn {
99% {
visibility: hidden;
}
100% {
visibility: visible;
}
}
EOF
# Print header
ui_print_header(undef, $text{'index_title'}, "", undef, 1, 1, 0, undef,
"<link rel=stylesheet href=\"$termlinks->{'css'}[0]\">\n".
"<script src=\"$termlinks->{'js'}[0]\"></script>\n".
"<script src=\"$termlinks->{'js'}[1]\"></script>\n".
"<script src=\"$termlinks->{'js'}[2]\"></script>\n".
"<style>$styles_inline</style>\n"
);
# Print main container
print "<div data-label=\"$text{'index_connecting'}\" id=\"terminal\"></div>\n";
# Get a free port that can be used for the socket. Normal browser sessions
# are revalidated by miniserv while the websocket stays open. Proxied or
# Basic-auth requests may not have a session cookie, so only those routes get
# a one-time backend handshake secret.
my $websocket_session_id = $session_id;
my $backend_session;
if (!$websocket_session_id) {
$websocket_session_id = generate_miniserv_websocket_token();
$backend_session = $websocket_session_id;
}
my $port = allocate_miniserv_websocket(
$module_name, undef, $backend_session);
# Decide which Unix account the terminal will run as
my $user = resolve_shell_user(\%access, $remote_user, \%in, \%config);
my @uinfo = getpwnam($user);
@uinfo || error(text('index_euser', html_escape($user)));
# Check for directory to start the shell in
my $dir = $in{'dir'};
# Launch the shell server on the allocated port
my $shellserver_cmd = "$module_config_directory/shellserver.pl";
if (!-r $shellserver_cmd) {
create_wrapper($shellserver_cmd, $module_name, "shellserver.pl");
}
# shellserver.pl validates the backend websocket key against SESSION_ID. For
# normal sessions miniserv forwards the browser session; no-cookie routes use
# the one-time backend_session stored in miniserv.conf above.
$ENV{'SESSION_ID'} = $websocket_session_id;
system_logged($shellserver_cmd." ".quotemeta($port)." ".quotemeta($user).
($dir ? " ".quotemeta($dir) : "").
" >$module_var_directory/websocket-connection-$port.out 2>&1 </dev/null");
# Open the terminal
my $url = get_miniserv_websocket_url($port, $config{'host'}, $module_name);
my $webGLAddon = $termlinks->{'js'}[3];
my $term_script = <<EOF;
(function() {
const socket = new WebSocket('$url', 'binary'),
termcont = document.getElementById('terminal'),
err_conn_cannot = 'Cannot connect to the socket ' + socket.url,
err_conn_lost = 'Connection to the socket ' + socket.url + ' lost',
webGLAddonLink = '$webGLAddon',
detectWebGLContext = (function() {
const canvas = document.createElement("canvas"),
gl = canvas.getContext("webgl") ||
canvas.getContext("experimental-webgl");
return gl instanceof WebGLRenderingContext ? true : false;
})();
socket.onopen = function() {
const term = new Terminal($term_opts{'Options'}),
attachAddon = new AttachAddon.AttachAddon(this),
fitAddon = new FitAddon.FitAddon(),
renderScript = document.createElement('script');
renderScript.src = webGLAddonLink;
renderScript.async = false;
document.body.appendChild(renderScript);
// Wait to load requested render addon
renderScript.addEventListener('load', function() {
let rendererAddon;
if (detectWebGLContext && typeof WebglAddon === 'object') {
rendererAddon = new WebglAddon.WebglAddon();
term.loadAddon(rendererAddon);
// Handle case of dropping WebGL context
if (rendererAddon.onContextLoss) {
rendererAddon.onContextLoss(function() {
rendererAddon.dispose();
});
}
}
term.loadAddon(attachAddon);
term.loadAddon(fitAddon);
term.open(termcont);
setTimeout(function() { term.focus() }, 6e2);
// On resize event triggered by fit()
term.onResize(function(e) {
socket.send('\\\\033[8;(' + e.rows + ');(' + e.cols + ')t');
});
// Observe on terminal container change
new ResizeObserver(function() {
fitAddon.fit();
}).observe(termcont);
});
};
socket.onerror = function() {
termcont.innerHTML = '<tt style="color: \#ff0000">Error: ' +
err_conn_cannot + '</tt>';
};
socket.onclose = function() {
termcont.innerHTML = '<tt style="color: \#ff0000">Error: ' +
err_conn_lost + '</tt>';
};
})();
EOF
# Return inline script data depending on type
print "<script>\n";
if ($xmlhr) {
print "var xterm_argv = ".
convert_to_json(
{ 'conf' => \%config,
'files' => $termlinks,
'socket_url' => $url,
'port' => $port,
'cols' => $env_cols,
'rows' => $env_rows,
'uinfo' => \@uinfo });
}
else {
print $term_script;
}
print "</script>\n";
ui_print_footer();
+12
View File
@@ -0,0 +1,12 @@
index_title=صالة
index_cpan=وحدة Perl <tt>$1</tt> مفقودة ، ولكن يمكن <a href='$2'>تثبيتها تلقائيًا</a> باستخدام وحدة Webmin's Perl Modules.
index_epel=يوصى بتمكين مستودع <a href='$1' target='_blank'>EPEL</a> أولاً.
index_suse=حزمة Perl <tt>perl-IO-Tty</tt> مفقودة ، ويجب تثبيتها يدويًا أولاً من مستودع <a href='$2' target='_blank'>devel: languages: perl</a>.
index_euser=خطأ: لا يمكن للمستخدم$1 تشغيل shell لأنه غير موجود!
index_connecting=توصيل ..
index_missing=وحدة Perl <tt>$1</tt> مفقودة. تحتاج إلى الاتصال بالمسؤول لحل هذه المشكلة.
index_eproxy=لا يمكن استخدام وحدة المحطة الطرفية عند الوصول إلى Webmin عبر خادم Webmin آخر
acl_user=قم بتشغيل shell كمستخدم Unix
acl_sameuser=مثل تسجيل الدخول إلى Webmin
acl_sudoenforce=فرض امتيازات sudo فقط
+12
View File
@@ -0,0 +1,12 @@
index_title=Терминал
index_cpan=Модулът Perl <tt>$1</tt> липсва, но може да бъде <a href='$2'>инсталиран автоматично</a> с помощта на модула Perl Modules на Webmin.
index_epel=Препоръчително е първо да активирате хранилището на <a href='$1' target='_blank'>EPEL</a>.
index_suse=Пакетът Perl <tt>perl-IO-Tty</tt> липсва и трябва първо да се инсталира ръчно от хранилището <a href='$2' target='_blank'>devel:languages:perl</a>.
index_euser=Грешка: Потребител $1 не може да стартира обвивката, тъй като тя не съществува!
index_connecting=Свързване ..
index_missing=Модулът на Perl <tt>$1</tt> липсва. Трябва да се свържете с вашия администратор, за да разрешите този проблем.
index_eproxy=Терминалният модул не може да се използва при достъп до Webmin през друг Webmin сървър
acl_user=Стартирайте shell като Unix потребител
acl_sameuser=Същото като влизане в Webmin
acl_sudoenforce=Наложете привилегии само за <em>sudo</em>
+12
View File
@@ -0,0 +1,12 @@
index_title=Terminal
index_cpan=Falta el mòdul de Perl <tt>$1</tt>, però es pot <a href='$2'>instal·lar automàticament</a> mitjançant el mòdul de mòduls de Perl de Webmin.
index_epel=Es recomana tenir activat primer el repositori <a href='$1' target='_blank'>EPEL</a>.
index_suse=Falta el paquet Perl <tt>perl-IO-Tty</tt> i primer s'ha d'instal·lar manualment des del repositori <a href='$2' target='_blank'>devel:languages:perl</a>.
index_euser=Error: l'usuari $1 no pot executar l'intèrpret d'ordres perquè no existeix!
index_connecting=Connectant ..
index_missing=Falta el mòdul Perl <tt>$1</tt>. Heu de contactar amb el vostre administrador per resoldre aquest problema.
index_eproxy=El mòdul Terminal no es pot utilitzar quan s'accedeix a Webmin mitjançant un altre servidor Webmin
acl_user=Executeu shell com a usuari Unix
acl_sameuser=Igual que l'inici de sessió de Webmin
acl_sudoenforce=Aplica els privilegis només de <em>sudo</em>
+12
View File
@@ -0,0 +1,12 @@
index_title=Terminál
index_cpan=Modul Perl <tt>$1</tt> chybí, ale lze jej <a href='$2'>nainstalovat automaticky</a> pomocí modulu Perl Modules od Webminu.
index_epel=Nejprve se doporučuje mít povoleno úložiště <a href='$1' target='_blank'>EPEL</a>.
index_suse=Balíček Perl <tt>perl-IO-Tty</tt> chybí a měl by být nejprve ručně nainstalován z úložiště <a href='$2' target='_blank'>devel:languages:perl</a>.
index_euser=Chyba: Uživatel $1 nemůže spustit shell, protože neexistuje!
index_connecting=Připojování ..
index_missing=Chybí modul Perl <tt>$1</tt>. Chcete-li tento problém vyřešit, musíte se obrátit na správce.
index_eproxy=Modul Terminál nelze použít při přístupu k Webminu přes jiný Webmin server
acl_user=Spusťte shell jako uživatel Unixu
acl_sameuser=Stejné jako přihlášení do Webminu
acl_sudoenforce=Vynutit pouze oprávnění <em>sudo</em>
+12
View File
@@ -0,0 +1,12 @@
index_title=Terminal
index_cpan=Perl-modulet <tt>$1</tt> mangler, men kan <a href='$2'>installeres automatisk</a> ved hjælp af Webmins Perl Modules-modul.
index_epel=Det anbefales at have <a href='$1' target='_blank'>EPEL</a>-lageret aktiveret først.
index_suse=Perl-pakken <tt>perl-IO-Tty</tt> mangler og bør først installeres manuelt fra <a href='$2' target='_blank'>devel:languages:perl</a>-lageret.
index_euser=Fejl: Bruger $1 kan ikke køre skallen, da den ikke eksisterer!
index_connecting=Tilslutning ..
index_missing=Perl-modulet <tt>$1</tt> mangler. Du skal kontakte din administrator for at løse dette problem.
index_eproxy=Terminalmodulet kan ikke bruges ved adgang til Webmin via en anden Webmin-server
acl_user=Kør shell som Unix-bruger
acl_sameuser=Samme som Webmin login
acl_sudoenforce=Håndhæv kun <em>sudo</em>-privilegier
+12
View File
@@ -0,0 +1,12 @@
index_title=Terminal
index_cpan=Das Perl-Modul <tt>$1</tt> fehlt, kann jedoch über das Perl-Module-Modul von Webmin <a href='$2'>automatisch installiert</a> werden.
index_epel=Es wird empfohlen, zuerst das <a href='$1' target='_blank'>EPEL</a>-Repository zu aktivieren.
index_suse=Das Perl-Paket <tt>perl-IO-Tty</tt> fehlt und sollte zunächst manuell aus dem <a href='$2' target='_blank'>devel:languages:perl</a>-Repository installiert werden.
index_euser=Fehler: Benutzer $1 kann die Shell nicht ausführen, da er nicht existiert!
index_connecting=Verbindung wird hergestellt...
index_missing=Das Perl-Modul <tt>$1</tt> fehlt. Sie müssen Ihren Administrator kontaktieren, um dieses Problem zu beheben.
index_eproxy=Das Terminal-Modul kann nicht verwendet werden, wenn auf Webmin über einen anderen Webmin-Server zugegriffen wird.
acl_user=Shell als Unix-Benutzer ausführen
acl_sameuser=Gleich wie Webmin-Anmeldung
acl_sudoenforce=<em>sudo</em>-exklusive Berechtigungen durchsetzen
+12
View File
@@ -0,0 +1,12 @@
index_title=Τερματικό
index_cpan=Η λειτουργική μονάδα Perl <tt>$1</tt> λείπει, αλλά μπορεί να <a href='$2'>εγκατασταθεί αυτόματα</a> χρησιμοποιώντας τη λειτουργική μονάδα Perl του Webmin.
index_epel=Συνιστάται να έχετε ενεργοποιήσει πρώτα το αποθετήριο <a href='$1' target='_blank'>EPEL</a>.
index_suse=Το πακέτο Perl <tt>perl-IO-Tty</tt> λείπει και θα πρέπει να εγκατασταθεί με μη αυτόματο τρόπο πρώτα από το αποθετήριο <a href='$2' target='_blank'>devel:languages:perl</a>.
index_euser=Σφάλμα : Ο χρήστης $1 δεν μπορεί να εκτελέσει το κέλυφος καθώς δεν υπάρχει!
index_connecting=Σύνδεση ..
index_missing=Η λειτουργική μονάδα Perl <tt>$1</tt> λείπει. Πρέπει να επικοινωνήσετε με τον διαχειριστή σας για να επιλύσετε αυτό το ζήτημα.
index_eproxy=Η λειτουργική μονάδα Terminal δεν μπορεί να χρησιμοποιηθεί κατά την πρόσβαση στο Webmin μέσω άλλου διακομιστή Webmin
acl_user=Εκτελέστε το κέλυφος ως χρήστης Unix
acl_sameuser=Το ίδιο με τη σύνδεση στο Webmin
acl_sudoenforce=Επιβολή προνομίων μόνο <em>sudo</em>
+14
View File
@@ -0,0 +1,14 @@
index_title=Terminal
index_cpan=The Perl module <tt>$1</tt> is missing, but can be <a href='$2'>installed automatically</a> using Webmin's Perl Modules module.
index_epel=It is recommended to have <a href='$1' target='_blank'>EPEL</a> repository enabled first.
index_suse=The Perl package <tt>perl-IO-Tty</tt> is missing, and should be manually installed first from <a href='$2' target='_blank'>devel:languages:perl</a> repository.
index_euser=Error : User $1 cannot run the shell as it does not exist!
index_connecting=Connecting ..
index_missing=The Perl module <tt>$1</tt> is missing. You need to contact your administrator to resolve this issue.
index_eproxy=The Terminal module cannot be used when accessing Webmin via another Webmin server
acl_user=Run shell as Unix user
acl_sameuser=Same as Webmin login
acl_sudoenforce=Enforce <em>sudo</em>-only privileges
__norefs=1
+12
View File
@@ -0,0 +1,12 @@
index_title=Terminal
index_cpan=Falta el módulo Perl <tt>$1</tt>, pero se puede <a href='$2'>instalar automáticamente</a> usando el módulo Perl Modules de Webmin.
index_epel=Se recomienda tener habilitado el repositorio <a href='$1' target='_blank'>EPEL</a> primero.
index_suse=Falta el paquete Perl <tt>perl-IO-Tty</tt>, y debe instalarse manualmente primero desde el repositorio <a href='$2' target='_blank'>devel:languages:perl</a>.
index_euser=Error: ¡El usuario $1 no puede ejecutar el shell porque no existe!
index_connecting=Conectando ..
index_missing=Falta el módulo Perl <tt>$1</tt>. Debe ponerse en contacto con su administrador para resolver este problema.
index_eproxy=El módulo Terminal no se puede utilizar al acceder a Webmin a través de otro servidor Webmin
acl_user=Ejecutar shell como usuario de Unix
acl_sameuser=Igual que el inicio de sesión de Webmin
acl_sudoenforce=Aplicar privilegios exclusivos de <em>sudo</em>
+12
View File
@@ -0,0 +1,12 @@
index_title=Terminala
index_cpan=<tt>$1</tt> Perl modulua falta da, baina <a href='$2'>automatikoki instalatu</a> daiteke Webmin-en Perl Modules modulua erabiliz.
index_epel=Lehenengo <a href='$1' target='_blank'>EPEL</a> biltegia gaituta izatea gomendatzen da.
index_suse=Perl paketea <tt>perl-IO-Tty</tt> falta da, eta eskuz instalatu behar da lehenik <a href='$2' target='_blank'>devel:languages:perl</a> biltegitik.
index_euser=Errorea: $1 erabiltzaileak ezin du shell-a exekutatu, ez baitago!
index_connecting=Konektatzen ..
index_missing=Perl modulua <tt>$1</tt> falta da. Zure administratzailearekin harremanetan jarri behar duzu arazo hau konpontzeko.
index_eproxy=Terminal modulua ezin da erabili Webmin beste Webmin zerbitzari baten bidez sartzean
acl_user=Exekutatu shell Unix erabiltzaile gisa
acl_sameuser=Webmin-en saioaren berdina
acl_sudoenforce=Erabili <em>sudo</em> soilik pribilegioak
+12
View File
@@ -0,0 +1,12 @@
index_title=پایانه
index_cpan=ماژول Perl <tt>$1</tt> وجود ندارد، اما می توان آن را <a href='$2'>به طور خودکار نصب کرد</a> با استفاده از ماژول Perl's Perl.
index_epel=توصیه می شود ابتدا مخزن <a href='$1' target='_blank'>EPEL</a> را فعال کنید.
index_suse=بسته Perl <tt>perl-IO-Tty</tt> وجود ندارد و باید ابتدا به صورت دستی از مخزن <a href='$2' target='_blank'>devel:languages:perl</a> نصب شود.
index_euser=خطا : کاربر $1 نمی تواند پوسته را اجرا کند زیرا وجود ندارد!
index_connecting=برقراری ارتباط ..
index_missing=ماژول پرل <tt>$1</tt> وجود ندارد. برای حل این مشکل باید با سرپرست خود تماس بگیرید.
index_eproxy=هنگام دسترسی به وبمین از طریق سرور وبمین دیگر نمی توان از ماژول ترمینال استفاده کرد
acl_user=شل را به عنوان کاربر یونیکس اجرا کنید
acl_sameuser=مانند ورود به وبمین
acl_sudoenforce=امتیازات فقط <em>sudo</em> را اعمال کنید
+12
View File
@@ -0,0 +1,12 @@
index_title=Terminaali
index_cpan=Perl-moduuli <tt>$1</tt> puuttuu, mutta se voidaan <a href='$2'>asentaa automaattisesti</a> Webminin Perl-moduulin avulla.
index_epel=On suositeltavaa ottaa <a href='$1' target='_blank'>EPEL</a>-arkisto käyttöön ensin.
index_suse=Perl-paketti <tt>perl-IO-Tty</tt> puuttuu, ja se tulee asentaa ensin manuaalisesti <a href='$2' target='_blank'>devel:languages:perl</a>-arkistosta.
index_euser=Virhe : Käyttäjä $1 ei voi suorittaa komentotulkkia, koska sitä ei ole olemassa!
index_connecting=Yhdistetään ..
index_missing=Perl-moduuli <tt>$1</tt> puuttuu. Sinun on otettava yhteyttä järjestelmänvalvojaasi tämän ongelman ratkaisemiseksi.
index_eproxy=Päätemoduulia ei voi käyttää käytettäessä Webminiä toisen Webmin-palvelimen kautta
acl_user=Suorita shell Unix-käyttäjänä
acl_sameuser=Sama kuin Webminin kirjautuminen
acl_sudoenforce=Pakota vain <em>sudo</em>-oikeudet
+12
View File
@@ -0,0 +1,12 @@
index_title=Terminal
index_cpan=Le module Perl <tt>$1</tt> est manquant, mais peut être <a href='$2'>installé automatiquement</a> à l'aide du module Modules Perl de Webmin.
index_epel=Il est recommandé d'avoir d'abord activé le référentiel <a href='$1' target='_blank'>EPEL</a>.
index_suse=Le package Perl <tt>perl-IO-Tty</tt> est manquant et doit d'abord être installé manuellement à partir du référentiel <a href='$2' target='_blank'>devel:languages:perl</a>.
index_euser=Erreur : l'utilisateur $1 ne peut pas exécuter le shell car il n'existe pas !
index_connecting=Connexion ..
index_missing=Le module Perl <tt>$1</tt> est manquant. Vous devez contacter votre administrateur pour résoudre ce problème.
index_eproxy=Le module Terminal ne peut pas être utilisé lors de l'accès à Webmin via un autre serveur Webmin
acl_user=Exécuter le shell en tant qu'utilisateur Unix
acl_sameuser=Identique à la connexion Webmin
acl_sudoenforce=Appliquer les privilèges réservés à <em>sudo</em>
+12
View File
@@ -0,0 +1,12 @@
index_title=Terminal
index_cpan=Nedostaje Perl modul <tt>$1</tt>, ali se može <a href='$2'>instalirati automatski</a> pomoću Webminovog modula Perl Modules.
index_epel=Preporuča se prvo omogućiti <a href='$1' target='_blank'>EPEL</a> repozitorij.
index_suse=Nedostaje Perl paket <tt>perl-IO-Tty</tt> i treba ga prvo ručno instalirati iz <a href='$2' target='_blank'>devel:languages:perl</a> repozitorija.
index_euser=Pogreška: Korisnik $1 ne može pokrenuti ljusku jer ona ne postoji!
index_connecting=Povezivanje ..
index_missing=Nedostaje Perl modul <tt>$1</tt>. Morate kontaktirati svog administratora kako biste riješili ovaj problem.
index_eproxy=Modul Terminal se ne može koristiti kada se Webminu pristupa preko drugog Webmin poslužitelja
acl_user=Pokrenite shell kao Unix korisnik
acl_sameuser=Isto kao prijava na Webmin
acl_sudoenforce=Nametnite privilegije samo <em>sudo</em>
+12
View File
@@ -0,0 +1,12 @@
index_title=Terminál
index_cpan=A <tt>$1</tt> Perl modul hiányzik, de <a href='$2'>automatikusan telepíthető</a> a Webmin Perl Modules moduljával.
index_epel=Javasoljuk, hogy először engedélyezze az <a href='$1' target='_blank'>EPEL</a> adattárat.
index_suse=A <tt>perl-IO-Tty</tt> Perl csomag hiányzik, ezért először manuálisan kell telepíteni a <a href='$2' target='_blank'>devel:languages:perl</a> tárolóból.
index_euser=Hiba: $1 felhasználó nem tudja futtatni a parancsértelmezőt, mert nem létezik!
index_connecting=Csatlakozás. ..
index_missing=A <tt>$1</tt> Perl modul hiányzik. A probléma megoldásához kapcsolatba kell lépnie a rendszergazdával.
index_eproxy=A terminálmodul nem használható, ha egy másik Webmin szerveren keresztül éri el a Webmin-t
acl_user=Futtassa a shell-t Unix felhasználóként
acl_sameuser=Ugyanaz, mint a Webmin bejelentkezésnél
acl_sudoenforce=Csak <em>sudo</em>-jogosultságok érvényesítése
+12
View File
@@ -0,0 +1,12 @@
index_title=terminale
index_cpan=Il modulo Perl <tt>$1</tt> manca, ma può essere <a href='$2'>installato automaticamente</a> utilizzando il modulo Perl Modules di Webmin.
index_epel=Si consiglia di abilitare prima il repository <a href='$1' target='_blank'>EPEL</a>.
index_suse=Il pacchetto Perl <tt>perl-IO-Tty</tt> è mancante e dovrebbe essere installato manualmente prima dal repository <a href='$2' target='_blank'>devel:languages:perl</a>.
index_euser=Errore: l'utente $1 non può eseguire la shell perché non esiste!
index_connecting=Collegamento ..
index_missing=Manca il modulo Perl <tt>$1</tt>. Devi contattare il tuo amministratore per risolvere questo problema.
index_eproxy=Il modulo Terminale non può essere utilizzato quando si accede a Webmin tramite un altro server Webmin
acl_user=Esegui shell come utente Unix
acl_sameuser=Come per l'accesso Webmin
acl_sudoenforce=Applica privilegi <em>sudo</em>-only
+12
View File
@@ -0,0 +1,12 @@
index_title=「ターミナル」
index_cpan=Perl モジュール <tt>$1</tt> がありませんが、Webmin の Perl モジュール モジュールを使用して<a href='$2'>自動的にインストール</a>できます。
index_epel=最初に <a href='$1' target='_blank'>EPEL</a> リポジトリを有効にすることをお勧めします。
index_suse=Perl パッケージ <tt>perl-IO-Tty</tt> がありません。最初に <a href='$2' target='_blank'>devel:languages:perl</a> リポジトリから手動でインストールする必要があります。
index_euser=エラー : ユーザー $1 はシェルが存在しないため実行できません!
index_connecting=接続中 ..
index_missing=Perl モジュール <tt>$1</tt> が見つかりません。この問題を解決するには、管理者に連絡する必要があります。
index_eproxy=別のWebminサーバー経由でWebminにアクセスする場合、ターミナルモジュールは使用できません。
acl_user=Unix ユーザーとしてシェルを実行する
acl_sameuser=Webmin ログインと同じ
acl_sudoenforce=<em>sudo</em> のみの権限を強制する
+12
View File
@@ -0,0 +1,12 @@
index_title=단말기
index_cpan=Perl 모듈 <tt>$1</tt>이(가) 누락되었지만 Webmin의 Perl 모듈 모듈을 사용하여 <a href='$2'>자동으로 설치</a>할 수 있습니다.
index_epel=먼저 <a href='$1' target='_blank'>EPEL</a> 저장소를 활성화하는 것이 좋습니다.
index_suse=Perl 패키지 <tt>perl-IO-Tty</tt>가 없습니다. 먼저 <a href='$2' target='_blank'>devel:languages:perl</a> 저장소에서 수동으로 설치해야 합니다.
index_euser=오류: 사용자 $1은(는) 쉘이 존재하지 않으므로 실행할 수 없습니다!
index_connecting=연결 중 ..
index_missing=Perl 모듈 <tt>$1</tt>이(가) 없습니다. 이 문제를 해결하려면 관리자에게 문의해야 합니다.
index_eproxy=다른 Webmin 서버를 통해 Webmin에 접속하는 경우 Terminal 모듈을 사용할 수 없습니다
acl_user=Unix 사용자로 쉘 실행
acl_sameuser=Webmin 로그인과 동일
acl_sudoenforce=<em>sudo</em> 전용 권한 적용
+12
View File
@@ -0,0 +1,12 @@
index_title=Terminal
index_cpan=Modul Perl <tt>$1</tt> tiada, tetapi boleh <a href='$2'>dipasang secara automatik</a> menggunakan modul Modul Perl Webmin.
index_epel=Adalah disyorkan untuk mendayakan repositori <a href='$1' target='_blank'>EPEL</a> dahulu.
index_suse=Pakej Perl <tt>perl-IO-Tty</tt> tiada dan harus dipasang secara manual terlebih dahulu daripada repositori <a href='$2' target='_blank'>devel:languages:perl</a>.
index_euser=Ralat : Pengguna $1 tidak boleh menjalankan shell kerana ia tidak wujud!
index_connecting=Menyambung ..
index_missing=Modul Perl <tt>$1</tt> tiada. Anda perlu menghubungi pentadbir anda untuk menyelesaikan isu ini.
index_eproxy=Modul Terminal tidak boleh digunakan apabila mengakses Webmin melalui pelayan Webmin yang lain
acl_user=Jalankan shell sebagai pengguna Unix
acl_sameuser=Sama seperti log masuk Webmin
acl_sudoenforce=Kuatkuasakan <em>sudo</em>-keistimewaan sahaja
+12
View File
@@ -0,0 +1,12 @@
index_title=Terminal
index_cpan=De Perl-module <tt>$1</tt> ontbreekt, maar kan <a href='$2'>automatisch</a> worden geïnstalleerd met behulp van Webmin's Perl Modules-module.
index_epel=Het wordt aanbevolen om eerst de <a href='$1' target='_blank'>EPEL</a>-repository ingeschakeld te hebben.
index_suse=Het Perl-pakket <tt>perl-IO-Tty</tt> ontbreekt en moet eerst handmatig worden geïnstalleerd vanuit de <a href='$2' target='_blank'>devel:languages:perl</a>-repository.
index_euser=Fout: Gebruiker $1 kan de shell niet uitvoeren omdat deze niet bestaat!
index_connecting=Verbinden ..
index_missing=De Perl-module <tt>$1</tt> ontbreekt. U moet contact opnemen met uw beheerder om dit probleem op te lossen.
index_eproxy=De Terminal-module kan niet worden gebruikt bij toegang tot Webmin via een andere Webmin-server
acl_user=Voer shell uit als Unix-gebruiker
acl_sameuser=Hetzelfde als inloggen bij Webmin
acl_sudoenforce=<em>sudo</em>-only privileges afdwingen
+12
View File
@@ -0,0 +1,12 @@
index_title=Terminal
index_cpan=Perl-modulen <tt>$1</tt> mangler, men kan <a href='$2'>installeres automatisk</a> ved hjelp av Webmins Perl Modules-modul.
index_epel=Det anbefales å ha <a href='$1' target='_blank'>EPEL</a>-depotet aktivert først.
index_suse=Perl-pakken <tt>perl-IO-Tty</tt> mangler, og bør først installeres manuelt fra <a href='$2' target='_blank'>devel:languages:perl</a>-depotet.
index_euser=Feil: Bruker $1 kan ikke kjøre skallet siden det ikke eksisterer!
index_connecting=Kobler til ..
index_missing=Perl-modulen <tt>$1</tt> mangler. Du må kontakte administratoren din for å løse dette problemet.
index_eproxy=Terminalmodulen kan ikke brukes når du får tilgang til Webmin via en annen Webmin-server
acl_user=Kjør shell som Unix-bruker
acl_sameuser=Samme som Webmin-pålogging
acl_sudoenforce=Håndhev kun <em>sudo</em>-privilegier
+12
View File
@@ -0,0 +1,12 @@
index_title=Terminal
index_cpan=Brakuje modułu Perla <tt>$1</tt>, ale można go <a href='$2'>zainstalować automatycznie</a> przy użyciu modułu modułów Perla Webmina.
index_epel=Zaleca się, aby najpierw włączyć repozytorium <a href='$1' target='_blank'>EPEL</a>.
index_suse=Brakuje pakietu Perl <tt>perl-IO-Tty</tt> i należy go najpierw ręcznie zainstalować z repozytorium <a href='$2' target='_blank'>devel:languages:perl</a>.
index_euser=Błąd: Użytkownik $1 nie może uruchomić powłoki, ponieważ ona nie istnieje!
index_connecting=Podłączanie ..
index_missing=Brak modułu Perla <tt>$1</tt>. Aby rozwiązać ten problem, musisz skontaktować się z administratorem.
index_eproxy=Modułu terminala nie można używać podczas uzyskiwania dostępu do Webmin za pośrednictwem innego serwera Webmin
acl_user=Uruchom powłokę jako użytkownik Unix
acl_sameuser=Tak samo jak logowanie do Webmina
acl_sudoenforce=Wymuś uprawnienia tylko <em>sudo</em>
+12
View File
@@ -0,0 +1,12 @@
index_title=terminal
index_cpan=O módulo Perl <tt>$1</tt> está faltando, mas pode ser <a href='$2'>instalado automaticamente</a> usando o módulo Perl Modules do Webmin.
index_epel=Recomenda-se ter o repositório <a href='$1' target='_blank'>EPEL</a> ativado primeiro.
index_suse=O pacote Perl <tt>perl-IO-Tty</tt> está ausente e deve ser instalado manualmente primeiro a partir do repositório <a href='$2' target='_blank'>devel:languages:perl</a>.
index_euser=Erro: O usuário $1 não pode executar o shell porque ele não existe!
index_connecting=Conectando ..
index_missing=O módulo Perl <tt>$1</tt> está ausente. Você precisa entrar em contato com o administrador para resolver esse problema.
index_eproxy=O módulo Terminal não pode ser usado ao acessar o Webmin por meio de outro servidor Webmin
acl_user=Executar shell como usuário Unix
acl_sameuser=Igual ao login do Webmin
acl_sudoenforce=Aplicar privilégios somente <em>sudo</em>
+12
View File
@@ -0,0 +1,12 @@
index_title=terminal
index_cpan=O módulo Perl <tt>$1</tt> está faltando, mas pode ser <a href='$2'>instalado automaticamente</a> usando o módulo Perl Modules do Webmin.
index_epel=Recomenda-se ter o repositório <a href='$1' target='_blank'>EPEL</a> ativado primeiro.
index_suse=O pacote Perl <tt>perl-IO-Tty</tt> está ausente e deve ser instalado manualmente primeiro a partir do repositório <a href='$2' target='_blank'>devel:languages:perl</a>.
index_euser=Erro: O usuário $1 não pode executar o shell porque ele não existe!
index_connecting=Conectando ..
index_missing=O módulo Perl <tt>$1</tt> está ausente. Você precisa entrar em contato com o administrador para resolver esse problema.
index_eproxy=O módulo Terminal não pode ser usado ao acessar o Webmin por meio de outro servidor Webmin
acl_user=Executar shell como usuário Unix
acl_sameuser=Igual ao login do Webmin
acl_sudoenforce=Aplicar privilégios somente <em>sudo</em>
+12
View File
@@ -0,0 +1,12 @@
index_title=Терминал
index_cpan=Модуль Perl <tt>$1</tt> отсутствует, но может быть <a href='$2'>установлен автоматически</a> с помощью модуля Webmin Perl Modules.
index_epel=Рекомендуется сначала включить репозиторий <a href='$1' target='_blank'>EPEL</a>.
index_suse=Пакет Perl <tt>perl-IO-Tty</tt> отсутствует и должен быть сначала установлен вручную из репозитория <a href='$2' target='_blank'>devel:languages:perl</a>.
index_euser=Ошибка: пользователь $1 не может запустить оболочку, так как ее не существует!
index_connecting=Подключение ..
index_missing=Модуль Perl <tt>$1</tt> отсутствует. Вам необходимо связаться с вашим администратором, чтобы решить эту проблему.
index_eproxy=Модуль терминала не может использоваться при доступе к Webmin через другой сервер Webmin
acl_user=Запустить оболочку от имени пользователя Unix
acl_sameuser=То же, что и вход в Webmin
acl_sudoenforce=Обеспечить привилегии только для <em>sudo</em>
+12
View File
@@ -0,0 +1,12 @@
index_title=Terminál
index_cpan=Modul Perl <tt>$1</tt> chýba, ale dá sa <a href='$2'>nainštalovať automaticky</a> pomocou modulu Perl Modules od Webmin.
index_epel=Najprv sa odporúča mať povolený <a href='$1' target='_blank'>EPEL</a> úložisko.
index_suse=Balík Perl <tt>perl-IO-Tty</tt> chýba a mal by byť najprv manuálne nainštalovaný z úložiska <a href='$2' target='_blank'>devel:languages:perl</a>.
index_euser=Chyba: Používateľ $1 nemôže spustiť shell, pretože neexistuje!
index_connecting=Pripája sa ..
index_missing=Chýba modul Perl <tt>$1</tt>. Ak chcete tento problém vyriešiť, musíte sa obrátiť na správcu.
index_eproxy=Modul Terminál nie je možné použiť pri prístupe k Webmin cez iný Webmin server
acl_user=Spustite shell ako používateľ Unixu
acl_sameuser=Rovnaké ako prihlásenie Webmin
acl_sudoenforce=Vynútiť iba oprávnenia <em>sudo</em>
+12
View File
@@ -0,0 +1,12 @@
index_title=Terminal
index_cpan=Perl-modulen <tt>$1</tt> saknas, men kan <a href='$2'>installeras automatiskt</a> med Webmins Perl Modules-modul.
index_epel=Det rekommenderas att ha <a href='$1' target='_blank'>EPEL</a>-förrådet aktiverat först.
index_suse=Perl-paketet <tt>perl-IO-Tty</tt> saknas och bör installeras manuellt först från <a href='$2' target='_blank'>devel:languages:perl</a>-förrådet.
index_euser=Fel: Användaren $1 kan inte köra skalet eftersom det inte finns!
index_connecting=Ansluter ..
index_missing=Perl-modulen <tt>$1</tt> saknas. Du måste kontakta din administratör för att lösa problemet.
index_eproxy=Terminalmodulen kan inte användas vid åtkomst till Webmin via en annan Webmin-server
acl_user=Kör skal som Unix-användare
acl_sameuser=Samma som Webmin-inloggning
acl_sudoenforce=Framtvinga endast <em>sudo</em>-privilegier
+12
View File
@@ -0,0 +1,12 @@
index_title=terminal
index_cpan=Perl modülü <tt>$1</tt> eksik, ancak Webmin'in Perl Modülleri modülü kullanılarak <a href='$2'>otomatik olarak kurulabilir</a>.
index_epel=Önce <a href='$1' target='_blank'>EPEL</a> deposunun etkinleştirilmesi önerilir.
index_suse=<tt>Perl-IO-Tty</tt> Perl paketi eksik ve önce <a href='$2' target='_blank'>devel:languages:Perl</a> deposundan manuel olarak yüklenmelidir.
index_euser=Hata : $1 kullanıcısı, mevcut olmadığı için kabuğu çalıştıramıyor!
index_connecting=Bağlanıyor ..
index_missing=<tt>$1</tt> Perl modülü eksik. Bu sorunu çözmek için yöneticinizle iletişime geçmeniz gerekir.
index_eproxy=Başka bir Webmin sunucusu üzerinden Webmin'e erişirken Terminal modülü kullanılamaz
acl_user=Shell'i Unix kullanıcısı olarak çalıştırın
acl_sameuser=Webmin oturum açma ile aynı
acl_sudoenforce=<em>sudo</em>-sadece ayrıcalıklarını uygula
+12
View File
@@ -0,0 +1,12 @@
index_title=Термінал
index_cpan=Модуль Perl <tt>$1</tt> відсутній, але його можна <a href='$2'>встановити автоматично</a> за допомогою модуля Perl Modules Webmin.
index_epel=Рекомендовано спочатку ввімкнути репозиторій <a href='$1' target='_blank'>EPEL</a>.
index_suse=Пакет Perl <tt>perl-IO-Tty</tt> відсутній, його слід спочатку встановити вручну зі сховища <a href='$2' target='_blank'>devel:languages:perl</a>.
index_euser=Помилка: користувач $1 не може запустити оболонку, оскільки її не існує!
index_connecting=Підключення ..
index_missing=Модуль Perl <tt>$1</tt> відсутній. Вам потрібно зв’язатися з адміністратором, щоб вирішити цю проблему.
index_eproxy=Модуль терміналу не можна використовувати під час доступу до Webmin через інший сервер Webmin
acl_user=Запустіть оболонку від імені користувача Unix
acl_sameuser=Те саме, що і вхід у Webmin
acl_sudoenforce=Примусово використовувати привілеї лише для <em>sudo</em>
+12
View File
@@ -0,0 +1,12 @@
index_title=终端
index_cpan=缺少 Perl 模块 <tt>$1</tt>,但可以使用 Webmin 的 Perl 模块模块<a href='$2'>自动安装</a>。
index_epel=建议首先启用 <a href='$1' target='_blank'>EPEL</a> 存储库。
index_suse=缺少 Perl 包 <tt>perl-IO-Tty</tt>,应首先从 <a href='$2' target='_blank'>devel:languages:perl</a> 存储库手动安装。
index_euser=错误:用户 $1 无法运行 shell,因为它不存在!
index_connecting=连接 ..
index_missing=Perl 模块 <tt>$1</tt> 丢失。您需要联系您的管理员来解决此问题。
index_eproxy=通过另一个 Webmin 服务器访问 Webmin 时无法使用终端模块
acl_user=以 Unix 用户身份运行 shell
acl_sameuser=与 Webmin 登录相同
acl_sudoenforce=强制仅使用 <em>sudo</em> 权限
+12
View File
@@ -0,0 +1,12 @@
index_title=終端
index_cpan=缺少 Perl 模塊 <tt>$1</tt>,但可以使用 Webmin 的 Perl 模塊模塊<a href='$2'>自動安裝</a>。
index_epel=建議首先啟用 <a href='$1' target='_blank'>EPEL</a> 存儲庫。
index_suse=缺少 Perl 包 <tt>perl-IO-Tty</tt>,應首先從 <a href='$2' target='_blank'>devel:languages:perl</a> 存儲庫手動安裝。
index_euser=錯誤:用戶 $1 無法運行 shell,因為它不存在!
index_connecting=連接 ..
index_missing=Perl 模塊 <tt>$1</tt> 丟失。您需要聯繫您的管理員來解決此問題。
index_eproxy=透過另一個 Webmin 伺服器存取 Webmin 時無法使用終端模組
acl_user=以 Unix 用戶身份運行 shell
acl_sameuser=與 Webmin 登錄相同
acl_sudoenforce=強制僅使用 <em>sudo</em> 權限
+4
View File
@@ -0,0 +1,4 @@
desc=Terminal
name=xterm
longdesc=Access the shell on your system without the need for a separate SSH client, using Xterm.js over Webmin WebSockets
websockets=1
+69
View File
@@ -0,0 +1,69 @@
# don't put duplicate lines in the history
HISTCONTROL=ignoredups:ignorespace
# append to the history file, don't overwrite it
shopt -s histappend
# for setting history length see HISTSIZE and HISTFILESIZE
HISTSIZE=1000
HISTFILESIZE=2000
# check the window size after each command and, if necessary,
# update the values of LINES and COLUMNS.
shopt -s checkwinsize
# enable color support of ls and also add handy aliases
if [ -x /usr/bin/dircolors ]; then
alias ls='ls --color=auto'
alias dir='dir --color=auto'
alias vdir='vdir --color=auto'
alias grep='grep --color=auto'
alias fgrep='fgrep --color=auto'
alias egrep='egrep --color=auto'
alias ip='ip --color=auto'
fi
# custom PS1
if [ "$(id -u)" -eq 0 ]; then
PS1='\[\033[1m\033[38;5;196m\]\u\[\033[1;39m\]@\[\033[38;5;82m\]\h:\[\033[38;5;213m\]\w\[\033[1;37m\]\$\[\033[0m\] '
else
PS1='\[\033[1m\033[38;5;208m\]\u\[\033[1;39m\]@\[\033[38;5;82m\]\h:\[\033[38;5;213m\]\w\[\033[1;37m\]\$\[\033[0m\] '
fi
# user default run time config
if [ -f ~/.bashrc ]; then
. ~/.bashrc
fi
# user default profile config
if [ -f ~/.bash_profile ]; then
. ~/.bash_profile
elif [ -f ~/.profile ]; then
. ~/.profile
fi
# user specific aliases and functions
if [ -d ~/.bashrc.d ]; then
for rc in ~/.bashrc.d/*; do
if [ -f "$rc" ]; then
. "$rc"
fi
done
fi
unset rc
# set XDG_RUNTIME_DIR and DBUS_SESSION_BUS_ADDRESS if not set
if [ -z "$XDG_RUNTIME_DIR" ]; then
uid=$(id -u)
xdg_runtime_dir="/run/user/$uid"
if [ -d "$xdg_runtime_dir" ] && [ -O "$xdg_runtime_dir" ] && \
[ -S "$xdg_runtime_dir/bus" ]; then
export XDG_RUNTIME_DIR="$xdg_runtime_dir"
export DBUS_SESSION_BUS_ADDRESS="unix:path=$XDG_RUNTIME_DIR/bus"
fi
unset uid xdg_runtime_dir
fi
+38
View File
@@ -0,0 +1,38 @@
# don't put duplicate lines in the history
HISTCONTROL=ignoredups:ignorespace
# for setting history length see HISTSIZE and HISTFILESIZE in bash(1)
HISTSIZE=1000
HISTFILESIZE=2000
# custom PS1
PS1='%F{magenta}%n%f@%F{green}%m%f:%1~%# '
# user default env config
if [ -f ~/.zshenv ]; then
. ~/.zshenv
fi
# user default profile config
if [ -f ~/.zprofile ]; then
. ~/.zprofile
fi
# user default run time config
if [ -f ~/.zshrc ]; then
. ~/.zshrc
fi
# set XDG_RUNTIME_DIR and DBUS_SESSION_BUS_ADDRESS if not set
if [ -z "$XDG_RUNTIME_DIR" ]; then
uid=$(id -u)
xdg_runtime_dir="/run/user/$uid"
if [ -d "$xdg_runtime_dir" ] && [ -O "$xdg_runtime_dir" ] && \
[ -S "$xdg_runtime_dir/bus" ]; then
export XDG_RUNTIME_DIR="$xdg_runtime_dir"
export DBUS_SESSION_BUS_ADDRESS="unix:path=$XDG_RUNTIME_DIR/bus"
fi
unset uid xdg_runtime_dir
fi
+2
View File
@@ -0,0 +1,2 @@
user=*
noconfig=1
+204
View File
@@ -0,0 +1,204 @@
#!/usr/local/bin/perl
# Start a websocket server connected to a shell
use strict;
use warnings;
no warnings 'uninitialized';
no warnings 'once';
use lib ("$ENV{'PERLLIB'}/vendor_perl");
use Net::WebSocket::Server;
use IO::Socket::INET;
use utf8;
require './xterm-lib.pl'; ## no critic
our (%config, $module_name, $module_root_directory);
my ($port, $user, $dir) = @ARGV;
# Switch to the user we're running as
my @uinfo = getpwnam($user);
my ($uid, $gid);
if ($user ne "root" && !$<) {
if (!@uinfo) {
remove_miniserv_websocket($port, $module_name);
die "User $user does not exist!";
}
$uid = $uinfo[2];
$gid = $uinfo[3];
}
else {
$uid = $gid = 0;
}
# Run the user's shell in a sub-process
foreign_require("proc");
clean_environment();
# Set locale
my $lang = $config{'locale'};
if ($lang) {
my @opts = ('LC_ALL', 'LANG', 'LANGUAGE');
$lang = 'en_US.UTF-8' if ($lang == 1);
foreach my $opt (@opts) {
$ENV{$opt} = trim($lang);
}
}
# Set terminal
$ENV{'USER'} = $user;
my $config_xterm = $config{'xterm'};
$config_xterm = 'xterm-256color'
if (!$config_xterm);
$config_xterm =~ s/\+/-/;
$ENV{'TERM'} = $config_xterm;
$ENV{'HOME'} = $uinfo[7];
chdir($dir || $uinfo[7] || "/");
my $shellcmd = $uinfo[8];
my $shellname = $shellcmd;
$shellname =~ s/^.*\///;
my $shellexec = $shellcmd;
my $shelllogin = "-".$shellname;
# Check for initialization file
if ($config{'rcfile'} ne '0') {
# Load shell init default file from module root directory or custom file
my $rcdir = "$module_root_directory/rc";
my $rcfile = $config{'rcfile'} eq '1' ?
"$rcdir/.".$shellname."rc" :
$config{'rcfile'};
if ($rcfile =~ /^\~\//) {
$rcfile =~ s/^\~\///;
$rcfile = "$uinfo[7]/$rcfile";
}
if (-r $rcfile) {
if ($shellname eq 'bash') {
# Bash
$shellexec = "$shellcmd --rcfile $rcfile";
}
elsif ($shellname eq 'zsh') {
# Zsh
$ENV{'ZDOTDIR'} = $rcdir;
}
# Cannot use login shell while passing other parameters,
# and it is not necessary as we already add init files
$shelllogin = undef;
}
}
my ($shellfh, $pid) = proc::pty_process_exec(
$shellexec, $uid, $gid, $shelllogin, 1);
reset_environment();
my $shcmd = "'$shellexec".($shelllogin ? " $shelllogin" : "")."'";
if (!$pid) {
remove_miniserv_websocket($port, $module_name);
die "Failed to run shell with $shcmd\n";
}
else {
error_stderr("Running shell $shcmd for user $user with pid $pid");
}
# Detach from controlling terminal
if (fork()) {
exit(0);
}
untie(*STDIN);
close(STDIN);
# Clean up when socket is terminated
$SIG{'ALRM'} = sub {
remove_miniserv_websocket($port, $module_name);
die "timeout waiting for connection";
};
alarm(60);
error_stderr("Listening on port $port");
my ($wsconn, $shellbuf);
my $server_socket = IO::Socket::INET->new(
Listen => 5,
LocalAddr => '127.0.0.1',
LocalPort => $port,
Proto => 'tcp',
ReuseAddr => 1,
);
$server_socket || die "failed to listen on port $port";
Net::WebSocket::Server->new(
listen => $server_socket,
on_connect => sub {
my ($serv, $conn) = @_;
error_stderr("WebSocket connection established");
if ($wsconn) {
error_stderr("Unexpected second connection to the same port");
$conn->disconnect();
return;
}
$wsconn = $conn;
alarm(0);
$conn->on(
handshake => sub {
# Is the key valid for this Webmin session?
my ($conn, $handshake) = @_;
my $key = $handshake->req->fields->{'sec-websocket-key'};
if (!verify_websocket_key($key, $main::session_id)) {
# Don't log the key or session ID — both are
# session-bearing secrets and the log file may
# be readable post-mortem.
error_stderr("WebSocket key does not match session ID");
$conn->disconnect();
}
},
ready => sub {
my ($conn) = @_;
$conn->send_binary($shellbuf) if ($shellbuf);
},
utf8 => sub {
my ($conn, $msg) = @_;
utf8::encode($msg) if (utf8::is_utf8($msg));
my ($rows, $cols) = parse_resize_message($msg);
if (defined($rows)) {
error_stderr("Got resize to $rows $cols");
eval {
$shellfh->set_winsize($rows, $cols);
};
# If failed make ioctl directly (TIOCSWINSZ)
# https://manpages.ubuntu.com/manpages/man2/ioctl_list.2.html
if ($@) {
ioctl($shellfh, 0x00005414, pack("s2", $rows, $cols));
}
kill('WINCH', $pid);
return;
}
if (!syswrite($shellfh, $msg, length($msg))) {
error_stderr("Write to shell failed : $!");
remove_miniserv_websocket($port, $module_name);
exit(1);
}
},
disconnect => sub {
error_stderr("WebSocket connection closed");
remove_miniserv_websocket($port, $module_name);
kill('KILL', $pid) if ($pid);
exit(0);
}
);
},
watch_readable => [
$shellfh => sub {
# Got something from the shell
my $buf;
my $ok = sysread($shellfh, $buf, 1024);
if ($ok <= 0) {
error_stderr("End of output from shell");
remove_miniserv_websocket($port, $module_name);
exit(0);
}
if ($wsconn) {
$wsconn->send_binary($buf);
}
else {
$shellbuf .= $buf;
}
},
],
)->start;
error_stderr("Exited WebSocket server");
remove_miniserv_websocket($port, $module_name);
cleanup_miniserv_websockets([$port], $module_name);
+60
View File
@@ -0,0 +1,60 @@
#!/usr/bin/perl
use strict;
use warnings;
use Test::More;
BEGIN {
eval { require Perl::Critic; 1 }
or plan skip_all => 'Perl::Critic not installed';
}
use File::Find;
sub script_dir
{
my $path = $0;
if ($path =~ m{^/}) {
$path =~ s{/[^/]+$}{};
return $path;
}
my $cwd = `pwd`;
chomp($cwd);
if ($path =~ m{/}) {
$path =~ s{/[^/]+$}{};
return $cwd.'/'.$path;
}
return $cwd;
}
my $bindir = script_dir();
my $module_dir = "$bindir/..";
chdir($module_dir) or die "chdir: $!";
my @files;
find(
sub {
return if -d;
return unless /\.(pl|cgi)\z/;
push(@files, $File::Find::name);
},
'.'
);
@files = sort @files;
if (!@files) {
plan skip_all => 'no perl files to check';
}
my $critic = Perl::Critic->new(
-profile => "$bindir/../../.perlcriticrc",
);
foreach my $file (@files) {
my @violations = $critic->critique($file);
is(scalar @violations, 0, "$file perlcritic");
if (@violations) {
diag join("", @violations);
}
}
done_testing();
+383
View File
@@ -0,0 +1,383 @@
#!/usr/bin/perl
# Functional tests for the xterm module.
#
# Loads xterm-lib.pl (and acl_security.pl) as libraries. shellserver.pl
# remains an executable entry point covered by compile tests; its pure
# helpers live in xterm-lib.pl so we can test them in isolation.
use strict;
use warnings;
use Test::More;
use Cwd qw(abs_path);
use File::Temp qw(tempdir);
# MIME::Base64 is loaded lazily inside the verify_websocket_key subtest;
# loading it before WebminCore triggers a prototype-mismatch warning when
# WebminCore re-declares encode_base64/decode_base64 with no prototype.
sub script_dir
{
my $path = $0;
if ($path =~ m{^/}) {
$path =~ s{/[^/]+$}{};
return $path;
}
my $cwd = `pwd`;
chomp($cwd);
if ($path =~ m{/}) {
$path =~ s{/[^/]+$}{};
return $cwd.'/'.$path;
}
return $cwd;
}
my $bindir = script_dir();
my $rootdir = abs_path("$bindir/../..") or die "rootdir: $!";
my $confdir = tempdir(CLEANUP => 1);
my $vardir = tempdir(CLEANUP => 1);
open(my $cfh, ">", "$confdir/config") or die "config: $!";
print $cfh "os_type=linux\nos_version=0\n";
close($cfh);
open(my $vfh, ">", "$confdir/var-path") or die "var-path: $!";
print $vfh "$vardir\n";
close($vfh);
$ENV{'WEBMIN_CONFIG'} = $confdir;
$ENV{'WEBMIN_VAR'} = $vardir;
$ENV{'FOREIGN_MODULE_NAME'} = 'xterm';
$ENV{'FOREIGN_ROOT_DIRECTORY'} = $rootdir;
chdir("$bindir/..") or die "chdir: $!";
# acl_security.pl requires xterm-lib.pl by path string. Load it once
# ourselves, then pre-populate %INC so the later require is a no-op and
# the test does not get duplicate-subroutine warnings. Test cwd is the
# module dir, so '.' is on the search path for do() to find it.
push @INC, '.';
{
my $file = "$bindir/../xterm-lib.pl";
do $file or die "load xterm-lib.pl: $@ $!";
$INC{$_} = $file for ('xterm-lib.pl', './xterm-lib.pl', $file);
}
require "./acl_security.pl";
our (%in, %access);
# config_pre_load —
# `size` only makes sense in old themes that don't drive a JS-side resize.
# Authentic (XMLHttpRequest) ships its own resize, so the option must be
# stripped from the config-editor listing in that branch and left alone
# otherwise. We exercise both paths plus the degenerate-arg cases that
# the production callsite can hand us.
subtest 'config_pre_load' => sub {
# XHR branch: size is removed from both the info hash and the order array.
{
local $ENV{'HTTP_X_REQUESTED_WITH'} = 'XMLHttpRequest';
my %info = (size => {}, fontsize => {}, locale => {});
my @order = qw(size fontsize locale);
config_pre_load(\%info, \@order);
ok(!exists $info{'size'}, 'XHR: size removed from info hash');
ok( exists $info{'fontsize'}, 'XHR: unrelated keys preserved');
is_deeply(\@order, [qw(fontsize locale)],
'XHR: size removed from order array');
}
# Non-XHR branch: nothing is touched.
{
local $ENV{'HTTP_X_REQUESTED_WITH'} = '';
my %info = (size => {}, fontsize => {});
my @order = qw(size fontsize);
config_pre_load(\%info, \@order);
ok(exists $info{'size'}, 'non-XHR: size preserved');
is_deeply(\@order, [qw(size fontsize)],
'non-XHR: order array preserved');
}
# Header unset behaves like non-XHR (no uninit warning).
{
local %ENV = %ENV;
delete $ENV{'HTTP_X_REQUESTED_WITH'};
my %info = (size => {});
my @warnings;
local $SIG{__WARN__} = sub { push @warnings, $_[0]; };
config_pre_load(\%info, undef);
ok(exists $info{'size'}, 'unset header: size preserved');
is(scalar @warnings, 0, 'unset header: no uninit warning');
}
# Order arg is optional / may be a non-arrayref — must not crash.
{
local $ENV{'HTTP_X_REQUESTED_WITH'} = 'XMLHttpRequest';
my %info = (size => {});
eval { config_pre_load(\%info, undef); };
is($@, '', 'XHR with undef order arg does not die');
ok(!exists $info{'size'}, 'XHR with undef order arg still strips size');
}
};
# verify_websocket_key — handshake auth
#
# miniserv.pl rewrites the inbound Sec-WebSocket-Key to base64(session_id)
# before forwarding to the shellserver. Equality of the rewritten key with
# our base64-encoded local copy of session_id proves the connection came
# through the Webmin proxy and is bound to this user's session. Anything
# else (missing, empty, mismatched, only whitespace) must reject.
subtest 'verify_websocket_key' => sub {
# require (not use) MIME::Base64 to avoid importing its prototyped
# encode_base64 into main:: where it would clash with WebminCore's.
require MIME::Base64;
my $sid = 'abcdef0123456789' x 2;
my $b64 = MIME::Base64::encode_base64($sid);
is(verify_websocket_key($b64, $sid), 1,
'base64(session_id) matches');
(my $stripped = $b64) =~ s/\s//g;
is(verify_websocket_key($stripped, $sid), 1,
'whitespace-stripped key still matches (encode_base64 wraps lines)');
is(verify_websocket_key('wrong-key', $sid), 0,
'arbitrary key rejected');
is(verify_websocket_key($b64, 'different-session'), 0,
'right key but wrong session rejected');
is(verify_websocket_key(undef, $sid), 0, 'undef key rejected');
is(verify_websocket_key($b64, undef), 0, 'undef session rejected');
is(verify_websocket_key('', $sid), 0, 'empty key rejected');
is(verify_websocket_key($b64, ''), 0, 'empty session rejected');
is(verify_websocket_key(" \t\n", $sid), 0,
'whitespace-only key rejected (would otherwise compare empty == empty)');
# Reflected attack: if a client could replay miniserv's rewritten key
# back as some OTHER session's id, we'd be in trouble. Pin: the function
# compares against the FULL base64 of the local session, not a substring.
my $other_sid = 'zzzz1111';
is(verify_websocket_key($b64, $other_sid), 0,
'key for one session never matches a different session');
};
# parse_resize_message — xterm.js resize signal
#
# The browser sends a custom out-of-band string on terminal resize:
# literal backslash + "033[8;(rows);(cols)t"
# Anything else is normal keyboard input and must be forwarded to the shell
# unchanged. Important security contract: a real ANSI CSI 8;... escape
# (chr(27)) must NOT be interpreted as resize — otherwise a remote that
# can write to the user's terminal output could be confused with input,
# though here input flows the other way it's still hygiene worth pinning.
subtest 'parse_resize_message' => sub {
is_deeply([parse_resize_message('\\033[8;(24);(80)t')],
[24, 80],
'valid resize message parses to (rows, cols)');
is_deeply([parse_resize_message('\\033[8;(1);(1)t')],
[1, 1], 'small terminal');
is_deeply([parse_resize_message('\\033[8;(9999);(9999)t')],
[9999, 9999], 'large terminal');
# Anything else is not a resize.
is_deeply([parse_resize_message('hello')], [], 'plain text is not resize');
is_deeply([parse_resize_message('')], [], 'empty string is not resize');
is_deeply([parse_resize_message(undef)], [], 'undef is not resize');
is_deeply([parse_resize_message("\033[8;24;80t")], [],
'real ANSI ESC sequence (chr 27) is not the custom format');
is_deeply([parse_resize_message('\\033[8;24;80t')], [],
'missing parens (real CSI shape) not accepted');
is_deeply([parse_resize_message('prefix\\033[8;(24);(80)t')], [],
'must match from start of message');
is_deeply([parse_resize_message('\\033[8;(24);(80)textra')], [],
'must match to end of message');
is_deeply([parse_resize_message('\\033[8;(-1);(80)t')], [],
'negative dimensions rejected');
is_deeply([parse_resize_message('\\033[8;(abc);(80)t')], [],
'non-numeric dimensions rejected');
# Return values are real numbers (not strings) so downstream ioctl()
# pack("s2", ...) sees a sane integer.
my ($r, $c) = parse_resize_message('\\033[8;(40);(120)t');
cmp_ok($r, '==', 40, 'rows is numeric');
cmp_ok($c, '==', 120, 'cols is numeric');
};
# resolve_shell_user — which Unix account does the terminal run as?
#
# The contract (see acl_security.pl for the UI / docs/access.html for the
# operator-facing model):
#
# - access user '*' → always the authenticated user. No override.
# - access user 'root', sudoenforce on, remote_user is a real local
# account → prefer the authenticated user over root.
# - access user 'root', remote_user same as access user (e.g. logged in as
# root) → keep root.
# - config user override → applies only when the resolved user is still
# 'root'.
# - in{user} → only honored when the resolved user is still 'root' AFTER
# the config override (i.e. the admin actually allowed root).
#
# This is the core privilege-boundary logic. Each scenario is a security
# contract.
subtest 'resolve_shell_user' => sub {
# access user '*': start from authenticated user. For non-root
# remote_user this is effectively "no override possible" — the
# trailing branches only act when $user eq 'root', so an alice-as-
# alice resolution can't be redirected.
is(resolve_shell_user({user => '*'}, 'alice', {}, {}),
'alice', "'*' with non-root authuser stays as authuser");
is(resolve_shell_user({user => '*'}, 'alice', {user => 'bob'}, {}),
'alice', "'*' with non-root authuser ignores in{user}");
is(resolve_shell_user({user => '*'}, 'alice', {}, {user => 'configured'}),
'alice', "'*' with non-root authuser ignores config{user}");
# Edge case carried forward from the inline version: when access='*'
# AND the authenticated user IS root, $user lands at "root" and the
# trailing config{user} / in{user} branches still apply. Functionally
# benign (root can do anything anyway) but pinned so a future refactor
# doesn't accidentally change it without intent.
is(resolve_shell_user({user => '*'}, 'root', {user => 'shell-svc'}, {}),
'shell-svc',
"'*' with root authuser: in{user} still routes (pre-existing behavior)");
# access user 'root', logged in as root → stay root.
is(resolve_shell_user({user => 'root', sudoenforce => 1},
'root', {}, {}),
'root',
"'root' + remote_user=root stays root");
# config{user} override applies when the resolved user is still root.
is(resolve_shell_user({user => 'root', sudoenforce => 1},
'root', {}, {user => 'shell-svc'}),
'shell-svc',
'config{user} overrides remaining-root');
# in{user} override is honored only when the resolved user is still
# 'root' — i.e. the admin explicitly allowed root.
is(resolve_shell_user({user => 'root', sudoenforce => '0'},
'root', {user => 'bob'}, {}),
'bob',
'in{user} override honored when user remained root');
# Empty in{user} doesn't trigger the override branch.
is(resolve_shell_user({user => 'root', sudoenforce => '0'},
'root', {user => ''}, {}),
'root', 'empty in{user} not treated as override');
# Empty / missing access{user} returns undef rather than producing a
# nonsense result the caller might try to exec.
is(resolve_shell_user({user => ''}, 'alice', {}, {}),
undef, 'empty access{user} returns undef');
is(resolve_shell_user({}, 'alice', {}, {}),
undef, 'missing access{user} returns undef');
# Specific named user in access{user} (neither '*' nor 'root') flows
# through unchanged.
is(resolve_shell_user({user => 'jenkins'},
'alice', {user => 'attacker'}, {}),
'jenkins',
'specific named access user is honored as-is and cannot be overridden');
# Sudoenforce path needs a real non-root local user with a home dir.
# Use the test process's own account when it's not running as root.
# This is the key privilege boundary: the admin allows 'root' in the
# ACL, sudoenforce is on, the Webmin-authenticated user exists locally
# → the shell drops to that user. Once de-rooted, the URL-borne
# in{user} parameter MUST NOT be honored as a re-escalation channel.
my @me = getpwuid($<);
SKIP: {
skip 'sudoenforce path needs a non-root local user', 4
if !@me || $me[0] eq 'root' || !$me[7];
my $local = $me[0];
# sudoenforce on + remote_user is a real local non-root account
# → drop to that user.
is(resolve_shell_user({user => 'root', sudoenforce => 1},
$local, {}, {}),
$local,
'sudoenforce on prefers authenticated non-root user when local');
# sudoenforce off keeps root even when remote_user is a local
# non-root account.
is(resolve_shell_user({user => 'root', sudoenforce => '0'},
$local, {}, {}),
'root',
'sudoenforce=0 keeps root regardless of authenticated user');
# config{user} does NOT override once the sudo path has already
# resolved away from root (the if-eq-root gate is closed).
is(resolve_shell_user({user => 'root', sudoenforce => 1},
$local, {}, {user => 'shell-svc'}),
$local,
'config{user} skipped when sudo path already de-rooted');
# Pinning current behavior: when access{user}='root' AND the
# user has supplied in{user}, the elsif's `!$in{'user'}` gate
# CLOSES the sudo-preference branch, leaving $user='root', and
# the trailing override then sets $user=in{user}. Effect:
# sudoenforce is a default ("prefer non-root if user didn't
# choose"), not a hard guard. An explicit ?user=X bypasses it
# even when the authenticated user has a local account.
#
# Worth flagging: the admin model treats access{user}='root' as
# "this Webmin user may run shells as any local user", and the
# trailing override honors that. But operators relying on
# sudoenforce as a hard "never run as root unless impossible"
# guard would be surprised. See bugs-found note.
is(resolve_shell_user({user => 'root', sudoenforce => 1},
$local, {user => 'daemon'}, {}),
'daemon',
'in{user} override bypasses sudoenforce (sudoenforce is a default, not a hard guard)');
}
};
# acl_security_save — parsing of the ACL editor form
#
# The mapping from form fields back into the stored ACL hash. user_def=1
# means "same as authenticated user" (stored as '*'); otherwise the typed
# username is stored verbatim. sudoenforce is 1/0 normalised.
subtest 'acl_security_save' => sub {
# user_def=1 → '*'
{
local %in = (user_def => 1, user => 'ignored', sudoenforce => 1);
my %o;
acl_security_save(\%o);
is($o{'user'}, '*', 'user_def=1 stores *');
is($o{'sudoenforce'}, 1, 'sudoenforce=1 stored as 1');
}
# user_def=0 → in{user} stored verbatim
{
local %in = (user_def => 0, user => 'root', sudoenforce => 0);
my %o;
acl_security_save(\%o);
is($o{'user'}, 'root', 'explicit user stored');
is($o{'sudoenforce'}, 0, 'sudoenforce=0 stored as 0');
}
# Falsy values normalize.
{
local %in = (user_def => 0, user => 'bob', sudoenforce => '');
my %o;
acl_security_save(\%o);
is($o{'sudoenforce'}, 0, 'empty sudoenforce normalizes to 0');
}
};
# Stock ACL files — `defaultacl` and `safeacl` are the templates Webmin
# applies when a new user (admin or non-admin) gets access to the module.
# Their values gate every later security check. Pin the defaults so a
# stray edit can't quietly loosen them.
subtest 'shipped ACL templates' => sub {
my %def;
open(my $df, '<', "$bindir/../defaultacl") or die "defaultacl: $!";
while (<$df>) { chomp; my ($k, $v) = split(/=/, $_, 2); $def{$k} = $v; }
close($df);
is($def{'user'}, 'root', 'defaultacl runs as root');
is($def{'sudoenforce'}, '1', 'defaultacl has sudoenforce ON');
my %safe;
open(my $sf, '<', "$bindir/../safeacl") or die "safeacl: $!";
while (<$sf>) { chomp; my ($k, $v) = split(/=/, $_, 2); $safe{$k} = $v; }
close($sf);
is($safe{'user'}, '*',
'safeacl runs as authenticated user (no root for non-admins)');
is($safe{'noconfig'}, '1',
'safeacl forbids the module config screen for non-admins');
};
done_testing();
+2
View File
@@ -0,0 +1,2 @@
// 0.13.0-beta.285
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.AttachAddon=t():e.AttachAddon=t()}(globalThis,()=>(()=>{"use strict";var e={};return(()=>{var t=e;function s(e,t,s){return e.addEventListener(t,s),{dispose:()=>{s&&e.removeEventListener(t,s)}}}Object.defineProperty(t,"__esModule",{value:!0}),t.AttachAddon=void 0,t.AttachAddon=class{constructor(e,t){this._disposables=[],this._socket=e,this._socket.binaryType="arraybuffer",this._bidirectional=!(t&&!1===t.bidirectional)}activate(e){this._disposables.push(s(this._socket,"message",t=>{const s=t.data;e.write("string"==typeof s?s:new Uint8Array(s))})),this._bidirectional&&(this._disposables.push(e.onData(e=>this._sendData(e))),this._disposables.push(e.onBinary(e=>this._sendBinary(e)))),this._disposables.push(s(this._socket,"close",()=>this.dispose())),this._disposables.push(s(this._socket,"error",()=>this.dispose()))}dispose(){for(const e of this._disposables)e.dispose()}_sendData(e){this._checkOpenSocket()&&this._socket.send(e)}_sendBinary(e){if(!this._checkOpenSocket())return;const t=new Uint8Array(e.length);for(let s=0;s<e.length;++s)t[s]=255&e.charCodeAt(s);this._socket.send(t)}_checkOpenSocket(){switch(this._socket.readyState){case WebSocket.OPEN:return!0;case WebSocket.CONNECTING:throw new Error("Attach addon was loaded before socket was open");case WebSocket.CLOSING:return console.warn("Attach addon socket is closing"),!1;case WebSocket.CLOSED:throw new Error("Attach addon socket is closed");default:throw new Error("Unexpected socket state")}}}})(),e})());
+2
View File
@@ -0,0 +1,2 @@
// 0.12.0-beta.285
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.FitAddon=t():e.FitAddon=t()}(globalThis,()=>(()=>{"use strict";var e={};return(()=>{var t=e;function i(e){return(t=e,t?.ownerDocument?.defaultView?t.ownerDocument.defaultView:window).getComputedStyle(e,null);var t}Object.defineProperty(t,"__esModule",{value:!0}),t.FitAddon=void 0,t.FitAddon=class{activate(e){this._terminal=e}dispose(){}fit(){const e=this.proposeDimensions();e&&this._terminal&&!isNaN(e.cols)&&!isNaN(e.rows)&&this._terminal.resize(e.cols,e.rows)}proposeDimensions(){if(!this._terminal)return;if(!this._terminal.element||!this._terminal.element.parentElement)return;const e=this._terminal.dimensions;if(!e||0===e.css.cell.width||0===e.css.cell.height)return;const t=this._terminal.options.scrollbar?.showScrollbar??!0,r=0!==this._terminal.options.scrollback&&t?this._terminal.options.scrollbar?.width??14:0,o=i(this._terminal.element.parentElement),n=Math.max(0,parseInt(o.getPropertyValue("height"),10)||0),s=Math.max(0,parseInt(o.getPropertyValue("width"),10)||0),l=i(this._terminal.element),a=n-((parseInt(l.getPropertyValue("padding-top"),10)||0)+(parseInt(l.getPropertyValue("padding-bottom"),10)||0)),p=s-((parseInt(l.getPropertyValue("padding-right"),10)||0)+(parseInt(l.getPropertyValue("padding-left"),10)||0))-r;return{cols:Math.max(2,Math.floor(p/e.css.cell.width)),rows:Math.max(1,Math.floor(a/e.css.cell.height))}}}})(),e})());
File diff suppressed because one or more lines are too long
+106
View File
@@ -0,0 +1,106 @@
# Common functions for the xterm module
BEGIN { push(@INC, ".."); }; ## no critic
use WebminCore;
use strict;
use warnings;
no warnings 'uninitialized';
our (%access, %config);
init_config();
%access = get_module_acl();
# config_pre_load(mod-info-ref, [mod-order-ref])
# Check if some config options are conditional,
# and if not allowed, remove them from listing
sub config_pre_load
{
my ($modconf_info, $modconf_order) = @_;
if (($ENV{'HTTP_X_REQUESTED_WITH'} || '') eq "XMLHttpRequest") {
# Size is not supported in Authentic, because resize works flawlessly
# and making it work would just add addition complexity for no good
# reason
delete($modconf_info->{'size'}) if (ref($modconf_info) eq 'HASH');
@{$modconf_order} = grep { $_ ne 'size' } @{$modconf_order}
if (ref($modconf_order) eq 'ARRAY');
}
}
# verify_websocket_key(client-key, session-id)
# Returns 1 if the client's Sec-WebSocket-Key matches the base64-encoded
# session ID, 0 otherwise. miniserv.pl rewrites the inbound handshake key
# to base64(session_id) before forwarding the upgrade to the shell server,
# so equality proves the connection came through the authenticated proxy
# and is bound to this Webmin session.
sub verify_websocket_key
{
my ($key, $sess) = @_;
return 0 if (!defined($key) || !defined($sess) || $sess eq '');
require MIME::Base64;
my $dsess = MIME::Base64::encode_base64($sess);
$key =~ s/\s//g;
$dsess =~ s/\s//g;
return 0 if ($key eq '' || $dsess eq '');
return $key eq $dsess ? 1 : 0;
}
# parse_resize_message(message)
# If $message is the resize signal that xterm.js sends on terminal resize
# (literal backslash-zero-three-three then "[8;(rows);(cols)t"), return
# (rows, cols) as integers. Otherwise return an empty list. The format is
# a custom out-of-band signal — not a real ANSI escape — so anything else
# is treated as regular keyboard input and forwarded to the shell.
sub parse_resize_message
{
my ($msg) = @_;
return () if (!defined($msg));
if ($msg =~ /^\\033\[8;\((\d+)\);\((\d+)\)t\z/) {
return ($1 + 0, $2 + 0);
}
return ();
}
# resolve_shell_user(\%access, $remote_user, \%in, \%config)
# Decide which Unix account the terminal will run as, given the module ACL
# (%access), the Webmin-authenticated user, the CGI input (%in, which may
# carry a 'user' override) and the module config (%config). Pure function:
# does no I/O beyond getpwnam() for the sudoenforce branch. The caller
# must still validate the result against getpwnam() before exec'ing a shell.
#
# Rules (preserved verbatim from the previous inline version in index.cgi):
# - access user "*" → start from $remote_user.
# - else, access user "root" with sudoenforce !=0 AND no explicit
# in{user} AND remote_user differs from "root" → prefer remote_user
# when it has a local home dir (sudo-preferred path).
# - config{user} can override a remaining "root" choice.
# - if the resolved user is still "root" AND in{user} is set, in{user}
# overrides — the admin explicitly allowed root, so any user is OK.
# Note the trailing in{user} branch runs regardless of how $user got to
# "root", so e.g. access="*" with remote_user="root" can still be
# overridden by in{user}. That's preserved here for compatibility; an
# operator who wants in{user} ignored for "*" should ensure the
# authenticated user isn't root.
sub resolve_shell_user
{
my ($access, $remote_user, $in, $config) = @_;
$in ||= {};
$config ||= {};
my $user = $access->{'user'};
return if (!defined($user) || $user eq '');
if ($user eq "*") {
$user = $remote_user;
}
elsif ($user eq "root" && $remote_user ne $user && !$in->{'user'} &&
(defined($access->{'sudoenforce'}) ? $access->{'sudoenforce'} : '') ne '0') {
my @uinfo = getpwnam($remote_user);
if (@uinfo && $uinfo[7]) {
$user = $remote_user;
}
}
$user = $config->{'user'} if ($user eq 'root' && $config->{'user'});
if ($user eq "root" && defined($in->{'user'}) && $in->{'user'} ne '') {
$user = $in->{'user'};
}
return $user;
}
1;
+10
View File
@@ -0,0 +1,10 @@
/*!
* Xterm.js v6.0.0 (https://github.com/xtermjs/xterm.js)
* Copyright (c) 2017-2026, The xterm.js authors (https://github.com/xtermjs/xterm.js)
* Copyright (c) 2014-2016, SourceLair Private Company (https://www.sourcelair.com)
* Copyright (c) 2012-2013, Christopher Jeffrey (https://github.com/chjj/)
* Licensed under MIT (https://github.com/xtermjs/xterm.js/blob/master/LICENSE)
*/
.xterm{cursor:text;position:relative;user-select:none;-ms-user-select:none;-webkit-user-select:none;}.xterm.focus,.xterm:focus{outline:none;}.xterm .xterm-helpers{position:absolute;top:0;z-index:5;}.xterm .xterm-helper-textarea{padding:0;border:0;margin:0;position:absolute;opacity:0;left:-9999em;top:0;width:0;height:0;z-index:-5;white-space:nowrap;overflow:hidden;resize:none;}.xterm .composition-view{background:#000;color:#FFF;display:none;position:absolute;white-space:nowrap;z-index:1;}.xterm .composition-view.active{display:block;}.xterm .xterm-viewport{background-color:#000;overflow-y:scroll;cursor:default;position:absolute;right:0;left:0;top:0;bottom:0;}.xterm .xterm-screen{position:relative;}.xterm .xterm-screen canvas{position:absolute;left:0;top:0;}.xterm-char-measure-element{display:inline-block;visibility:hidden;position:absolute;top:0;left:-9999em;line-height:normal;}.xterm.enable-mouse-events{cursor:default;}.xterm.xterm-cursor-pointer,.xterm .xterm-cursor-pointer{cursor:pointer;}.xterm.column-select.focus{cursor:crosshair;}.xterm .xterm-accessibility:not(.debug),.xterm .xterm-message{position:absolute;left:0;top:0;bottom:0;right:0;z-index:10;color:transparent;pointer-events:none;}.xterm .xterm-accessibility-tree:not(.debug) *::selection{color:transparent;}.xterm .xterm-accessibility-tree{font-family:monospace;user-select:text;white-space:pre;}.xterm .xterm-accessibility-tree>div{transform-origin:left;width:fit-content;}.xterm .live-region{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden;}.xterm-dim{opacity:1!important;}.xterm-underline-1{text-decoration:underline}.xterm-underline-2{text-decoration:double underline}.xterm-underline-3{text-decoration:wavy underline}.xterm-underline-4{text-decoration:dotted underline}.xterm-underline-5{text-decoration:dashed underline}.xterm-overline{text-decoration:overline;}.xterm-overline.xterm-underline-1{text-decoration:overline underline}.xterm-overline.xterm-underline-2{text-decoration:overline double underline}.xterm-overline.xterm-underline-3{text-decoration:overline wavy underline}.xterm-overline.xterm-underline-4{text-decoration:overline dotted underline}.xterm-overline.xterm-underline-5{text-decoration:overline dashed underline}.xterm-strikethrough{text-decoration:line-through;}.xterm-screen .xterm-decoration-container .xterm-decoration{z-index:6;position:absolute;}.xterm-screen .xterm-decoration-container .xterm-decoration.xterm-decoration-top-layer{z-index:7;}.xterm-decoration-overview-ruler{z-index:8;position:absolute;top:0;right:0;pointer-events:none;}.xterm-decoration-top{z-index:2;position:relative;}.xterm .xterm-scrollable-element>.scrollbar{cursor:default;}.xterm .xterm-scrollable-element>.scrollbar>.scra{cursor:pointer;font-size:11px!important;}.xterm .xterm-scrollable-element>.visible{opacity:1;background:rgba(0,0,0,0);transition:opacity 100ms linear;z-index:11;}.xterm .xterm-scrollable-element>.invisible{opacity:0;pointer-events:none;}.xterm .xterm-scrollable-element>.invisible.fade{transition:opacity 800ms linear;}.xterm .xterm-scrollable-element>.shadow{position:absolute;display:none;}.xterm .xterm-scrollable-element>.shadow.top{display:block;top:0;left:3px;height:3px;width:100%;box-shadow:var(--vscode-scrollbar-shadow,#000) 0 6px 6px -6px inset;}.xterm .xterm-scrollable-element>.shadow.left{display:block;top:3px;left:0;height:100%;width:3px;box-shadow:var(--vscode-scrollbar-shadow,#000) 6px 0 6px -6px inset;}.xterm .xterm-scrollable-element>.shadow.top-left-corner{display:block;top:0;left:0;height:3px;width:3px;}.xterm .xterm-scrollable-element>.shadow.top.left{box-shadow:var(--vscode-scrollbar-shadow,#000) 6px 0 6px -6px inset;}
/* custom */
.xterm .xterm-decoration-overview-ruler { display: none; }
+10
View File
File diff suppressed because one or more lines are too long