1
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
2
const ByteArray = imports.byteArray;
3
const DBus = imports.dbus;
4
const GLib = imports.gi.GLib;
5
const GObject = imports.gi.GObject;
6
const Lang = imports.lang;
7
const Mainloop = imports.mainloop;
8
const NetworkManager = imports.gi.NetworkManager;
9
const NMClient = imports.gi.NMClient;
10
const Shell = imports.gi.Shell;
11
const Signals = imports.signals;
12
const St = imports.gi.St;
14
const Main = imports.ui.main;
15
const PanelMenu = imports.ui.panelMenu;
16
const PopupMenu = imports.ui.popupMenu;
17
const MessageTray = imports.ui.messageTray;
18
const ModemManager = imports.misc.modemManager;
19
const Util = imports.misc.util;
21
const NMConnectionCategory = {
29
const NMAccessPointSecurity = {
39
// small optimization, to avoid using [] all the time
40
const NM80211Mode = NetworkManager['80211Mode'];
41
const NM80211ApFlags = NetworkManager['80211ApFlags'];
42
const NM80211ApSecurityFlags = NetworkManager['80211ApSecurityFlags'];
44
// number of wireless networks that should be visible
45
// (the remaining are placed into More...)
46
const NUM_VISIBLE_NETWORKS = 5;
48
function macToArray(string) {
49
return string.split(':').map(function(el) {
50
return parseInt(el, 16);
54
function macCompare(one, two) {
55
for (let i = 0; i < 6; i++) {
62
function ssidCompare(one, two) {
65
if (one.length != two.length)
67
for (let i = 0; i < one.length; i++) {
74
// shared between NMNetworkMenuItem and NMDeviceWWAN
75
function signalToIcon(value) {
87
// shared between NMNetworkMenuItem and NMDeviceWireless
88
function sortAccessPoints(accessPoints) {
89
return accessPoints.sort(function (one, two) {
90
return two.strength - one.strength;
94
function ssidToLabel(ssid) {
95
let label = NetworkManager.utils_ssid_to_utf8(ssid);
97
label = _("<unknown>");
101
function NMNetworkMenuItem() {
102
this._init.apply(this, arguments);
105
NMNetworkMenuItem.prototype = {
106
__proto__: PopupMenu.PopupBaseMenuItem.prototype,
108
_init: function(accessPoints, title, params) {
109
PopupMenu.PopupBaseMenuItem.prototype._init.call(this, params);
111
accessPoints = sortAccessPoints(accessPoints);
112
this.bestAP = accessPoints[0];
115
let ssid = this.bestAP.get_ssid();
116
title = ssidToLabel(ssid);
119
this._label = new St.Label({ text: title });
120
this.addActor(this._label);
121
this._icons = new St.BoxLayout({ style_class: 'nm-menu-item-icons' });
122
this.addActor(this._icons, { align: St.Align.END });
124
this._signalIcon = new St.Icon({ icon_name: this._getIcon(),
125
style_class: 'popup-menu-icon' });
126
this._icons.add_actor(this._signalIcon);
128
this._secureIcon = new St.Icon({ style_class: 'popup-menu-icon' });
129
if (this.bestAP._secType != NMAccessPointSecurity.UNKNOWN &&
130
this.bestAP._secType != NMAccessPointSecurity.NONE)
131
this._secureIcon.icon_name = 'network-wireless-encrypted';
132
this._icons.add_actor(this._secureIcon);
134
this._accessPoints = [ ];
135
for (let i = 0; i < accessPoints.length; i++) {
136
let ap = accessPoints[i];
137
// need a wrapper object here, because the access points can be shared
138
// between many NMNetworkMenuItems
141
updateId: ap.connect('notify::strength', Lang.bind(this, this._updated))
143
this._accessPoints.push(apObj);
147
_updated: function(ap) {
148
if (ap.strength > this.bestAP.strength)
151
this._signalIcon.icon_name = this._getIcon();
154
_getIcon: function() {
155
if (this.bestAP.mode == NM80211Mode.ADHOC)
156
return 'network-workgroup';
158
return 'network-wireless-signal-' + signalToIcon(this.bestAP.strength);
161
updateAccessPoints: function(accessPoints) {
162
for (let i = 0; i < this._accessPoints.length; i++) {
163
let apObj = this._accessPoints[i];
164
apObj.ap.disconnect(apObj.updateId);
168
accessPoints = sortAccessPoints(accessPoints);
169
this.bestAP = accessPoints[0];
170
this._accessPoints = [ ];
171
for (let i = 0; i < accessPoints; i++) {
172
let ap = accessPoints[i];
175
updateId: ap.connect('notify::strength', Lang.bind(this, this._updated))
177
this._accessPoints.push(apObj);
181
destroy: function() {
182
for (let i = 0; i < this._accessPoints.length; i++) {
183
let apObj = this._accessPoints[i];
184
apObj.ap.disconnect(apObj.updateId);
188
PopupMenu.PopupBaseMenuItem.prototype.destroy.call(this);
192
function NMWiredSectionTitleMenuItem() {
193
this._init.apply(this, arguments);
196
NMWiredSectionTitleMenuItem.prototype = {
197
__proto__: PopupMenu.PopupSwitchMenuItem.prototype,
199
_init: function(label, params) {
200
params = params || { };
201
params.style_class = 'popup-subtitle-menu-item';
202
PopupMenu.PopupSwitchMenuItem.prototype._init.call(this, label, false, params);
205
updateForDevice: function(device) {
207
this._device = device;
208
this.setStatus(device.getStatusLabel());
209
this.setToggleState(device.connected);
214
activate: function(event) {
215
PopupMenu.PopupSwitchMenuItem.prototype.activate.call(this, event);
218
log('Section title activated when there is more than one device, should be non reactive');
222
let newState = this._switch.state;
224
// Immediately reset the switch to false, it will be updated appropriately
225
// by state-changed signals in devices (but fixes the VPN not being in sync
226
// if the ActiveConnection object is never seen by libnm-glib)
227
this._switch.setToggleState(false);
230
this._device.activate();
232
this._device.deactivate();
236
function NMWirelessSectionTitleMenuItem() {
237
this._init.apply(this, arguments);
240
NMWirelessSectionTitleMenuItem.prototype = {
241
__proto__: PopupMenu.PopupSwitchMenuItem.prototype,
243
_init: function(client, property, title, params) {
244
params = params || { };
245
params.style_class = 'popup-subtitle-menu-item';
246
PopupMenu.PopupSwitchMenuItem.prototype._init.call(this, title, false, params);
248
this._client = client;
249
this._property = property + '_enabled';
250
this._propertyHardware = property + '_hardware_enabled';
251
this._setEnabledFunc = property + '_set_enabled';
253
this._client.connect('notify::' + property + '-enabled', Lang.bind(this, this._propertyChanged));
254
this._client.connect('notify::' + property + '-hardware-enabled', Lang.bind(this, this._propertyChanged));
256
this._propertyChanged();
259
updateForDevice: function(device) {
260
// we show the switch
261
// - if there not just one device
262
// - if the switch is off
263
// - if the device is activated or disconnected
264
if (device && this._softwareEnabled && this._hardwareEnabled) {
265
let text = device.getStatusLabel();
266
this.setStatus(text);
268
this.setStatus(null);
271
activate: function(event) {
272
PopupMenu.PopupSwitchMenuItem.prototype.activate.call(this, event);
274
this._client[this._setEnabledFunc](this._switch.state);
277
_propertyChanged: function() {
278
this._softwareEnabled = this._client[this._property];
279
this._hardwareEnabled = this._client[this._propertyHardware];
281
let enabled = this._softwareEnabled && this._hardwareEnabled;
282
this.setToggleState(enabled);
283
if (!this._hardwareEnabled)
284
/* Translators: this indicates that wireless or wwan is disabled by hardware killswitch */
285
this.setStatus(_("disabled"));
287
this.emit('enabled-changed', enabled);
291
function NMDevice() {
292
throw new TypeError('Instantanting abstract class NMDevice');
295
NMDevice.prototype = {
296
_init: function(client, device, connections) {
297
this.device = device;
299
this.device._delegate = this;
300
this._stateChangedId = this.device.connect('state-changed', Lang.bind(this, this._deviceStateChanged));
302
this._stateChangedId = 0;
305
this._client = client;
306
this._connections = [ ];
307
for (let i = 0; i < connections.length; i++) {
308
if (!connections[i]._uuid)
310
if (!this.connectionValid(connections[i]))
312
// record the connection
314
connection: connections[i],
315
name: connections[i]._name,
316
uuid: connections[i]._uuid,
317
timestamp: connections[i]._timestamp,
319
this._connections.push(obj);
321
this._connections.sort(this._connectionSortFunction);
322
this._activeConnection = null;
323
this._activeConnectionItem = null;
324
this._autoConnectionItem = null;
325
this._overflowItem = null;
328
this.statusItem = new PopupMenu.PopupSwitchMenuItem(this._getDescription(), this.connected, { style_class: 'popup-subtitle-menu-item' });
329
this._statusChanged = this.statusItem.connect('toggled', Lang.bind(this, function(item, state) {
334
this.emit('enabled-changed');
337
this._updateStatusItem();
339
this.section = new PopupMenu.PopupMenuSection();
341
this._createSection();
344
destroy: function() {
346
this.device._delegate = null;
348
if (this._stateChangedId) {
349
// Need to go through GObject.Object.prototype because
350
// nm_device_disconnect conflicts with g_signal_disconnect
351
GObject.Object.prototype.disconnect.call(this.device, this._stateChangedId);
352
this._stateChangedId = 0;
354
if (this._carrierChangedId) {
355
// see above for why this is needed
356
GObject.Object.prototype.disconnect.call(this.device, this._carrierChangedId);
357
this._carrierChangedId = 0;
359
if (this._firmwareChangedId) {
360
GObject.Object.prototype.disconnect.call(this.device, this._firmwareChangedId);
361
this._firmwareChangedId = 0;
364
this._clearSection();
366
this.statusItem.destroy();
367
this.section.destroy();
370
deactivate: function() {
371
this.device.disconnect(null);
374
activate: function() {
375
if (this._activeConnection)
379
// pick the most recently used connection and connect to that
380
// or if no connections ever set, create an automatic one
381
if (this._connections.length > 0) {
382
this._client.activate_connection(this._connections[0].connection, this.device, null, null);
383
} else if (this._autoConnectionName) {
384
let connection = this._createAutomaticConnection();
386
this._client.add_and_activate_connection(connection, this.device, null, null);
391
return this.device.state == NetworkManager.DeviceState.ACTIVATED;
394
setActiveConnection: function(activeConnection) {
395
if (activeConnection == this._activeConnection)
400
if (this._activeConnectionItem) {
401
this._activeConnectionItem.destroy();
402
this._activeConnectionItem = null;
405
this._activeConnection = activeConnection;
407
this._clearSection();
408
this._createSection();
411
checkConnection: function(connection) {
412
let exists = this._findConnection(connection._uuid) != -1;
413
let valid = this.connectionValid(connection);
414
if (exists && !valid)
415
this.removeConnection(connection);
416
else if (!exists && valid)
417
this.addConnection(connection);
420
addConnection: function(connection) {
421
// record the connection
423
connection: connection,
424
name: connection._name,
425
uuid: connection._uuid,
426
timestamp: connection._timestamp,
428
this._connections.push(obj);
429
this._connections.sort(this._connectionSortFunction);
431
this._clearSection();
432
this._createSection();
435
removeConnection: function(connection) {
436
if (!connection._uuid) {
437
log('Cannot remove a connection without an UUID');
440
let pos = this._findConnection(connection._uuid);
442
// this connection was never added, nothing to do here
446
let obj = this._connections[pos];
449
this._connections.splice(pos, 1);
451
if (this._connections.length <= 1) {
452
// We need to show the automatic connection again
453
// (or in the case of NMDeviceWired, we want to hide
454
// the only explicit connection)
455
this._clearSection();
456
this._createSection();
460
connectionValid: function(connection) {
461
return this.device.connection_valid(connection);
464
_connectionSortFunction: function(one, two) {
465
if (one.timestamp == two.timestamp)
466
return GLib.utf8_collate(one.name, two.name);
468
return two.timestamp - one.timestamp;
471
setEnabled: function(enabled) {
472
// do nothing by default, we want to keep the conneciton list visible
473
// in the majority of cases (wired, wwan, vpn)
476
getStatusLabel: function() {
477
switch(this.device.state) {
478
case NetworkManager.DeviceState.DISCONNECTED:
479
case NetworkManager.DeviceState.ACTIVATED:
481
case NetworkManager.DeviceState.UNMANAGED:
482
/* Translators: this is for network devices that are physically present but are not
483
under NetworkManager's control (and thus cannot be used in the menu) */
484
return _("unmanaged");
485
case NetworkManager.DeviceState.DEACTIVATING:
486
return _("disconnecting...");
487
case NetworkManager.DeviceState.PREPARE:
488
case NetworkManager.DeviceState.CONFIG:
489
case NetworkManager.DeviceState.IP_CONFIG:
490
case NetworkManager.DeviceState.IP_CHECK:
491
case NetworkManager.DeviceState.SECONDARIES:
492
return _("connecting...");
493
case NetworkManager.DeviceState.NEED_AUTH:
494
/* Translators: this is for network connections that require some kind of key or password */
495
return _("authentication required");
496
case NetworkManager.DeviceState.UNAVAILABLE:
497
// This state is actually a compound of various states (generically unavailable,
498
// firmware missing, carrier not available), that are exposed by different properties
499
// (whose state may or may not updated when we receive state-changed).
500
if (!this._firmwareMissingId)
501
this._firmwareMissingId = this.device.connect('notify::firmware-missing', Lang.bind(this, this._substateChanged));
502
if (this.device.firmware_missing) {
503
/* Translators: this is for devices that require some kind of firmware or kernel
504
module, which is missing */
505
return _("firmware missing");
507
if (this.device.capabilities & NetworkManager.DeviceCapabilities.CARRIER_DETECT) {
508
if (!this._carrierChangedId)
509
this._carrierChangedId = this.device.connect('notify::carrier', Lang.bind(this, this._substateChanged));
511
/* Translators: this is for wired network devices that are physically disconnected */
512
return _("cable unplugged");
515
/* Translators: this is for a network device that cannot be activated (for example it
516
is disabled by rfkill, or it has no coverage */
517
return _("unavailable");
518
case NetworkManager.DeviceState.FAILED:
519
return _("connection failed");
521
log('Device state invalid, is %d'.format(this.device.state));
527
_createAutomaticConnection: function() {
528
throw new TypeError('Invoking pure virtual function NMDevice.createAutomaticConnection');
531
_findConnection: function(uuid) {
532
for (let i = 0; i < this._connections.length; i++) {
533
let obj = this._connections[i];
534
if (obj.uuid == uuid)
540
_clearSection: function() {
542
this.section.removeAll();
543
this._autoConnectionItem = null;
544
this._activeConnectionItem = null;
545
this._overflowItem = null;
546
for (let i = 0; i < this._connections.length; i++) {
547
this._connections[i].item = null;
551
_shouldShowConnectionList: function() {
552
return (this.device.state >= NetworkManager.DeviceState.DISCONNECTED);
555
_createSection: function() {
556
if (!this._shouldShowConnectionList())
559
if (this._activeConnection) {
560
this._createActiveConnectionItem();
561
this.section.addMenuItem(this._activeConnectionItem);
563
if (this._connections.length > 0) {
564
let activeOffset = this._activeConnectionItem ? 1 : 0;
566
for(let j = 0; j < this._connections.length; ++j) {
567
let obj = this._connections[j];
568
if (this._activeConnection &&
569
obj.connection == this._activeConnection._connection)
571
obj.item = this._createConnectionItem(obj);
573
if (j + activeOffset >= NUM_VISIBLE_NETWORKS) {
574
if (!this._overflowItem) {
575
this._overflowItem = new PopupMenu.PopupSubMenuMenuItem(_("More..."));
576
this.section.addMenuItem(this._overflowItem);
578
this._overflowItem.menu.addMenuItem(obj.item);
580
this.section.addMenuItem(obj.item);
582
} else if (this._autoConnectionName) {
583
this._autoConnectionItem = new PopupMenu.PopupMenuItem(this._autoConnectionName);
584
this._autoConnectionItem.connect('activate', Lang.bind(this, function() {
585
let connection = this._createAutomaticConnection();
587
this._client.add_and_activate_connection(connection, this.device, null, null);
589
this.section.addMenuItem(this._autoConnectionItem);
593
_createConnectionItem: function(obj) {
594
let connection = obj.connection;
595
let item = new PopupMenu.PopupMenuItem(obj.name);
597
item.connect('activate', Lang.bind(this, function() {
598
this._client.activate_connection(connection, this.device, null, null);
603
_createActiveConnectionItem: function() {
605
let active = this._activeConnection._connection;
607
title = active._name;
609
/* TRANSLATORS: this is the indication that a connection for another logged in user is active,
610
and we cannot access its settings (including the name) */
611
title = _("Connected (private)");
613
this._activeConnectionItem = new PopupMenu.PopupMenuItem(title, { reactive: false });
614
this._activeConnectionItem.setShowDot(true);
617
_deviceStateChanged: function(device, newstate, oldstate, reason) {
618
if (newstate == oldstate) {
619
log('device emitted state-changed without actually changing state');
623
if (oldstate == NetworkManager.DeviceState.ACTIVATED) {
624
this.emit('network-lost');
627
if (newstate == NetworkManager.DeviceState.FAILED) {
628
this.emit('activation-failed', reason);
631
this._updateStatusItem();
633
this._clearSection();
634
this._createSection();
635
this.emit('state-changed');
638
_updateStatusItem: function() {
639
if (this._carrierChangedId) {
640
// see above for why this is needed
641
GObject.Object.prototype.disconnect.call(this.device, this._carrierChangedId);
642
this._carrierChangedId = 0;
644
if (this._firmwareChangedId) {
645
GObject.Object.prototype.disconnect.call(this.device, this._firmwareChangedId);
646
this._firmwareChangedId = 0;
649
this.statusItem.setStatus(this.getStatusLabel());
650
this.statusItem.setToggleState(this.connected);
653
_substateChanged: function() {
654
this.statusItem.setStatus(this.getStatusLabel());
656
this.emit('state-changed');
659
_getDescription: function() {
660
let dev_product = this.device.get_product();
661
let dev_vendor = this.device.get_vendor();
662
if (!dev_product || !dev_vendor)
665
let product = Util.fixupPCIDescription(dev_product);
666
let vendor = Util.fixupPCIDescription(dev_vendor);
669
// Another quick hack; if all of the fixed up vendor string
670
// is found in product, ignore the vendor.
671
if (product.indexOf(vendor) == -1)
678
Signals.addSignalMethods(NMDevice.prototype);
681
function NMDeviceWired() {
682
this._init.apply(this, arguments);
685
NMDeviceWired.prototype = {
686
__proto__: NMDevice.prototype,
688
_init: function(client, device, connections) {
689
this._autoConnectionName = _("Auto Ethernet");
690
this.category = NMConnectionCategory.WIRED;
692
NMDevice.prototype._init.call(this, client, device, connections);
695
_createSection: function() {
696
NMDevice.prototype._createSection.call(this);
698
// if we have only one connection (normal or automatic)
699
// we hide the connection list, and use the switch to control
701
// we can do it here because addConnection and removeConnection
702
// both call _createSection at some point
703
if (this._connections.length <= 1)
704
this.section.actor.hide();
706
this.section.actor.show();
709
_createAutomaticConnection: function() {
710
let connection = new NetworkManager.Connection();
711
connection._uuid = NetworkManager.utils_uuid_generate();
712
connection.add_setting(new NetworkManager.SettingWired());
713
connection.add_setting(new NetworkManager.SettingConnection({
714
uuid: connection._uuid,
715
id: this._autoConnectionName,
716
type: NetworkManager.SETTING_WIRED_SETTING_NAME,
723
function NMDeviceModem() {
724
this._init.apply(this, arguments);
727
NMDeviceModem.prototype = {
728
__proto__: NMDevice.prototype,
730
_init: function(client, device, connections) {
733
this._enabled = true;
734
this.mobileDevice = null;
735
this._connectionType = 'ppp';
737
this._capabilities = device.current_capabilities;
738
if (this._capabilities & NetworkManager.DeviceModemCapabilities.GSM_UMTS) {
740
this.mobileDevice = new ModemManager.ModemGsm(device.udi);
741
this._connectionType = NetworkManager.SETTING_GSM_SETTING_NAME;
742
} else if (this._capabilities & NetworkManager.DeviceModemCapabilities.CDMA_EVDO) {
744
this.mobileDevice = new ModemManager.ModemCdma(device.udi);
745
this._connectionType = NetworkManager.SETTING_CDMA_SETTING_NAME;
746
} else if (this._capabilities & NetworkManager.DeviceModemCapabilities.LTE) {
748
// FIXME: support signal quality
752
this.category = NMConnectionCategory.WWAN;
753
this._autoConnectionName = _("Auto broadband");
755
this.category = NMConnectionCategory.WIRED;
756
this._autoConnectionName = _("Auto dial-up");
759
if (this.mobileDevice) {
760
this._operatorNameId = this.mobileDevice.connect('notify::operator-name', Lang.bind(this, function() {
761
if (this._operatorItem) {
762
let name = this.mobileDevice.operator_name;
764
this._operatorItem.label.text = name;
765
this._operatorItem.actor.show();
767
this._operatorItem.actor.hide();
770
this._signalQualityId = this.mobileDevice.connect('notify::signal-quality', Lang.bind(this, function() {
771
if (this._operatorItem) {
772
this._operatorItem.setIcon(this._getSignalIcon());
777
NMDevice.prototype._init.call(this, client, device, connections);
780
setEnabled: function(enabled) {
781
this._enabled = enabled;
782
if (this.category == NMConnectionCategory.WWAN) {
784
// prevent "network unavailable" statuses
785
this.statusItem.setStatus(null);
787
this.statusItem.setStatus(this.getStatusLabel());
790
NMDevice.prototype.setEnabled.call(this, enabled);
794
return this._enabled && this.device.state == NetworkManager.DeviceState.ACTIVATED;
797
destroy: function() {
798
if (this._operatorNameId) {
799
this.mobileDevice.disconnect(this._operatorNameId);
800
this._operatorNameId = 0;
802
if (this._signalQualityId) {
803
this.mobileDevice.disconnect(this._signalQualityId);
804
this._signalQualityId = 0;
807
NMDevice.prototype.destroy.call(this);
810
_getSignalIcon: function() {
811
return 'network-cellular-signal-' + signalToIcon(this.mobileDevice.signal_quality);
814
_createSection: function() {
815
if (!this._shouldShowConnectionList())
818
if (this.mobileDevice) {
819
// If operator_name is null, just pass the empty string, as the item is hidden anyway
820
this._operatorItem = new PopupMenu.PopupImageMenuItem(this.mobileDevice.operator_name || '',
821
this._getSignalIcon(),
822
{ reactive: false });
823
if (!this.mobileDevice.operator_name)
824
this._operatorItem.actor.hide();
825
this.section.addMenuItem(this._operatorItem);
828
NMDevice.prototype._createSection.call(this);
831
_clearSection: function() {
832
this._operatorItem = null;
834
NMDevice.prototype._clearSection.call(this);
837
_createAutomaticConnection: function() {
838
// Mobile wizard is too complex for the shell UI and
839
// is handled by the network panel
840
Util.spawn(['gnome-control-center', 'network',
841
'connect-3g', this.device.get_path()]);
846
function NMDeviceBluetooth() {
847
this._init.apply(this, arguments);
850
NMDeviceBluetooth.prototype = {
851
__proto__: NMDevice.prototype,
853
_init: function(client, device, connections) {
854
this._autoConnectionName = this._makeConnectionName(device);
855
device.connect('notify::name', Lang.bind(this, this._updateAutoConnectionName));
857
this.category = NMConnectionCategory.WWAN;
859
NMDevice.prototype._init.call(this, client, device, connections);
862
_createAutomaticConnection: function() {
863
let connection = new NetworkManager.Connection;
864
connection._uuid = NetworkManager.utils_uuid_generate();
865
connection.add_setting(new NetworkManager.SettingBluetooth);
866
connection.add_setting(new NetworkManager.SettingConnection({
867
uuid: connection._uuid,
868
id: this._autoConnectionName,
869
type: NetworkManager.SETTING_BLUETOOTH_SETTING_NAME,
875
_makeConnectionName: function(device) {
876
let name = device.name;
878
return _("Auto %s").format(name);
880
return _("Auto bluetooth");
883
_updateAutoConnectionName: function() {
884
this._autoConnectionName = this._makeConnectionName(this.device);
886
this._clearSection();
887
this._createSection();
892
// Not a real device, but I save a lot code this way
893
function NMDeviceVPN() {
894
this._init.apply(this, arguments);
897
NMDeviceVPN.prototype = {
898
__proto__: NMDevice.prototype,
900
_init: function(client) {
901
// Disable autoconnections
902
this._autoConnectionName = null;
903
this.category = NMConnectionCategory.VPN;
905
NMDevice.prototype._init.call(this, client, null, [ ]);
908
connectionValid: function(connection) {
909
return connection._type == NetworkManager.SETTING_VPN_SETTING_NAME;
913
return this._connections.length == 0;
917
return !!this._activeConnection;
920
setActiveConnection: function(activeConnection) {
921
NMDevice.prototype.setActiveConnection.call(this, activeConnection);
923
this.emit('active-connection-changed');
926
_shouldShowConnectionList: function() {
930
deactivate: function() {
931
if (this._activeConnection)
932
this._client.deactivate_connection(this._activeConnection);
935
getStatusLabel: function() {
940
function NMDeviceWireless() {
941
this._init.apply(this, arguments);
944
NMDeviceWireless.prototype = {
945
__proto__: NMDevice.prototype,
947
_init: function(client, device, connections) {
948
this.category = NMConnectionCategory.WIRELESS;
950
this._overflowItem = null;
951
this._networks = [ ];
953
// breaking the layers with this, but cannot call
954
// this.connectionValid until I have a device
955
this.device = device;
957
let validConnections = connections.filter(Lang.bind(this, function(connection) {
958
return this.connectionValid(connection);
960
let accessPoints = device.get_access_points() || [ ];
961
for (let i = 0; i < accessPoints.length; i++) {
962
// Access points are grouped by network
963
let ap = accessPoints[i];
965
if (ap.get_ssid() == null) {
966
// hidden access point cannot be added, we need to know
967
// the SSID and security details to connect
968
// nevertheless, the access point can acquire a SSID when
969
// NetworkManager connects to it (via nmcli or the control-center)
970
ap._notifySsidId = ap.connect('notify::ssid', Lang.bind(this, this._notifySsidCb));
974
let pos = this._findNetwork(ap);
977
obj = this._networks[pos];
978
obj.accessPoints.push(ap);
980
obj = { ssid: ap.get_ssid(),
982
security: this._getApSecurityType(ap),
987
obj.ssidText = ssidToLabel(obj.ssid);
988
this._networks.push(obj);
991
// Check if some connection is valid for this AP
992
for (let j = 0; j < validConnections.length; j++) {
993
let connection = validConnections[j];
994
if (ap.connection_valid(connection) &&
995
obj.connections.indexOf(connection) == -1) {
996
obj.connections.push(connection);
1001
if (this.device.active_access_point) {
1002
let networkPos = this._findNetwork(this.device.active_access_point);
1004
if (networkPos == -1) // the connected access point is invisible
1005
this._activeNetwork = null;
1007
this._activeNetwork = this._networks[networkPos];
1009
this._activeNetwork = null;
1011
this._networks.sort(this._networkSortFunction);
1013
this._apChangedId = device.connect('notify::active-access-point', Lang.bind(this, this._activeApChanged));
1014
this._apAddedId = device.connect('access-point-added', Lang.bind(this, this._accessPointAdded));
1015
this._apRemovedId = device.connect('access-point-removed', Lang.bind(this, this._accessPointRemoved));
1017
NMDevice.prototype._init.call(this, client, device, validConnections);
1020
destroy: function() {
1021
if (this._apChangedId) {
1022
// see above for this HACK
1023
GObject.Object.prototype.disconnect.call(this.device, this._apChangedId);
1024
this._apChangedId = 0;
1027
if (this._apAddedId) {
1028
GObject.Object.prototype.disconnect.call(this.device, this._apAddedId);
1029
this._apAddedId = 0;
1032
if (this._apRemovedId) {
1033
GObject.Object.prototype.disconnect.call(this.device, this._apRemovedId);
1034
this._apRemovedId = 0;
1037
NMDevice.prototype.destroy.call(this);
1040
setEnabled: function(enabled) {
1042
this.statusItem.actor.show();
1043
this.section.actor.show();
1045
this.statusItem.actor.hide();
1046
this.section.actor.hide();
1050
activate: function() {
1051
if (this._activeConnection)
1055
// among all visible networks, pick the last recently used connection
1057
let bestApObj = null;
1059
for (let i = 0; i < this._networks.length; i++) {
1060
let apObj = this._networks[i];
1061
for (let j = 0; j < apObj.connections.length; j++) {
1062
let connection = apObj.connections[j];
1063
if (connection._timestamp > bestTime) {
1065
bestTime = connection._timestamp;
1072
for (let i = 0; i < bestApObj.accessPoints.length; i++) {
1073
let ap = bestApObj.accessPoints[i];
1074
if (ap.connection_valid(best)) {
1075
this._client.activate_connection(best, this.device, ap.dbus_path, null);
1082
// XXX: what else to do?
1083
// for now, just pick a random network
1084
// (this function is called in a corner case anyway, that is, only when
1085
// the user toggles the switch and has more than one wireless device)
1086
if (this._networks.length > 0) {
1087
let connection = this._createAutomaticConnection(this._networks[0]);
1088
let accessPoints = sortAccessPoints(this._networks[0].accessPoints);
1089
this._client.add_and_activate_connection(connection, this.device, accessPoints[0].dbus_path, null);
1093
_notifySsidCb: function(accessPoint) {
1094
if (accessPoint.get_ssid() != null) {
1095
accessPoint.disconnect(accessPoint._notifySsidId);
1096
accessPoint._notifySsidId = 0;
1097
this._accessPointAdded(this.device, accessPoint);
1101
_activeApChanged: function() {
1102
this._activeNetwork = null;
1104
let activeAp = this.device.active_access_point;
1107
let res = this._findExistingNetwork(activeAp);
1110
this._activeNetwork = this._networks[res.network];
1113
// we don't refresh the view here, setActiveConnection will
1116
_getApSecurityType: function(accessPoint) {
1117
if (accessPoint._secType)
1118
return accessPoint._secType;
1120
let flags = accessPoint.flags;
1121
let wpa_flags = accessPoint.wpa_flags;
1122
let rsn_flags = accessPoint.rsn_flags;
1124
if (rsn_flags != NM80211ApSecurityFlags.NONE) {
1125
/* RSN check first so that WPA+WPA2 APs are treated as RSN/WPA2 */
1126
if (rsn_flags & NM80211ApSecurityFlags.KEY_MGMT_802_1X)
1127
type = NMAccessPointSecurity.WPA2_ENT;
1128
else if (rsn_flags & NM80211ApSecurityFlags.KEY_MGMT_PSK)
1129
type = NMAccessPointSecurity.WPA2_PSK;
1130
} else if (wpa_flags != NM80211ApSecurityFlags.NONE) {
1131
if (wpa_flags & NM80211ApSecurityFlags.KEY_MGMT_802_1X)
1132
type = NMAccessPointSecurity.WPA_ENT;
1133
else if (wpa_flags & NM80211ApSecurityFlags.KEY_MGMT_PSK)
1134
type = NMAccessPointSecurity.WPA_PSK;
1136
if (flags & NM80211ApFlags.PRIVACY)
1137
type = NMAccessPointSecurity.WEP;
1139
type = NMAccessPointSecurity.NONE;
1142
// cache the found value to avoid checking flags all the time
1143
accessPoint._secType = type;
1147
_networkSortFunction: function(one, two) {
1148
let oneHasConnection = one.connections.length != 0;
1149
let twoHasConnection = two.connections.length != 0;
1151
// place known connections first
1152
// (-1 = good order, 1 = wrong order)
1153
if (oneHasConnection && !twoHasConnection)
1155
else if (!oneHasConnection && twoHasConnection)
1158
let oneHasSecurity = one.security != NMAccessPointSecurity.NONE;
1159
let twoHasSecurity = two.security != NMAccessPointSecurity.NONE;
1161
// place secure connections first
1162
// (we treat WEP/WPA/WPA2 the same as there is no way to
1163
// take them apart from the UI)
1164
if (oneHasSecurity && !twoHasSecurity)
1166
else if (!oneHasSecurity && twoHasSecurity)
1169
// sort alphabetically
1170
return GLib.utf8_collate(one.ssidText, two.ssidText);
1173
_networkCompare: function(network, accessPoint) {
1174
if (!ssidCompare(network.ssid, accessPoint.get_ssid()))
1176
if (network.mode != accessPoint.mode)
1178
if (network.security != this._getApSecurityType(accessPoint))
1184
_findExistingNetwork: function(accessPoint) {
1185
for (let i = 0; i < this._networks.length; i++) {
1186
let apObj = this._networks[i];
1187
for (let j = 0; j < apObj.accessPoints.length; j++) {
1188
if (apObj.accessPoints[j] == accessPoint)
1189
return { network: i, ap: j };
1196
_findNetwork: function(accessPoint) {
1197
if (accessPoint.get_ssid() == null)
1200
for (let i = 0; i < this._networks.length; i++) {
1201
if (this._networkCompare(this._networks[i], accessPoint))
1207
_accessPointAdded: function(device, accessPoint) {
1208
if (accessPoint.get_ssid() == null) {
1209
// This access point is not visible yet
1210
// Wait for it to get a ssid
1211
accessPoint._notifySsidId = accessPoint.connect('notify::ssid', Lang.bind(this, this._notifySsidCb));
1215
let pos = this._findNetwork(accessPoint);
1217
let needsupdate = false;
1220
apObj = this._networks[pos];
1221
if (apObj.accessPoints.indexOf(accessPoint) != -1) {
1222
log('Access point was already seen, not adding again');
1226
apObj.accessPoints.push(accessPoint);
1228
apObj.item.updateAccessPoints(apObj.accessPoints);
1230
apObj = { ssid: accessPoint.get_ssid(),
1231
mode: accessPoint.mode,
1232
security: this._getApSecurityType(accessPoint),
1235
accessPoints: [ accessPoint ]
1237
apObj.ssidText = ssidToLabel(apObj.ssid);
1241
// check if this enables new connections for this group
1242
for (let i = 0; i < this._connections.length; i++) {
1243
let connection = this._connections[i].connection;
1244
if (accessPoint.connection_valid(connection) &&
1245
apObj.connections.indexOf(connection) == -1) {
1246
apObj.connections.push(connection);
1248
// this potentially changes the order
1255
apObj.item.destroy();
1258
this._networks.splice(pos, 1);
1260
if (this._networks.length == 0) {
1261
// only network in the list
1262
this._networks.push(apObj);
1263
this._clearSection();
1264
this._createSection();
1268
// skip networks that should appear earlier
1271
pos < this._networks.length &&
1272
this._networkSortFunction(this._networks[pos], apObj) < 0; ++pos) {
1273
if (this._networks[pos] != this._activeNetwork)
1277
// (re-)add the network
1278
this._networks.splice(pos, 0, apObj);
1280
if (this._shouldShowConnectionList()) {
1281
menuPos += (this._activeConnectionItem ? 1 : 0);
1282
this._createNetworkItem(apObj, menuPos);
1287
_accessPointRemoved: function(device, accessPoint) {
1288
let res = this._findExistingNetwork(accessPoint);
1291
log('Removing an access point that was never added');
1295
let apObj = this._networks[res.network];
1296
apObj.accessPoints.splice(res.ap, 1);
1298
if (apObj.accessPoints.length == 0) {
1299
if (this._activeNetwork == apObj)
1300
this._activeNetwork = null;
1303
apObj.item.destroy();
1305
if (this._overflowItem) {
1306
if (!apObj.isMore) {
1307
// we removed an item in the main menu, and we have a more submenu
1308
// we need to extract the first item in more and move it to the submenu
1310
let apObj = this._overflowItem.menu.firstMenuItem;
1312
apObj.item.destroy();
1314
this._createNetworkItem(apObj, NUM_VISIBLE_NETWORKS-1);
1318
// This can happen if the removed connection is from the overflow
1319
// menu, or if we just moved the last connection out from the menu
1320
if (this._overflowItem.menu.length == 0) {
1321
this._overflowItem.destroy();
1322
this._overflowItem = null;
1325
this._networks.splice(res.network, 1);
1327
} else if (apObj.item)
1328
apObj.item.updateAccessPoints(apObj.accessPoints);
1331
_createAPItem: function(connection, accessPointObj, useConnectionName) {
1332
let item = new NMNetworkMenuItem(accessPointObj.accessPoints, useConnectionName ? connection._name : undefined);
1333
item._connection = connection;
1334
item.connect('activate', Lang.bind(this, function() {
1335
let accessPoints = sortAccessPoints(accessPointObj.accessPoints);
1336
for (let i = 0; i < accessPoints.length; i++) {
1337
if (accessPoints[i].connection_valid(connection)) {
1338
this._client.activate_connection(connection, this.device, accessPoints[i].dbus_path, null);
1346
_clearSection: function() {
1347
NMDevice.prototype._clearSection.call(this);
1349
for (let i = 0; i < this._networks.length; i++)
1350
this._networks[i].item = null;
1351
this._overflowItem = null;
1354
removeConnection: function(connection) {
1355
if (!connection._uuid)
1357
let pos = this._findConnection(connection._uuid);
1359
// removing connection that was never added
1363
let obj = this._connections[pos];
1364
this._connections.splice(pos, 1);
1366
let anyauto = false, forceupdate = false;
1367
for (let i = 0; i < this._networks.length; i++) {
1368
let apObj = this._networks[i];
1369
let connections = apObj.connections;
1370
for (let k = 0; k < connections.length; k++) {
1371
if (connections[k]._uuid == connection._uuid) {
1372
// remove the connection from the access point group
1373
connections.splice(k);
1374
anyauto = connections.length == 0;
1377
// this potentially changes the sorting order
1382
if (apObj.item instanceof PopupMenu.PopupSubMenuMenuItem) {
1383
let items = apObj.item.menu.getMenuItems();
1384
if (items.length == 2) {
1385
// we need to update the connection list to convert this to a normal item
1388
for (let j = 0; j < items.length; j++) {
1389
if (items[j]._connection._uuid == connection._uuid) {
1396
apObj.item.destroy();
1405
if (forceupdate || anyauto) {
1406
this._networks.sort(this._networkSortFunction);
1407
this._clearSection();
1408
this._createSection();
1412
addConnection: function(connection) {
1413
// record the connection
1415
connection: connection,
1416
name: connection._name,
1417
uuid: connection._uuid,
1419
this._connections.push(obj);
1421
// find an appropriate access point
1422
let forceupdate = false;
1423
for (let i = 0; i < this._networks.length; i++) {
1424
let apObj = this._networks[i];
1426
// Check if connection is valid for any of these access points
1427
for (let k = 0; k < apObj.accessPoints.length; k++) {
1428
let ap = apObj.accessPoints[k];
1429
if (ap.connection_valid(connection)) {
1430
apObj.connections.push(connection);
1431
// this potentially changes the sorting order
1439
this._networks.sort(this._networkSortFunction);
1440
this._clearSection();
1441
this._createSection();
1445
_createActiveConnectionItem: function() {
1447
if (this._activeConnection._connection) {
1448
let connection = this._activeConnection._connection;
1449
if (this._activeNetwork)
1450
this._activeConnectionItem = new NMNetworkMenuItem(this._activeNetwork.accessPoints, undefined,
1451
{ reactive: false });
1453
this._activeConnectionItem = new PopupMenu.PopupImageMenuItem(connection._name,
1454
'network-wireless-connected',
1455
{ reactive: false });
1457
// We cannot read the connection (due to ACL, or API incompatibility), but we still show signal if we have it
1459
if (this._activeNetwork)
1460
this._activeConnectionItem = new NMNetworkMenuItem(this._activeNetwork.accessPoints, undefined,
1461
{ reactive: false });
1463
this._activeConnectionItem = new PopupMenu.PopupImageMenuItem(_("Connected (private)"),
1464
'network-wireless-connected',
1465
{ reactive: false });
1467
this._activeConnectionItem.setShowDot(true);
1470
_createAutomaticConnection: function(apObj) {
1472
let ssid = NetworkManager.utils_ssid_to_utf8(apObj.ssid);
1474
/* TRANSLATORS: this the automatic wireless connection name (including the network name) */
1475
name = _("Auto %s").format(ssid);
1477
name = _("Auto wireless");
1479
let connection = new NetworkManager.Connection();
1480
connection.add_setting(new NetworkManager.SettingWireless());
1481
connection.add_setting(new NetworkManager.SettingConnection({
1483
autoconnect: true, // NetworkManager will know to ignore this if appropriate
1484
uuid: NetworkManager.utils_uuid_generate(),
1485
type: NetworkManager.SETTING_WIRELESS_SETTING_NAME
1490
_createNetworkItem: function(apObj, position) {
1491
if(!apObj.accessPoints || apObj.accessPoints.length == 0) {
1492
// this should not happen, but I have no idea why it happens
1496
if(apObj.connections.length > 0) {
1497
if (apObj.connections.length == 1)
1498
apObj.item = this._createAPItem(apObj.connections[0], apObj, false);
1500
let title = apObj.ssidText;
1501
apObj.item = new PopupMenu.PopupSubMenuMenuItem(title);
1502
apObj.item._apObj = apObj;
1503
for (let i = 0; i < apObj.connections.length; i++)
1504
apObj.item.menu.addMenuItem(this._createAPItem(apObj.connections[i], apObj, true));
1507
apObj.item = new NMNetworkMenuItem(apObj.accessPoints);
1508
apObj.item._apObj = apObj;
1509
apObj.item.connect('activate', Lang.bind(this, function() {
1510
let accessPoints = sortAccessPoints(apObj.accessPoints);
1511
if ( (accessPoints[0]._secType == NMAccessPointSecurity.WPA2_ENT)
1512
|| (accessPoints[0]._secType == NMAccessPointSecurity.WPA_ENT)) {
1513
// 802.1x-enabled APs require further configuration, so they're
1514
// handled in gnome-control-center
1515
Util.spawn(['gnome-control-center', 'network', 'connect-8021x-wifi',
1516
this.device.get_path(), accessPoints[0].dbus_path]);
1518
let connection = this._createAutomaticConnection(apObj);
1519
this._client.add_and_activate_connection(connection, this.device, accessPoints[0].dbus_path, null)
1523
if (position < NUM_VISIBLE_NETWORKS) {
1524
apObj.isMore = false;
1525
this.section.addMenuItem(apObj.item, position);
1527
if (!this._overflowItem) {
1528
this._overflowItem = new PopupMenu.PopupSubMenuMenuItem(_("More..."));
1529
this.section.addMenuItem(this._overflowItem);
1531
this._overflowItem.menu.addMenuItem(apObj.item, position - NUM_VISIBLE_NETWORKS);
1532
apObj.isMore = true;
1536
_createSection: function() {
1537
if (!this._shouldShowConnectionList())
1540
if(this._activeConnection) {
1541
this._createActiveConnectionItem();
1542
this.section.addMenuItem(this._activeConnectionItem);
1545
let activeOffset = this._activeConnectionItem ? 1 : 0;
1547
for(let j = 0; j < this._networks.length; j++) {
1548
let apObj = this._networks[j];
1549
if (apObj == this._activeNetwork)
1552
this._createNetworkItem(apObj, j + activeOffset);
1557
function NMApplet() {
1558
this._init.apply(this, arguments);
1560
NMApplet.prototype = {
1561
__proto__: PanelMenu.SystemStatusButton.prototype,
1564
PanelMenu.SystemStatusButton.prototype._init.call(this, 'network-error');
1566
this._client = NMClient.Client.new();
1568
this._statusSection = new PopupMenu.PopupMenuSection();
1569
this._statusItem = new PopupMenu.PopupMenuItem('', { style_class: 'popup-inactive-menu-item', reactive: false });
1570
this._statusSection.addMenuItem(this._statusItem);
1571
this._statusSection.addAction(_("Enable networking"), Lang.bind(this, function() {
1572
this._client.networking_enabled = true;
1574
this._statusSection.actor.hide();
1575
this.menu.addMenuItem(this._statusSection);
1576
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
1578
this._devices = { };
1580
this._devices.wired = {
1581
section: new PopupMenu.PopupMenuSection(),
1583
item: new NMWiredSectionTitleMenuItem(_("Wired"))
1586
this._devices.wired.section.addMenuItem(this._devices.wired.item);
1587
this._devices.wired.section.actor.hide();
1588
this.menu.addMenuItem(this._devices.wired.section);
1589
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
1591
this._devices.wireless = {
1592
section: new PopupMenu.PopupMenuSection(),
1594
item: this._makeToggleItem('wireless', _("Wireless"))
1596
this._devices.wireless.section.addMenuItem(this._devices.wireless.item);
1597
this._devices.wireless.section.actor.hide();
1598
this.menu.addMenuItem(this._devices.wireless.section);
1599
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
1601
this._devices.wwan = {
1602
section: new PopupMenu.PopupMenuSection(),
1604
item: this._makeToggleItem('wwan', _("Mobile broadband"))
1606
this._devices.wwan.section.addMenuItem(this._devices.wwan.item);
1607
this._devices.wwan.section.actor.hide();
1608
this.menu.addMenuItem(this._devices.wwan.section);
1609
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
1611
this._devices.vpn = {
1612
section: new PopupMenu.PopupMenuSection(),
1613
device: new NMDeviceVPN(this._client),
1614
item: new NMWiredSectionTitleMenuItem(_("VPN Connections"))
1616
this._devices.vpn.device.connect('active-connection-changed', Lang.bind(this, function() {
1617
this._devices.vpn.item.updateForDevice(this._devices.vpn.device);
1619
this._devices.vpn.item.updateForDevice(this._devices.vpn.device);
1620
this._devices.vpn.section.addMenuItem(this._devices.vpn.item);
1621
this._devices.vpn.section.addMenuItem(this._devices.vpn.device.section);
1622
this._devices.vpn.section.actor.hide();
1623
this.menu.addMenuItem(this._devices.vpn.section);
1624
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
1625
this.menu.addSettingsAction(_("Network Settings"), 'gnome-network-panel.desktop');
1627
this._activeConnections = [ ];
1628
this._connections = [ ];
1630
this._mainConnection = null;
1631
this._activeAccessPointUpdateId = 0;
1632
this._activeAccessPoint = null;
1633
this._mobileUpdateId = 0;
1634
this._mobileUpdateDevice = null;
1638
this._dtypes[NetworkManager.DeviceType.ETHERNET] = NMDeviceWired;
1639
this._dtypes[NetworkManager.DeviceType.WIFI] = NMDeviceWireless;
1640
this._dtypes[NetworkManager.DeviceType.MODEM] = NMDeviceModem;
1641
this._dtypes[NetworkManager.DeviceType.BT] = NMDeviceBluetooth;
1642
// TODO: WiMax support
1646
this._ctypes[NetworkManager.SETTING_WIRELESS_SETTING_NAME] = NMConnectionCategory.WIRELESS;
1647
this._ctypes[NetworkManager.SETTING_WIRED_SETTING_NAME] = NMConnectionCategory.WIRED;
1648
this._ctypes[NetworkManager.SETTING_PPPOE_SETTING_NAME] = NMConnectionCategory.WIRED;
1649
this._ctypes[NetworkManager.SETTING_PPP_SETTING_NAME] = NMConnectionCategory.WIRED;
1650
this._ctypes[NetworkManager.SETTING_BLUETOOTH_SETTING_NAME] = NMConnectionCategory.WWAN;
1651
this._ctypes[NetworkManager.SETTING_CDMA_SETTING_NAME] = NMConnectionCategory.WWAN;
1652
this._ctypes[NetworkManager.SETTING_GSM_SETTING_NAME] = NMConnectionCategory.WWAN;
1653
this._ctypes[NetworkManager.SETTING_VPN_SETTING_NAME] = NMConnectionCategory.VPN;
1655
this._settings = NMClient.RemoteSettings.new(null);
1656
this._connectionsReadId = this._settings.connect('connections-read', Lang.bind(this, function() {
1657
this._readConnections();
1658
this._readDevices();
1659
this._syncNMState();
1661
// Connect to signals late so that early signals don't find in inconsistent state
1662
// and connect only once (this signal handler can be called again if NetworkManager goes up and down)
1663
if (!this._inited) {
1664
this._inited = true;
1665
this._client.connect('notify::manager-running', Lang.bind(this, this._syncNMState));
1666
this._client.connect('notify::networking-enabled', Lang.bind(this, this._syncNMState));
1667
this._client.connect('notify::state', Lang.bind(this, this._syncNMState));
1668
this._client.connect('notify::active-connections', Lang.bind(this, this._updateIcon));
1669
this._client.connect('device-added', Lang.bind(this, this._deviceAdded));
1670
this._client.connect('device-removed', Lang.bind(this, this._deviceRemoved));
1671
this._settings.connect('new-connection', Lang.bind(this, this._newConnection));
1676
_ensureSource: function() {
1677
if (!this._source) {
1678
this._source = new NMMessageTraySource();
1679
this._source.connect('destroy', Lang.bind(this, function() {
1680
this._source = null;
1682
Main.messageTray.add(this._source);
1686
_makeToggleItem: function(type, title) {
1687
let item = new NMWirelessSectionTitleMenuItem(this._client, type, title);
1688
item.connect('enabled-changed', Lang.bind(this, function(item, enabled) {
1689
let devices = this._devices[type].devices;
1690
devices.forEach(function(dev) {
1691
dev.setEnabled(enabled);
1693
this._syncSectionTitle(type);
1698
_syncSectionTitle: function(category) {
1699
let devices = this._devices[category].devices;
1700
let item = this._devices[category].item;
1701
let section = this._devices[category].section;
1702
if (devices.length == 0)
1703
section.actor.hide();
1705
section.actor.show();
1706
if (devices.length == 1) {
1707
let dev = devices[0];
1708
dev.statusItem.actor.hide();
1709
item.updateForDevice(dev);
1711
devices.forEach(function(dev) {
1712
dev.statusItem.actor.show();
1714
// remove status text from the section title item
1715
item.updateForDevice(null);
1720
_readDevices: function() {
1721
let devices = this._client.get_devices() || [ ];
1722
for (let i = 0; i < devices.length; ++i) {
1723
this._deviceAdded(this._client, devices[i]);
1727
_notifyForDevice: function(device, iconName, title, text, urgency) {
1728
if (device._notification)
1729
device._notification.destroy();
1731
/* must call after destroying previous notification,
1732
or this._source will be cleared */
1733
this._ensureSource();
1735
let icon = new St.Icon({ icon_name: iconName,
1736
icon_type: St.IconType.SYMBOLIC,
1737
icon_size: this._source.ICON_SIZE
1739
device._notification = new MessageTray.Notification(this._source, title, text,
1741
device._notification.setUrgency(urgency);
1742
device._notification.setTransient(true);
1743
device._notification.connect('destroy', function() {
1744
device._notification = null;
1746
this._source.notify(device._notification);
1749
_deviceAdded: function(client, device) {
1750
if (device._delegate) {
1751
// already seen, not adding again
1754
let wrapperClass = this._dtypes[device.get_device_type()];
1756
let wrapper = new wrapperClass(this._client, device, this._connections);
1758
wrapper._activationFailedId = wrapper.connect('activation-failed', Lang.bind(this, function(device, reason) {
1759
// XXX: nm-applet has no special text depending on reason
1760
// but I'm not sure of this generic message
1761
this._notifyForDevice(device, 'network-error',
1762
_("Connection failed"),
1763
_("Activation of network connection failed"),
1764
MessageTray.Urgency.HIGH);
1766
wrapper._deviceStateChangedId = wrapper.connect('state-changed', Lang.bind(this, function(dev) {
1767
this._syncSectionTitle(dev.category);
1769
wrapper._destroyId = wrapper.connect('destroy', function(wrapper) {
1770
wrapper.disconnect(wrapper._activationFailedId);
1771
wrapper.disconnect(wrapper._deviceStateChangedId);
1772
wrapper.disconnect(wrapper._destroyId);
1774
let section = this._devices[wrapper.category].section;
1775
let devices = this._devices[wrapper.category].devices;
1777
section.addMenuItem(wrapper.section, 1);
1778
section.addMenuItem(wrapper.statusItem, 1);
1779
devices.push(wrapper);
1781
this._syncSectionTitle(wrapper.category);
1783
log('Invalid network device type, is ' + device.get_device_type());
1786
_deviceRemoved: function(client, device) {
1787
if (!device._delegate) {
1788
log('Removing a network device that was not added');
1792
let wrapper = device._delegate;
1795
let devices = this._devices[wrapper.category].devices;
1796
let pos = devices.indexOf(wrapper);
1797
devices.splice(pos, 1);
1799
this._syncSectionTitle(wrapper.category)
1802
_syncActiveConnections: function() {
1803
let closedConnections = [ ];
1804
let newActiveConnections = this._client.get_active_connections() || [ ];
1805
for (let i = 0; i < this._activeConnections.length; i++) {
1806
let a = this._activeConnections[i];
1807
if (newActiveConnections.indexOf(a) == -1) // connection is removed
1808
closedConnections.push(a);
1811
for (let i = 0; i < closedConnections.length; i++) {
1812
let active = closedConnections[i];
1813
if (active._primaryDevice) {
1814
active._primaryDevice.setActiveConnection(null);
1815
active._primaryDevice = null;
1817
if (active._inited) {
1818
active.disconnect(active._notifyStateId);
1819
active.disconnect(active._notifyDefaultId);
1820
active.disconnect(active._notifyDefault6Id);
1821
active._inited = false;
1825
this._activeConnections = newActiveConnections;
1826
this._mainConnection = null;
1827
let activating = null;
1828
let default_ip4 = null;
1829
let default_ip6 = null;
1830
for (let i = 0; i < this._activeConnections.length; i++) {
1831
let a = this._activeConnections[i];
1834
a._notifyDefaultId = a.connect('notify::default', Lang.bind(this, this._updateIcon));
1835
a._notifyDefault6Id = a.connect('notify::default6', Lang.bind(this, this._updateIcon));
1836
a._notifyStateId = a.connect('notify::state', Lang.bind(this, this._notifyActivated));
1841
if (!a._connection) {
1842
a._connection = this._settings.get_connection_by_path(a.connection);
1844
if (a._connection) {
1845
a._type = a._connection._type;
1846
a._section = this._ctypes[a._type];
1848
a._connection = null;
1851
log('Cannot find connection for active (or connection cannot be read)');
1860
if (a.state == NetworkManager.ActiveConnectionState.ACTIVATING)
1863
if (!a._primaryDevice) {
1864
if (a._type != NetworkManager.SETTING_VPN_SETTING_NAME) {
1865
// find a good device to be considered primary
1866
a._primaryDevice = null;
1867
let devices = a.get_devices();
1868
for (let j = 0; j < devices.length; j++) {
1871
a._primaryDevice = d._delegate;
1876
a._primaryDevice = this._devices.vpn.device
1878
if (a._primaryDevice)
1879
a._primaryDevice.setActiveConnection(a);
1881
if (a.state == NetworkManager.ActiveConnectionState.ACTIVATED
1882
&& a._primaryDevice && a._primaryDevice._notification) {
1883
a._primaryDevice._notification.destroy();
1884
a._primaryDevice._notification = null;
1889
this._mainConnection = activating || default_ip4 || default_ip6 || this._activeConnections[0] || null;
1892
_notifyActivated: function(activeConnection) {
1893
if (activeConnection.state == NetworkManager.ActiveConnectionState.ACTIVATED
1894
&& activeConnection._primaryDevice && activeConnection._primaryDevice._notification) {
1895
activeConnection._primaryDevice._notification.destroy();
1896
activeConnection._primaryDevice._notification = null;
1902
_readConnections: function() {
1903
let connections = this._settings.list_connections();
1904
for (let i = 0; i < connections.length; i++) {
1905
let connection = connections[i];
1906
if (connection._uuid) {
1907
// connection was already seen (for example because NetworkManager was restarted)
1910
connection._removedId = connection.connect('removed', Lang.bind(this, this._connectionRemoved));
1911
connection._updatedId = connection.connect('updated', Lang.bind(this, this._updateConnection));
1913
this._updateConnection(connection);
1914
this._connections.push(connection);
1918
_newConnection: function(settings, connection) {
1919
if (connection._uuid) {
1920
// connection was already seen
1924
connection._removedId = connection.connect('removed', Lang.bind(this, this._connectionRemoved));
1925
connection._updatedId = connection.connect('updated', Lang.bind(this, this._updateConnection));
1927
this._updateConnection(connection);
1928
this._connections.push(connection);
1933
_connectionRemoved: function(connection) {
1934
let pos = this._connections.indexOf(connection);
1936
this._connections.splice(connection);
1938
let section = connection._section;
1940
if (section == NMConnectionCategory.VPN) {
1941
this._devices.vpn.device.removeConnection(connection);
1942
if (this._devices.vpn.device.empty)
1943
this._devices.vpn.section.actor.hide();
1944
} else if (section != NMConnectionCategory.INVALID) {
1945
let devices = this._devices[section].devices;
1946
for (let i = 0; i < devices.length; i++)
1947
devices[i].removeConnection(connection);
1950
connection._uuid = null;
1951
connection.disconnect(connection._removedId);
1952
connection.disconnect(connection._updatedId);
1955
_updateConnection: function(connection) {
1956
let connectionSettings = connection.get_setting_by_name(NetworkManager.SETTING_CONNECTION_SETTING_NAME);
1957
connection._type = connectionSettings.type;
1959
connection._section = this._ctypes[connection._type] || NMConnectionCategory.INVALID;
1960
connection._name = connectionSettings.id;
1961
connection._uuid = connectionSettings.uuid;
1962
connection._timestamp = connectionSettings.timestamp;
1964
let section = connection._section;
1966
if (connection._section == NMConnectionCategory.INVALID)
1968
if (section == NMConnectionCategory.VPN) {
1969
this._devices.vpn.device.checkConnection(connection);
1970
this._devices.vpn.section.actor.show();
1972
let devices = this._devices[section].devices;
1973
for (let i = 0; i < devices.length; i++) {
1974
devices[i].checkConnection(connection);
1979
_hideDevices: function() {
1980
this._devicesHidden = true;
1982
for (let category in this._devices)
1983
this._devices[category].section.actor.hide();
1986
_showNormal: function() {
1987
if (!this._devicesHidden) // nothing to do
1989
this._devicesHidden = false;
1991
this._statusSection.actor.hide();
1993
this._syncSectionTitle('wired');
1994
this._syncSectionTitle('wireless');
1995
this._syncSectionTitle('wwan');
1997
if (!this._devices.vpn.device.empty)
1998
this._devices.vpn.section.actor.show();
2001
_syncNMState: function() {
2002
if (!this._client.manager_running) {
2003
log('NetworkManager is not running, hiding...');
2010
if (!this._client.networking_enabled) {
2011
this.setIcon('network-offline');
2012
this._hideDevices();
2013
this._statusItem.label.text = _("Networking is disabled");
2014
this._statusSection.actor.show();
2022
_updateIcon: function() {
2023
this._syncActiveConnections();
2024
let mc = this._mainConnection;
2025
let hasApIcon = false;
2026
let hasMobileIcon = false;
2029
this.setIcon('network-offline');
2030
} else if (mc.state == NetworkManager.ActiveConnectionState.ACTIVATING) {
2031
switch (mc._section) {
2032
case NMConnectionCategory.WWAN:
2033
this.setIcon('network-cellular-acquiring');
2035
case NMConnectionCategory.WIRELESS:
2036
this.setIcon('network-wireless-acquiring');
2038
case NMConnectionCategory.WIRED:
2039
this.setIcon('network-wired-acquiring');
2041
case NMConnectionCategory.VPN:
2042
this.setIcon('network-vpn-acquiring');
2045
// fallback to a generic connected icon
2046
// (it could be a private connection of some other user)
2047
this.setIcon('network-wired-acquiring');
2051
switch (mc._section) {
2052
case NMConnectionCategory.WIRELESS:
2053
dev = mc._primaryDevice;
2055
let ap = dev.device.active_access_point;
2056
let mode = dev.device.mode;
2058
if (mode != NM80211Mode.ADHOC) {
2059
log('An active wireless connection, in infrastructure mode, involves no access point?');
2062
this.setIcon('network-wireless-connected');
2064
if (this._accessPointUpdateId && this._activeAccessPoint != ap) {
2065
this._activeAccessPoint.disconnect(this._accessPointUpdateId);
2066
this._activeAccessPoint = ap;
2067
this._activeAccessPointUpdateId = ap.connect('notify::strength', Lang.bind(function() {
2068
this.setIcon('network-wireless-signal-' + signalToIcon(ap.strength));
2071
this.setIcon('network-wireless-signal-' + signalToIcon(ap.strength));
2076
log('Active connection with no primary device?');
2079
case NMConnectionCategory.WIRED:
2080
this.setIcon('network-wired');
2082
case NMConnectionCategory.WWAN:
2083
dev = mc._primaryDevice;
2085
log('Active connection with no primary device?');
2088
if (!dev.mobileDevice) {
2089
// this can happen for bluetooth in PAN mode
2090
this.setIcon('network-cellular-connected');
2094
if (this._mobileUpdateId && this._mobileUpdateDevice != dev) {
2095
this._mobileUpdateDevice.disconnect(this._mobileUpdateId);
2096
this._mobileUpdateDevice = dev.mobileDevice;
2097
this._mobileUpdateId = dev.mobileDevice.connect('notify::signal-quality', Lang.bind(this, function() {
2098
this.setIcon('network-cellular-signal-' + signalToIcon(dev.mobileDevice.signal_quality));
2101
this.setIcon('network-cellular-signal-' + signalToIcon(dev.mobileDevice.signal_quality));
2102
hasMobileIcon = true;
2104
case NMConnectionCategory.VPN:
2105
this.setIcon('network-vpn');
2108
// fallback to a generic connected icon
2109
// (it could be a private connection of some other user)
2110
this.setIcon('network-wired');
2115
// cleanup stale signal connections
2117
if (!hasApIcon && this._activeAccessPointUpdateId) {
2118
this._activeAccessPoint.disconnect(this._activeAccessPointUpdateId);
2119
this._activeAccessPoint = null;
2120
this._activeAccessPointUpdateId = 0;
2122
if (!hasMobileIcon && this._mobileUpdateId) {
2123
this._mobileUpdateDevice.disconnect(this._mobileUpdateId);
2124
this._mobileUpdateDevice = null;
2125
this._mobileUpdateId = 0;
2130
function NMMessageTraySource() {
2134
NMMessageTraySource.prototype = {
2135
__proto__: MessageTray.Source.prototype,
2138
MessageTray.Source.prototype._init.call(this, _("Network Manager"));
2140
let icon = new St.Icon({ icon_name: 'network-transmit-receive',
2141
icon_type: St.IconType.SYMBOLIC,
2142
icon_size: this.ICON_SIZE
2144
this._setSummaryIcon(icon);