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