1/* 2 * Copyright 2003-2005, Waldemar Kornewald <wkornew@gmx.net> 3 * Distributed under the terms of the MIT License. 4 */ 5 6#include "DialUpView.h" 7#include "DialUpAddon.h" 8 9#include <cstring> 10#include "InterfaceUtils.h" 11#include "MessageDriverSettingsUtils.h" 12#include "TextRequestDialog.h" 13 14#include <PPPInterface.h> 15#include <settings_tools.h> 16#include <TemplateList.h> 17 18#include <Application.h> 19 20#include <Alert.h> 21#include <Button.h> 22#include <CheckBox.h> 23#include <MenuBar.h> 24#include <MenuField.h> 25#include <MenuItem.h> 26#include <Messenger.h> 27#include <PopUpMenu.h> 28#include <StringView.h> 29#include <TabView.h> 30 31#include <Directory.h> 32#include <Entry.h> 33#include <Path.h> 34 35 36// GUI constants 37static const uint32 kInterfaceFieldWidth = 175; 38 39// message constants 40static const uint32 kMsgCreateNew = 'NEWI'; 41static const uint32 kMsgFinishCreateNew = 'FNEW'; 42static const uint32 kMsgDeleteCurrent = 'DELI'; 43static const uint32 kMsgSelectInterface = 'SELI'; 44static const uint32 kMsgConnectButton = 'CONI'; 45static const uint32 kMsgUpdateDefaultInterface = 'UPDT'; 46 47// labels 48static const char *kLabelInterface = "Interface: "; 49static const char *kLabelInterfaceName = "Interface Name: "; 50static const char *kLabelCreateNewInterface = "Create New Interface"; 51static const char *kLabelCreateNew = "Create New..."; 52static const char *kLabelDefaultInterface = "Default"; 53static const char *kLabelDeleteCurrent = "Delete Current"; 54static const char *kLabelConnect = "Connect"; 55static const char *kLabelDisconnect = "Disconnect"; 56static const char *kLabelOK = "OK"; 57 58// connection status strings 59static const char *kTextConnecting = "Connecting..."; 60static const char *kTextConnectionEstablished = "Connection established."; 61static const char *kTextNotConnected = "Not connected."; 62static const char *kTextDeviceUpFailed = "Failed to connect."; 63static const char *kTextAuthenticating = "Authenticating..."; 64static const char *kTextAuthenticationFailed = "Authentication failed!"; 65static const char *kTextConnectionLost = "Connection lost!"; 66static const char *kTextCreationError = "Error creating interface!"; 67static const char *kTextNoInterfacesFound = "Please create a new interface..."; 68static const char *kTextChooseInterfaceName = "Please choose a new name for this " 69 "interface."; 70 71static const char *kErrorTitle = "Error"; 72static const char *kErrorNoPPPStack = "Error: Could not access the PPP stack!"; 73static const char *kErrorInterfaceExists = "Error: An interface with this name " 74 "already exists!"; 75static const char *kErrorLoadingFailed = "Error: Failed loading interface! The " 76 "current settings will be deleted."; 77static const char *kErrorSavingFailed = "Error: Failed saving interface settings!"; 78 79 80DialUpView::DialUpView(BRect frame) 81 : BView(frame, "DialUpView", B_FOLLOW_NONE, 0), 82 fListener(this), 83 fCurrentItem(NULL), 84 fKeepLabel(false) 85{ 86 BRect bounds = Bounds(); 87 // for caching 88 SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 89 90 // add messenger to us so add-ons can contact us 91 BMessenger messenger(this); 92 fSettings.Addons().AddMessenger(DUN_MESSENGER, messenger); 93 94 // create pop-up with all interfaces and "New..."/"Delete current" items 95 fInterfaceMenu = new BPopUpMenu(kLabelCreateNew); 96 BRect rect = bounds; 97 rect.InsetBy(5, 5); 98 rect.right = kInterfaceFieldWidth; 99 rect.bottom = rect.top + 20; 100 fMenuField = new BMenuField(rect, "Interfaces", kLabelInterface, fInterfaceMenu); 101 fMenuField->SetDivider(StringWidth(fMenuField->Label()) + 5); 102 rect.top += 3; 103 rect.bottom -= 2; 104 rect.left = rect.right + 5; 105 rect.right = bounds.right - 5; 106 fDefaultInterface = new BCheckBox(rect, "Default", kLabelDefaultInterface, 107 new BMessage(kMsgUpdateDefaultInterface)); 108 rect.left = bounds.left + 5; 109 rect.top = rect.bottom + 12; 110 rect.bottom = bounds.bottom 111 - 20 // height of bottom controls 112 - 20; // space for bottom controls 113 fTabView = new BTabView(rect, "TabView", B_WIDTH_FROM_LABEL); 114 BRect tabViewRect(fTabView->Bounds()); 115 tabViewRect.bottom -= fTabView->TabHeight(); 116 fSettings.Addons().AddRect(DUN_TAB_VIEW_RECT, tabViewRect); 117 118 BRect tmpRect(rect); 119 tmpRect.top += (tmpRect.Height() - 15) / 2; 120 tmpRect.bottom = tmpRect.top + 15; 121 fStringView = new BStringView(tmpRect, "NoInterfacesFound", 122 kTextNoInterfacesFound); 123 fStringView->SetAlignment(B_ALIGN_CENTER); 124 fStringView->Hide(); 125 tmpRect.top = tmpRect.bottom + 10; 126 tmpRect.bottom = tmpRect.top + 25; 127 fCreateNewButton = new BButton(tmpRect, "CreateNewButton", 128 kLabelCreateNewInterface, new BMessage(kMsgCreateNew)); 129 fCreateNewButton->ResizeToPreferred(); 130 tmpRect.left = (rect.Width() - fCreateNewButton->Bounds().Width()) / 2 + rect.left; 131 fCreateNewButton->MoveTo(tmpRect.left, tmpRect.top); 132 fCreateNewButton->Hide(); 133 134 rect.top = rect.bottom + 15; 135 rect.bottom = rect.top + 15; 136 rect.right = rect.left + 200; 137 fStatusView = new BStringView(rect, "StatusView", kTextNotConnected); 138 139 rect.InsetBy(0, -5); 140 rect.left = rect.right + 5; 141 rect.right = bounds.right - 5; 142 fConnectButton = new BButton(rect, "ConnectButton", kLabelConnect, 143 new BMessage(kMsgConnectButton)); 144 145 AddChild(fMenuField); 146 AddChild(fDefaultInterface); 147 AddChild(fTabView); 148 AddChild(fStringView); 149 AddChild(fCreateNewButton); 150 AddChild(fStatusView); 151 AddChild(fConnectButton); 152 153 // initialize 154 fListener.WatchManager(); 155 LoadInterfaces(); 156 fSettings.LoadAddons(); 157 CreateTabs(); 158 fCurrentItem = NULL; 159 // reset, otherwise SelectInterface will not load the settings 160 SelectInterface(0); 161 UpdateControls(); 162} 163 164 165DialUpView::~DialUpView() 166{ 167 fListener.StopWatchingInterface(); 168 fListener.StopWatchingManager(); 169 fSettings.SaveSettingsToFile(); 170} 171 172 173void 174DialUpView::AttachedToWindow() 175{ 176 fInterfaceMenu->SetTargetForItems(this); 177 fCreateNewButton->SetTarget(this); 178 fConnectButton->SetTarget(this); 179 fDefaultInterface->SetTarget(this); 180 fDefaultInterface->Hide(); 181 // TODO: remove this when we have full COD support 182 183 if(fListener.InitCheck() != B_OK) { 184 (new BAlert(kErrorTitle, kErrorNoPPPStack, kLabelOK, 185 NULL, NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT))->Go(NULL); 186 fConnectButton->Hide(); 187 } 188} 189 190 191void 192DialUpView::MessageReceived(BMessage *message) 193{ 194 switch(message->what) { 195 case PPP_REPORT_MESSAGE: 196 HandleReportMessage(message); 197 break; 198 199 // ------------------------------------------------- 200 case kMsgCreateNew: { 201 UpdateControls(); 202 (new TextRequestDialog(kLabelCreateNewInterface, kTextChooseInterfaceName, 203 kLabelInterfaceName))->Go( 204 new BInvoker(new BMessage(kMsgFinishCreateNew), this)); 205 } break; 206 207 case kMsgFinishCreateNew: { 208 int32 which; 209 message->FindInt32("which", &which); 210 const char *name = message->FindString("text"); 211 if(which == 1 && name && strlen(name) > 0) 212 AddInterface(name, true); 213 214 if(fCurrentItem) 215 fCurrentItem->SetMarked(true); 216 217 UpdateControls(); 218 219 // a newly created interface is set to default if there is no default one 220 if(PPPManager::DefaultInterface() == "") { 221 fDefaultInterface->SetValue(true); 222 PPPManager::SetDefaultInterface(name); 223 } 224 } break; 225 // ------------------------------------------------- 226 227 case kMsgDeleteCurrent: { 228 if(!fCurrentItem) 229 return; 230 231 const char *name = fCurrentItem->Message()->FindString("name"); 232 if(PPPManager::DefaultInterface() == name) 233 PPPManager::SetDefaultInterface(""); 234 fInterfaceMenu->RemoveItem(fCurrentItem); 235 BDirectory settings; 236 PPPManager::GetSettingsDirectory(&settings); 237 BEntry entry; 238 settings.FindEntry(name, &entry); 239 entry.Remove(); 240 delete fCurrentItem; 241 fCurrentItem = NULL; 242 243 BMenuItem *marked = fInterfaceMenu->FindMarked(); 244 if(marked) 245 marked->SetMarked(false); 246 247 UpdateControls(); 248 SelectInterface(0); 249 // this stops watching the deleted interface 250 } break; 251 252 case kMsgSelectInterface: { 253 int32 index; 254 message->FindInt32("index", &index); 255 SelectInterface(index); 256 } break; 257 258 case kMsgConnectButton: { 259 if(!fCurrentItem) 260 return; 261 262 BringUpOrDown(); 263 } break; 264 265 case kMsgUpdateDefaultInterface: 266 UpdateDefaultInterface(); 267 break; 268 269 default: 270 BView::MessageReceived(message); 271 } 272} 273 274 275void 276DialUpView::BringUpOrDown() 277{ 278 fSettings.SaveSettingsToFile(); 279 BMessage settings; 280 fSettings.SaveSettings(&settings); 281 282 PPPInterface interface; 283 ppp_interface_info_t info; 284 285 // if going up: delete interface in order for the settings changes to take effect 286 interface = fListener.Manager().InterfaceWithName( 287 fCurrentItem->Message()->FindString("name")); 288 interface.GetInterfaceInfo(&info); 289 if(interface.InitCheck() == B_OK && info.info.state == PPP_INITIAL_STATE 290 && info.info.phase == PPP_DOWN_PHASE) 291 fListener.Manager().DeleteInterface(interface.ID()); 292 293 interface = fListener.Manager().CreateInterfaceWithName( 294 fCurrentItem->Message()->FindString("name")); 295 296 if(interface.InitCheck() != B_OK) { 297 Window()->Lock(); 298 fStatusView->SetText(kTextCreationError); 299 Window()->Unlock(); 300 return; 301 } 302 303 interface.GetInterfaceInfo(&info); 304 if(info.info.state == PPP_INITIAL_STATE && info.info.phase == PPP_DOWN_PHASE) { 305 interface.SetPassword(fSettings.SessionPassword()); 306 interface.SetAskBeforeConnecting(false); 307 interface.Up(); 308 } else 309 interface.Down(); 310} 311 312 313void 314DialUpView::HandleReportMessage(BMessage *message) 315{ 316 if(!fCurrentItem) 317 return; 318 319 ppp_interface_id id; 320 if(message->FindInt32("interface", reinterpret_cast<int32*>(&id)) != B_OK 321 || (fListener.Interface() != PPP_UNDEFINED_INTERFACE_ID 322 && id != fListener.Interface())) 323 return; 324 325 int32 type, code; 326 message->FindInt32("type", &type); 327 message->FindInt32("code", &code); 328 329 if(type == PPP_MANAGER_REPORT && code == PPP_REPORT_INTERFACE_CREATED) { 330 PPPInterface interface(id); 331 if(interface.InitCheck() != B_OK 332 || strcasecmp(interface.Name(), 333 fCurrentItem->Message()->FindString("name"))) 334 return; 335 336 WatchInterface(id); 337 } else if(type == PPP_CONNECTION_REPORT) 338 UpdateStatus(code); 339 else if(type == PPP_DESTRUCTION_REPORT) 340 WatchInterface(fListener.Manager().InterfaceWithName( 341 fCurrentItem->Message()->FindString("name"))); 342} 343 344 345void 346DialUpView::CreateTabs() 347{ 348 // create tabs for all registered and valid tab add-ons 349 DialUpAddon *addon; 350 BView *target; 351 float width, height; 352 TemplateList<DialUpAddon*> addons; 353 354 for(int32 index = 0; 355 fSettings.Addons().FindPointer(DUN_TAB_ADDON_TYPE, index, 356 reinterpret_cast<void**>(&addon)) == B_OK; 357 index++) { 358 if(!addon || addon->Position() < 0) 359 continue; 360 361 int32 insertIndex = 0; 362 for(; insertIndex < addons.CountItems(); insertIndex++) 363 if(addons.ItemAt(insertIndex)->Position() > addon->Position()) 364 break; 365 366 addons.AddItem(addon, insertIndex); 367 } 368 369 for(int32 index = 0; index < addons.CountItems(); index++) { 370 addon = addons.ItemAt(index); 371 372 if(!addon->GetPreferredSize(&width, &height)) 373 continue; 374 375 target = addon->CreateView(BPoint(0, 0)); 376 if(!target) 377 continue; 378 379 fTabView->AddTab(target, NULL); 380 } 381} 382 383 384void 385DialUpView::UpdateStatus(int32 code) 386{ 387 switch(code) { 388 case PPP_REPORT_DEVICE_UP_FAILED: 389 case PPP_REPORT_AUTHENTICATION_FAILED: 390 case PPP_REPORT_DOWN_SUCCESSFUL: 391 case PPP_REPORT_CONNECTION_LOST: 392 fConnectButton->SetLabel(kLabelConnect); 393 break; 394 395 default: 396 fConnectButton->SetLabel(kLabelDisconnect); 397 } 398 399 // maybe the status string must not be changed (codes that set fKeepLabel to false 400 // should still be handled) 401 if(fKeepLabel && code != PPP_REPORT_GOING_UP && code != PPP_REPORT_UP_SUCCESSFUL) 402 return; 403 404 if(fListener.InitCheck() != B_OK) { 405 fStatusView->SetText(kErrorNoPPPStack); 406 return; 407 } 408 409 // only errors should set fKeepLabel to true 410 switch(code) { 411 case PPP_REPORT_GOING_UP: 412 fKeepLabel = false; 413 fStatusView->SetText(kTextConnecting); 414 break; 415 416 case PPP_REPORT_UP_SUCCESSFUL: 417 fKeepLabel = false; 418 fStatusView->SetText(kTextConnectionEstablished); 419 break; 420 421 case PPP_REPORT_DOWN_SUCCESSFUL: 422 fStatusView->SetText(kTextNotConnected); 423 break; 424 425 case PPP_REPORT_DEVICE_UP_FAILED: 426 fKeepLabel = true; 427 fStatusView->SetText(kTextDeviceUpFailed); 428 break; 429 430 case PPP_REPORT_AUTHENTICATION_REQUESTED: 431 fStatusView->SetText(kTextAuthenticating); 432 break; 433 434 case PPP_REPORT_AUTHENTICATION_FAILED: 435 fKeepLabel = true; 436 fStatusView->SetText(kTextAuthenticationFailed); 437 break; 438 439 case PPP_REPORT_CONNECTION_LOST: 440 fKeepLabel = true; 441 fStatusView->SetText(kTextConnectionLost); 442 break; 443 } 444} 445 446 447void 448DialUpView::WatchInterface(ppp_interface_id ID) 449{ 450 fListener.WatchInterface(ID); 451 452 // update status 453 PPPInterface interface(fListener.Interface()); 454 if(interface.InitCheck() != B_OK) 455 UpdateStatus(PPP_REPORT_DOWN_SUCCESSFUL); 456} 457 458 459void 460DialUpView::LoadInterfaces() 461{ 462 fInterfaceMenu->AddSeparatorItem(); 463 fInterfaceMenu->AddItem(new BMenuItem(kLabelCreateNewInterface, 464 new BMessage(kMsgCreateNew))); 465 fDeleterItem = new BMenuItem(kLabelDeleteCurrent, 466 new BMessage(kMsgDeleteCurrent)); 467 fInterfaceMenu->AddItem(fDeleterItem); 468 469 BDirectory settingsDirectory; 470 BEntry entry; 471 BPath path; 472 PPPManager::GetSettingsDirectory(&settingsDirectory); 473 while(settingsDirectory.GetNextEntry(&entry) == B_OK) { 474 if(entry.IsFile()) { 475 entry.GetPath(&path); 476 AddInterface(path.Leaf(), true); 477 } 478 } 479} 480 481 482void 483DialUpView::AddInterface(const char *name, bool isNew = false) 484{ 485 if(FindInterface(name)) { 486 (new BAlert(kErrorTitle, kErrorInterfaceExists, kLabelOK, 487 NULL, NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT))->Go(NULL); 488 return; 489 } 490 491 BMessage *message = new BMessage(kMsgSelectInterface); 492 message->AddString("name", name); 493 BString label(name); 494 if(PPPManager::DefaultInterface() == label) 495 label << " (" << kLabelDefaultInterface << ")"; 496 BMenuItem *item = new BMenuItem(label.String(), message); 497 item->SetTarget(this); 498 int32 index = FindNextMenuInsertionIndex(fInterfaceMenu, name); 499 if(index > CountInterfaces()) 500 index = CountInterfaces(); 501 fInterfaceMenu->AddItem(item, index); 502 UpdateControls(); 503 504 item->SetMarked(true); 505 SelectInterface(index, isNew); 506} 507 508 509void 510DialUpView::SelectInterface(int32 index, bool isNew = false) 511{ 512 BMenuItem *item = fInterfaceMenu->FindMarked(); 513 if(fCurrentItem && item == fCurrentItem) 514 return; 515 516 if(fCurrentItem && !fSettings.SaveSettingsToFile()) 517 (new BAlert(kErrorTitle, kErrorSavingFailed, kLabelOK, 518 NULL, NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT))->Go(NULL); 519 520 if(index >= CountInterfaces() || index < 0) { 521 if(CountInterfaces() > 0) 522 SelectInterface(0); 523 else { 524 fCurrentItem = NULL; 525 WatchInterface(PPP_UNDEFINED_INTERFACE_ID); 526 } 527 } else { 528 fCurrentItem = fInterfaceMenu->ItemAt(index); 529 if(!fCurrentItem) { 530 SelectInterface(0); 531 return; 532 } 533 534 const char *name = fCurrentItem->Message() ? 535 fCurrentItem->Message()->FindString("name") : NULL; 536 fDefaultInterface->SetValue(name && PPPManager::DefaultInterface() == name); 537 fCurrentItem->SetMarked(true); 538 fDeleterItem->SetEnabled(true); 539 fInterfaceMenu->Superitem()->SetLabel(name); 540 WatchInterface(fListener.Manager().InterfaceWithName(name)); 541 } 542 543 UpdateControls(); 544 545 if(!fCurrentItem) 546 fSettings.LoadSettings(NULL, false); 547 // tell modules to unload all settings 548 else if(!isNew && !fSettings.LoadSettings( 549 fCurrentItem->Message()->FindString("name"), false)) { 550 (new BAlert(kErrorTitle, kErrorLoadingFailed, kLabelOK, 551 NULL, NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT))->Go(NULL); 552 fSettings.LoadSettings(fCurrentItem->Message()->FindString("name"), true); 553 } else if(isNew && !fSettings.LoadSettings( 554 fCurrentItem->Message()->FindString("name"), true)) 555 (new BAlert(kErrorTitle, kErrorLoadingFailed, kLabelOK, 556 NULL, NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT))->Go(NULL); 557} 558 559 560int32 561DialUpView::CountInterfaces() const 562{ 563 return fInterfaceMenu->CountItems() - 3; 564} 565 566 567BMenuItem* 568DialUpView::FindInterface(BString name) 569{ 570 BMenuItem *item; 571 for(int32 index = 0; index < CountInterfaces(); index++) { 572 item = fInterfaceMenu->ItemAt(index); 573 if(item && item->Message() && item->Message()->HasString("name") 574 && name == item->Message()->FindString("name")) 575 return item; 576 } 577 578 return NULL; 579} 580 581 582void 583DialUpView::UpdateControls() 584{ 585 if(fTabView->IsHidden() && CountInterfaces() > 0) { 586 fInterfaceMenu->SetLabelFromMarked(true); 587 fStringView->Hide(); 588 fCreateNewButton->Hide(); 589 fTabView->Show(); 590 fDefaultInterface->Show(); 591 fConnectButton->SetEnabled(true); 592 } else if(!fTabView->IsHidden() && CountInterfaces() == 0) { 593 fDeleterItem->SetEnabled(false); 594 fInterfaceMenu->SetRadioMode(false); 595 fInterfaceMenu->Superitem()->SetLabel(kLabelCreateNew); 596 fTabView->Hide(); 597 fDefaultInterface->Hide(); 598 fStringView->Show(); 599 fCreateNewButton->Show(); 600 fConnectButton->SetEnabled(false); 601 } 602 603 // move default checkbox next to interface menu (its size might have changed) 604 float width = fInterfaceMenu->StringWidth(fMenuField->Label()) 605 + fInterfaceMenu->StringWidth(fInterfaceMenu->Superitem()->Label()) + 30; 606 if(width > kInterfaceFieldWidth) 607 width = kInterfaceFieldWidth; 608 fDefaultInterface->MoveTo(fMenuField->Frame().left + width, 609 fDefaultInterface->Frame().top); 610} 611 612 613void 614DialUpView::UpdateDefaultInterface() 615{ 616 const char *name = fCurrentItem->Message()->FindString("name"); 617 BMenuItem *defaultItem = FindInterface(PPPManager::DefaultInterface()); 618 if(fDefaultInterface->Value()) { 619 if(!PPPManager::SetDefaultInterface(name)) { 620 fDefaultInterface->SetValue(0); 621 return; 622 } 623 if(defaultItem) 624 defaultItem->SetLabel(defaultItem->Message()->FindString("name")); 625 626 BString label(name); 627 label << " (" << kLabelDefaultInterface << ")"; 628 fCurrentItem->SetLabel(label.String()); 629 } else { 630 PPPManager::SetDefaultInterface(""); 631 fCurrentItem->SetLabel(name); 632 } 633} 634