diff --git a/root/usr/share/smanager/lib/SrvMngr.pm b/root/usr/share/smanager/lib/SrvMngr.pm index f012d62..0fb80e2 100644 --- a/root/usr/share/smanager/lib/SrvMngr.pm +++ b/root/usr/share/smanager/lib/SrvMngr.pm @@ -37,7 +37,7 @@ use esmith::NavigationDB; # no UTF8 raw is ok for ASCII only flat file use SrvMngr_Auth qw(check_admin_access); #this is overwrittrn with the "release" by the spec file - release can be "99.el8.sme" -our $VERSION = '93.el8.sme'; +our $VERSION = '94.el8.sme'; #Extract the release value if ($VERSION =~ /^(\d+)/) { $VERSION = $1; # $1 contains the matched numeric digits @@ -328,6 +328,7 @@ sub setup_routing { $if_admin->get('/datetime')->to('datetime#main')->name('datetime'); $if_admin->post('/datetimeu')->to('datetime#do_update')->name('datetimeu'); $if_admin->get('/datetimed')->to('datetime#do_display')->name('datetimed'); + $if_admin->post('/datetimet')->to('datetime#do_testntp')->name('datetimet'); $if_admin->get('/directory')->to('directory#main')->name('directory'); diff --git a/root/usr/share/smanager/lib/SrvMngr/Controller/Datetime-Custom.pm b/root/usr/share/smanager/lib/SrvMngr/Controller/Datetime-Custom.pm index 4551848..2cd2d2b 100644 --- a/root/usr/share/smanager/lib/SrvMngr/Controller/Datetime-Custom.pm +++ b/root/usr/share/smanager/lib/SrvMngr/Controller/Datetime-Custom.pm @@ -13,6 +13,8 @@ use esmith::NetworksDB::UTF8; use esmith::HostsDB; use esmith::DomainsDB::UTF8; +use DateTime; + use constant FALSE => 0; use constant TRUE => 1; @@ -79,6 +81,7 @@ my $ddb; my $now_sec = sprintf('%02d', $today_sec); my $current_year = $today_year; my $ntpserverurl = $cdb->get_prop('ntpd','NTPServer'); + my $now = DateTime->now( time_zone => 'local' ); my %ret = ( # fields from Inputs 'time_mode'=>($ntpserverurl eq '' ? 'dat_manually_set' : 'dat_ntp_server'), @@ -90,6 +93,8 @@ my $ddb; 'minute'=>"$now_min", 'second'=>"$now_sec", 'ntpstatus' => $cdb->get_prop('ntpd','status') || 'disabled', + # and the current time as a full format + 'currentdatetime' => $now->strftime('%Y-%m-%dT%H:%M:%S') ); return %ret; @@ -224,38 +229,12 @@ sub validate_change_datetime { $timezone = "US/Eastern"; } my $month = $c->param('month'); - - if ($month =~ /^(\d{1,2})$/) { - $month = $1; - } else { - $month = "1"; - } - - if (($month < 1) || ($month > 12)) { - return $c->l('dat_INVALID_MONTH') . " $month. " . $c->l('dat_MONTH_BETWEEN_1_AND_12'); - } my $day = $c->param('day'); - - if ($day =~ /^(\d{1,2})$/) { - $day = $1; - } else { - $day = "1"; - } - - if (($day < 1) || ($day > 31)) { - return $c->l('dat_INVALID_DAY') . " $day. " . $c->l('dat_BETWEEN_1_AND_31'); - } my $year = $c->param('year'); + if (!is_valid_date($year, $month, $day)){ + return $c->l('dat_Invalid_date') + } - if ($year =~ /^(\d{4})$/) { - $year = $1; - } else { - $year = "2000"; - } - - if (($year < 1900) || ($year > 2200)) { - return $c->l('dat_INVALID_YEAR') . " $year. " . $c->l('dat_FOUR_DIGIT_YEAR'); - } my $hour = $c->param('hour'); if ($hour =~ /^(\d{1,2})$/) { @@ -289,24 +268,6 @@ sub validate_change_datetime { if (($second < 0) || ($second > 59)) { return $c->l('dat_INVALID_SECOND') . " $second. " . $c->l('dat_BETWEEN_0_AND_59'); } - #my $ampm = $c->param('Ampm'); - - #Move to 24 hours clock - not using AM/PM. - #if ($ampm =~ /^(AM|PM)$/) { - #$ampm = $1; - #} else { - #$ampm = "AM"; - #} - # force AM so that it actually works on 24hr clock. - #$ampm = "AM"; - - - # convert to 24 hour time - #$hour = $hour % 12; - - #if ($ampm eq "PM") { - # $hour = $hour + 12; - #} #-------------------------------------------------- # Store time zone in configuration database @@ -325,10 +286,27 @@ sub validate_change_datetime { # and hardware clock #-------------------------------------------------- my $newdate = sprintf "%02d%02d%02d%02d%04d.%02d", $month, $day, $hour, $minute, $year, $second; - esmith::util::backgroundCommand(2, "/sbin/e-smith/signal-event", "timezone-update", $newdate); + $c->app->log->info("Changing date manually to $newdate"); + esmith::util::backgroundCommand(2, "/sbin/e-smith/signal-event", "timezone-update", $newdate); #TEMP!!! return ''; } ## end sub validate_change_datetime +sub is_valid_date { + my ($year, $month, $day) = @_; + + # Check if all parts are defined and integers + return 0 unless defined $year && defined $month && defined $day; + return 0 unless $year =~ /^\d+$/ && $month =~ /^\d+$/ && $day =~ /^\d+$/; + + # Try to construct a DateTime object + eval { + DateTime->new(year => $year, month => $month, day => $day); + 1; + } or return 0; + + return 1; +} + sub update_ntpserver { my $c = shift; my $ntpserver = shift; @@ -378,6 +356,4 @@ sub disable_ntp { return ''; } ## end sub disable_ntp - - 1; \ No newline at end of file diff --git a/root/usr/share/smanager/lib/SrvMngr/Controller/Datetime.pm b/root/usr/share/smanager/lib/SrvMngr/Controller/Datetime.pm index ce6dffd..4bdb676 100644 --- a/root/usr/share/smanager/lib/SrvMngr/Controller/Datetime.pm +++ b/root/usr/share/smanager/lib/SrvMngr/Controller/Datetime.pm @@ -8,6 +8,9 @@ package SrvMngr::Controller::Datetime; # heading : System # description : Date and time # navigation : 4000 300 +# +# ######name : datetimet, method : post, url : /datetimet, ctlact : datetime#testntp +# # routes : end # # Documentation: https://wiki.contribs.org/Datetime @@ -115,7 +118,7 @@ sub do_update { my $c = shift; $c->app->log->info($c->log_req); - $c->app->log->info($c->param('month')); + #$c->app->log->info($c->param('month')); #The most common ones - you might want to delete some of these if they are not used. @@ -173,7 +176,7 @@ sub do_update { $c->render(template => "datetime"); return } else { - if ($c->param('time_mode') eq 'data_manually_set') { + if ($c->param('time_mode') eq 'dat_manually_set') { $c->stash( success => $c->l('dat_UPDATING_CLOCK')); } else { $c->stash( success => $c->l('dat_SETTINGS_CHANGED')); @@ -279,5 +282,52 @@ sub do_display { dat_data => \%dat_data ); $c->render(template => "datetime"); -} +} + +sub do_testntp { + my $c = shift; + my $server = $c->req->json->{ntpserver} // ''; + + # Strict validation: hostname or IPv4 + unless ($server =~ /^(?=.{1,253}$)([a-zA-Z0-9](?:[a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?)*|\d{1,3}(?:\.\d{1,3}){3})$/) { + return $c->render(json => { success => 0, error => 'Invalid server name or IP' }); + } + + my $timeout = 5; + my @cmd = ('timeout', $timeout, 'ntpdate', '-q', $server); + + # Run ntpdate and capture output + my $output = qx{@cmd 2>&1}; + $c->app->log->info($output); + my $exit_code = $? >> 8; + + # Parse for known errors + if ($exit_code == 124) { + return $c->render(json => { success => 0, error => "Timeout: NTP server did not respond within $timeout seconds" }); + } + if ($output =~ /no server suitable for synchronization found/i) { + return $c->render(json => { success => 0, error => "No suitable NTP server found or server unreachable" }); + } + if ($output =~ /Name or service not known|Temporary failure in name resolution/i) { + return $c->render(json => { success => 0, error => "DNS resolution failed for $server" }); + } + if ($output =~ /ntpdig: no eligible servers/i) { + return $c->render(json => { success => 0, error => "Not a an NTP server" }); + } + if ($output =~ /permission denied/i) { + return $c->render(json => { success => 0, error => "Permission denied running ntpdate" }); + } + if ($exit_code != 0) { + return $c->render(json => { success => 0, error => "ntpdate failed (exit code $exit_code): $output" }); + } + + # Extract date and time down to seconds from adjust line + my ($datetime) = $output =~ /^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})/m; + + if ($datetime) { + return $c->render(json => { success => 1, time => $datetime }); + } else { + return $c->render(json => { success => 0, error => "Could not parse date/time from NTP server response." });} +} + 1; \ No newline at end of file diff --git a/root/usr/share/smanager/lib/SrvMngr/I18N/Modules/Datetime/datetime_en.lex b/root/usr/share/smanager/lib/SrvMngr/I18N/Modules/Datetime/datetime_en.lex index f0c0582..5fecd5e 100644 --- a/root/usr/share/smanager/lib/SrvMngr/I18N/Modules/Datetime/datetime_en.lex +++ b/root/usr/share/smanager/lib/SrvMngr/I18N/Modules/Datetime/datetime_en.lex @@ -1,5 +1,5 @@ - 'dat_FORM_TITLE' => 'Date and time configuration', +'dat_The_time_is_currently' => 'The time is currently:', 'dat_INITIAL_DESC' => 'This is where you configure the date and time of this server. You may use an existing network time server or manually set the date and time for your time zone.', 'dat_SET_DATE_TITLE' => 'Set Date and Time', @@ -58,4 +58,5 @@ clock, and will not try to synchronize from a time server.', 'dat_ntp_server' => 'NTP server', 'dat_manually_set' => 'Set manually', 'dat_NTP_Server_URL' =>'NTP Server URL:', -'dat_set_manually' =>'Set Date and Time:', \ No newline at end of file +'dat_set_manually' =>'Set Date and Time:', +'dat_Invalid_date' => 'Invalid date', \ No newline at end of file diff --git a/root/usr/share/smanager/themes/default/public/css/datetime.css b/root/usr/share/smanager/themes/default/public/css/datetime.css index b0289a7..766f86d 100644 --- a/root/usr/share/smanager/themes/default/public/css/datetime.css +++ b/root/usr/share/smanager/themes/default/public/css/datetime.css @@ -7,7 +7,7 @@ .datetime-label-col { background: #e8f3e2; /* light green */ padding: 1em 0em 0em 0em; - min-width: 192px; + min-width: 30%; display: flex; align-items: flex-start; justify-content: flex-end; @@ -16,7 +16,7 @@ } .datetime-label { - display: block; + display:inline-flex; } .datetime-fields-col { @@ -26,4 +26,26 @@ border: 1px solid #ccc; border-left: none; border-radius: 0 4px 4px 0; -} \ No newline at end of file +} + +.datetime-clock { + min-width: 20em; + display:inline-flex; + border:0px; + padding:5px; +} + +.datetime-clock-label { + background-color:#e8f3e2; + display:inline-flex; + width:30%; + font-weight:bold; + text-align:right; + + +} + +.ntp-test-result { font-weight: bold; } +.ntp-test-success { color: green; } +.ntp-test-error { color: red; } +.ntp-test-wait { color: #333; } \ No newline at end of file diff --git a/root/usr/share/smanager/themes/default/public/js/datetime.js b/root/usr/share/smanager/themes/default/public/js/datetime.js index 0f75a5b..6641923 100644 --- a/root/usr/share/smanager/themes/default/public/js/datetime.js +++ b/root/usr/share/smanager/themes/default/public/js/datetime.js @@ -15,4 +15,87 @@ document.addEventListener('DOMContentLoaded', function() { select.addEventListener('change', toggleSections); toggleSections(); // Set initial state +}); + +document.addEventListener('DOMContentLoaded', function() { + // Parse the initial server time from the input value + const clockElement = document.getElementById('real-time-clock'); + if (!clockElement) return; + + // Get the initial server time from the input's value + let serverTime = new Date(clockElement.value.replace(' ', 'T')); + + function updateDateTime() { + // Format the date/time string as desired + const daysOfWeek = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']; + const months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']; + + const dayOfWeek = daysOfWeek[serverTime.getDay()]; + const month = months[serverTime.getMonth()]; + const day = serverTime.getDate(); + const year = serverTime.getFullYear(); + + let hours = serverTime.getHours(); + const ampm = hours >= 12 ? 'PM' : 'AM'; + hours = hours % 12 || 12; + + const minutes = serverTime.getMinutes().toString().padStart(2, '0'); + const seconds = serverTime.getSeconds().toString().padStart(2, '0'); + + const dateTimeString = `${dayOfWeek}, ${month} ${day}, ${year} ${hours}:${minutes}:${seconds} ${ampm}`; + clockElement.value = dateTimeString; + + // Advance serverTime by one second + serverTime.setSeconds(serverTime.getSeconds() + 1); + } + + updateDateTime(); + setInterval(updateDateTime, 1000); +}); + +document.addEventListener('DOMContentLoaded', function() { + const btn = document.getElementById('test-ntp-btn'); + const input = document.getElementById('ntpserver'); + const result = document.getElementById('ntp-test-result'); + + btn.addEventListener('click', function() { + const server = input.value.trim(); + result.className = 'ntp-test-result'; // reset + + if (!server) { + result.textContent = "Please enter a server address."; + result.classList.add('ntp-test-error'); + return; + } + result.textContent = "Testing..."; + result.classList.add('ntp-test-wait'); + + fetch('/smanager/datetimet', { + method: 'POST', + headers: {'Content-Type': 'application/json'}, + body: JSON.stringify({ ntpserver: server }) + }) + .then(response => { + if (!response.ok) { + // HTTP error, e.g., 404, 500 + throw new Error(`HTTP error: ${response.status} ${response.statusText}`); + } + return response.json(); + }) + .then(data => { + result.className = 'ntp-test-result'; // reset + if (data.success) { + result.textContent = `Server time: ${data.time}`; + result.classList.add('ntp-test-success'); + } else { + result.textContent = `Error: ${data.error}`; + result.classList.add('ntp-test-error'); + } + }) + .catch(error => { + // Network error or thrown HTTP error + result.className = 'ntp-test-result ntp-test-error'; + result.textContent = `Request failed: ${error.message}`; + }); + }); }); \ No newline at end of file diff --git a/root/usr/share/smanager/themes/default/templates/datetime.html.ep b/root/usr/share/smanager/themes/default/templates/datetime.html.ep index c983e12..c795e13 100644 --- a/root/usr/share/smanager/themes/default/templates/datetime.html.ep +++ b/root/usr/share/smanager/themes/default/templates/datetime.html.ep @@ -27,7 +27,15 @@
+
+
+ %= label_for 'real-time-clock' => $c->l('dat_The_time_is_currently'), class => 'datetime-clock-label'
+
+
+ %= text_field 'clock', id => 'real-time-clock', readonly => 'readonly', class => 'datetime-clock' , value => $dat_data->{currentdatetime}
+
% if ($dat_data->{ntpstatus} eq 'disabled') {