Add NetworkTime as an application, based on Axel's original. This is mostly

based on Hamish Morrison's work on a GCI task. The changes I made from his
patch were to move this from preferences to apps (I think it is more of an app
than a preflet) and to change the name back to NetworkTime (he had made it
NTP, but most people probably won't know what that stands for.)

This also fixes #2412.

I noticed some changes this could use but wanted to add it before making any
changes so there is an SVN history of those changes. I'll probably also make an
icon in the next few days.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@40088 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Ryan Leavengood 2011-01-03 05:47:33 +00:00
parent 9530027447
commit aa252ae103
12 changed files with 1641 additions and 0 deletions

View File

@ -36,6 +36,7 @@ HaikuSubInclude mediaconverter ;
HaikuSubInclude mediaplayer ;
HaikuSubInclude midiplayer ;
HaikuSubInclude networkstatus ;
HaikuSubInclude networktime ;
HaikuSubInclude overlayimage ;
HaikuSubInclude packageinstaller ;
HaikuSubInclude packagemanager ;

View File

@ -0,0 +1,248 @@
/*
* Copyright 2004, pinc Software. All Rights Reserved.
* Distributed under the terms of the MIT license.
*/
#include "EditServerListWindow.h"
#include "NetworkTime.h"
#include <Application.h>
#include <TextView.h>
#include <ScrollView.h>
#include <Button.h>
#include <Screen.h>
#include <string.h>
#include <stdlib.h>
static const uint32 kMsgServersEdited = 'sedt';
static const char *kServerDelimiters = ", \n\t";
struct tokens {
char *buffer;
char *next;
const char *delimiters;
};
static void
put_tokens(tokens &tokens)
{
free(tokens.buffer);
tokens.buffer = NULL;
}
static status_t
prepare_tokens(tokens &tokens, const char *text, const char *delimiters)
{
tokens.buffer = strdup(text);
if (tokens.buffer == NULL)
return B_NO_MEMORY;
tokens.delimiters = delimiters;
tokens.next = tokens.buffer;
return B_OK;
}
static const char *
next_token(tokens &tokens)
{
char *token = strtok(tokens.next, tokens.delimiters);
if (tokens.next != NULL)
tokens.next = NULL;
if (token == NULL)
put_tokens(tokens);
return token;
}
// #pragma mark -
EditServerListWindow::EditServerListWindow(BRect position, const BMessage &settings)
: BWindow(position, "Edit Server List", B_TITLED_WINDOW,
B_ASYNCHRONOUS_CONTROLS),
fSettings(settings)
{
BRect rect = Bounds();
BView *view = new BView(rect, NULL, B_FOLLOW_ALL, B_WILL_DRAW);
view->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
AddChild(view);
rect = view->Bounds().InsetByCopy(5, 5);
BButton *button = new BButton(rect, "defaults", "Reset To Defaults",
new BMessage(kMsgResetServerList), B_FOLLOW_LEFT_RIGHT | B_FOLLOW_BOTTOM);
float width, height;
button->GetPreferredSize(&width, &height);
button->ResizeTo(rect.Width(), height);
button->MoveBy(0, rect.Height() - height);
button->SetTarget(be_app);
view->AddChild(button);
rect.bottom -= 5 + height;
rect.InsetBy(2, 2);
rect.right -= B_V_SCROLL_BAR_WIDTH;
fServerView = new BTextView(rect, "list",
rect.OffsetToCopy(B_ORIGIN).InsetByCopy(3, 3), B_FOLLOW_ALL);
fServerView->SetStylable(true);
BScrollView *scrollView = new BScrollView("scroller", fServerView, B_FOLLOW_ALL, B_WILL_DRAW, false, true);
view->AddChild(scrollView);
UpdateServerView(settings);
SetSizeLimits(200, 16384, 200, 16384);
if (Frame().LeftTop() == B_ORIGIN) {
// center on screen
BScreen screen;
MoveTo((screen.Frame().Width() - Bounds().Width()) / 2,
(screen.Frame().Height() - Bounds().Height()) / 2);
}
}
EditServerListWindow::~EditServerListWindow()
{
}
void
EditServerListWindow::UpdateDefaultServerView(const BMessage &message)
{
int32 defaultServer;
if (message.FindInt32("default server", &defaultServer) != B_OK)
return;
BFont font(be_plain_font);
fServerView->SetFontAndColor(0, fServerView->TextLength(),
&font, B_FONT_FAMILY_AND_STYLE);
struct tokens tokens;
const char *server;
if (fSettings.FindString("server", defaultServer, &server) == B_OK
&& prepare_tokens(tokens, fServerView->Text(), kServerDelimiters) == B_OK) {
const char *token;
while ((token = next_token(tokens)) != NULL) {
if (strcasecmp(token, server))
continue;
int32 start = int32(token - tokens.buffer);
BFont font;
font.SetFace(B_BOLD_FACE);
fServerView->SetFontAndColor(start, start + strlen(server),
&font, B_FONT_FAMILY_AND_STYLE);
break;
}
put_tokens(tokens);
}
}
void
EditServerListWindow::UpdateServerView(const BMessage &message)
{
if (message.HasString("server") || message.HasBool("reset servers"))
fServerView->SetText("");
int32 defaultServer;
if (message.FindInt32("default server", &defaultServer) != B_OK)
defaultServer = 0;
const char *server;
int32 index = 0;
for (; message.FindString("server", index, &server) == B_OK; index++) {
int32 start, end;
fServerView->GetSelection(&start, &end);
fServerView->Insert(server);
fServerView->Insert("\n");
}
UpdateDefaultServerView(message);
}
void
EditServerListWindow::UpdateServerList()
{
// get old default server
int32 defaultServer;
if (fSettings.FindInt32("default server", &defaultServer) != B_OK)
defaultServer = 0;
const char *server;
if (fSettings.FindString("server", defaultServer, &server) != B_OK)
server = NULL;
// take over server list
struct tokens tokens;
if (prepare_tokens(tokens, fServerView->Text(), kServerDelimiters) == B_OK) {
BMessage servers(kMsgUpdateSettings);
int32 count = 0, newDefaultServer = -1;
const char *token;
while ((token = next_token(tokens)) != NULL) {
servers.AddString("server", token);
if (!strcasecmp(token, server))
newDefaultServer = count;
count++;
}
if (count != 0) {
if (newDefaultServer == -1)
newDefaultServer = 0;
servers.AddInt32("default server", newDefaultServer);
be_app->PostMessage(&servers);
} else
be_app->PostMessage(kMsgResetServerList);
}
}
bool
EditServerListWindow::QuitRequested()
{
be_app->PostMessage(kMsgEditServerListWindowClosed);
BMessage update(kMsgUpdateSettings);
update.AddRect("edit servers frame", Frame());
be_app->PostMessage(&update);
UpdateServerList();
return true;
}
void
EditServerListWindow::MessageReceived(BMessage *message)
{
switch (message->what) {
case kMsgServersEdited:
UpdateServerList();
break;
case kMsgSettingsUpdated:
if (message->HasString("server"))
UpdateServerView(*message);
else
UpdateDefaultServerView(*message);
break;
}
}

View File

@ -0,0 +1,32 @@
/*
* Copyright 2004, pinc Software. All Rights Reserved.
* Distributed under the terms of the MIT license.
*/
#ifndef EDIT_SERVER_LIST_WINDOW_H
#define EDIT_SERVER_LIST_WINDOW_H
#include <Window.h>
class BTextView;
class EditServerListWindow : public BWindow {
public:
EditServerListWindow(BRect position, const BMessage &settings);
virtual ~EditServerListWindow();
virtual bool QuitRequested();
virtual void MessageReceived(BMessage *message);
private:
void UpdateDefaultServerView(const BMessage &message);
void UpdateServerView(const BMessage &message);
void UpdateServerList();
const BMessage &fSettings;
BTextView *fServerView;
};
#endif /* EDIT_SERVER_LIST_WINDOW_H */

View File

@ -0,0 +1,21 @@
SubDir HAIKU_TOP src apps networktime ;
Application NetworkTime :
EditServerListWindow.cpp
NetworkTime.cpp
ntp.cpp
UpdateWindow.cpp
$(DRIVER_SETTINGS)
: be $(TARGET_LIBSTDC++) $(HAIKU_LOCALE_LIBS) $(HAIKU_NETWORK_LIBS)
: NetworkTime.rdef
;
DoCatalogs NetworkTime :
x-vnd.Haiku-NetworkTime
:
EditServerListWindow.cpp
NetworkTime.cpp
ntp.cpp
UpdateWindow.cpp
;

View File

@ -0,0 +1,54 @@
/*
* Copyright 2004, pinc Software. All Rights Reserved.
* Distributed under the terms of the MIT license.
*/
#ifndef MONITOR_H
#define MONITOR_H
#include <SupportDefs.h>
#include <stdarg.h>
class Monitor {
public:
virtual ~Monitor() {}
virtual void Update(const char *text, ...);
virtual void Update(float progress, const char *text = NULL, ...);
virtual void Done(status_t status);
private:
virtual void update(float progress, const char *text, va_list args) = 0;
};
inline void
Monitor::Update(const char *text, ...)
{
va_list args;
va_start(args, text);
update(-1.f, text, args);
va_end(args);
}
inline void
Monitor::Update(float progress, const char *text, ...)
{
va_list args;
va_start(args, text);
update(progress, text, args);
va_end(args);
}
inline void
Monitor::Done(status_t /*status*/)
{
}
#endif /* MONITOR_H */

View File

@ -0,0 +1,670 @@
/*
* Copyright 2004, pinc Software. All Rights Reserved.
* Distributed under the terms of the MIT license.
*/
#include "NetworkTime.h"
#include "UpdateWindow.h"
#include "EditServerListWindow.h"
#include "ntp.h"
#include <Application.h>
#include <Path.h>
#include <File.h>
#include <FindDirectory.h>
#include <Alert.h>
#include <TextView.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <getopt.h>
#include <ctype.h>
status_t update_time(const BMessage &settings, Monitor *monitor);
status_t update_time(const BMessage &server, Monitor *monitor, thread_id *_asyncThread);
static const uint32 kMsgNetworkTimeSettings = 'NtpS';
extern const char *__progname;
static const char *sProgramName = __progname;
class Settings {
public:
Settings();
~Settings();
void CopyMessage(BMessage &message) const;
const BMessage &Message() const;
void UpdateFrom(BMessage &message);
void ResetServersToDefaults();
void ResetToDefaults();
status_t Load();
status_t Save();
private:
status_t GetPath(BPath &path);
BMessage fMessage;
bool fWasUpdated;
};
class PrintMonitor : public Monitor {
private:
virtual void
update(float progress, const char *text, va_list args)
{
if (progress != -1.f)
printf("%3g%% ", progress);
else
printf(" ");
if (text != NULL)
vprintf(text, args);
putchar('\n');
}
};
class MessengerMonitor : public Monitor {
public:
MessengerMonitor(BMessenger messenger)
:
fMessenger(messenger)
{
}
virtual void
Done(status_t status)
{
BMessage message(kMsgStatusUpdate);
message.AddInt32("status", status);
fMessenger.SendMessage(&message);
fMessenger.SendMessage(kMsgStopTimeUpdate);
// This deadlocks Dano
//be_app_messenger.SendMessage(kMsgStopTimeUpdate);
}
private:
virtual void
update(float progress, const char *text, va_list args)
{
BMessage message(kMsgStatusUpdate);
if (progress != -1.f)
message.AddFloat("progress", progress);
if (text != NULL) {
char buffer[2048];
vsprintf(buffer, text, args);
message.AddString("message", buffer);
}
fMessenger.SendMessage(&message);
}
BMessenger fMessenger;
};
class NetworkTimeApplication : public BApplication {
public:
NetworkTimeApplication(Settings &settings);
virtual ~NetworkTimeApplication();
virtual void ReadyToRun();
virtual void MessageReceived(BMessage *message);
virtual void AboutRequested();
void BroadcastSettingsChanges(BMessage &message);
private:
Settings &fSettings;
thread_id fUpdateThread;
BWindow *fServerListWindow;
};
status_t
adopt_int32(const BMessage &from, BMessage &to, const char *name, bool &updated)
{
int32 value;
if (from.FindInt32(name, &value) != B_OK)
return B_ENTRY_NOT_FOUND;
int32 original;
if (to.FindInt32(name, &original) == B_OK) {
if (value == original)
return B_OK;
updated = true;
return to.ReplaceInt32(name, value);
}
updated = true;
return to.AddInt32(name, value);
}
status_t
adopt_bool(const BMessage &from, BMessage &to, const char *name, bool &updated)
{
bool value;
if (from.FindBool(name, &value) != B_OK)
return B_ENTRY_NOT_FOUND;
bool original;
if (to.FindBool(name, &original) == B_OK) {
if (value == original)
return B_OK;
updated = true;
return to.ReplaceBool(name, value);
}
updated = true;
return to.AddBool(name, value);
}
status_t
adopt_rect(const BMessage &from, BMessage &to, const char *name, bool &updated)
{
BRect rect;
if (from.FindRect(name, &rect) != B_OK)
return B_ENTRY_NOT_FOUND;
BRect original;
if (to.FindRect(name, &original) == B_OK) {
if (rect == original)
return B_OK;
updated = true;
return to.ReplaceRect(name, rect);
}
updated = true;
return to.AddRect(name, rect);
}
// #pragma mark -
class UpdateLooper : public BLooper {
public:
UpdateLooper(const BMessage &settings, Monitor *monitor);
void MessageReceived(BMessage *message);
private:
const BMessage &fSettings;
Monitor *fMonitor;
};
UpdateLooper::UpdateLooper(const BMessage &settings, Monitor *monitor)
: BLooper("update looper"),
fSettings(settings),
fMonitor(monitor)
{
PostMessage(kMsgUpdateTime);
}
void
UpdateLooper::MessageReceived(BMessage *message)
{
if (message->what != kMsgUpdateTime)
return;
update_time(fSettings, fMonitor);
}
// #pragma mark -
NetworkTimeApplication::NetworkTimeApplication(Settings &settings)
: BApplication("application/x-vnd.Haiku-NetworkTime"),
fSettings(settings),
fUpdateThread(-1),
fServerListWindow(NULL)
{
}
NetworkTimeApplication::~NetworkTimeApplication()
{
}
void
NetworkTimeApplication::ReadyToRun()
{
BRect rect;
if (fSettings.Message().FindRect("status frame", &rect) != B_OK)
rect.Set(0, 0, 300, 150);
UpdateWindow *window = new UpdateWindow(rect, fSettings.Message());
window->Show();
}
void
NetworkTimeApplication::BroadcastSettingsChanges(BMessage &message)
{
BWindow *window;
int32 index = 0;
while ((window = WindowAt(index++)) != NULL)
window->PostMessage(&message);
}
void
NetworkTimeApplication::MessageReceived(BMessage *message)
{
switch (message->what) {
case kMsgUpdateSettings:
{
fSettings.UpdateFrom(*message);
message->what = kMsgSettingsUpdated;
BroadcastSettingsChanges(*message);
break;
}
case kMsgResetServerList:
{
fSettings.ResetServersToDefaults();
BMessage updated = fSettings.Message();
updated.what = kMsgSettingsUpdated;
BroadcastSettingsChanges(updated);
break;
}
case kMsgStopTimeUpdate:
if (fUpdateThread >= B_OK)
kill_thread(fUpdateThread);
fUpdateThread = -1;
break;
case kMsgUpdateTime:
{
if (fUpdateThread >= B_OK)
break;
MessengerMonitor *monitor = NULL;
BMessenger messenger;
if (message->FindMessenger("monitor", &messenger) == B_OK)
monitor = new MessengerMonitor(messenger);
update_time(fSettings.Message(), monitor, &fUpdateThread);
break;
}
case kMsgEditServerList:
if (fServerListWindow == NULL) {
BRect rect;
if (fSettings.Message().FindRect("edit servers frame", &rect) != B_OK)
rect.Set(0, 0, 250, 250);
fServerListWindow = new EditServerListWindow(rect, fSettings.Message());
}
fServerListWindow->Show();
break;
case kMsgEditServerListWindowClosed:
fServerListWindow = NULL;
break;
}
}
void
NetworkTimeApplication::AboutRequested()
{
BAlert *alert = new BAlert("about", "Network Time\n"
"\twritten by Axel Dörfler\n\n"
"\tCopyright " B_UTF8_COPYRIGHT "2004, pinc Software.\n"
"\tAll Rights Reserved.\n", "Ok");
BTextView *view = alert->TextView();
BFont font;
view->SetStylable(true);
view->GetFont(&font);
font.SetSize(18);
font.SetFace(B_BOLD_FACE);
view->SetFontAndColor(0, 12, &font);
alert->Go();
}
// #pragma mark -
Settings::Settings()
:
fMessage(kMsgNetworkTimeSettings),
fWasUpdated(false)
{
ResetToDefaults();
Load();
}
Settings::~Settings()
{
if (fWasUpdated)
Save();
}
void
Settings::CopyMessage(BMessage &message) const
{
message = fMessage;
}
const BMessage &
Settings::Message() const
{
return fMessage;
}
void
Settings::UpdateFrom(BMessage &message)
{
if (message.HasBool("reset servers")) {
fMessage.RemoveName("server");
fWasUpdated = true;
}
if (message.HasString("server")) {
// remove old servers
fMessage.RemoveName("server");
const char *server;
int32 index = 0;
while (message.FindString("server", index++, &server) == B_OK) {
fMessage.AddString("server", server);
}
fWasUpdated = true;
}
adopt_int32(message, fMessage, "default server", fWasUpdated);
adopt_bool(message, fMessage, "auto mode", fWasUpdated);
adopt_bool(message, fMessage, "knows auto mode", fWasUpdated);
adopt_bool(message, fMessage, "try all servers", fWasUpdated);
adopt_bool(message, fMessage, "choose default server", fWasUpdated);
adopt_rect(message, fMessage, "status frame", fWasUpdated);
adopt_rect(message, fMessage, "edit servers frame", fWasUpdated);
}
void
Settings::ResetServersToDefaults()
{
fMessage.RemoveName("server");
fMessage.AddString("server", "pool.ntp.org");
fMessage.AddString("server", "de.pool.ntp.org");
fMessage.AddString("server", "time.nist.gov");
if (fMessage.ReplaceInt32("default server", 0) != B_OK)
fMessage.AddInt32("default server", 0);
}
void
Settings::ResetToDefaults()
{
fMessage.MakeEmpty();
ResetServersToDefaults();
fMessage.AddBool("knows auto mode", false);
fMessage.AddBool("auto mode", false);
fMessage.AddBool("try all servers", true);
fMessage.AddBool("choose default server", true);
fMessage.AddRect("status frame", BRect(0, 0, 300, 150));
fMessage.AddRect("edit servers frame", BRect(0, 0, 250, 250));
}
status_t
Settings::Load()
{
status_t status;
BPath path;
if ((status = GetPath(path)) != B_OK)
return status;
BFile file(path.Path(), B_READ_ONLY);
if ((status = file.InitCheck()) != B_OK)
return status;
BMessage load;
if ((status = load.Unflatten(&file)) != B_OK)
return status;
if (load.what != kMsgNetworkTimeSettings)
return B_BAD_TYPE;
fMessage = load;
return B_OK;
}
status_t
Settings::Save()
{
status_t status;
BPath path;
if ((status = GetPath(path)) != B_OK)
return status;
BFile file(path.Path(), B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE);
if ((status = file.InitCheck()) != B_OK)
return status;
file.SetSize(0);
return fMessage.Flatten(&file);
}
status_t
Settings::GetPath(BPath &path)
{
status_t status;
if ((status = find_directory(B_USER_SETTINGS_DIRECTORY, &path)) != B_OK)
return status;
path.Append("pinc.networktime settings");
return B_OK;
}
// #pragma mark -
status_t
update_time(const BMessage &settings, Monitor *monitor)
{
int32 defaultServer;
if (settings.FindInt32("default server", &defaultServer) != B_OK)
defaultServer = 0;
status_t status = B_ENTRY_NOT_FOUND;
const char *server;
if (settings.FindString("server", defaultServer, &server) == B_OK)
status = ntp_update_time(server, monitor);
// try other servers if we should
if (status != B_OK && settings.FindBool("try all servers")) {
for (int32 index = 0; ; index++) {
if (index == defaultServer)
index++;
if (settings.FindString("server", index, &server) != B_OK)
break;
status = ntp_update_time(server, monitor);
if (status == B_OK) {
if (be_app != NULL && settings.FindBool("choose default server")) {
BMessage update(kMsgUpdateSettings);
update.AddInt32("default server", index);
be_app->PostMessage(&update);
}
break;
}
}
}
if (monitor != NULL)
monitor->Done(status);
delete monitor;
return status;
}
status_t
update_time(const BMessage &settings, Monitor *monitor, thread_id *_asyncThread)
{
if (_asyncThread != NULL) {
BLooper *looper = new UpdateLooper(settings, monitor);
*_asyncThread = looper->Run();
return B_OK;
}
return update_time(settings, monitor);
}
bool
parse_time(char *arg, int32 *_seconds)
{
char *unit;
if (isdigit(arg[0]))
*_seconds = strtoul(arg, &unit, 10);
else
return false;
if (unit[0] == '\0' || !strcmp(unit, "m")) {
*_seconds *= 60;
return true;
}
if (!strcmp(unit, "h")) {
*_seconds *= 60 * 60;
return true;
}
if (!strcmp(unit, "s"))
return true;
return false;
}
void
usage(void)
{
fprintf(stderr, "usage: %s [-s|--server <host>] [-p|--periodic <interval>] [-n|--silent] [-g|--nogui]\n"
" -s, --server\t\tContact \"host\" instead of saved list\n"
" -p, --periodic\tPeriodically query for the time, interval specified in minutes\n"
" -n, --silent\t\tDo not print progress\n"
" -g, --nogui\t\tDo not show graphical user interface (implied by other options)\n",
sProgramName);
exit(0);
}
int
main(int argc, char **argv)
{
Settings settings;
if (argc > 1) {
// when there are any command line options, we will switch to command line mode
BMessage message;
settings.CopyMessage(message);
// we may want to override the global settings with command line options
static struct option const longopts[] = {
{"server", required_argument, NULL, 's'},
{"periodic", required_argument, NULL, 'p'},
{"silent", no_argument, NULL, 'n'},
{"nogui", no_argument, NULL, 'g'},
{"help", no_argument, NULL, 'h'},
{NULL, 0, NULL, 0}
};
int32 interval = 0;
bool silent = false;
char option;
while ((option = getopt_long(argc, argv, "s:p:h", longopts, NULL)) != -1) {
switch (option) {
case 's':
message.RemoveName("server");
message.AddString("server", optarg);
message.ReplaceInt32("default server", 0);
break;
case 'n':
silent = true;
break;
case 'g':
// default in command line mode
break;
case 'p':
if (parse_time(optarg, &interval))
break;
// supposed to fall through
case 'h':
default:
usage();
}
}
while (true) {
Monitor *monitor = NULL;
if (!silent)
monitor = new PrintMonitor();
update_time(message, monitor, NULL);
if (interval == 0)
break;
snooze(interval * 1000000LL);
}
} else {
NetworkTimeApplication app(settings);
app.Run();
}
return 0;
}

View File

@ -0,0 +1,22 @@
/*
* Copyright 2004, pinc Software. All Rights Reserved.
* Distributed under the terms of the MIT license.
*/
#ifndef NETWORK_TIME_H
#define NETWORK_TIME_H
#include <SupportDefs.h>
static const uint32 kMsgUpdateTime = 'uptm';
static const uint32 kMsgStopTimeUpdate = 'stpt';
static const uint32 kMsgStatusUpdate = 'stup';
static const uint32 kMsgUpdateSettings = 'upse';
static const uint32 kMsgSettingsUpdated = 'seUp';
static const uint32 kMsgResetServerList = 'rtsl';
static const uint32 kMsgEditServerList = 'edsl';
static const uint32 kMsgEditServerListWindowClosed = 'eswc';
#endif /* NETWORK_TIME_H */

View File

@ -0,0 +1,19 @@
resource app_signature "application/x-vnd.Haiku-NetworkTime";
resource app_version {
major = 1,
middle = 0,
minor = 0,
/* 0 = development 1 = alpha 2 = beta
3 = gamma 4 = golden master 5 = final */
variety = 2,
internal = 0,
short_info = "NetworkTime",
long_info = "NetworkTime ©2004 pinc Software"
};
resource app_flags B_SINGLE_LAUNCH;

View File

@ -0,0 +1,303 @@
/*
* Copyright 2004, pinc Software. All Rights Reserved.
* Distributed under the terms of the MIT license.
*/
#include "UpdateWindow.h"
#include "NetworkTime.h"
#include <Application.h>
#include <MenuBar.h>
#include <MenuItem.h>
#include <StatusBar.h>
#include <Button.h>
#include <Alert.h>
#include <Screen.h>
#include <stdio.h>
static const uint32 kMsgAutoMode = 'aumd';
static const uint32 kMsgTryAllServers = 'tasr';
static const uint32 kMsgChooseDefaultServer = 'cdsr';
static const uint32 kMsgDefaultServer = 'dsrv';
UpdateWindow::UpdateWindow(BRect position, const BMessage &settings)
: BWindow(position, "Network Time", B_TITLED_WINDOW,
B_NOT_ZOOMABLE | B_NOT_RESIZABLE | B_ASYNCHRONOUS_CONTROLS),
fSettings(settings)
{
bool autoMode;
if (settings.FindBool("auto mode", &autoMode) != B_OK)
autoMode = false;
// pressing the control key will avoid auto quit
fAutoQuit = modifiers() & B_CONTROL_KEY ? false : autoMode;
bool showMenu = !fAutoQuit;
BMenuBar *bar = new BMenuBar(Bounds(), "");
BMenu *menu;
BMenuItem *item;
menu = new BMenu("Project");
menu->AddItem(item = new BMenuItem("About" B_UTF8_ELLIPSIS, new BMessage(B_ABOUT_REQUESTED), '?'));
item->SetTarget(be_app);
menu->AddItem(new BMenuItem("Quit", new BMessage(B_QUIT_REQUESTED), 'Q'));
bar->AddItem(menu);
fServerMenu = new BMenu("Servers");
fServerMenu->SetRadioMode(true);
BuildServerMenu();
menu = new BMenu("Options");
menu->AddItem(fTryAllServersItem = new BMenuItem("Try All Servers",
new BMessage(kMsgTryAllServers)));
menu->AddItem(fChooseDefaultServerItem = new BMenuItem("Choose Default Server Automatically",
new BMessage(kMsgChooseDefaultServer)));
menu->AddItem(fAutoModeItem = new BMenuItem("Auto Mode", new BMessage(kMsgAutoMode)));
menu->AddSeparatorItem();
menu->AddItem(fServerMenu);
menu->AddItem(new BMenuItem("Edit Server List" B_UTF8_ELLIPSIS, new BMessage(kMsgEditServerList)));
bar->AddItem(menu);
if (showMenu)
AddChild(bar);
BRect rect = Bounds();
if (showMenu)
rect.top = bar->Bounds().Height();
BView *view = new BView(rect, NULL, B_FOLLOW_ALL, B_WILL_DRAW);
view->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
AddChild(view);
rect = view->Bounds().InsetByCopy(5, 5);
fStatusBar = new BStatusBar(rect, "status", NULL, NULL);
float width, height;
fStatusBar->GetPreferredSize(&width, &height);
fStatusBar->ResizeTo(rect.Width(), height);
fStatusBar->SetResizingMode(B_FOLLOW_TOP | B_FOLLOW_LEFT_RIGHT);
view->AddChild(fStatusBar);
BMessage *buttonMessage = new BMessage(fAutoQuit ? kMsgStopTimeUpdate : kMsgUpdateTime);
buttonMessage->AddMessenger("monitor", this);
if (showMenu) {
rect.top += height + 4;
fButton = new BButton(rect, NULL, "Update", buttonMessage,
B_FOLLOW_TOP | B_FOLLOW_LEFT_RIGHT);
fButton->ResizeToPreferred();
if (autoMode)
fButton->SetLabel("Stop");
fButton->MoveBy((rect.Width() - fButton->Bounds().Width()) / 2, 0);
view->AddChild(fButton);
ResizeTo(Bounds().Width(),
bar->Bounds().Height() + fButton->Frame().bottom + 5);
} else {
fButton = NULL;
ResizeTo(Bounds().Width(), height + 9);
}
SetSizeLimits(200, 16384, Bounds().Height(), Bounds().Height());
if (Frame().LeftTop() == B_ORIGIN) {
// center on screen
BScreen screen;
MoveTo((screen.Frame().Width() - Bounds().Width()) / 2,
(screen.Frame().Height() - Bounds().Height()) / 2);
}
// Update views
BMessage update = fSettings;
update.what = kMsgSettingsUpdated;
PostMessage(&update);
// Automatically start update in "auto mode"
if (fAutoQuit) {
BMessage update(kMsgUpdateTime);
update.AddMessenger("monitor", this);
be_app->PostMessage(&update);
}
}
UpdateWindow::~UpdateWindow()
{
}
bool
UpdateWindow::QuitRequested()
{
BMessage update(kMsgUpdateSettings);
update.AddRect("status frame", Frame());
be_app_messenger.SendMessage(&update);
be_app->PostMessage(B_QUIT_REQUESTED);
return true;
}
void
UpdateWindow::BuildServerMenu()
{
// remove old entries
BMenuItem *item;
while ((item = fServerMenu->RemoveItem(0L)) != NULL)
delete item;
int32 defaultServer;
if (fSettings.FindInt32("default server", &defaultServer) != B_OK)
defaultServer = 0;
const char *server;
for (int32 i = 0; fSettings.FindString("server", i, &server) == B_OK; i++) {
fServerMenu->AddItem(item = new BMenuItem(server, new BMessage(kMsgDefaultServer)));
if (i == defaultServer)
item->SetMarked(true);
}
}
void
UpdateWindow::MessageReceived(BMessage *message)
{
switch (message->what) {
case kMsgSettingsUpdated:
{
if (message->HasString("server"))
BuildServerMenu();
else {
int32 index;
if (message->FindInt32("default server", &index) == B_OK) {
BMenuItem *item = fServerMenu->ItemAt(index);
if (item != NULL)
item->SetMarked(true);
}
}
bool boolean;
if (message->FindBool("auto mode", &boolean) == B_OK)
fAutoModeItem->SetMarked(boolean);
if (message->FindBool("try all servers", &boolean) == B_OK)
fTryAllServersItem->SetMarked(boolean);
if (message->FindBool("choose default server", &boolean) == B_OK)
fChooseDefaultServerItem->SetMarked(boolean);
break;
}
case kMsgAutoMode:
{
fAutoModeItem->SetMarked(!fAutoModeItem->IsMarked());
if (!fSettings.FindBool("knows auto mode")) {
// we should alert the user about the changed behaviour
(new BAlert("notice",
"Attention!\n\n"
"Activating this mode let's the user interface only show the "
"bare minimum, and you will not be able to reach the menu "
"anymore.\n"
"Also, the application will quit immediately after the time has "
"been updated successfully; it will only stay if updating fails, "
"so that you can catch the reason for the error.\n\n"
"If you want to get the full interface again, you have to "
"press the control key while starting this application.\n\n"
"This warning will appear only the first time you select this mode.",
"Ok"))->Go();
}
BMessage update(kMsgUpdateSettings);
update.AddBool("auto mode", fAutoModeItem->IsMarked());
update.AddBool("knows auto mode", true);
be_app->PostMessage(&update);
break;
}
case kMsgTryAllServers:
{
fTryAllServersItem->SetMarked(!fTryAllServersItem->IsMarked());
BMessage update(kMsgUpdateSettings);
update.AddBool("try all servers", fTryAllServersItem->IsMarked());
be_app->PostMessage(&update);
break;
}
case kMsgChooseDefaultServer:
{
fChooseDefaultServerItem->SetMarked(!fChooseDefaultServerItem->IsMarked());
BMessage update(kMsgUpdateSettings);
update.AddBool("choose default server", fChooseDefaultServerItem->IsMarked());
be_app->PostMessage(&update);
break;
}
case kMsgDefaultServer:
{
int32 index;
if (message->FindInt32("index", &index) != B_OK)
break;
BMessage update(kMsgUpdateSettings);
update.AddInt32("default server", index);
be_app->PostMessage(&update);
break;
}
case kMsgEditServerList:
be_app->PostMessage(kMsgEditServerList);
break;
case kMsgStopTimeUpdate:
if (fButton != NULL) {
fButton->SetLabel("Update");
fButton->Message()->what = kMsgUpdateTime;
}
BButton *source;
if (message->FindPointer("source", (void **)&source) == B_OK
&& fButton == source)
fStatusBar->SetText("Stopped.");
be_app->PostMessage(message);
break;
case kMsgUpdateTime:
if (fButton != NULL) {
fButton->SetLabel("Stop");
fButton->Message()->what = kMsgStopTimeUpdate;
}
be_app->PostMessage(message);
break;
case kMsgStatusUpdate:
float progress;
if (message->FindFloat("progress", &progress) == B_OK)
fStatusBar->Update(progress - fStatusBar->CurrentValue());
const char *text;
if (message->FindString("message", &text) == B_OK)
fStatusBar->SetText(text);
status_t status;
if (message->FindInt32("status", (int32 *)&status) == B_OK) {
if (status == B_OK && fAutoQuit)
QuitRequested();
else if (fButton != NULL) {
fButton->SetLabel("Update");
fButton->Message()->what = kMsgUpdateTime;
}
}
break;
default:
BWindow::MessageReceived(message);
}
}

View File

@ -0,0 +1,36 @@
/*
* Copyright 2004, pinc Software. All Rights Reserved.
* Distributed under the terms of the MIT license.
*/
#ifndef UPDATE_WINDOW_H
#define UPDATE_WINDOW_H
#include <Window.h>
class BStatusBar;
class BButton;
class BMenuItem;
class BMenu;
class UpdateWindow : public BWindow {
public:
UpdateWindow(BRect position, const BMessage &settings);
virtual ~UpdateWindow();
virtual bool QuitRequested();
virtual void MessageReceived(BMessage *message);
void BuildServerMenu();
private:
const BMessage &fSettings;
BStatusBar *fStatusBar;
BButton *fButton;
BMenuItem *fAutoModeItem, *fTryAllServersItem, *fChooseDefaultServerItem;
BMenu *fServerMenu;
bool fAutoQuit;
};
#endif /* UPDATE_WINDOW_H */

View File

@ -0,0 +1,220 @@
/*
* Copyright 2004-2009, pinc Software. All Rights Reserved.
* Distributed under the terms of the MIT license.
*/
#include "ntp.h"
#include <OS.h>
#include <errno.h>
#include <netdb.h>
#include <string.h>
#include <stdio.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <unistd.h>
/* This structure and its data fields are described in RFC 1305
* "Network Time Protocol (Version 3)" in appendix A.
*/
struct fixed32 {
int16 integer;
uint16 fraction;
void
SetTo(int16 integer, uint16 fraction = 0)
{
this->integer = htons(integer);
this->fraction = htons(fraction);
}
int16 Integer() { return htons(integer); }
uint16 Fraction() { return htons(fraction); }
};
struct ufixed64 {
uint32 integer;
uint32 fraction;
void
SetTo(uint32 integer, uint32 fraction = 0)
{
this->integer = htonl(integer);
this->fraction = htonl(fraction);
}
uint32 Integer() { return htonl(integer); }
uint32 Fraction() { return htonl(fraction); }
};
struct ntp_data {
uint8 mode : 3;
uint8 version : 3;
uint8 leap_indicator : 2;
uint8 stratum;
int8 poll;
int8 precision; /* in seconds of the nearest power of two */
fixed32 root_delay;
fixed32 root_dispersion;
uint32 root_identifier;
ufixed64 reference_timestamp;
ufixed64 originate_timestamp;
ufixed64 receive_timestamp;
ufixed64 transmit_timestamp;
/* optional authenticator follows (96 bits) */
};
#define NTP_PORT 123
#define NTP_VERSION_3 3
enum ntp_leap_warnings {
LEAP_NO_WARNING = 0,
LEAP_LAST_MINUTE_61_SECONDS,
LEAP_LAST_MINUTE_59_SECONDS,
LEAP_CLOCK_NOT_IN_SYNC,
};
enum ntp_modes {
MODE_RESERVED = 0,
MODE_SYMMETRIC_ACTIVE,
MODE_SYMMETRIC_PASSIVE,
MODE_CLIENT,
MODE_SERVER,
MODE_BROADCAST,
MODE_NTP_CONTROL_MESSAGE,
};
const uint32 kUTCtoGMT = 12 * 60 * 60;
uint32
seconds_system_difference(void)
{
const uint32 kYear2000 = 3155713200UL;
// that many seconds from year 1900 to 2000.
struct tm tm;
memset(&tm, 0, sizeof(struct tm));
tm.tm_mday = 1;
tm.tm_year = 100;
return kYear2000 - mktime(&tm);
}
uint32
seconds_since_1900(void)
{
return seconds_system_difference() + real_time_clock();
}
status_t
ntp_update_time(const char *hostname, Monitor *monitor)
{
if (monitor)
monitor->Update(0, "Contacting server \"%s\"...", hostname);
hostent *server = gethostbyname(hostname);
if (server == NULL) {
if (monitor)
monitor->Update("Could not contact server.");
return B_ENTRY_NOT_FOUND;
}
if (monitor)
monitor->Update(25, "Sending request...");
ntp_data message;
memset(&message, 0, sizeof(ntp_data));
message.leap_indicator = LEAP_CLOCK_NOT_IN_SYNC;
message.version = NTP_VERSION_3;
message.mode = MODE_CLIENT;
message.stratum = 1; // primary reference
message.precision = -5; // 2^-5 ~ 32-64 Hz precision
message.root_delay.SetTo(1); // 1 sec
message.root_dispersion.SetTo(1);
message.transmit_timestamp.SetTo(seconds_since_1900());
int connection = socket(AF_INET, SOCK_DGRAM, 0);
if (connection < 0) {
if (monitor)
monitor->Update("Could not create socket: %s", strerror(errno));
return errno;
}
struct sockaddr_in address;
address.sin_family = AF_INET;
address.sin_port = htons(NTP_PORT);
address.sin_addr.s_addr = *(uint32 *)server->h_addr_list[0];
if (sendto(connection, (char *)&message, sizeof(ntp_data),
0, (struct sockaddr *)&address, sizeof(address)) < 0) {
if (monitor)
monitor->Update("Sending request failed: %s", strerror(errno));
return errno;
}
// message is sent, now just wait for something to be returned...
if (monitor)
monitor->Update(50, "Waiting for answer...");
fd_set waitForReceived;
FD_ZERO(&waitForReceived);
FD_SET(connection, &waitForReceived);
struct timeval timeout;
timeout.tv_sec = 3;
timeout.tv_usec = 0;
// we'll wait 3 seconds for the answer
if (select(connection + 1, &waitForReceived, NULL, NULL, &timeout) <= 0) {
if (monitor)
monitor->Update("Waiting for answer failed: %s", strerror(errno));
return errno;
}
message.transmit_timestamp.SetTo(0);
socklen_t addressSize = sizeof(address);
if (recvfrom(connection, (char *)&message, sizeof(ntp_data), 0,
(sockaddr *)&address, &addressSize) < (ssize_t)sizeof(ntp_data)) {
if (monitor)
monitor->Update("Message receiving failed: %s", strerror(errno));
close(connection);
return errno;
}
close(connection);
if (message.transmit_timestamp.Integer() == 0) {
if (monitor)
monitor->Update("Received invalid time.");
return B_BAD_VALUE;
}
time_t now = message.transmit_timestamp.Integer() - seconds_system_difference() + kUTCtoGMT;
if (monitor) {
char buffer[64];
strftime(buffer, sizeof(buffer), "%a %b %T %Y", localtime(&now));
monitor->Update(100, "Message received: %s", buffer);
}
set_real_time_clock(now);
return B_OK;
}

View File

@ -0,0 +1,15 @@
/*
* Copyright 2004, pinc Software. All Rights Reserved.
* Distributed under the terms of the MIT license.
*/
#ifndef NTP_H
#define NTP_H
#include "Monitor.h"
#include <SupportDefs.h>
extern status_t ntp_update_time(const char *host, Monitor *monitor = NULL);
#endif /* NTP_H */