1 /*
2 * PROJECT: ReactOS Timedate Control Panel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: dll/cpl/timedate/internettime.c
5 * PURPOSE: Internet Time property page
6 * COPYRIGHT: Copyright 2006 Ged Murphy <gedmurphy@gmail.com>
7 *
8 */
9
10 #include "timedate.h"
11 #include <stdlib.h>
12
13 DWORD WINAPI W32TimeSyncNow(LPCWSTR cmdline, UINT blocking, UINT flags);
14 SYNC_STATUS SyncStatus;
15
16 static VOID
CreateNTPServerList(HWND hwnd)17 CreateNTPServerList(HWND hwnd)
18 {
19 HWND hList;
20 WCHAR szValName[MAX_VALUE_NAME];
21 WCHAR szData[256];
22 DWORD dwIndex = 0;
23 DWORD dwValSize;
24 DWORD dwNameSize;
25 DWORD dwDefault = 1;
26 LONG lRet;
27 HKEY hKey;
28
29 hList = GetDlgItem(hwnd,
30 IDC_SERVERLIST);
31
32 lRet = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
33 L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\DateTime\\Servers",
34 0,
35 KEY_QUERY_VALUE,
36 &hKey);
37 if (lRet != ERROR_SUCCESS)
38 return;
39
40 while (TRUE)
41 {
42 dwValSize = MAX_VALUE_NAME * sizeof(WCHAR);
43 szValName[0] = L'\0';
44 lRet = RegEnumValueW(hKey,
45 dwIndex,
46 szValName,
47 &dwValSize,
48 NULL,
49 NULL,
50 (LPBYTE)szData,
51 &dwNameSize);
52 if (lRet == ERROR_SUCCESS)
53 {
54 /* Get date from default reg value */
55 if (wcscmp(szValName, L"") == 0) // if (Index == 0)
56 {
57 dwDefault = _wtoi(szData);
58 dwIndex++;
59 }
60 else
61 {
62 SendMessageW(hList,
63 CB_ADDSTRING,
64 0,
65 (LPARAM)szData);
66 dwIndex++;
67 }
68 }
69 else if (lRet != ERROR_MORE_DATA)
70 {
71 break;
72 }
73 }
74
75 if (dwDefault < 1 || dwDefault > dwIndex)
76 dwDefault = 1;
77
78 /* Server reg entries count from 1,
79 * Combo boxes count from 0 */
80 dwDefault--;
81
82 SendMessageW(hList,
83 CB_SETCURSEL,
84 dwDefault,
85 0);
86
87 RegCloseKey(hKey);
88 }
89
90
91 /* Set the selected server in the registry */
92 static VOID
SetNTPServer(HWND hwnd,BOOL bBeginUpdate)93 SetNTPServer(HWND hwnd, BOOL bBeginUpdate)
94 {
95 HKEY hKey;
96 HWND hList;
97 UINT uSel;
98 WCHAR szSel[4];
99 LONG lRet;
100 WCHAR buffer[256];
101 WCHAR szFormat[BUFSIZE];
102
103 hList = GetDlgItem(hwnd,
104 IDC_SERVERLIST);
105
106 uSel = (UINT)SendMessageW(hList, CB_GETCURSEL, 0, 0);
107
108 SendDlgItemMessageW(hwnd, IDC_SERVERLIST, WM_GETTEXT, _countof(buffer), (LPARAM)buffer);
109
110 /* If the condition is true that means the user wants to update (synchronize) the time */
111 if (bBeginUpdate)
112 {
113 /* Inform the user that the synchronization is about to begin (depending on how reachable the NTP server is) */
114 StringCchPrintfW(szFormat, _countof(szFormat), SyncStatus.szSyncWait, buffer);
115 SetDlgItemTextW(hwnd, IDC_SUCSYNC, szFormat);
116 }
117
118 /* If there is new data entered then save it in the registry
119 The same key name of "0" is used to store all user entered values
120 */
121 if (uSel == -1)
122 {
123 lRet = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
124 L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\DateTime\\Servers",
125 0,
126 KEY_SET_VALUE,
127 &hKey);
128 if (lRet != ERROR_SUCCESS)
129 {
130 DisplayWin32Error(lRet);
131 return;
132 }
133 lRet = RegSetValueExW(hKey,
134 L"0",
135 0,
136 REG_SZ,
137 (LPBYTE)buffer,
138 (wcslen(buffer) + 1) * sizeof(WCHAR));
139 if (lRet != ERROR_SUCCESS)
140 DisplayWin32Error(lRet);
141 }
142
143 /* Server reg entries count from 1,
144 * Combo boxes count from 0 */
145 uSel++;
146
147 /* Convert to wide char */
148 _itow(uSel, szSel, 10);
149
150 lRet = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
151 L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\DateTime\\Servers",
152 0,
153 KEY_SET_VALUE,
154 &hKey);
155 if (lRet != ERROR_SUCCESS)
156 {
157 DisplayWin32Error(lRet);
158 return;
159 }
160
161 lRet = RegSetValueExW(hKey,
162 L"",
163 0,
164 REG_SZ,
165 (LPBYTE)szSel,
166 (wcslen(szSel) + 1) * sizeof(WCHAR));
167 if (lRet != ERROR_SUCCESS)
168 DisplayWin32Error(lRet);
169
170 RegCloseKey(hKey);
171 }
172
173
174 static VOID
EnableDialogText(HWND hwnd)175 EnableDialogText(HWND hwnd)
176 {
177 BOOL bChecked;
178 UINT uCheck;
179
180 uCheck = (UINT)SendDlgItemMessageW(hwnd, IDC_AUTOSYNC, BM_GETCHECK, 0, 0);
181 bChecked = (uCheck == BST_CHECKED) ? TRUE : FALSE;
182
183 EnableWindow(GetDlgItem(hwnd, IDC_SERVERTEXT), bChecked);
184 EnableWindow(GetDlgItem(hwnd, IDC_SERVERLIST), bChecked);
185 EnableWindow(GetDlgItem(hwnd, IDC_UPDATEBUTTON), bChecked);
186 EnableWindow(GetDlgItem(hwnd, IDC_SUCSYNC), bChecked);
187 EnableWindow(GetDlgItem(hwnd, IDC_NEXTSYNC), bChecked);
188 }
189
190 static VOID
SyncNTPStatusInit(HWND hwnd)191 SyncNTPStatusInit(HWND hwnd)
192 {
193 /* Initialize the Synchronization NTP status members */
194 LoadStringW(hApplet, IDS_INETTIMEWELCOME, SyncStatus.szSyncInit, _countof(SyncStatus.szSyncInit));
195 LoadStringW(hApplet, IDS_INETTIMESUCSYNC, SyncStatus.szSyncSuc, _countof(SyncStatus.szSyncSuc));
196 LoadStringW(hApplet, IDS_INETTIMESYNCING, SyncStatus.szSyncWait, _countof(SyncStatus.szSyncWait));
197 LoadStringW(hApplet, IDS_INETTIMEERROR, SyncStatus.szSyncErr, _countof(SyncStatus.szSyncErr));
198 LoadStringW(hApplet, IDS_INETTIMESUCFILL, SyncStatus.szSyncType, _countof(SyncStatus.szSyncType));
199
200 /*
201 * TODO: XP's and Server 2003's timedate.cpl loads the last successful attempt of the NTP synchronization
202 * displaying the last time and date of the said sync. I have no idea how does timedate.cpl remember its last
203 * successful sync so for the time being, we will only load the initial remark string.
204 */
205 SetDlgItemTextW(hwnd, IDC_SUCSYNC, SyncStatus.szSyncInit);
206 }
207
208 static VOID
UpdateNTPStatus(HWND hwnd,DWORD dwReason)209 UpdateNTPStatus(HWND hwnd, DWORD dwReason)
210 {
211 WCHAR szFormat[BUFSIZE];
212 WCHAR szNtpServerName[MAX_VALUE_NAME];
213 WCHAR szLocalDate[BUFSIZE];
214 WCHAR szLocalTime[BUFSIZE];
215 HWND hDlgComboList;
216
217 /* Retrieve the server NTP name from the edit box */
218 hDlgComboList = GetDlgItem(hwnd, IDC_SERVERLIST);
219 SendMessageW(hDlgComboList, WM_GETTEXT, _countof(szNtpServerName), (LPARAM)szNtpServerName);
220
221 /* Iterate over the case reasons so we can compute the exact status of the NTP synchronization */
222 switch (dwReason)
223 {
224 /* The NTP time synchronization has completed successfully */
225 case ERROR_SUCCESS:
226 {
227 /* Get the current date based on the locale identifier */
228 GetDateFormatW(LOCALE_USER_DEFAULT,
229 DATE_SHORTDATE,
230 NULL,
231 NULL,
232 szLocalDate,
233 _countof(szLocalDate));
234
235 /* Get the current time based on the locale identifier */
236 GetTimeFormatW(LOCALE_USER_DEFAULT,
237 TIME_NOSECONDS,
238 NULL,
239 NULL,
240 szLocalTime,
241 _countof(szLocalTime));
242
243 /* Format the resource sting with the given NTP server name and the current time data */
244 StringCchPrintfW(szFormat, _countof(szFormat), SyncStatus.szSyncSuc, szNtpServerName, szLocalDate, szLocalTime);
245 SetDlgItemTextW(hwnd, IDC_SUCSYNC, szFormat);
246 break;
247 }
248
249 /* Empty field data has been caught -- simply tell the user to write the NTP name to continue */
250 case ERROR_INVALID_DATA:
251 {
252 SetDlgItemTextW(hwnd, IDC_SUCSYNC, SyncStatus.szSyncType);
253 DPRINT("UpdateNTPStatus(): The user didn't submit any NTP server name!\n");
254 break;
255 }
256
257 /* General failure -- the NTP synchronization has failed for whatever reason */
258 default:
259 {
260 StringCchPrintfW(szFormat, _countof(szFormat), SyncStatus.szSyncErr, szNtpServerName);
261 SetDlgItemTextW(hwnd, IDC_SUCSYNC, szFormat);
262 DPRINT("UpdateNTPStatus(): Failed to synchronize the time! (Error: %lu).\n", dwReason);
263 break;
264 }
265 }
266 }
267
268 static VOID
GetSyncSetting(HWND hwnd)269 GetSyncSetting(HWND hwnd)
270 {
271 HKEY hKey;
272 WCHAR szData[8];
273 DWORD dwSize;
274
275 if (RegOpenKeyExW(HKEY_LOCAL_MACHINE,
276 L"SYSTEM\\CurrentControlSet\\Services\\W32Time\\Parameters",
277 0,
278 KEY_QUERY_VALUE,
279 &hKey) == ERROR_SUCCESS)
280 {
281 dwSize = 8 * sizeof(WCHAR);
282 if (RegQueryValueExW(hKey,
283 L"Type",
284 NULL,
285 NULL,
286 (LPBYTE)szData,
287 &dwSize) == ERROR_SUCCESS)
288 {
289 if (wcscmp(szData, L"NTP") == 0)
290 SendDlgItemMessageW(hwnd, IDC_AUTOSYNC, BM_SETCHECK, BST_CHECKED, 0);
291 else
292 SendDlgItemMessageW(hwnd, IDC_AUTOSYNC, BM_SETCHECK, BST_UNCHECKED, 0);
293 }
294
295 RegCloseKey(hKey);
296 }
297 }
298
299
300 static VOID
OnInitDialog(HWND hwnd)301 OnInitDialog(HWND hwnd)
302 {
303 GetSyncSetting(hwnd);
304 EnableDialogText(hwnd);
305 CreateNTPServerList(hwnd);
306 SyncNTPStatusInit(hwnd);
307 SetWindowLongPtr(hwnd, DWLP_USER, (LONG_PTR)FALSE);
308 }
309
310 static VOID
OnAutoSync(BOOL Sync)311 OnAutoSync(BOOL Sync)
312 {
313 HKEY hKey;
314 LONG lRet;
315 LPCWSTR szAuto;
316
317 if (Sync)
318 szAuto = L"NTP";
319 else
320 szAuto = L"NoSync";
321
322 lRet = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
323 L"SYSTEM\\CurrentControlSet\\Services\\W32Time\\Parameters",
324 0,
325 KEY_SET_VALUE,
326 &hKey);
327 if (lRet != ERROR_SUCCESS)
328 {
329 DisplayWin32Error(lRet);
330 return;
331 }
332
333 lRet = RegSetValueExW(hKey,
334 L"Type",
335 0,
336 REG_SZ,
337 (LPBYTE)szAuto,
338 (wcslen(szAuto) + 1) * sizeof(WCHAR));
339 if (lRet != ERROR_SUCCESS)
340 DisplayWin32Error(lRet);
341
342 RegCloseKey(hKey);
343 }
344
345 static DWORD WINAPI
UpdateThread(_In_ LPVOID lpParameter)346 UpdateThread(
347 _In_ LPVOID lpParameter)
348 {
349 HWND hwndDlg;
350 DWORD dwError;
351
352 hwndDlg = (HWND)lpParameter;
353
354 SetNTPServer(hwndDlg, TRUE);
355
356 dwError = W32TimeSyncNow(L"localhost", 0, 0);
357 UpdateNTPStatus(hwndDlg, dwError);
358
359 SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG_PTR)FALSE);
360 return 0;
361 }
362
363 static VOID
OnUpdate(HWND hwndDlg)364 OnUpdate(
365 HWND hwndDlg)
366 {
367 if ((BOOL)GetWindowLongPtr(hwndDlg, DWLP_USER) == FALSE)
368 {
369 SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG_PTR)TRUE);
370
371 if (CreateThread(NULL, 0, UpdateThread, (PVOID)hwndDlg, 0, NULL) == NULL)
372 {
373 UpdateNTPStatus(hwndDlg, GetLastError());
374 SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG_PTR)FALSE);
375 }
376 }
377 }
378
379 /* Property page dialog callback */
380 INT_PTR CALLBACK
InetTimePageProc(HWND hwndDlg,UINT uMsg,WPARAM wParam,LPARAM lParam)381 InetTimePageProc(HWND hwndDlg,
382 UINT uMsg,
383 WPARAM wParam,
384 LPARAM lParam)
385 {
386 switch (uMsg)
387 {
388 case WM_INITDIALOG:
389 OnInitDialog(hwndDlg);
390 break;
391
392 case WM_COMMAND:
393 switch(LOWORD(wParam))
394 {
395 case IDC_UPDATEBUTTON:
396 OnUpdate(hwndDlg);
397 break;
398
399 case IDC_SERVERLIST:
400 if ((HIWORD(wParam) == CBN_SELCHANGE) || (HIWORD(wParam) == CBN_EDITCHANGE))
401 {
402 /* Enable the 'Apply' button */
403 PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
404 }
405 break;
406
407 case IDC_AUTOSYNC:
408 if (HIWORD(wParam) == BN_CLICKED)
409 {
410 EnableDialogText(hwndDlg);
411
412 /* Enable the 'Apply' button */
413 PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
414 }
415 break;
416 }
417 break;
418
419 case WM_DESTROY:
420 break;
421
422 case WM_NOTIFY:
423 {
424 LPNMHDR lpnm = (LPNMHDR)lParam;
425
426 switch (lpnm->code)
427 {
428 case PSN_APPLY:
429 SetNTPServer(hwndDlg, FALSE);
430
431 if (SendDlgItemMessageW(hwndDlg, IDC_AUTOSYNC, BM_GETCHECK, 0, 0) == BST_CHECKED)
432 OnAutoSync(TRUE);
433 else
434 OnAutoSync(FALSE);
435
436 return TRUE;
437
438 default:
439 break;
440 }
441 }
442 break;
443 }
444
445 return FALSE;
446 }
447