tcp: Fix 64 bit build with debugging features enabled.
[haiku.git] / src / preferences / time / NetworkTimeView.cpp
blob4d44b3071a4aec7520cbced4e82d41c56ae8da05
1 /*
2 * Copyright 2011-2014 Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
5 * Authors:
6 * Axel Dörfler, axeld@pinc-software.de
7 * Hamish Morrison, hamish@lavabit.com
8 * John Scipione, jscipione@gmail.com
9 */
12 #include "NetworkTimeView.h"
14 #include <ctype.h>
15 #include <stdio.h>
16 #include <string.h>
18 #include <Alert.h>
19 #include <Button.h>
20 #include <Catalog.h>
21 #include <CheckBox.h>
22 #include <File.h>
23 #include <FindDirectory.h>
24 #include <Invoker.h>
25 #include <ListItem.h>
26 #include <ListView.h>
27 #include <Path.h>
28 #include <ScrollView.h>
29 #include <Size.h>
30 #include <TextControl.h>
32 #include "ntp.h"
33 #include "TimeMessages.h"
36 #undef B_TRANSLATION_CONTEXT
37 #define B_TRANSLATION_CONTEXT "Time"
40 // #pragma mark - Settings
43 Settings::Settings()
45 fMessage(kMsgNetworkTimeSettings)
47 ResetToDefaults();
48 Load();
52 Settings::~Settings()
54 Save();
58 void
59 Settings::AddServer(const char* server)
61 if (_GetStringByValue("server", server) == B_ERROR)
62 fMessage.AddString("server", server);
66 const char*
67 Settings::GetServer(int32 index) const
69 const char* server;
70 fMessage.FindString("server", index, &server);
71 return server;
75 void
76 Settings::RemoveServer(const char* server)
78 int32 index = _GetStringByValue("server", server);
79 if (index != B_ERROR) {
80 fMessage.RemoveData("server", index);
82 int32 count;
83 fMessage.GetInfo("server", NULL, &count);
84 if (GetDefaultServer() >= count)
85 SetDefaultServer(count - 1);
90 void
91 Settings::SetDefaultServer(int32 index)
93 if (fMessage.ReplaceInt32("default server", index) != B_OK)
94 fMessage.AddInt32("default server", index);
98 int32
99 Settings::GetDefaultServer() const
101 int32 index;
102 fMessage.FindInt32("default server", &index);
103 return index;
107 void
108 Settings::SetTryAllServers(bool boolean)
110 fMessage.ReplaceBool("try all servers", boolean);
114 bool
115 Settings::GetTryAllServers() const
117 bool boolean;
118 fMessage.FindBool("try all servers", &boolean);
119 return boolean;
123 void
124 Settings::SetSynchronizeAtBoot(bool boolean)
126 fMessage.ReplaceBool("synchronize at boot", boolean);
130 bool
131 Settings::GetSynchronizeAtBoot() const
133 bool boolean;
134 fMessage.FindBool("synchronize at boot", &boolean);
135 return boolean;
139 void
140 Settings::ResetServersToDefaults()
142 fMessage.RemoveName("server");
144 fMessage.AddString("server", "pool.ntp.org");
145 fMessage.AddString("server", "de.pool.ntp.org");
146 fMessage.AddString("server", "time.nist.gov");
148 if (fMessage.ReplaceInt32("default server", 0) != B_OK)
149 fMessage.AddInt32("default server", 0);
153 void
154 Settings::ResetToDefaults()
156 fMessage.MakeEmpty();
157 ResetServersToDefaults();
159 fMessage.AddBool("synchronize at boot", true);
160 fMessage.AddBool("try all servers", true);
164 void
165 Settings::Revert()
167 fMessage = fOldMessage;
171 bool
172 Settings::SettingsChanged()
174 ssize_t oldSize = fOldMessage.FlattenedSize();
175 ssize_t newSize = fMessage.FlattenedSize();
177 if (oldSize != newSize || oldSize < 0 || newSize < 0)
178 return true;
180 char* oldBytes = new (std::nothrow) char[oldSize];
181 if (oldBytes == NULL)
182 return true;
184 fOldMessage.Flatten(oldBytes, oldSize);
185 char* newBytes = new (std::nothrow) char[newSize];
186 if (newBytes == NULL) {
187 delete[] oldBytes;
188 return true;
190 fMessage.Flatten(newBytes, newSize);
192 int result = memcmp(oldBytes, newBytes, oldSize);
194 delete[] oldBytes;
195 delete[] newBytes;
197 return result != 0;
201 status_t
202 Settings::Load()
204 status_t status;
206 BPath path;
207 if ((status = _GetPath(path)) != B_OK)
208 return status;
210 BFile file(path.Path(), B_READ_ONLY);
211 if ((status = file.InitCheck()) != B_OK)
212 return status;
214 BMessage load;
215 if ((status = load.Unflatten(&file)) != B_OK)
216 return status;
218 if (load.what != kMsgNetworkTimeSettings)
219 return B_BAD_TYPE;
221 fMessage = load;
222 fOldMessage = fMessage;
223 return B_OK;
227 status_t
228 Settings::Save()
230 status_t status;
232 BPath path;
233 if ((status = _GetPath(path)) != B_OK)
234 return status;
236 BFile file(path.Path(), B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE);
237 if ((status = file.InitCheck()) != B_OK)
238 return status;
240 file.SetSize(0);
242 return fMessage.Flatten(&file);
246 int32
247 Settings::_GetStringByValue(const char* name, const char* value)
249 const char* string;
250 for (int32 index = 0; fMessage.FindString(name, index, &string) == B_OK;
251 index++) {
252 if (strcmp(string, value) == 0)
253 return index;
256 return B_ERROR;
260 status_t
261 Settings::_GetPath(BPath& path)
263 status_t status = find_directory(B_USER_SETTINGS_DIRECTORY, &path);
264 if (status != B_OK)
265 return status;
267 path.Append("networktime settings");
269 return B_OK;
273 // #pragma mark - NetworkTimeView
276 NetworkTimeView::NetworkTimeView(const char* name)
278 BGroupView(name, B_VERTICAL, B_USE_DEFAULT_SPACING),
279 fSettings(),
280 fServerTextControl(NULL),
281 fAddButton(NULL),
282 fRemoveButton(NULL),
283 fResetButton(NULL),
284 fServerListView(NULL),
285 fTryAllServersCheckBox(NULL),
286 fSynchronizeAtBootCheckBox(NULL),
287 fSynchronizeButton(NULL),
288 fTextColor(ui_color(B_CONTROL_TEXT_COLOR)),
289 fInvalidColor(ui_color(B_FAILURE_COLOR)),
290 fUpdateThread(-1)
292 fSettings.Load();
293 _InitView();
297 NetworkTimeView::~NetworkTimeView()
299 delete fServerTextControl;
300 delete fAddButton;
301 delete fRemoveButton;
302 delete fResetButton;
303 delete fServerListView;
304 delete fTryAllServersCheckBox;
305 delete fSynchronizeAtBootCheckBox;
306 delete fSynchronizeButton;
310 void
311 NetworkTimeView::MessageReceived(BMessage* message)
313 switch (message->what) {
314 case kMsgSetDefaultServer:
316 int32 currentSelection = fServerListView->CurrentSelection();
317 if (currentSelection < 0)
318 fServerListView->Select(fSettings.GetDefaultServer());
319 else {
320 fSettings.SetDefaultServer(currentSelection);
321 Looper()->PostMessage(new BMessage(kMsgChange));
323 break;
326 case kMsgServerEdited:
328 bool isValid = _IsValidServerName(fServerTextControl->Text());
329 fServerTextControl->TextView()->SetFontAndColor(0,
330 fServerTextControl->TextView()->TextLength(), NULL, 0,
331 isValid ? &fTextColor : &fInvalidColor);
332 fAddButton->SetEnabled(isValid);
333 break;
336 case kMsgAddServer:
337 if (!_IsValidServerName(fServerTextControl->Text()))
338 break;
340 fSettings.AddServer(fServerTextControl->Text());
341 _UpdateServerList();
342 fServerTextControl->SetText("");
343 Looper()->PostMessage(new BMessage(kMsgChange));
344 break;
346 case kMsgRemoveServer:
348 int32 currentSelection = fServerListView->CurrentSelection();
349 if (currentSelection < 0)
350 break;
352 fSettings.RemoveServer(((BStringItem*)
353 fServerListView->ItemAt(currentSelection))->Text());
354 _UpdateServerList();
355 Looper()->PostMessage(new BMessage(kMsgChange));
356 break;
359 case kMsgResetServerList:
360 fSettings.ResetServersToDefaults();
361 _UpdateServerList();
362 Looper()->PostMessage(new BMessage(kMsgChange));
363 break;
365 case kMsgTryAllServers:
366 fSettings.SetTryAllServers(
367 fTryAllServersCheckBox->Value());
368 Looper()->PostMessage(new BMessage(kMsgChange));
369 break;
371 case kMsgSynchronizeAtBoot:
372 fSettings.SetSynchronizeAtBoot(fSynchronizeAtBootCheckBox->Value());
373 Looper()->PostMessage(new BMessage(kMsgChange));
374 break;
376 case kMsgStopSynchronization:
377 if (fUpdateThread >= B_OK)
378 kill_thread(fUpdateThread);
380 _DoneSynchronizing();
381 break;
383 case kMsgSynchronize:
385 if (fUpdateThread >= B_OK)
386 break;
388 BMessenger* messenger = new BMessenger(this);
389 update_time(fSettings, messenger, &fUpdateThread);
390 fSynchronizeButton->SetLabel(B_TRANSLATE("Stop"));
391 fSynchronizeButton->Message()->what = kMsgStopSynchronization;
392 break;
395 case kMsgSynchronizationResult:
397 _DoneSynchronizing();
399 status_t status;
400 if (message->FindInt32("status", (int32 *)&status) == B_OK) {
401 if (status == B_OK)
402 return;
404 const char* errorString;
405 message->FindString("error string", &errorString);
406 char buffer[256];
408 int32 errorCode;
409 if (message->FindInt32("error code", &errorCode) == B_OK) {
410 snprintf(buffer, sizeof(buffer),
411 B_TRANSLATE("The following error occured "
412 "while synchronizing:\r\n%s: %s"),
413 errorString, strerror(errorCode));
414 } else {
415 snprintf(buffer, sizeof(buffer),
416 B_TRANSLATE("The following error occured "
417 "while synchronizing:\r\n%s"),
418 errorString);
421 BAlert* alert = new BAlert(B_TRANSLATE("Time"), buffer,
422 B_TRANSLATE("OK"));
423 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
424 alert->Go();
426 break;
429 case kMsgRevert:
430 fSettings.Revert();
431 fTryAllServersCheckBox->SetValue(fSettings.GetTryAllServers());
432 fSynchronizeAtBootCheckBox->SetValue(
433 fSettings.GetSynchronizeAtBoot());
434 _UpdateServerList();
435 break;
440 void
441 NetworkTimeView::AttachedToWindow()
443 fServerTextControl->SetTarget(this);
444 fServerListView->SetTarget(this);
445 fAddButton->SetTarget(this);
446 fAddButton->SetEnabled(false);
447 fRemoveButton->SetTarget(this);
448 fResetButton->SetTarget(this);
449 fTryAllServersCheckBox->SetTarget(this);
450 fSynchronizeAtBootCheckBox->SetTarget(this);
451 fSynchronizeButton->SetTarget(this);
455 bool
456 NetworkTimeView::CheckCanRevert()
458 return fSettings.SettingsChanged();
462 void
463 NetworkTimeView::_InitView()
465 fServerTextControl = new BTextControl(NULL, NULL,
466 new BMessage(kMsgAddServer));
467 fServerTextControl->SetModificationMessage(new BMessage(kMsgServerEdited));
469 const float kButtonWidth = fServerTextControl->Frame().Height();
471 fAddButton = new BButton("add", "+", new BMessage(kMsgAddServer));
472 fAddButton->SetToolTip(B_TRANSLATE("Add"));
473 fAddButton->SetExplicitSize(BSize(kButtonWidth, kButtonWidth));
475 fRemoveButton = new BButton("remove", "−", new BMessage(kMsgRemoveServer));
476 fRemoveButton->SetToolTip(B_TRANSLATE("Remove"));
477 fRemoveButton->SetExplicitSize(BSize(kButtonWidth, kButtonWidth));
479 fServerListView = new BListView("serverList");
480 fServerListView->SetExplicitMinSize(BSize(B_SIZE_UNSET, kButtonWidth * 4));
481 fServerListView->SetSelectionMessage(new BMessage(kMsgSetDefaultServer));
482 BScrollView* scrollView = new BScrollView("serverScrollView",
483 fServerListView, B_FRAME_EVENTS | B_WILL_DRAW, false, true);
484 _UpdateServerList();
486 fTryAllServersCheckBox = new BCheckBox("tryAllServers",
487 B_TRANSLATE("Try all servers"), new BMessage(kMsgTryAllServers));
488 fTryAllServersCheckBox->SetValue(fSettings.GetTryAllServers());
490 fSynchronizeAtBootCheckBox = new BCheckBox("autoUpdate",
491 B_TRANSLATE("Synchronize at boot"),
492 new BMessage(kMsgSynchronizeAtBoot));
493 fSynchronizeAtBootCheckBox->SetValue(fSettings.GetSynchronizeAtBoot());
495 fResetButton = new BButton("reset",
496 B_TRANSLATE("Reset to default server list"),
497 new BMessage(kMsgResetServerList));
499 fSynchronizeButton = new BButton("update", B_TRANSLATE("Synchronize"),
500 new BMessage(kMsgSynchronize));
502 BLayoutBuilder::Group<>(this, B_VERTICAL)
503 .AddGroup(B_VERTICAL, B_USE_SMALL_SPACING)
504 .AddGroup(B_HORIZONTAL, B_USE_SMALL_SPACING)
505 .Add(fServerTextControl)
506 .Add(fAddButton)
507 .End()
508 .AddGroup(B_HORIZONTAL, B_USE_SMALL_SPACING)
509 .Add(scrollView)
510 .AddGroup(B_VERTICAL, B_USE_SMALL_SPACING)
511 .Add(fRemoveButton)
512 .AddGlue()
513 .End()
514 .End()
515 .End()
516 .AddGroup(B_HORIZONTAL)
517 .AddGroup(B_VERTICAL, 0)
518 .Add(fTryAllServersCheckBox)
519 .Add(fSynchronizeAtBootCheckBox)
520 .End()
521 .End()
522 .AddGroup(B_HORIZONTAL)
523 .AddGlue()
524 .Add(fResetButton)
525 .Add(fSynchronizeButton)
526 .End()
527 .SetInsets(B_USE_DEFAULT_SPACING);
531 void
532 NetworkTimeView::_UpdateServerList()
534 BListItem* item;
535 while ((item = fServerListView->RemoveItem((int32)0)) != NULL)
536 delete item;
538 const char* server;
539 int32 index = 0;
540 while ((server = fSettings.GetServer(index++)) != NULL)
541 fServerListView->AddItem(new BStringItem(server));
543 fServerListView->Select(fSettings.GetDefaultServer());
544 fServerListView->ScrollToSelection();
546 fRemoveButton->SetEnabled(fServerListView->CountItems() > 0);
550 void
551 NetworkTimeView::_DoneSynchronizing()
553 fUpdateThread = -1;
554 fSynchronizeButton->SetLabel(B_TRANSLATE("Synchronize again"));
555 fSynchronizeButton->Message()->what = kMsgSynchronize;
559 bool
560 NetworkTimeView::_IsValidServerName(const char* serverName)
562 if (serverName == NULL || *serverName == '\0')
563 return false;
565 for (int32 i = 0; serverName[i] != '\0'; i++) {
566 char c = serverName[i];
567 // Simple URL validation, no scheme should be present
568 if (!(isalnum(c) || c == '.' || c == '-' || c == '_'))
569 return false;
572 return true;
576 // #pragma mark - update functions
579 int32
580 update_thread(void* params)
582 BList* list = (BList*)params;
583 BMessenger* messenger = (BMessenger*)list->ItemAt(1);
585 const char* errorString = NULL;
586 int32 errorCode = 0;
587 status_t status = update_time(*(Settings*)list->ItemAt(0),
588 &errorString, &errorCode);
590 BMessage result(kMsgSynchronizationResult);
591 result.AddInt32("status", status);
592 result.AddString("error string", errorString);
593 if (errorCode != 0)
594 result.AddInt32("error code", errorCode);
596 messenger->SendMessage(&result);
597 delete messenger;
599 return B_OK;
603 status_t
604 update_time(const Settings& settings, BMessenger* messenger,
605 thread_id* thread)
607 BList* params = new BList(2);
608 params->AddItem((void*)&settings);
609 params->AddItem((void*)messenger);
610 *thread = spawn_thread(update_thread, "ntpUpdate", 64, params);
612 return resume_thread(*thread);
616 status_t
617 update_time(const Settings& settings, const char** errorString,
618 int32* errorCode)
620 int32 defaultServer = settings.GetDefaultServer();
622 status_t status = B_ENTRY_NOT_FOUND;
623 const char* server = settings.GetServer(defaultServer);
625 if (server != NULL)
626 status = ntp_update_time(server, errorString, errorCode);
628 if (status != B_OK && settings.GetTryAllServers()) {
629 for (int32 index = 0; ; index++) {
630 if (index == defaultServer)
631 index++;
633 server = settings.GetServer(index);
634 if (server == NULL)
635 break;
637 status = ntp_update_time(server, errorString, errorCode);
638 if (status == B_OK)
639 break;
643 return status;