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 } 308 309 static VOID 310 OnAutoSync(BOOL Sync) 311 { 312 HKEY hKey; 313 LONG lRet; 314 LPCWSTR szAuto; 315 316 if (Sync) 317 szAuto = L"NTP"; 318 else 319 szAuto = L"NoSync"; 320 321 lRet = RegOpenKeyExW(HKEY_LOCAL_MACHINE, 322 L"SYSTEM\\CurrentControlSet\\Services\\W32Time\\Parameters", 323 0, 324 KEY_SET_VALUE, 325 &hKey); 326 if (lRet != ERROR_SUCCESS) 327 { 328 DisplayWin32Error(lRet); 329 return; 330 } 331 332 lRet = RegSetValueExW(hKey, 333 L"Type", 334 0, 335 REG_SZ, 336 (LPBYTE)szAuto, 337 (wcslen(szAuto) + 1) * sizeof(WCHAR)); 338 if (lRet != ERROR_SUCCESS) 339 DisplayWin32Error(lRet); 340 341 RegCloseKey(hKey); 342 } 343 344 /* Property page dialog callback */ 345 INT_PTR CALLBACK 346 InetTimePageProc(HWND hwndDlg, 347 UINT uMsg, 348 WPARAM wParam, 349 LPARAM lParam) 350 { 351 switch (uMsg) 352 { 353 case WM_INITDIALOG: 354 OnInitDialog(hwndDlg); 355 break; 356 357 case WM_COMMAND: 358 switch(LOWORD(wParam)) 359 { 360 case IDC_UPDATEBUTTON: 361 { 362 DWORD dwError; 363 364 SetNTPServer(hwndDlg, TRUE); 365 366 dwError = W32TimeSyncNow(L"localhost", 0, 0); 367 UpdateNTPStatus(hwndDlg, dwError); 368 } 369 break; 370 371 case IDC_SERVERLIST: 372 if ((HIWORD(wParam) == CBN_SELCHANGE) || (HIWORD(wParam) == CBN_EDITCHANGE)) 373 { 374 /* Enable the 'Apply' button */ 375 PropSheet_Changed(GetParent(hwndDlg), hwndDlg); 376 } 377 break; 378 379 case IDC_AUTOSYNC: 380 if (HIWORD(wParam) == BN_CLICKED) 381 { 382 EnableDialogText(hwndDlg); 383 384 /* Enable the 'Apply' button */ 385 PropSheet_Changed(GetParent(hwndDlg), hwndDlg); 386 } 387 break; 388 } 389 break; 390 391 case WM_DESTROY: 392 break; 393 394 case WM_NOTIFY: 395 { 396 LPNMHDR lpnm = (LPNMHDR)lParam; 397 398 switch (lpnm->code) 399 { 400 case PSN_APPLY: 401 SetNTPServer(hwndDlg, FALSE); 402 403 if (SendDlgItemMessageW(hwndDlg, IDC_AUTOSYNC, BM_GETCHECK, 0, 0) == BST_CHECKED) 404 OnAutoSync(TRUE); 405 else 406 OnAutoSync(FALSE); 407 408 return TRUE; 409 410 default: 411 break; 412 } 413 } 414 break; 415 } 416 417 return FALSE; 418 } 419