xref: /haiku/src/apps/networkstatus/NetworkStatusView.cpp (revision 14e3d1b5768e7110b3d5c0855833267409b71dbb)
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