1 /*
2  * Copyright (C) 2008 Fred Chien <fred@lxde.org>
3  *               2012 Michael Rawson <michaelrawson76@gmail.com>
4  *               2014 Andriy Grytsenko <andrej@rep.kiev.ua>
5  *
6  * This file is a part of LXPanel project.
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software Foundation,
20  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21  */
22 
23 #if HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26 
27 #include <sys/types.h>
28 #include <stdio.h>
29 #include <string.h>
30 #include <glib.h>
31 #include <glib/gi18n.h>
32 #include <pthread.h>
33 #include <iwlib.h>
34 #include "nsconfig.h"
35 #include "netstat.h"
36 #include "lxnm_client.h"
37 #include "statusicon.h"
38 #include "passwd_gui.h"
39 #include "devproc.h"
40 #include "wireless.h"
41 #include "plugin.h"
42 #include "misc.h"
43 #include "dbg.h"
44 
45 /* 1 second */
46 #define NETSTAT_IFACE_POLL_DELAY 3000
47 
actionProcess(void * arg)48 static void* actionProcess(void *arg)
49 {
50     ENTER;
51     RET(GINT_TO_POINTER(system((char *)arg)));
52 }
53 
54 /* menu handlers */
ethernet_repair(GtkWidget * widget,netdev_info * ni)55 static void ethernet_repair(GtkWidget *widget, netdev_info *ni)
56 {
57     if (ni->ns->fixcmd) {
58         pthread_t actionThread;
59         pthread_attr_t attr;
60         char *fixcmd;
61 
62         fixcmd = g_strdup_printf(ni->ns->fixcmd, ni->netdev_list->info.ifname);
63 
64         /* Note: Better would be to join the thread, and thus get a status
65          * message. But detaching is currently simpler to implement. */
66         pthread_attr_init(&attr);
67         pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
68         pthread_create(&actionThread, &attr, actionProcess, fixcmd);
69         pthread_attr_destroy(&attr);
70     } else {
71         lxnm_send_command(ni->ns->fnetd->lxnmchannel, LXNM_ETHERNET_REPAIR, ni->netdev_list->info.ifname);
72     }
73 }
74 
ethernet_down(GtkWidget * widget,netdev_info * ni)75 static void ethernet_down(GtkWidget *widget, netdev_info *ni)
76 {
77     lxnm_send_command(ni->ns->fnetd->lxnmchannel, LXNM_ETHERNET_DOWN, ni->netdev_list->info.ifname);
78 }
79 
wireless_connect(GtkWidget * widget,ap_setting * aps)80 static void wireless_connect(GtkWidget *widget, ap_setting *aps)
81 {
82     char *cmdargs;
83 
84     /* without encryption */
85     if (!aps->apinfo->haskey) {
86         cmdargs = lxnm_wireless_command_make(aps->ifname, aps->apinfo->essid, aps->apinfo->apaddr, "",
87                                    aps->apinfo->en_method, aps->apinfo->key_mgmt,
88                                    aps->apinfo->group, aps->apinfo->pairwise);
89         lxnm_send_command(aps->gio, LXNM_WIRELESS_CONNECT, cmdargs);
90         g_free(cmdargs);
91     } else {
92         /* with encryption */
93         if (aps->ni->netdev_list->info.pg!=NULL)
94             passwd_gui_destroy(aps->ni->netdev_list->info.pg);
95 
96         /* record information for password dialog */
97         ap_setting *aps_new;
98         ap_info *apinfo;
99 	apinfo = malloc(sizeof(ap_info));
100 	if (aps->apinfo->essid==NULL)
101 		apinfo->essid = NULL;
102 	else
103 		apinfo->essid = g_strdup(aps->apinfo->essid);
104 
105 	apinfo->apaddr = g_strdup(aps->apinfo->apaddr);
106 	apinfo->quality = aps->apinfo->quality;
107 	apinfo->en_method = aps->apinfo->en_method;
108 	apinfo->pairwise = aps->apinfo->pairwise;
109 	apinfo->group = aps->apinfo->group;
110 	apinfo->key_mgmt = aps->apinfo->key_mgmt;
111 	apinfo->haskey = aps->apinfo->haskey;
112 
113         aps_new = g_new0(ap_setting, 1);
114         aps_new->ni = aps->ni;
115         aps_new->gio = aps->gio;
116         aps_new->ifname = g_strdup(aps->ifname);
117         aps_new->apinfo = apinfo;
118         /* create dialog window for typing password */
119         aps->ni->netdev_list->info.pg = passwd_gui_new(aps_new);
120         //passwd_gui_set_style(aps->ni->netdev_list->info.pg, gtk_style_new());
121     }
122 }
123 
124 static void
g_free_weaknotify(void * ptr,GObject * dummy)125 g_free_weaknotify(void *ptr, GObject *dummy)
126 {
127     g_free(ptr);
128 }
129 
130 static GtkWidget *
wireless_menu(netdev_info * ni)131 wireless_menu(netdev_info *ni)
132 {
133     APLIST *aplist;
134     APLIST *ptr;
135     GtkWidget *menu;
136     GtkWidget *menu_item;
137     GtkWidget *wireless_label;
138 
139     /* AP status widget */
140     GtkWidget *item_box;
141     GtkWidget *essid_label;
142     GtkWidget *lockicon;
143     GtkWidget *signal_quality;
144     gdouble quality_per;
145     ap_setting *aps;
146 
147     /* create menu */
148     menu = gtk_menu_new();
149     g_signal_connect(menu, "selection-done", G_CALLBACK(gtk_widget_destroy), NULL);
150 
151     /* Scanning AP */
152     aplist = wireless_scanning(ni->ns->fnetd->iwsockfd, ni->netdev_list->info.ifname);
153     if (aplist!=NULL) {
154         /* release AP list after menu */
155         g_object_weak_ref(G_OBJECT(menu), wireless_aplist_free, aplist);
156         ptr = aplist;
157         do {
158             /* skip hidden AP with Encryption */
159             if (ptr->info->haskey&&ptr->info->essid==NULL) {
160                 /* handle next AP */
161                 ptr = ptr->next;
162 				continue;
163             }
164 
165             aps = g_new0(ap_setting, 1);
166             aps->ni = ni;
167             aps->gio = ni->ns->fnetd->lxnmchannel;
168             aps->ifname = ni->netdev_list->info.ifname;
169 			aps->apinfo = ptr->info;
170 
171             /* create a new item */
172             menu_item = gtk_menu_item_new();
173             item_box = gtk_hbox_new(FALSE, 0);
174 
175             /* Encryption */
176             if (aps->apinfo->haskey) {
177                 lockicon = lxpanel_image_new_for_icon(NULL, ICONS_WL_LOCK, 18, NULL);
178                 gtk_box_pack_start(GTK_BOX(item_box), lockicon, FALSE, FALSE, 0);
179             }
180 
181             /* ESSID */
182             if (aps->apinfo->essid==NULL)
183                 essid_label = gtk_label_new(_("<Hidden Access Point>"));
184             else
185                 essid_label = gtk_label_new(aps->apinfo->essid);
186 
187             gtk_label_set_justify(GTK_LABEL(essid_label), GTK_JUSTIFY_LEFT);
188             gtk_misc_set_padding(GTK_MISC(essid_label), 2, 0);
189             gtk_box_pack_start(GTK_BOX(item_box), essid_label, TRUE, FALSE, 0);
190 
191             /* Quality */
192             quality_per = (gdouble)((double)aps->apinfo->quality/100);
193             if (quality_per>1)
194                 quality_per = 1;
195             else if (quality_per<0)
196                 quality_per = 0;
197 
198             signal_quality = gtk_progress_bar_new();
199             gtk_widget_set_size_request(signal_quality, 100, -1);
200 #if GTK_CHECK_VERSION(3, 0, 0)
201             gtk_orientable_set_orientation(GTK_ORIENTABLE(signal_quality), GTK_ORIENTATION_HORIZONTAL);
202 #else
203             gtk_progress_bar_set_orientation(GTK_PROGRESS_BAR(signal_quality), GTK_PROGRESS_LEFT_TO_RIGHT);
204 #endif
205             gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(signal_quality), quality_per);
206             gtk_box_pack_start(GTK_BOX(item_box), signal_quality, FALSE, FALSE, 0);
207 
208             /* add this item to menu */
209             gtk_container_add(GTK_CONTAINER(menu_item), item_box);
210             gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
211             g_signal_connect(G_OBJECT(menu_item), "activate", G_CALLBACK(wireless_connect), aps);
212             g_object_weak_ref(G_OBJECT(menu_item), g_free_weaknotify, aps);
213 
214             /* handle next AP */
215             ptr = ptr->next;
216         } while(ptr!=NULL);
217     } else {
218         /* we do not found any wireless networks */
219         menu_item = gtk_menu_item_new();
220         wireless_label = gtk_label_new(_("Wireless Networks not found in range"));
221         gtk_label_set_justify(GTK_LABEL(wireless_label), GTK_JUSTIFY_LEFT);
222         gtk_widget_set_sensitive(GTK_WIDGET(wireless_label), FALSE);
223         gtk_container_add(GTK_CONTAINER(menu_item), wireless_label);
224         gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
225     }
226 
227     gtk_widget_show_all(menu);
228 
229     return menu;
230 }
231 
menupopup(GtkWidget * widget,GdkEvent * event,netdev_info * ni)232 static gint menupopup(GtkWidget *widget, GdkEvent *event, netdev_info *ni)
233 {
234     GdkEventButton *event_button;
235 
236     g_return_val_if_fail (event != NULL, FALSE);
237 
238 //    if (event->type == GDK_BUTTON_PRESS) {
239         event_button = (GdkEventButton *) event;
240     if (event->type == GDK_BUTTON_PRESS && event_button->button == 1) {
241 //        if (event_button->button == 1) {
242             /* wireless device */
243             if (ni->netdev_list->info.wireless) {
244                 gtk_menu_popup(GTK_MENU(wireless_menu(ni)), NULL, NULL, NULL, NULL, event_button->button, event_button->time);
245 //            }
246             return TRUE;
247         } else {
248 //        } else if (event_button->button == 3) {
249             GtkWidget *menu;
250             GtkWidget *menu_item;
251 
252             /* create menu */
253             menu = gtk_menu_new();
254 
255             /* Repair */
256             menu_item = gtk_menu_item_new_with_label(_("Repair"));
257             gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
258             g_signal_connect(G_OBJECT(menu_item), "activate", G_CALLBACK(ethernet_repair), ni);
259 
260             /* interface down */
261             menu_item = gtk_menu_item_new_with_label(_("Disable"));
262             gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
263             g_signal_connect(G_OBJECT(menu_item), "activate", G_CALLBACK(ethernet_down), ni);
264 
265             gtk_widget_show_all(menu);
266 
267             gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, event_button->button, event_button->time);
268             return TRUE;
269         }
270     }
271 
272     return FALSE;
273 }
274 
select_icon(gboolean plug,gboolean connected,int stat)275 static char *select_icon(gboolean plug, gboolean connected, int stat)
276 {
277     if (!plug)
278         return ICONS_DISCONNECT;
279 
280     switch(stat) {
281         case NETDEV_STAT_NORMAL:
282             return ICONS_CONNECTED;
283             break;
284         case NETDEV_STAT_PROBLEM:
285             return ICONS_PROBLEM;
286             break;
287         case NETDEV_STAT_RENEW:
288             return ICONS_RENEW;
289             break;
290         case NETDEV_STAT_BOTHRS:
291             return ICONS_BOTHRS;
292             break;
293         case NETDEV_STAT_SENDDATA:
294             return ICONS_SENDDATA;
295             break;
296         case NETDEV_STAT_RECVDATA:
297             return ICONS_RECVDATA;
298             break;
299         default:
300             return NULL;
301     }
302 }
303 
select_icon_theme(gboolean plug,gboolean connected,int stat)304 static char *select_icon_theme(gboolean plug, gboolean connected, int stat)
305 {
306     if (!plug)
307         return ICONS_DISCONNECT_THEME;
308 
309     switch(stat) {
310         case NETDEV_STAT_NORMAL:
311             return ICONS_CONNECTED_THEME;
312             break;
313         case NETDEV_STAT_PROBLEM:
314             return ICONS_PROBLEM_THEME;
315             break;
316         case NETDEV_STAT_RENEW:
317             return ICONS_RENEW_THEME;
318             break;
319         case NETDEV_STAT_BOTHRS:
320             return ICONS_BOTHRS_THEME;
321             break;
322         case NETDEV_STAT_SENDDATA:
323             return ICONS_SENDDATA_THEME;
324             break;
325         case NETDEV_STAT_RECVDATA:
326             return ICONS_RECVDATA_THEME;
327             break;
328         default:
329             return NULL;
330     }
331 }
332 
refresh_systray(netstat * ns,NETDEVLIST_PTR netdev_list)333 static void refresh_systray(netstat *ns, NETDEVLIST_PTR netdev_list)
334 {
335     NETDEVLIST_PTR ptr;
336     char *tooltip;
337     const char *icon, *theme_icon;
338 
339     if (netdev_list==NULL) {
340         return;
341     }
342 
343     ptr = netdev_list;
344     do {
345         if (!ptr->info.enable) {
346             if (ptr->info.status_icon!=NULL) {
347                 set_statusicon_visible(ptr->info.status_icon, FALSE);
348             }
349         } else if (ptr->info.updated) {
350             if (!ptr->info.plug) {
351                 if (ptr->info.wireless)
352                     tooltip = g_strdup_printf("%s\n  %s", ptr->info.ifname, _("Wireless Connection has no connectivity"));
353 				else
354                     tooltip = g_strdup_printf("%s\n  %s", ptr->info.ifname, _("Network cable is plugged out"));
355             } else if (!ptr->info.connected)
356                 tooltip = g_strdup_printf("%s\n  %s", ptr->info.ifname, _("Connection has limited or no connectivity"));
357             else if (ptr->info.flags & IFF_POINTOPOINT)
358                 tooltip = g_strdup_printf("%s\n  %s\t%s\n  %s\t%s\n  %s\t%s\n\n %s(%s/%s)\n   %lu/%lu %s\n   %lu/%lu %s",
359                                                                  ptr->info.ifname,
360                                                                 _("IP Address:"), ptr->info.ipaddr,
361                                                                 _("Remote IP:"), ptr->info.dest,
362                                                                 _("Netmask:"), ptr->info.mask,
363                                                                 _("Activity"), _("Sent"),_("Received"),
364                                                                 ptr->info.trans_bytes, ptr->info.recv_bytes, _("bytes"),
365                                                                 ptr->info.trans_packets, ptr->info.recv_packets, _("packets"));
366             else if (ptr->info.wireless)
367                 tooltip = g_strdup_printf("%s(%s) - %s(%d%%)\n  %s\t%s\n  %s\t%s\n  %s\t%s\n  %s\t%s\n  %s\t%s\n\n %s(%s/%s)\n   %lu/%lu %s\n   %lu/%lu %s",
368                                                                 ptr->info.ifname, _("Wireless"),
369                                                                 ptr->info.essid, ptr->info.quality,
370                                                                 _("Protocol:"), ptr->info.protocol,
371                                                                 _("IP Address:"), ptr->info.ipaddr,
372                                                                 _("Broadcast:"), ptr->info.bcast,
373                                                                 _("Netmask:"), ptr->info.mask,
374                                                                 _("HW Address:"), ptr->info.mac,
375                                                                 _("Activity"), _("Sent"), _("Received"),
376                                                                 ptr->info.trans_bytes, ptr->info.recv_bytes, _("bytes"),
377                                                                 ptr->info.trans_packets, ptr->info.recv_packets, _("packets"));
378 
379             else
380                 tooltip = g_strdup_printf("%s\n  %s\t%s\n  %s\t%s\n  %s\t%s\n  %s\t%s\n\n %s(%s/%s)\n   %lu/%lu %s\n   %lu/%lu %s",
381                                                                 ptr->info.ifname,
382                                                                 _("IP Address:"), ptr->info.ipaddr,
383                                                                 _("Broadcast:"), ptr->info.bcast,
384                                                                 _("Netmask:"), ptr->info.mask,
385                                                                 _("HW Address:"), ptr->info.mac,
386                                                                 _("Activity"), _("Sent"), _("Received"),
387                                                                 ptr->info.trans_bytes, ptr->info.recv_bytes, _("bytes"),
388                                                                 ptr->info.trans_packets, ptr->info.recv_packets, _("packets"));
389 
390             icon = select_icon(ptr->info.plug, ptr->info.connected, ptr->info.status);
391             if (ns->use_theme)
392                 theme_icon = select_icon_theme(ptr->info.plug, ptr->info.connected, ptr->info.status);
393             else
394                 theme_icon = icon;
395             /* status icon doesn't exist  */
396             if (ptr->info.status_icon==NULL) {
397                 netdev_info *ni;
398                 ni = malloc(sizeof(netdev_info));
399                 ni->ns = ns;
400                 ni->netdev_list = ptr;
401 
402                 ptr->info.status_icon = create_statusicon(ns->panel, ns->mainw, icon, tooltip, theme_icon);
403                 g_signal_connect(ptr->info.status_icon->main, "button-press-event", G_CALLBACK(menupopup), ni);
404                 g_object_weak_ref(G_OBJECT(ptr->info.status_icon->main), g_free_weaknotify, ni);
405             } else {
406                 set_statusicon_tooltips(ptr->info.status_icon, tooltip);
407                 update_statusicon(ptr->info.status_icon, icon, theme_icon);
408                 set_statusicon_visible(ptr->info.status_icon, TRUE);
409             }
410             g_free(tooltip);
411         }
412         ptr = ptr->next;
413     } while(ptr!=NULL);
414 }
415 
refresh_devstat(netstat * ns)416 static gboolean refresh_devstat(netstat *ns)
417 {
418     if (g_source_is_destroyed(g_main_current_source()))
419         return FALSE;
420     netproc_listener(ns->fnetd);
421 #ifdef DEBUG
422     netproc_print(ns->fnetd->netdevlist);
423 #endif
424     refresh_systray(ns, ns->fnetd->netdevlist);
425     netproc_devicelist_clear(&ns->fnetd->netdevlist);
426     return TRUE;
427 }
428 
429 /* Plugin constructor */
netstat_destructor(gpointer user_data)430 static void netstat_destructor(gpointer user_data)
431 {
432     netstat *ns = (netstat *) user_data;
433 
434     ENTER;
435     g_source_remove(ns->ttag);
436     netproc_netdevlist_clear(&ns->fnetd->netdevlist);
437     /* The widget is destroyed in plugin_stop().
438     gtk_widget_destroy(ns->mainw);
439     */
440     lxnm_close(ns->fnetd->lxnmchannel);
441     close(ns->fnetd->sockfd);
442     close(ns->fnetd->iwsockfd);
443     g_free(ns->fnetd);
444     g_free(ns->fixcmd);
445     g_free(ns);
446     RET();
447 }
448 
netstat_constructor(LXPanel * panel,config_setting_t * settings)449 static GtkWidget *netstat_constructor(LXPanel *panel, config_setting_t *settings)
450 {
451     netstat *ns;
452     const char *tmp;
453     int tmp_int;
454     GtkWidget *p;
455 
456     ENTER;
457     ns = g_new0(netstat, 1);
458     g_return_val_if_fail(ns != NULL, NULL);
459     ns->panel = panel;
460     /* apply config */
461     if (config_setting_lookup_string(settings, "FixCommand", &tmp))
462         ns->fixcmd = g_strdup(tmp);
463     if (config_setting_lookup_int(settings, "UseTheme", &tmp_int))
464         ns->use_theme = !!tmp_int;
465 
466     /* initializing */
467     ns->fnetd = malloc(sizeof(FNETD));
468     ns->fnetd->netdevlist = NULL;
469     ns->fnetd->sockfd = socket(AF_INET, SOCK_DGRAM, 0);
470     ns->fnetd->iwsockfd = iw_sockets_open();
471     ns->fnetd->lxnmchannel = lxnm_socket();
472 
473     /* main */
474     ns->mainw = panel_box_new(panel, FALSE, 1);
475     gtk_widget_show_all(ns->mainw);
476 
477     /* Initializing network device list*/
478     ns->fnetd->netdev_fp = netproc_open();
479     ns->fnetd->dev_count = netproc_netdevlist_clear(&ns->fnetd->netdevlist);
480     ns->fnetd->dev_count = netproc_scandevice(ns->fnetd->sockfd, ns->fnetd->iwsockfd, ns->fnetd->netdev_fp, &ns->fnetd->netdevlist);
481     netproc_close(ns->fnetd->netdev_fp);
482     refresh_systray(ns, ns->fnetd->netdevlist);
483 
484     ns->ttag = g_timeout_add(NETSTAT_IFACE_POLL_DELAY, (GSourceFunc)refresh_devstat, ns);
485 
486     p = gtk_event_box_new();
487     lxpanel_plugin_set_data(p, ns, netstat_destructor);
488     gtk_widget_set_has_window(p, FALSE);
489     gtk_container_add((GtkContainer*)p, ns->mainw);
490 
491     RET(p);
492 }
493 
orientation_changed(LXPanel * panel,GtkWidget * p)494 static void orientation_changed(LXPanel *panel, GtkWidget *p)
495 {
496     netstat *ns = lxpanel_plugin_get_data(p);
497 
498     gtk_orientable_set_orientation(GTK_ORIENTABLE(ns->mainw),
499                                    panel_get_orientation(panel));
500 }
501 
502 FM_DEFINE_MODULE(lxpanel_gtk, netstat)
503 
504 LXPanelPluginInit fm_module_init_lxpanel_gtk = {
505     .name = N_("Manage Networks"),
506     .description = N_("Monitor and Manage networks"),
507 
508     .new_instance = netstat_constructor,
509     .reconfigure = orientation_changed,
510 };
511 
512 /* vim: set sw=4 sts=4 et : */
513