xref: /reactos/dll/cpl/timedate/internettime.c (revision d7fd62d4)
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
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
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
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
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
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
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
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
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
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
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
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