vfs: check userland buffers before reading them.
[haiku.git] / src / preferences / network / NetworkWindow.cpp
blobdbeb791329c8678674233e0767190ecfe10659b2
1 /*
2 * Copyright 2004-2015 Haiku Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
5 * Authors:
6 * Adrien Destugues, <pulkomandy@pulkomandy.tk>
7 * Axel Dörfler, <axeld@pinc-software.de>
8 * Alexander von Gluck, <kallisti5@unixzen.com>
9 */
12 #include "NetworkWindow.h"
14 #include <net/if.h>
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
19 #include <Alert.h>
20 #include <Application.h>
21 #include <Button.h>
22 #include <Catalog.h>
23 #include <CheckBox.h>
24 #include <ControlLook.h>
25 #include <Deskbar.h>
26 #include <Directory.h>
27 #include <LayoutBuilder.h>
28 #include <NetworkDevice.h>
29 #include <NetworkInterface.h>
30 #include <NetworkNotifications.h>
31 #include <NetworkRoster.h>
32 #include <OutlineListView.h>
33 #include <Path.h>
34 #include <PathFinder.h>
35 #include <PathMonitor.h>
36 #include <Roster.h>
37 #include <ScrollView.h>
38 #include <StringItem.h>
39 #include <SymLink.h>
41 #define ENABLE_PROFILES 0
42 #if ENABLE_PROFILES
43 # include <PopUpMenu.h>
44 #endif
46 #include "InterfaceListItem.h"
47 #include "InterfaceView.h"
48 #include "ServiceListItem.h"
51 const char* kNetworkStatusSignature = "application/x-vnd.Haiku-NetworkStatus";
53 static const uint32 kMsgProfileSelected = 'prof';
54 static const uint32 kMsgProfileManage = 'mngp';
55 static const uint32 kMsgProfileNew = 'newp';
56 static const uint32 kMsgRevert = 'rvrt';
57 static const uint32 kMsgToggleReplicant = 'trep';
58 static const uint32 kMsgItemSelected = 'ItSl';
60 BMessenger gNetworkWindow;
63 #undef B_TRANSLATION_CONTEXT
64 #define B_TRANSLATION_CONTEXT "NetworkWindow"
67 class TitleItem : public BStringItem {
68 public:
69 TitleItem(const char* title)
71 BStringItem(title)
75 void DrawItem(BView* owner, BRect bounds, bool complete)
77 owner->SetFont(be_bold_font);
78 BStringItem::DrawItem(owner, bounds, complete);
79 owner->SetFont(be_plain_font);
82 void Update(BView* owner, const BFont* font)
84 BStringItem::Update(owner, be_bold_font);
89 // #pragma mark -
92 NetworkWindow::NetworkWindow()
94 BWindow(BRect(100, 100, 400, 400), B_TRANSLATE("Network"), B_TITLED_WINDOW,
95 B_ASYNCHRONOUS_CONTROLS | B_NOT_ZOOMABLE | B_AUTO_UPDATE_SIZE_LIMITS),
96 fServicesItem(NULL),
97 fDialUpItem(NULL),
98 fVPNItem(NULL),
99 fOtherItem(NULL)
101 // Profiles section
102 #if ENABLE_PROFILES
103 BPopUpMenu* profilesPopup = new BPopUpMenu("<none>");
104 _BuildProfilesMenu(profilesPopup, kMsgProfileSelected);
106 BMenuField* profilesMenuField = new BMenuField("profiles_menu",
107 B_TRANSLATE("Profile:"), profilesPopup);
109 profilesMenuField->SetFont(be_bold_font);
110 profilesMenuField->SetEnabled(false);
111 #endif
113 // Settings section
115 fRevertButton = new BButton("revert", B_TRANSLATE("Revert"),
116 new BMessage(kMsgRevert));
118 BMessage* message = new BMessage(kMsgToggleReplicant);
119 BCheckBox* showReplicantCheckBox = new BCheckBox("showReplicantCheckBox",
120 B_TRANSLATE("Show network status in Deskbar"), message);
121 showReplicantCheckBox->SetExplicitMaxSize(
122 BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET));
123 showReplicantCheckBox->SetValue(_IsReplicantInstalled());
125 fListView = new BOutlineListView("list", B_SINGLE_SELECTION_LIST,
126 B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE | B_FRAME_EVENTS | B_NAVIGABLE);
127 fListView->SetSelectionMessage(new BMessage(kMsgItemSelected));
129 BScrollView* scrollView = new BScrollView("ScrollView", fListView,
130 0, false, true);
131 scrollView->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET));
133 fAddOnShellView = new BView("add-on shell", 0,
134 new BGroupLayout(B_VERTICAL));
135 fAddOnShellView->SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
137 fInterfaceView = new InterfaceView();
139 // Build the layout
140 BLayoutBuilder::Group<>(this, B_VERTICAL)
141 .SetInsets(B_USE_WINDOW_SPACING)
143 #if ENABLE_PROFILES
144 .AddGroup(B_HORIZONTAL, B_USE_SMALL_SPACING)
145 .Add(profilesMenuField)
146 .AddGlue()
147 .End()
148 #endif
149 .AddGroup(B_HORIZONTAL, B_USE_DEFAULT_SPACING)
150 .Add(scrollView)
151 .Add(fAddOnShellView)
152 .End()
153 .Add(showReplicantCheckBox)
154 .AddGroup(B_HORIZONTAL, B_USE_DEFAULT_SPACING)
155 .Add(fRevertButton)
156 .AddGlue()
157 .End();
159 gNetworkWindow = this;
161 _ScanInterfaces();
162 _ScanAddOns();
163 _UpdateRevertButton();
165 fListView->Select(0);
166 _SelectItem(fListView->ItemAt(0));
167 // Call this manually, so that CenterOnScreen() below already
168 // knows the final window size.
170 // Set size of the list view from its contents
171 float width;
172 float height;
173 fListView->GetPreferredSize(&width, &height);
174 width += 2 * be_control_look->DefaultItemSpacing();
175 fListView->SetExplicitSize(BSize(width, B_SIZE_UNSET));
176 fListView->SetExplicitMinSize(BSize(width, std::min(height, 400.f)));
178 CenterOnScreen();
180 fSettings.StartMonitoring(this);
181 start_watching_network(B_WATCH_NETWORK_INTERFACE_CHANGES
182 | B_WATCH_NETWORK_LINK_CHANGES | B_WATCH_NETWORK_WLAN_CHANGES, this);
186 NetworkWindow::~NetworkWindow()
188 stop_watching_network(this);
189 fSettings.StopMonitoring(this);
193 bool
194 NetworkWindow::QuitRequested()
196 be_app->PostMessage(B_QUIT_REQUESTED);
197 return true;
201 void
202 NetworkWindow::MessageReceived(BMessage* message)
204 switch (message->what) {
205 case kMsgProfileNew:
206 break;
208 case kMsgProfileSelected:
210 const char* path;
211 if (message->FindString("path", &path) != B_OK)
212 break;
214 // TODO!
215 break;
218 case kMsgItemSelected:
220 BListItem* listItem = fListView->FullListItemAt(
221 fListView->FullListCurrentSelection());
222 if (listItem == NULL)
223 break;
225 _SelectItem(listItem);
226 break;
229 case kMsgRevert:
231 SettingsMap::const_iterator iterator = fSettingsMap.begin();
232 for (; iterator != fSettingsMap.end(); iterator++)
233 iterator->second->Revert();
234 break;
237 case kMsgToggleReplicant:
239 _ShowReplicant(
240 message->GetInt32("be:value", B_CONTROL_OFF) == B_CONTROL_ON);
241 break;
244 case B_PATH_MONITOR:
246 fSettings.Update(message);
247 break;
250 case B_NETWORK_MONITOR:
251 _BroadcastConfigurationUpdate(*message);
252 break;
254 case BNetworkSettings::kMsgInterfaceSettingsUpdated:
255 case BNetworkSettings::kMsgNetworkSettingsUpdated:
256 case BNetworkSettings::kMsgServiceSettingsUpdated:
257 _BroadcastSettingsUpdate(message->what);
258 break;
260 case kMsgSettingsItemUpdated:
261 // TODO: update list item
262 _UpdateRevertButton();
263 break;
265 default:
266 inherited::MessageReceived(message);
271 void
272 NetworkWindow::_BuildProfilesMenu(BMenu* menu, int32 what)
274 char currentProfile[256] = { 0 };
276 menu->SetRadioMode(true);
278 BDirectory dir("/boot/system/settings/network/profiles");
279 if (dir.InitCheck() == B_OK) {
280 BEntry entry;
282 dir.Rewind();
283 while (dir.GetNextEntry(&entry) >= 0) {
284 BPath name;
285 entry.GetPath(&name);
287 if (entry.IsSymLink() &&
288 strcmp("current", name.Leaf()) == 0) {
289 BSymLink symlink(&entry);
291 if (symlink.IsAbsolute())
292 // oh oh, sorry, wrong symlink...
293 continue;
295 symlink.ReadLink(currentProfile, sizeof(currentProfile));
296 continue;
299 if (!entry.IsDirectory())
300 continue;
302 BMessage* message = new BMessage(what);
303 message->AddString("path", name.Path());
305 BMenuItem* item = new BMenuItem(name.Leaf(), message);
306 menu->AddItem(item);
310 menu->AddSeparatorItem();
311 menu->AddItem(new BMenuItem(B_TRANSLATE("New" B_UTF8_ELLIPSIS),
312 new BMessage(kMsgProfileNew)));
313 menu->AddItem(new BMenuItem(B_TRANSLATE("Manage" B_UTF8_ELLIPSIS),
314 new BMessage(kMsgProfileManage)));
316 if (currentProfile[0] != '\0') {
317 BMenuItem* item = menu->FindItem(currentProfile);
318 if (item != NULL) {
319 // TODO: translate
320 BString label(item->Label());
321 label << " (current)";
322 item->SetLabel(label.String());
323 item->SetMarked(true);
329 void
330 NetworkWindow::_ScanInterfaces()
332 // Try existing devices first
333 BNetworkRoster& roster = BNetworkRoster::Default();
334 BNetworkInterface interface;
335 uint32 cookie = 0;
337 while (roster.GetNextInterface(&cookie, interface) == B_OK) {
338 if ((interface.Flags() & IFF_LOOPBACK) != 0)
339 continue;
341 BNetworkDevice device(interface.Name());
342 BNetworkInterfaceType type = B_NETWORK_INTERFACE_TYPE_OTHER;
344 if (device.IsWireless())
345 type = B_NETWORK_INTERFACE_TYPE_WIFI;
346 else if (device.IsEthernet())
347 type = B_NETWORK_INTERFACE_TYPE_ETHERNET;
349 InterfaceListItem* item = new InterfaceListItem(interface.Name(), type);
350 item->SetExpanded(true);
352 fInterfaceItemMap.insert(std::pair<BString, InterfaceListItem*>(
353 BString(interface.Name()), item));
354 fListView->AddItem(item);
357 // TODO: Then consider those from the settings (for example, for USB)
361 void
362 NetworkWindow::_ScanAddOns()
364 BStringList paths;
365 BPathFinder::FindPaths(B_FIND_PATH_ADD_ONS_DIRECTORY, "Network Settings",
366 paths);
368 // Collect add-on paths by name, so that each name will only be
369 // loaded once.
370 typedef std::map<BString, BPath> PathMap;
371 PathMap addOnMap;
373 for (int32 i = 0; i < paths.CountStrings(); i++) {
374 BDirectory directory(paths.StringAt(i));
375 BEntry entry;
376 while (directory.GetNextEntry(&entry) == B_OK) {
377 BPath path;
378 if (entry.GetPath(&path) != B_OK)
379 continue;
381 if (addOnMap.find(path.Leaf()) == addOnMap.end())
382 addOnMap.insert(std::pair<BString, BPath>(path.Leaf(), path));
386 for (PathMap::const_iterator addOnIterator = addOnMap.begin();
387 addOnIterator != addOnMap.end(); addOnIterator++) {
388 const BPath& path = addOnIterator->second;
390 image_id image = load_add_on(path.Path());
391 if (image < 0) {
392 printf("Failed to load %s addon: %s.\n", path.Path(),
393 strerror(image));
394 continue;
397 BNetworkSettingsAddOn* (*instantiateAddOn)(image_id image,
398 BNetworkSettings& settings);
400 status_t status = get_image_symbol(image,
401 "instantiate_network_settings_add_on",
402 B_SYMBOL_TYPE_TEXT, (void**)&instantiateAddOn);
403 if (status != B_OK) {
404 // No "addon instantiate function" symbol found in this addon
405 printf("No symbol \"instantiate_network_settings_add_on\" found "
406 "in %s addon: not a network setup addon!\n", path.Path());
407 unload_add_on(image);
408 continue;
411 BNetworkSettingsAddOn* addOn = instantiateAddOn(image, fSettings);
412 if (addOn == NULL) {
413 unload_add_on(image);
414 continue;
417 fAddOns.AddItem(addOn);
419 // Per interface items
420 ItemMap::const_iterator iterator = fInterfaceItemMap.begin();
421 for (; iterator != fInterfaceItemMap.end(); iterator++) {
422 const BString& interface = iterator->first;
423 BListItem* interfaceItem = iterator->second;
425 uint32 cookie = 0;
426 while (true) {
427 BNetworkSettingsItem* item = addOn->CreateNextInterfaceItem(
428 cookie, interface.String());
429 if (item == NULL)
430 break;
432 fSettingsMap[item->ListItem()] = item;
433 fListView->AddUnder(item->ListItem(), interfaceItem);
435 fListView->SortItemsUnder(interfaceItem, true,
436 NetworkWindow::_CompareListItems);
439 // Generic items
440 uint32 cookie = 0;
441 while (true) {
442 BNetworkSettingsItem* item = addOn->CreateNextItem(cookie);
443 if (item == NULL)
444 break;
446 fSettingsMap[item->ListItem()] = item;
447 fListView->AddUnder(item->ListItem(),
448 _ListItemFor(item->Type()));
451 _SortItemsUnder(fServicesItem);
452 _SortItemsUnder(fDialUpItem);
453 _SortItemsUnder(fVPNItem);
454 _SortItemsUnder(fOtherItem);
457 fListView->SortItemsUnder(NULL, true,
458 NetworkWindow::_CompareTopLevelListItems);
462 BNetworkSettingsItem*
463 NetworkWindow::_SettingsItemFor(BListItem* item)
465 SettingsMap::const_iterator found = fSettingsMap.find(item);
466 if (found != fSettingsMap.end())
467 return found->second;
469 return NULL;
473 void
474 NetworkWindow::_SortItemsUnder(BListItem* item)
476 if (item != NULL)
477 fListView->SortItemsUnder(item, true, NetworkWindow::_CompareListItems);
481 BListItem*
482 NetworkWindow::_ListItemFor(BNetworkSettingsType type)
484 switch (type) {
485 case B_NETWORK_SETTINGS_TYPE_SERVICE:
486 if (fServicesItem == NULL)
487 fServicesItem = _CreateItem(B_TRANSLATE("Services"));
488 return fServicesItem;
490 case B_NETWORK_SETTINGS_TYPE_DIAL_UP:
491 if (fDialUpItem == NULL)
492 fDialUpItem = _CreateItem(B_TRANSLATE("Dial Up"));
493 return fDialUpItem;
495 case B_NETWORK_SETTINGS_TYPE_VPN:
496 if (fVPNItem == NULL)
497 fVPNItem = _CreateItem(B_TRANSLATE("VPN"));
498 return fVPNItem;
500 case B_NETWORK_SETTINGS_TYPE_OTHER:
501 if (fOtherItem == NULL)
502 fOtherItem = _CreateItem(B_TRANSLATE("Other"));
503 return fOtherItem;
505 default:
506 return NULL;
511 BListItem*
512 NetworkWindow::_CreateItem(const char* label)
514 BListItem* item = new TitleItem(label);
515 item->SetExpanded(true);
516 fListView->AddItem(item);
517 return item;
521 void
522 NetworkWindow::_SelectItem(BListItem* listItem)
524 while (fAddOnShellView->CountChildren() > 0)
525 fAddOnShellView->ChildAt(0)->RemoveSelf();
527 BView* nextView = NULL;
529 BNetworkSettingsItem* item = _SettingsItemFor(listItem);
530 if (item != NULL) {
531 nextView = item->View();
532 } else {
533 InterfaceListItem* item = dynamic_cast<InterfaceListItem*>(
534 listItem);
535 if (item != NULL) {
536 fInterfaceView->SetTo(item->Name());
537 nextView = fInterfaceView;
541 if (nextView != NULL)
542 fAddOnShellView->AddChild(nextView);
546 void
547 NetworkWindow::_BroadcastSettingsUpdate(uint32 type)
549 for (int32 index = 0; index < fListView->FullListCountItems(); index++) {
550 BNetworkSettingsListener* listener
551 = dynamic_cast<BNetworkSettingsListener*>(
552 fListView->FullListItemAt(index));
553 if (listener != NULL)
554 listener->SettingsUpdated(type);
557 SettingsMap::const_iterator iterator = fSettingsMap.begin();
558 for (; iterator != fSettingsMap.end(); iterator++)
559 iterator->second->SettingsUpdated(type);
561 _UpdateRevertButton();
565 void
566 NetworkWindow::_BroadcastConfigurationUpdate(const BMessage& message)
568 for (int32 index = 0; index < fListView->FullListCountItems(); index++) {
569 BNetworkConfigurationListener* listener
570 = dynamic_cast<BNetworkConfigurationListener*>(
571 fListView->FullListItemAt(index));
572 if (listener != NULL)
573 listener->ConfigurationUpdated(message);
576 SettingsMap::const_iterator iterator = fSettingsMap.begin();
577 for (; iterator != fSettingsMap.end(); iterator++)
578 iterator->second->ConfigurationUpdated(message);
580 // TODO: improve invalidated region to the one that matters
581 fListView->Invalidate();
582 _UpdateRevertButton();
586 void
587 NetworkWindow::_UpdateRevertButton()
589 bool enabled = false;
590 SettingsMap::const_iterator iterator = fSettingsMap.begin();
591 for (; iterator != fSettingsMap.end(); iterator++) {
592 if (iterator->second->IsRevertable()) {
593 enabled = true;
594 break;
598 fRevertButton->SetEnabled(enabled);
602 void
603 NetworkWindow::_ShowReplicant(bool show)
605 if (show) {
606 const char* argv[] = {"--deskbar", NULL};
608 status_t status = be_roster->Launch(kNetworkStatusSignature, 1, argv);
609 if (status != B_OK) {
610 BString errorMessage;
611 errorMessage.SetToFormat(
612 B_TRANSLATE("Installing NetworkStatus in Deskbar failed: %s"),
613 strerror(status));
614 BAlert* alert = new BAlert(B_TRANSLATE("launch error"),
615 errorMessage, B_TRANSLATE("Ok"));
616 alert->Go(NULL);
618 } else {
619 BDeskbar deskbar;
620 deskbar.RemoveItem("NetworkStatus");
625 bool
626 NetworkWindow::_IsReplicantInstalled()
628 BDeskbar deskbar;
629 return deskbar.HasItem("NetworkStatus");
633 /*static*/ const char*
634 NetworkWindow::_ItemName(const BListItem* item)
636 if (const BNetworkInterfaceListItem* listItem = dynamic_cast<
637 const BNetworkInterfaceListItem*>(item))
638 return listItem->Label();
640 if (const ServiceListItem* listItem = dynamic_cast<
641 const ServiceListItem*>(item))
642 return listItem->Label();
644 if (const BStringItem* stringItem = dynamic_cast<const BStringItem*>(item))
645 return stringItem->Text();
647 return NULL;
651 /*static*/ int
652 NetworkWindow::_CompareTopLevelListItems(const BListItem* a, const BListItem* b)
654 if (a == b)
655 return 0;
657 if (const InterfaceListItem* itemA
658 = dynamic_cast<const InterfaceListItem*>(a)) {
659 if (const InterfaceListItem* itemB
660 = dynamic_cast<const InterfaceListItem*>(b)) {
661 return strcasecmp(itemA->Name(), itemB->Name());
663 return -1;
664 } else if (dynamic_cast<const InterfaceListItem*>(b) != NULL)
665 return 1;
667 if (a == fDialUpItem)
668 return -1;
669 if (b == fDialUpItem)
670 return 1;
672 if (a == fServicesItem)
673 return -1;
674 if (b == fServicesItem)
675 return 1;
677 return _CompareListItems(a, b);
681 /*static*/ int
682 NetworkWindow::_CompareListItems(const BListItem* a, const BListItem* b)
684 if (a == b)
685 return 0;
687 const char* nameA = _ItemName(a);
688 const char* nameB = _ItemName(b);
690 if (nameA != NULL && nameB != NULL)
691 return strcasecmp(nameA, nameB);
692 if (nameA != NULL)
693 return 1;
694 if (nameB != NULL)
695 return -1;
697 return (addr_t)a > (addr_t)b ? 1 : -1;