1 /* 2 * Copyright 2006-2007, Haiku, Inc. All Rights Reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Axel Dörfler, axeld@pinc-software.de 7 * Hugo Santos, hugosantos@gmail.com 8 * Dario Casalinuovo 9 */ 10 11 12 #include "NetworkStatusView.h" 13 14 #include <arpa/inet.h> 15 #include <net/if.h> 16 #include <stdio.h> 17 #include <stdlib.h> 18 #include <string.h> 19 #include <sys/socket.h> 20 #include <sys/sockio.h> 21 #include <unistd.h> 22 23 #include <Alert.h> 24 #include <Application.h> 25 #include <Bitmap.h> 26 #include <Deskbar.h> 27 #include <Dragger.h> 28 #include <Drivers.h> 29 #include <IconUtils.h> 30 #include <MenuItem.h> 31 #include <MessageRunner.h> 32 #include <PopUpMenu.h> 33 #include <Resources.h> 34 #include <Roster.h> 35 #include <String.h> 36 #include <TextView.h> 37 38 #include <net_notifications.h> 39 40 #include "NetworkStatus.h" 41 #include "NetworkStatusIcons.h" 42 43 44 static const char *kStatusDescriptions[] = { 45 "Unknown", 46 "No Link", 47 "No stateful configuration", 48 "Configuring", 49 "Ready" 50 }; 51 52 extern "C" _EXPORT BView *instantiate_deskbar_item(void); 53 54 55 const uint32 kMsgShowConfiguration = 'shcf'; 56 const uint32 kMsgOpenNetworkPreferences = 'onwp'; 57 58 const uint32 kMinIconWidth = 16; 59 const uint32 kMinIconHeight = 16; 60 61 62 class SocketOpener { 63 public: 64 SocketOpener() 65 { 66 fSocket = socket(AF_INET, SOCK_DGRAM, 0); 67 } 68 69 ~SocketOpener() 70 { 71 close(fSocket); 72 } 73 74 status_t InitCheck() 75 { 76 return fSocket >= 0 ? B_OK : B_ERROR; 77 } 78 79 operator int() const 80 { 81 return fSocket; 82 } 83 84 private: 85 int fSocket; 86 }; 87 88 89 // #pragma mark - 90 91 92 NetworkStatusView::NetworkStatusView(BRect frame, int32 resizingMode, 93 bool inDeskbar) 94 : BView(frame, kDeskbarItemName, resizingMode, 95 B_WILL_DRAW | B_FRAME_EVENTS), 96 fInDeskbar(inDeskbar), 97 fStatus(kStatusUnknown) 98 { 99 _Init(); 100 101 if (!inDeskbar) { 102 // we were obviously added to a standard window - let's add a dragger 103 frame.OffsetTo(B_ORIGIN); 104 frame.top = frame.bottom - 7; 105 frame.left = frame.right - 7; 106 BDragger* dragger = new BDragger(frame, this, 107 B_FOLLOW_RIGHT | B_FOLLOW_BOTTOM); 108 AddChild(dragger); 109 } else 110 _Update(); 111 } 112 113 114 NetworkStatusView::NetworkStatusView(BMessage* archive) 115 : BView(archive), 116 fInDeskbar(false) 117 { 118 app_info info; 119 if (be_app->GetAppInfo(&info) == B_OK 120 && !strcasecmp(info.signature, "application/x-vnd.Be-TSKB")) 121 fInDeskbar = true; 122 123 _Init(); 124 } 125 126 127 NetworkStatusView::~NetworkStatusView() 128 { 129 } 130 131 132 void 133 NetworkStatusView::_Init() 134 { 135 for (int i = 0; i < kStatusCount; i++) { 136 fBitmaps[i] = NULL; 137 } 138 139 _UpdateBitmaps(); 140 } 141 142 143 void 144 NetworkStatusView::_UpdateBitmaps() 145 { 146 for (int i = 0; i < kStatusCount; i++) { 147 delete fBitmaps[i]; 148 fBitmaps[i] = NULL; 149 } 150 151 image_info info; 152 if (our_image(info) != B_OK) 153 return; 154 155 BFile file(info.name, B_READ_ONLY); 156 if (file.InitCheck() < B_OK) 157 return; 158 159 BResources resources(&file); 160 #ifdef HAIKU_TARGET_PLATFORM_HAIKU 161 if (resources.InitCheck() < B_OK) 162 return; 163 #endif 164 165 for (int i = 0; i < kStatusCount; i++) { 166 const void* data = NULL; 167 size_t size; 168 data = resources.LoadResource(B_VECTOR_ICON_TYPE, 169 kNetworkStatusNoDevice + i, &size); 170 if (data != NULL) { 171 BBitmap* icon = new BBitmap(Bounds(), B_RGBA32); 172 if (icon->InitCheck() == B_OK 173 && BIconUtils::GetVectorIcon((const uint8 *)data, 174 size, icon) == B_OK) { 175 fBitmaps[i] = icon; 176 } else 177 delete icon; 178 } 179 } 180 } 181 182 183 void 184 NetworkStatusView::_Quit() 185 { 186 if (fInDeskbar) { 187 BDeskbar deskbar; 188 deskbar.RemoveItem(kDeskbarItemName); 189 } else 190 be_app->PostMessage(B_QUIT_REQUESTED); 191 } 192 193 194 NetworkStatusView * 195 NetworkStatusView::Instantiate(BMessage* archive) 196 { 197 if (!validate_instantiation(archive, "NetworkStatusView")) 198 return NULL; 199 200 return new NetworkStatusView(archive); 201 } 202 203 204 status_t 205 NetworkStatusView::Archive(BMessage* archive, bool deep) const 206 { 207 status_t status = BView::Archive(archive, deep); 208 if (status == B_OK) 209 status = archive->AddString("add_on", kSignature); 210 if (status == B_OK) 211 status = archive->AddString("class", "NetworkStatusView"); 212 213 return status; 214 } 215 216 217 void 218 NetworkStatusView::AttachedToWindow() 219 { 220 BView::AttachedToWindow(); 221 if (Parent()) 222 SetViewColor(Parent()->ViewColor()); 223 else 224 SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 225 226 SetLowColor(ViewColor()); 227 228 start_watching_network( 229 B_WATCH_NETWORK_INTERFACE_CHANGES | B_WATCH_NETWORK_LINK_CHANGES, this); 230 231 _Update(); 232 } 233 234 235 void 236 NetworkStatusView::DetachedFromWindow() 237 { 238 stop_watching_network(this); 239 } 240 241 242 void 243 NetworkStatusView::MessageReceived(BMessage* message) 244 { 245 switch (message->what) { 246 case B_NETWORK_MONITOR: 247 _Update(); 248 break; 249 250 case kMsgShowConfiguration: 251 _ShowConfiguration(message); 252 break; 253 254 case kMsgOpenNetworkPreferences: 255 _OpenNetworksPreferences(); 256 break; 257 258 case B_ABOUT_REQUESTED: 259 _AboutRequested(); 260 break; 261 262 case B_QUIT_REQUESTED: 263 _Quit(); 264 break; 265 266 default: 267 BView::MessageReceived(message); 268 } 269 } 270 271 272 void 273 NetworkStatusView::FrameResized(float width, float height) 274 { 275 _UpdateBitmaps(); 276 Invalidate(); 277 } 278 279 280 void 281 NetworkStatusView::Draw(BRect updateRect) 282 { 283 if (fBitmaps[fStatus] == NULL) 284 return; 285 286 SetDrawingMode(B_OP_ALPHA); 287 DrawBitmap(fBitmaps[fStatus]); 288 SetDrawingMode(B_OP_COPY); 289 } 290 291 292 void 293 NetworkStatusView::_ShowConfiguration(BMessage* message) 294 { 295 static const struct information_entry { 296 const char* label; 297 int32 control; 298 } kInformationEntries[] = { 299 { "Address", SIOCGIFADDR }, 300 { "Broadcast", SIOCGIFBRDADDR }, 301 { "Netmask", SIOCGIFNETMASK }, 302 { NULL } 303 }; 304 305 SocketOpener socket; 306 if (socket.InitCheck() != B_OK) 307 return; 308 309 const char* name; 310 if (message->FindString("interface", &name) != B_OK) 311 return; 312 313 ifreq request; 314 if (!_PrepareRequest(request, name)) 315 return; 316 317 BString text = name; 318 text += " information:\n"; 319 size_t boldLength = text.Length(); 320 321 for (int i = 0; kInformationEntries[i].label; i++) { 322 if (ioctl(socket, kInformationEntries[i].control, &request, 323 sizeof(request)) < 0) { 324 continue; 325 } 326 327 char address[32]; 328 sockaddr_in* inetAddress = NULL; 329 switch (kInformationEntries[i].control) { 330 case SIOCGIFNETMASK: 331 inetAddress = (sockaddr_in*)&request.ifr_mask; 332 break; 333 default: 334 inetAddress = (sockaddr_in*)&request.ifr_addr; 335 break; 336 } 337 338 if (inet_ntop(AF_INET, &inetAddress->sin_addr, address, 339 sizeof(address)) == NULL) { 340 return; 341 } 342 343 text += "\n"; 344 text += kInformationEntries[i].label; 345 text += ": "; 346 text += address; 347 } 348 349 BAlert* alert = new BAlert(name, text.String(), "Ok"); 350 BTextView* view = alert->TextView(); 351 BFont font; 352 353 view->SetStylable(true); 354 view->GetFont(&font); 355 font.SetFace(B_BOLD_FACE); 356 view->SetFontAndColor(0, boldLength, &font); 357 358 alert->Go(NULL); 359 } 360 361 362 void 363 NetworkStatusView::MouseDown(BPoint point) 364 { 365 BPopUpMenu* menu = new BPopUpMenu(B_EMPTY_STRING, false, false); 366 menu->SetAsyncAutoDestruct(true); 367 menu->SetFont(be_plain_font); 368 369 for (int32 i = 0; i < fInterfaces.CountItems(); i++) { 370 BString& name = *fInterfaces.ItemAt(i); 371 372 BString label = name; 373 label += ": "; 374 label += kStatusDescriptions[ 375 _DetermineInterfaceStatus(name.String())]; 376 377 BMessage* info = new BMessage(kMsgShowConfiguration); 378 info->AddString("interface", name.String()); 379 menu->AddItem(new BMenuItem(label.String(), info)); 380 } 381 382 menu->AddSeparatorItem(); 383 //menu->AddItem(new BMenuItem("About NetworkStatus" B_UTF8_ELLIPSIS, 384 // new BMessage(B_ABOUT_REQUESTED))); 385 menu->AddItem(new BMenuItem("Open Networks Preferences" B_UTF8_ELLIPSIS, 386 new BMessage(kMsgOpenNetworkPreferences))); 387 388 if (fInDeskbar) 389 menu->AddItem(new BMenuItem("Quit", new BMessage(B_QUIT_REQUESTED))); 390 menu->SetTargetForItems(this); 391 392 ConvertToScreen(&point); 393 menu->Go(point, true, true, true); 394 } 395 396 397 void 398 NetworkStatusView::_AboutRequested() 399 { 400 BAlert* alert = new BAlert("about", "NetworkStatus\n" 401 "\twritten by Axel Dörfler and Hugo Santos\n" 402 "\tCopyright 2007, Haiku, Inc.\n", "Ok"); 403 BTextView *view = alert->TextView(); 404 BFont font; 405 406 view->SetStylable(true); 407 408 view->GetFont(&font); 409 font.SetSize(18); 410 font.SetFace(B_BOLD_FACE); 411 view->SetFontAndColor(0, 13, &font); 412 413 alert->Go(); 414 } 415 416 417 bool 418 NetworkStatusView::_PrepareRequest(struct ifreq& request, const char* name) 419 { 420 if (strlen(name) > IF_NAMESIZE) 421 return false; 422 423 strcpy(request.ifr_name, name); 424 return true; 425 } 426 427 428 int32 429 NetworkStatusView::_DetermineInterfaceStatus(const char* name) 430 { 431 SocketOpener socket; 432 if (socket.InitCheck() != B_OK) 433 return kStatusUnknown; 434 435 ifreq request; 436 if (!_PrepareRequest(request, name)) 437 return kStatusUnknown; 438 439 uint32 flags = 0; 440 if (ioctl(socket, SIOCGIFFLAGS, &request, sizeof(struct ifreq)) == 0) 441 flags = request.ifr_flags; 442 443 int32 status = kStatusNoLink; 444 445 // TODO: no kStatusLinkNoConfig yet 446 447 if (flags & IFF_CONFIGURING) 448 status = kStatusConnecting; 449 else if ((flags & (IFF_UP | IFF_LINK)) == (IFF_UP | IFF_LINK)) 450 status = kStatusReady; 451 452 return status; 453 } 454 455 456 void 457 NetworkStatusView::_Update(bool force) 458 { 459 SocketOpener socket; 460 if (socket.InitCheck() != B_OK) 461 return; 462 463 // iterate over all interfaces and retrieve minimal status 464 465 ifconf config; 466 config.ifc_len = sizeof(config.ifc_value); 467 if (ioctl(socket, SIOCGIFCOUNT, &config, sizeof(struct ifconf)) < 0) 468 return; 469 470 uint32 count = (uint32)config.ifc_value; 471 if (count == 0) 472 return; 473 474 void* buffer = malloc(count * sizeof(struct ifreq)); 475 if (buffer == NULL) 476 return; 477 478 config.ifc_len = count * sizeof(struct ifreq); 479 config.ifc_buf = buffer; 480 if (ioctl(socket, SIOCGIFCONF, &config, sizeof(struct ifconf)) < 0) { 481 free(buffer); 482 return; 483 } 484 485 ifreq* interface = (ifreq*)buffer; 486 487 int32 oldStatus = fStatus; 488 fStatus = kStatusUnknown; 489 fInterfaces.MakeEmpty(); 490 491 for (uint32 i = 0; i < count; i++) { 492 if (strncmp(interface->ifr_name, "loop", 4) && interface->ifr_name[0]) { 493 fInterfaces.AddItem(new BString(interface->ifr_name)); 494 int32 status = _DetermineInterfaceStatus(interface->ifr_name); 495 if (status > fStatus) 496 fStatus = status; 497 } 498 499 interface = (ifreq *)((addr_t)interface + IF_NAMESIZE 500 + interface->ifr_addr.sa_len); 501 } 502 503 free(buffer); 504 505 if (fStatus != oldStatus) 506 Invalidate(); 507 } 508 509 510 void 511 NetworkStatusView::_OpenNetworksPreferences() 512 { 513 status_t status = be_roster->Launch("application/x-vnd.Haiku-Network"); 514 if (status < B_OK) { 515 BString errorMessage("Launching the Network preflet failed.\n\n" 516 "Error: "); 517 errorMessage << strerror(status); 518 BAlert* alert = new BAlert("launch error", errorMessage.String(), 519 "Ok"); 520 521 // asynchronous alert in order to not block replicant host application 522 alert->Go(NULL); 523 } 524 } 525 526 527 // #pragma mark - 528 529 530 extern "C" _EXPORT BView * 531 instantiate_deskbar_item(void) 532 { 533 return new NetworkStatusView(BRect(0, 0, 15, 15), 534 B_FOLLOW_LEFT | B_FOLLOW_TOP, true); 535 } 536 537