ONO::Apps::Admin::Screen::Info::System

package ONO::Apps::Admin::Screen::Info::System;
################################################################################
# COPYRIGHT / LICENSE #
################################################################################
#
# This file is part of the ONO Software Project.
#
# Copyright (C) 2000-2025 Jos KIRPS [ www.kirps.com | jos_AT_kirps_DOT_com ]
# and The Joopita Project [ www.joopita.org | contact_AT_joopita_DOT_com ]
#
# This file, as well as other parts of the ONO Software Project or related
# elements, are FREE SOFTWARE available under the ARTISTIC LICENSE 2.0.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
#
# For the full license, see /ono/osr/license/LICENSE.txt, or write to
# jos_AT_kirps_DOT_com or contact_AT_joopita_DOT_com.
#
################################################################################
# END OF COPYRIGHT / LICENSE, HERE COMES THE CODE ... #
################################################################################


use strict;

use ONO::IO;

use ONO::ToolBox::System;
use ONO::ToolBox::Test;

use ONO::Core::HostOS;
use ONO::Core::Version;

use ONO::Lib::DateTime::ToolBox;
use ONO::Lib::Data::Crypt;
use ONO::Lib::Web::Client;
use ONO::Lib::Video::ToolBox;
use ONO::Cron::CronSync::ToolBox;

###############################################################################
# ONO
###############################################################################

sub screen {

my (
$self,
$db,
$community,
$lang,
$BLK_ref,
$vars_ref,
$sql_ref,
$ono_ref,
) = @_;

#: Display basic system information, and warn if important stuff on the
#: server is missing.

my %BLK = %$BLK_ref;
my %vars = %$vars_ref;
my %ono = %$ono_ref;

my ($TITLE,$SUBTITLE,$SCREEN,$LEFT,$RIGHT,$TOOLS);

my (
$sec,$min,$hour,
$mday,$mon,$year,
$wday,$yday,$timestamp,
) = ONO::Lib::DateTime::ToolBox->get;

my ($ver,$rev,$build) = ONO::Core::Version->ver;

$TITLE = "ONO System Info";
$SUBTITLE = "Version $ver$rev, build $build";

my $USER_AGENT = $ENV{'HTTP_USER_AGENT'};
$USER_AGENT =~ s~\(~<div class="small col9">\(~g;
$USER_AGENT =~ s~\)~\)</div>~g;

my $cpu = ONO::IO->cpu("b");
my $mem = ONO::IO->mem;
my ($mem2,$used,$free,$mem_per) = ONO::Core::HostOS->mem_load;
my $ip = ONO::IO->ip;
my $os = ONO::IO->os("vV");

my ($PERL_MODULES,$PM1,$PM2) = ONO::Core::HostOS->cpan("H");
my $PM_ERR;
if ($PM1) {
$PM_ERR .= qq~<div class="lightred bold">$PM1 required modules are missing, please install them right away.</div>~;
}
if ($PM2) {
$PM_ERR .= qq~<div class="orange">$PM2 recommended modules are missing, please consider installing them.</div>~;
}

my $MOD_PERL;
my $MOD_PERL2 = qq~<strong>not enabled</strong> <span class="orange">(slower, not recommended for production systems)</span>~;
if ($ENV{'MOD_PERL'}) {
$MOD_PERL = ", $ENV{'MOD_PERL'}";
$MOD_PERL2 = "<strong>enabled</strong> (version $ENV{'MOD_PERL'})";
}

my ($LIBS,$libs_missing);
foreach my $lib ('convert','exiftool','gs','ffmpeg','heif-convert','openai','pdftk','wkhtmltoimage','wkhtmltopdf') {
my $which = ONO::IO->which($lib);
if ($which =~ /[a-z]/) {
$LIBS .= "$which, ";
} else {
$LIBS .= qq~<span class="lightred">$lib not found</span>, ~;
$libs_missing++;
}
}
$LIBS =~ s~, $~~;
if ($libs_missing) {
$LIBS .= qq~<div class="lightred">$libs_missing libraries are missing, some ONO features may thus not be available.</div>~;
}

my ($CRON,$QUEUE);

foreach my $job ('cronsync','service') {

my $lastrun = ONO::IO->load("var/ono/data/$job/lastrun/$job.txt");

if (!$lastrun) {
$CRON .= qq~<strong>$job</strong> <span class="lightred">not running, please fix!</span>~;
} else {
my $lastrun2 = ONO::Lib::DateTime::ToolBox->get($lastrun,0,"iz",$lang);
if ($lastrun < $timestamp-60) {
$CRON .= qq~<strong>$job</strong> <span class="lightred">was last run $lastrun2, looks like a problem, please fix!</span>~;
} else {
$CRON .= "<strong>$job</strong> was last run $lastrun2";
}

}

$CRON .= "<br>";

}

foreach my $queue (ONO::IO->ls("var/tmp/cronsync")) {
if ($queue !~ /\./ && $queue =~ /[a-z]/) {
my $jobs_in_queue;
foreach my $job (ONO::IO->ls("var/tmp/cronsync/$queue")) {
if ($job =~ /\.txt/) {
$jobs_in_queue++;
}
}
if ($jobs_in_queue) {
$QUEUE .= qq~<span class="bold">$jobs_in_queue</span> jobs in queue '$queue', ~;
}
}
}

$QUEUE =~ s~, $~~;

if (!$QUEUE) {
$QUEUE = qq~cronsync job pipeline is empty (<span class="bold">ok</span>).~;
}

if (ONO::IO->exists("/var/log/syslog/$year$mon$mday.log")) {
$CRON .= "<strong>syslog</strong> monitoring seems to be running";
} else {
$CRON .= "<strong>syslog</strong> monitoring not running (optional)";
}

my $HTTPS = qq~<strong>HTTP</strong> <span class="lightred">is not secure, please upgrade to SSL/HTTPS!</span>~;
if ($ENV{'HTTPS'}) {
$HTTPS = qq~<strong>SSL / HTTPS</strong> (secure)~;
}
if (ONO::IO->load("etc/https.conf") =~ /https on/) {
$HTTPS = qq~<strong>SSL / HTTPS</strong> (proxy mode)~;
}

my $DATABASE = qq~<strong>unknown</strong> <span class="orange">(strange, please investigate...)</span>~;
foreach my $line (ONO::IO->list("/etc/sql/ono.conf")) {
if ($line =~ m~^driver (.*?)\n$~) {
my $NAME = uc $1;
my $INFO = "-";
if ($NAME =~ /ODBC/) {
$NAME = "ODBC";
}
if ($NAME =~ /MYSQL/) {
$NAME = "MySQL";
$INFO = ONO::Core::HostOS->mysql();
}
$DATABASE = qq~<strong>$NAME</strong> ($INFO)~;
}
}

if ($db) {
$DATABASE .= qq~: connected (ok)~;
} else {
$DATABASE .= qq~: <span class="orange">cannot connect to the database, please check the connection!</span>~;
}

my @disks;
foreach my $disk (ONO::IO->disks) {
@disks = (@disks,$disk);
}

my $DRIVES = ONO::ToolBox::System->diskfree_display(\@disks,"sb");

my $CRYPT = qq~<span class="orange"><span class="bold">standard password encryption</span> - install Crypt::Password to enable stronger SHA algorithms!</span>~;

if (ONO::Lib::Data::Crypt->stronghash("ono","AA","sha512")) {
$CRYPT = qq~<span class="bold">SHA-2</span> strong 512 bit password encryption enabled~;

if (ONO::Lib::Data::Crypt->strongtest) {
$CRYPT .= qq~ (tested, ok)~;
} else {
my $ERR = ONO::Lib::Data::Crypt->strongtest_error();
$CRYPT .= qq~ <span class="orange">(TEST FAILED, 'testpass' returned $ERR)</span>~;
}

}
use ONO::ToolBox::Test;
my ($err,$TESTS) = ONO::ToolBox::Test->media_processors();

if ($err) {
$TESTS = qq~<div class="lightred bold">$err ERRORS:</div><div class="small col6 lh125">$TESTS</div>~;
} else {
$TESTS = qq~<div class="bold">OK, no errors found:</div><div class="small col6 lh125">$TESTS</div>~;
}

foreach my $key ('site','domain','ip') {
if (!$ono{$key}) {
$ono{$key} = qq~<span class="lightred">not set - please fix!</span>~;
}
}

$SCREEN = qq~<table class="wide_table bt bb">
<tr class="row vtop bt bb">
<td class="tar">software:</td>
<td><strong>ONO</strong></td>
</tr>
<tr class="row vtop bt bb">
<td class="tar">release:</td>
<td><strong>$ver</strong> ($ver$rev - version $ver revision $rev, software build $build)</td>
</tr>
<tr class="row vtop bt bb">
<td class="w25 tar">site:</td>
<td class="w75"><strong>$ono{'site'}</strong></td>
</tr>
<tr class="row vtop bt bb">
<td class="tar">domain:</td>
<td><strong>$ono{'domain'}</strong> [host: $ENV{'HTTP_HOST'}]</td>
</tr>
<tr class="row vtop bt bb">
<td class="tar">IP:</td>
<td><strong>$ono{'ip'}</strong> [host IP: $ip]</td>
</tr>
<tr class="row vtop bt bb">
<td class="tar">connection:</td>
<td>$HTTPS</td>
</tr>
<tr class="row vtop bt bb">
<td class="tar">CPU:</td>
<td>$cpu</td>
</tr>
<tr class="row vtop bt bb">
<td class="tar">RAM:</td>
<td><strong>$mem</strong> ($mem_per\% used)</td>
</tr>
<tr class="row vtop bt bb">
<td class="tar">drives:</td>
<td>$DRIVES</td>
</tr>
<tr class="row vtop bt bb">
<td class="tar">host OS:</td>
<td><strong>$os</strong></td>
</tr>
<tr class="row vtop bt bb">
<td class="tar">webserver:</td>
<td>
<strong>$ENV{'SERVER_SOFTWARE'}, $ENV{'SERVER_PROTOCOL'}, $ENV{'GATEWAY_INTERFACE'}, Perl $ENV{'VERSIONER_PERL_VERSION'}$MOD_PERL</strong>
<div class="small lh125 col6">$PERL_MODULES</div>$PM_ERR
</td>
</tr>
<tr class="row vtop bt bb">
<td class="tar">mod_perl:</td>
<td>$MOD_PERL2</td>
</tr>
<tr class="row vtop bt bb">
<td class="tar">libraries:</td>
<td>$LIBS</td>
</tr>
<tr class="row vtop bt bb">
<td class="tar">database:</td>
<td>$DATABASE</td>
</tr>
<tr class="row vtop bt bb">
<td class="tar">tests:</td>
<td>$TESTS</td>
</tr>
<tr class="row vtop bt bb">
<td class="tar">javascript:</td>
<td>
<div id="ono_js_library" class="bold"><span class="lightred">Error: ONO library could not be loaded</span></div>
<script>onojs_version('ono_js_library');</script>
</td>
</tr>
<tr class="row vtop bt bb">
<td class="tar">encryption:</td>
<td>$CRYPT</td>
</tr>
<tr class="row vtop bt bb">
<td class="tar">cron:</td>
<td>$CRON</td>
</tr>
<tr class="row vtop bt bb">
<td class="tar">queue:</td>
<td>$QUEUE</td>
</tr>
<tr class="row vtop bt bb">
<td class="tar">creator:</td>
<td><strong>The Joopita Project</strong> (<a href="https://www.joopita.com/" target="_blank">www.joopita.com</a>)</td>
</tr>
<tr class="row vtop bt bb">
<td class="tar nowrap">concept & design:</td>
<td><strong>The Joopita Project</strong> (<a href="https://www.joopita.com/" target="_blank">www.joopita.com</a>)</td>
</tr>
<tr class="row vtop bt bb">
<td class="tar nowrap">development:</td>
<td><strong>The Joopita Project</strong> (<a href="https://www.joopita.com/" target="_blank">www.joopita.com</a>)</td>
</tr>
<tr class="row vtop bt bb">
<td class="tar">copyright:</td>
<td>©2000-2010 Jos KIRPS & The WobbleWolf Project<br>©2010-$ono{'year'} Jos KIRPS & The Joopita Project</td>
</tr>
</table>
<div class="inline auto">
<a href="/ono/admin/?/tools_server/" class="button_green mt10">settings & more...</a>
<a href="/ono/admin/?/tools_server/&setup=os" class="button_yellow mt10">OS setup</a>
<a href="/ono/admin/?/tools_server/&setup=database" class="button_yellow mt10">Database setup</a>
<a href="/ono/admin/?/tools_server/&setup=webserver" class="button_yellow mt10">Webserver setup</a>
<a href="/ono/admin/?/tools_server/&setup=fileserver" class="button_yellow mt10">Fileserver setup</a>
<a href="/ono/admin/?/tools_server/&setup=mailserver" class="button_yellow mt10">Mailserver setup</a>
</div>
~;

$RIGHT .= &apache_restart("","mb10");
$RIGHT .= &mailserver_restart("","mb20");
$RIGHT .= &server_management("","mb20");

my (
$status,$os_status,$browser_status,
$os,$os_ver,$os_info,
$browser,$browser_ver,$browser_info,
) = ONO::Lib::Web::Client->client();

my $USER_STATUS = "OK";
if ($status) {
$USER_STATUS = "Maybe your OS and/or browser should be updated!";
if ($status == 2) {
$USER_STATUS = "Your OS and/or browser seem to be deprecated!";
}
}

$RIGHT .= qq~<div class="box_fabric">
<h3>Client (you)</h3>
<div class="box_paper">
<div class="bold">IP:</div>
<div class="ml10 mb10">$ENV{'REMOTE_ADDR'}</div>
<div class="bold">User Agent:</div>
<div class="ml10 mb10">$USER_AGENT</div>
<div class="bold">OS:</div>
<div class="ml10 mb10">$os_info</div>
<div class="bold">Browser:</div>
<div class="ml10 mb10">$browser_info</div>
<div class="bold">Status:</div>
<div class="ml10">$USER_STATUS</div>
</div>
</div>
~;

return ($TITLE,$SUBTITLE,$SCREEN,$LEFT,$RIGHT,$TOOLS);

}

sub apache_restart {

#: Restart the Apache webserver (trigger feature)

my $CRON_DIR = ONO::Cron::CronSync::ToolBox->dir();

my $RIGHT;

foreach my $job (ONO::IO->ls("$CRON_DIR/jobs")) {
if ($job =~ /\.txt/) {

my ($option_t,$type,$restart);

foreach my $line (ONO::IO->list("$CRON_DIR/jobs/$job")) {

if ($line =~ /option:t/) {
$option_t++;
}
if ($line =~ /type:apache_control/) {
$type++;
}
if ($line =~ /apache_control:restart/) {
$restart++;
}

}

if ($option_t && $type && $restart) {

if ($ENV{'REQUEST_URI'} =~ /trigger_apache_restart=1/) {
ONO::IO->mkdir("$CRON_DIR/trigger");
ONO::IO->store("$CRON_DIR/trigger/apache-$job","Apache restart has been triggered");
ONO::IO->chmod("$CRON_DIR/trigger/apache-$job",777);
}

my $URL = $ENV{'REQUEST_URI'};
$URL =~ s~\&(.*)$~~;
if ($URL !~ /\?/) {
$URL .= "?";
}

my $RESTART = qq~<div class="mb10">You may trigger an Apache webserver restart by pressing the button below.</div>
<div class="inline"><a href="$URL&trigger_apache_restart=1" class="button_orange">restart</a></div>
~;

if (ONO::IO->exists("$CRON_DIR/trigger/apache-$job")) {
$RESTART = qq~<div class="lightred mb10">Apache webserver restart has been requested, should be executed soon...</div>~;
if ($ENV{'REQUEST_URI'} =~ /trigger_apache_restart=1/) {
$RESTART .= qq~<div class="lightred mb10">DO NOT RELOAD THIS PAGE BY HITTING THE RELOAD BUTTON IN YOUR BROWSER, AS THIS WOULD INITIATE A NEW TRIGGER!</div>~;
}
$RESTART .= qq~<div class="inline"><a href="$URL" class="button_green">reload screen</a></div>~;
}

if (ONO::IO->exists("$CRON_DIR/lastrun/restart-apache-$job")) {
my $lastrun = ONO::IO->load("$CRON_DIR/lastrun/restart-apache-$job");
if ($lastrun > 1500000000) {
$lastrun = ONO::Lib::DateTime::ToolBox->get($lastrun,0,"iz","en");
$RESTART .= qq~<div class="mt10 bt"><div class="mt10 col9 small lh125">Apache webserver has last been restarted <span class="bold">$lastrun</span> using this trigger.</div></div>~;
}
}

$RIGHT = qq~<div class="box_yellow $_[1]">
<h3 class="yellow">Apache restart</h3>
<div class="box_paper">
$RESTART
</div>
</div>
~;

}

}
}

return $RIGHT;

}

sub mailserver_restart {

#: Restart the mailserver (trigger feature)

my $CRON_DIR = ONO::Cron::CronSync::ToolBox->dir();

my $RIGHT;

foreach my $job (ONO::IO->ls("$CRON_DIR/jobs")) {
if ($job =~ /\.txt/) {

my $os_manage;

foreach my $line (ONO::IO->list("$CRON_DIR/jobs/$job")) {

if ($line =~ /type:os_manage/) {
$os_manage++;
}

}

if ($os_manage) {

if ($ENV{'REQUEST_URI'} =~ /trigger_mailserver_restart=1/) {
ONO::IO->mkdir("$CRON_DIR/trigger");
ONO::IO->store("$CRON_DIR/trigger/mailserver-$job","Mailserver restart has been triggered");
ONO::IO->chmod("$CRON_DIR/trigger/mailserver-$job",777);
}

my $URL = $ENV{'REQUEST_URI'};
$URL =~ s~\&(.*)$~~;
if ($URL !~ /\?/) {
$URL .= "?";
}

my $RESTART = qq~<div class="mb10">You may trigger a mailserver reload & restart by pressing the button below.</div>
<div class="inline"><a href="$URL&trigger_mailserver_restart=1" class="button_orange">restart</a></div>
~;

if (ONO::IO->exists("$CRON_DIR/trigger/mailserver-$job")) {
$RESTART = qq~<div class="lightred mb10">Mailserver reload & restart has been requested, should be executed soon...</div>~;
if ($ENV{'REQUEST_URI'} =~ /trigger_mailserver_restart=1/) {
$RESTART .= qq~<div class="lightred mb10">DO NOT RELOAD THIS PAGE BY HITTING THE RELOAD BUTTON IN YOUR BROWSER, AS THIS WOULD INITIATE A NEW TRIGGER!</div>~;
}
$RESTART .= qq~<div class="inline"><a href="$URL" class="button_green">reload screen</a></div>~;
}

if (ONO::IO->exists("$CRON_DIR/lastrun/restart-mailserver-$job")) {
my $lastrun = ONO::IO->load("$CRON_DIR/lastrun/restart-mailserver-$job");
if ($lastrun > 1500000000) {
$lastrun = ONO::Lib::DateTime::ToolBox->get($lastrun,0,"iz","en");
$RESTART .= qq~<div class="mt10 bt"><div class="mt10 col9 small lh125">Mailserver has last been restarted <span class="bold">$lastrun</span> using this trigger.</div></div>~;
}
}

$RIGHT = qq~<div class="box_yellow $_[1]">
<h3 class="yellow">Mailserver restart</h3>
<div class="box_paper">
$RESTART
</div>
</div>
~;

}

}
}


return $RIGHT;

}

sub server_management {

#: Server management tools (maybe deprecated?)

my $CRON_DIR = ONO::Cron::CronSync::ToolBox->dir();

my $RIGHT;

foreach my $job (ONO::IO->ls("$CRON_DIR/jobs")) {
if ($job =~ /\.txt/) {

my $os_manage;

foreach my $line (ONO::IO->list("$CRON_DIR/jobs/$job")) {

if ($line =~ /type:os_readconf/) {
$os_manage++;
}

}

if ($os_manage) {

$RIGHT = qq~<div class="box_green $_[1]">
<h3 class="green">Server Management</h3>
<div class="box_paper">
Server Management Tools (OS, Apache, MySQL, SMB/CIFS and Mail) are available on this installation.
<div class="inline"><a href="/ono/admin/?/tools_server/" class="button_green mt10">Server Tools</a></div>
</div>
</div>
~;

}

}
}


return $RIGHT;

}

###############################################################################
# end of script
###############################################################################

1;

__END__