reactos/dll/cpl/timedate/internettime.c
Bișoc George 5a81a5bc14
[TIMEDATE] Implement the dynamic NTP status notification (#2352)
Time/Date control panel applet displays the status of the NTP server synchronization with the system informing the user the actual condition of the said sync, if it has failed or suceeded. The commit also implements IDS_INETTIMEWELCOME which serves as a placeholder as Time/Date loads the last successful attempt of NTP synchronization which I don't know how the CPL remembers it. With that said, IDS_INETTIMEWELCOME will stay for a while until someone figures out how timedate.cpl works internally.
CORE-16684
2020-02-24 19:56:02 +09:00

424 lines
13 KiB
C

/*
* PROJECT: ReactOS Timedate Control Panel
* LICENSE: GPL - See COPYING in the top level directory
* FILE: dll/cpl/timedate/internettime.c
* PURPOSE: Internet Time property page
* COPYRIGHT: Copyright 2006 Ged Murphy <gedmurphy@gmail.com>
*
*/
#include "timedate.h"
#include <stdlib.h>
DWORD WINAPI W32TimeSyncNow(LPCWSTR cmdline, UINT blocking, UINT flags);
SYNC_STATUS SyncStatus;
static VOID
CreateNTPServerList(HWND hwnd)
{
HWND hList;
WCHAR szValName[MAX_VALUE_NAME];
WCHAR szData[256];
DWORD dwIndex = 0;
DWORD dwValSize;
DWORD dwNameSize;
DWORD dwDefault = 1;
LONG lRet;
HKEY hKey;
hList = GetDlgItem(hwnd,
IDC_SERVERLIST);
lRet = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\DateTime\\Servers",
0,
KEY_QUERY_VALUE,
&hKey);
if (lRet != ERROR_SUCCESS)
return;
while (TRUE)
{
dwValSize = MAX_VALUE_NAME * sizeof(WCHAR);
szValName[0] = L'\0';
lRet = RegEnumValueW(hKey,
dwIndex,
szValName,
&dwValSize,
NULL,
NULL,
(LPBYTE)szData,
&dwNameSize);
if (lRet == ERROR_SUCCESS)
{
/* Get date from default reg value */
if (wcscmp(szValName, L"") == 0) // if (Index == 0)
{
dwDefault = _wtoi(szData);
dwIndex++;
}
else
{
SendMessageW(hList,
CB_ADDSTRING,
0,
(LPARAM)szData);
dwIndex++;
}
}
else if (lRet != ERROR_MORE_DATA)
{
break;
}
}
if (dwDefault < 1 || dwDefault > dwIndex)
dwDefault = 1;
/* Server reg entries count from 1,
* Combo boxes count from 0 */
dwDefault--;
SendMessageW(hList,
CB_SETCURSEL,
dwDefault,
0);
RegCloseKey(hKey);
}
/* Set the selected server in the registry */
static VOID
SetNTPServer(HWND hwnd, BOOL bBeginUpdate)
{
HKEY hKey;
HWND hList;
UINT uSel;
WCHAR szSel[4];
LONG lRet;
WCHAR buffer[256];
WCHAR szFormat[BUFSIZE];
hList = GetDlgItem(hwnd,
IDC_SERVERLIST);
uSel = (UINT)SendMessageW(hList, CB_GETCURSEL, 0, 0);
SendDlgItemMessageW(hwnd, IDC_SERVERLIST, WM_GETTEXT, _countof(buffer), (LPARAM)buffer);
/* If the condition is true that means the user wants to update (synchronize) the time */
if (bBeginUpdate)
{
/* Inform the user that the synchronization is about to begin (depending on how reachable the NTP server is) */
StringCchPrintfW(szFormat, _countof(szFormat), SyncStatus.szSyncWait, buffer);
SyncStatus.lpszSyncStatus = szFormat;
SetDlgItemTextW(hwnd, IDC_SUCSYNC, SyncStatus.lpszSyncStatus);
}
/* If there is new data entered then save it in the registry
The same key name of "0" is used to store all user entered values
*/
if (uSel == -1)
{
lRet = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\DateTime\\Servers",
0,
KEY_SET_VALUE,
&hKey);
if (lRet != ERROR_SUCCESS)
{
DisplayWin32Error(lRet);
return;
}
lRet = RegSetValueExW(hKey,
L"0",
0,
REG_SZ,
(LPBYTE)buffer,
(wcslen(buffer) + 1) * sizeof(WCHAR));
if (lRet != ERROR_SUCCESS)
DisplayWin32Error(lRet);
}
/* Server reg entries count from 1,
* Combo boxes count from 0 */
uSel++;
/* Convert to wide char */
_itow(uSel, szSel, 10);
lRet = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\DateTime\\Servers",
0,
KEY_SET_VALUE,
&hKey);
if (lRet != ERROR_SUCCESS)
{
DisplayWin32Error(lRet);
return;
}
lRet = RegSetValueExW(hKey,
L"",
0,
REG_SZ,
(LPBYTE)szSel,
(wcslen(szSel) + 1) * sizeof(WCHAR));
if (lRet != ERROR_SUCCESS)
DisplayWin32Error(lRet);
RegCloseKey(hKey);
}
static VOID
EnableDialogText(HWND hwnd)
{
BOOL bChecked;
UINT uCheck;
uCheck = (UINT)SendDlgItemMessageW(hwnd, IDC_AUTOSYNC, BM_GETCHECK, 0, 0);
bChecked = (uCheck == BST_CHECKED) ? TRUE : FALSE;
EnableWindow(GetDlgItem(hwnd, IDC_SERVERTEXT), bChecked);
EnableWindow(GetDlgItem(hwnd, IDC_SERVERLIST), bChecked);
EnableWindow(GetDlgItem(hwnd, IDC_UPDATEBUTTON), bChecked);
EnableWindow(GetDlgItem(hwnd, IDC_SUCSYNC), bChecked);
EnableWindow(GetDlgItem(hwnd, IDC_NEXTSYNC), bChecked);
}
static VOID
SyncNTPStatusInit(HWND hwnd)
{
/* Initialize the Synchronization NTP status members */
LoadStringW(hApplet, IDS_INETTIMEWELCOME, SyncStatus.szSyncInit, _countof(SyncStatus.szSyncInit));
LoadStringW(hApplet, IDS_INETTIMESUCSYNC, SyncStatus.szSyncSuc, _countof(SyncStatus.szSyncSuc));
LoadStringW(hApplet, IDS_INETTIMESYNCING, SyncStatus.szSyncWait, _countof(SyncStatus.szSyncWait));
LoadStringW(hApplet, IDS_INETTIMEERROR, SyncStatus.szSyncErr, _countof(SyncStatus.szSyncErr));
LoadStringW(hApplet, IDS_INETTIMESUCFILL, SyncStatus.szSyncType, _countof(SyncStatus.szSyncType));
/*
* TODO: XP's and Server 2003's timedate.cpl loads the last successful attempt of the NTP synchronization
* displaying the last time and date of the said sync. I have no idea how does timedate.cpl remember its last
* successful sync so for the time being, we will only load the initial remark string.
*/
SyncStatus.lpszSyncStatus = SyncStatus.szSyncInit;
SetDlgItemTextW(hwnd, IDC_SUCSYNC, SyncStatus.lpszSyncStatus);
}
static VOID
UpdateNTPStatus(HWND hwnd, DWORD dwReason)
{
WCHAR szFormat[BUFSIZE];
WCHAR szNtpServerName[MAX_VALUE_NAME];
WCHAR szLocalDate[BUFSIZE];
WCHAR szLocalTime[BUFSIZE];
HWND hDlgComboList;
/* Retrieve the server NTP name from the edit box */
hDlgComboList = GetDlgItem(hwnd, IDC_SERVERLIST);
SendMessageW(hDlgComboList, WM_GETTEXT, _countof(szNtpServerName), (LPARAM)szNtpServerName);
/* Iterate over the case reasons so we can compute the exact status of the NTP synchronization */
switch (dwReason)
{
/* The NTP time synchronization has completed successfully */
case ERROR_SUCCESS:
{
/* Get the current date based on the locale identifier */
GetDateFormatW(LOCALE_USER_DEFAULT,
DATE_SHORTDATE,
NULL,
NULL,
szLocalDate,
_countof(szLocalDate));
/* Get the current time based on the locale identifier */
GetTimeFormatW(LOCALE_USER_DEFAULT,
TIME_NOSECONDS,
NULL,
NULL,
szLocalTime,
_countof(szLocalTime));
/* Format the resource sting with the given NTP server name and the current time data */
StringCchPrintfW(szFormat, _countof(szFormat), SyncStatus.szSyncSuc, szNtpServerName, szLocalDate, szLocalTime);
SyncStatus.lpszSyncStatus = szFormat;
SetDlgItemTextW(hwnd, IDC_SUCSYNC, SyncStatus.lpszSyncStatus);
break;
}
/* Empty field data has been caught -- simply tell the user to write the NTP name to continue */
case ERROR_INVALID_DATA:
{
SyncStatus.lpszSyncStatus = SyncStatus.szSyncType;
SetDlgItemTextW(hwnd, IDC_SUCSYNC, SyncStatus.lpszSyncStatus);
DPRINT("UpdateNTPStatus(): The user didn't submit any NTP server name!\n");
break;
}
/* General failure -- the NTP synchronization has failed for whatever reason */
default:
{
StringCchPrintfW(szFormat, _countof(szFormat), SyncStatus.szSyncErr, szNtpServerName);
SyncStatus.lpszSyncStatus = szFormat;
SetDlgItemTextW(hwnd, IDC_SUCSYNC, SyncStatus.lpszSyncStatus);
DPRINT("UpdateNTPStatus(): Failed to synchronize the time! (Error: %lu).\n", dwReason);
break;
}
}
}
static VOID
GetSyncSetting(HWND hwnd)
{
HKEY hKey;
WCHAR szData[8];
DWORD dwSize;
if (RegOpenKeyExW(HKEY_LOCAL_MACHINE,
L"SYSTEM\\CurrentControlSet\\Services\\W32Time\\Parameters",
0,
KEY_QUERY_VALUE,
&hKey) == ERROR_SUCCESS)
{
dwSize = 8 * sizeof(WCHAR);
if (RegQueryValueExW(hKey,
L"Type",
NULL,
NULL,
(LPBYTE)szData,
&dwSize) == ERROR_SUCCESS)
{
if (wcscmp(szData, L"NTP") == 0)
SendDlgItemMessageW(hwnd, IDC_AUTOSYNC, BM_SETCHECK, BST_CHECKED, 0);
else
SendDlgItemMessageW(hwnd, IDC_AUTOSYNC, BM_SETCHECK, BST_UNCHECKED, 0);
}
RegCloseKey(hKey);
}
}
static VOID
OnInitDialog(HWND hwnd)
{
GetSyncSetting(hwnd);
EnableDialogText(hwnd);
CreateNTPServerList(hwnd);
SyncNTPStatusInit(hwnd);
}
static VOID
OnAutoSync(BOOL Sync)
{
HKEY hKey;
LONG lRet;
LPCWSTR szAuto;
if (Sync)
szAuto = L"NTP";
else
szAuto = L"NoSync";
lRet = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
L"SYSTEM\\CurrentControlSet\\Services\\W32Time\\Parameters",
0,
KEY_SET_VALUE,
&hKey);
if (lRet != ERROR_SUCCESS)
{
DisplayWin32Error(lRet);
return;
}
lRet = RegSetValueExW(hKey,
L"Type",
0,
REG_SZ,
(LPBYTE)szAuto,
(wcslen(szAuto) + 1) * sizeof(WCHAR));
if (lRet != ERROR_SUCCESS)
DisplayWin32Error(lRet);
RegCloseKey(hKey);
}
/* Property page dialog callback */
INT_PTR CALLBACK
InetTimePageProc(HWND hwndDlg,
UINT uMsg,
WPARAM wParam,
LPARAM lParam)
{
switch (uMsg)
{
case WM_INITDIALOG:
OnInitDialog(hwndDlg);
break;
case WM_COMMAND:
switch(LOWORD(wParam))
{
case IDC_UPDATEBUTTON:
{
DWORD dwError;
SetNTPServer(hwndDlg, TRUE);
dwError = W32TimeSyncNow(L"localhost", 0, 0);
UpdateNTPStatus(hwndDlg, dwError);
}
break;
case IDC_SERVERLIST:
if ((HIWORD(wParam) == CBN_SELCHANGE) || (HIWORD(wParam) == CBN_EDITCHANGE))
{
/* Enable the 'Apply' button */
PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
}
break;
case IDC_AUTOSYNC:
if (HIWORD(wParam) == BN_CLICKED)
{
EnableDialogText(hwndDlg);
/* Enable the 'Apply' button */
PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
}
break;
}
break;
case WM_DESTROY:
break;
case WM_NOTIFY:
{
LPNMHDR lpnm = (LPNMHDR)lParam;
switch (lpnm->code)
{
case PSN_APPLY:
SetNTPServer(hwndDlg, FALSE);
if (SendDlgItemMessageW(hwndDlg, IDC_AUTOSYNC, BM_GETCHECK, 0, 0) == BST_CHECKED)
OnAutoSync(TRUE);
else
OnAutoSync(FALSE);
return TRUE;
default:
break;
}
}
break;
}
return FALSE;
}