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