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