~jbicha/ubuntu/oneiric/gnome-shell/oneiric-3.2.2.1

« back to all changes in this revision

Viewing changes to js/ui/status/network.js

  • Committer: Package Import Robot
  • Author(s): Jeremy Bicha
  • Date: 2011-09-07 09:09:05 UTC
  • mfrom: (1.1.29 upstream)
  • Revision ID: package-import@ubuntu.com-20110907090905-kbo4fewcg12zt99u
Tags: 3.1.90.1-0ubuntu1
* New upstream release.
* debian/control: Bump build-depends on new mutter
* debian/patches/01_favorite_apps.patch: Updated
* debian/patches/03_remove-glx-dependency-on-armel.patch: Refreshed
* debian/patches/04_build-without-caribou.patch
  - Build without caribou since Ubuntu uses onboard and our System 
    Settings doesn't support choosing a different screen keyboard yet

Show diffs side-by-side

added added

removed removed

Lines of Context:
45
45
// (the remaining are placed into More...)
46
46
const NUM_VISIBLE_NETWORKS = 5;
47
47
 
48
 
const NMAppletHelperInterface = {
49
 
    name: 'org.gnome.network_manager_applet',
50
 
    methods: [
51
 
        { name: 'ConnectToHiddenNetwork', inSignature: '', outSignature: '' },
52
 
        { name: 'CreateWifiNetwork', inSignature: '', outSignature: '' },
53
 
        { name: 'ConnectTo8021xNetwork', inSignature: 'oo', outSignature: '' },
54
 
        { name: 'ConnectTo3gNetwork', inSignature: 'o', outSignature: '' }
55
 
    ],
56
 
};
57
 
const NMAppletProxy = DBus.makeProxyClass(NMAppletHelperInterface);
58
 
 
59
48
function macToArray(string) {
60
49
    return string.split(':').map(function(el) {
61
50
        return parseInt(el, 16);
102
91
    });
103
92
}
104
93
 
 
94
function ssidToLabel(ssid) {
 
95
    let label = NetworkManager.utils_ssid_to_utf8(ssid);
 
96
    if (!label)
 
97
        label = _("<unknown>");
 
98
    return label;
 
99
}
 
100
 
105
101
function NMNetworkMenuItem() {
106
102
    this._init.apply(this, arguments);
107
103
}
117
113
 
118
114
        if (!title) {
119
115
            let ssid = this.bestAP.get_ssid();
120
 
            if (ssid)
121
 
                title = NetworkManager.utils_ssid_to_utf8(ssid);
122
 
            if (!title)
123
 
                title = _("<unknown>");
 
116
            title = ssidToLabel(ssid);
124
117
        }
125
118
 
126
119
        this._label = new St.Label({ text: title });
631
624
            this.emit('network-lost');
632
625
        }
633
626
 
634
 
        switch(newstate) {
635
 
        case NetworkManager.DeviceState.NEED_AUTH:
636
 
            // FIXME: make this have a real effect
637
 
            // (currently we rely on a running nm-applet)
638
 
            this.emit('need-auth');
639
 
            break;
640
 
        case NetworkManager.DeviceState.FAILED:
 
627
        if (newstate == NetworkManager.DeviceState.FAILED) {
641
628
            this.emit('activation-failed', reason);
642
 
            break;
643
629
        }
644
630
 
645
631
        this._updateStatusItem();
674
660
        let dev_product = this.device.get_product();
675
661
        let dev_vendor = this.device.get_vendor();
676
662
        if (!dev_product || !dev_vendor)
677
 
            return null;
 
663
            return '';
678
664
 
679
665
        let product = Util.fixupPCIDescription(dev_product);
680
666
        let vendor = Util.fixupPCIDescription(dev_vendor);
748
734
        this.mobileDevice = null;
749
735
        this._connectionType = 'ppp';
750
736
 
751
 
        this._applet_proxy = new NMAppletProxy(DBus.session,
752
 
                                               'org.gnome.network_manager_applet',
753
 
                                               '/org/gnome/network_manager_applet');
754
 
 
755
737
        this._capabilities = device.current_capabilities;
756
738
        if (this._capabilities & NetworkManager.DeviceModemCapabilities.GSM_UMTS) {
757
739
            is_wwan = true;
853
835
    },
854
836
 
855
837
    _createAutomaticConnection: function() {
856
 
        // Mobile wizard is handled by nm-applet for now...
857
 
        this._applet_proxy.ConnectTo3gNetworkRemote(this.device.get_path(),
858
 
                                                    Lang.bind(this, function(results, err) {
859
 
                                                        if (err)
860
 
                                                            log(err);
861
 
                                                    }));
 
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()]);
862
842
        return null;
863
843
    }
864
844
};
970
950
        this._overflowItem = null;
971
951
        this._networks = [ ];
972
952
 
973
 
        this._applet_proxy = new NMAppletProxy(DBus.session,
974
 
                                               'org.gnome.network_manager_applet',
975
 
                                               '/org/gnome/network_manager_applet');
976
 
 
977
953
        // breaking the layers with this, but cannot call
978
954
        // this.connectionValid until I have a device
979
955
        this.device = device;
985
961
        for (let i = 0; i < accessPoints.length; i++) {
986
962
            // Access points are grouped by network
987
963
            let ap = accessPoints[i];
 
964
 
 
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));
 
971
                continue;
 
972
            }
 
973
 
988
974
            let pos = this._findNetwork(ap);
989
975
            let obj;
990
976
            if (pos != -1) {
998
984
                        item: null,
999
985
                        accessPoints: [ ap ]
1000
986
                      };
1001
 
                obj.ssidText = NetworkManager.utils_ssid_to_utf8(obj.ssid);
 
987
                obj.ssidText = ssidToLabel(obj.ssid);
1002
988
                this._networks.push(obj);
1003
989
            }
1004
990
 
1011
997
                }
1012
998
            }
1013
999
        }
 
1000
 
1014
1001
        if (this.device.active_access_point) {
1015
 
            this._activeNetwork = this._networks[this._findNetwork(this.device.active_access_point)];
 
1002
            let networkPos = this._findNetwork(this.device.active_access_point);
 
1003
 
 
1004
            if (networkPos == -1) // the connected access point is invisible
 
1005
                this._activeNetwork = null;
 
1006
            else
 
1007
                this._activeNetwork = this._networks[networkPos];
1016
1008
        } else {
1017
1009
            this._activeNetwork = null;
1018
1010
        }
1098
1090
        }
1099
1091
    },
1100
1092
 
 
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);
 
1098
        }
 
1099
    },
 
1100
 
1101
1101
    _activeApChanged: function() {
1102
1102
        this._activeNetwork = null;
1103
1103
 
1105
1105
 
1106
1106
        if (activeAp) {
1107
1107
            let pos = this._findNetwork(activeAp);
1108
 
            this._activeNetwork = this._networks[pos];
 
1108
 
 
1109
            if (pos != -1)
 
1110
                this._activeNetwork = this._networks[pos];
1109
1111
        }
1110
1112
 
1111
1113
        // we don't refresh the view here, setActiveConnection will
1180
1182
    },
1181
1183
 
1182
1184
    _findNetwork: function(accessPoint) {
 
1185
        if (accessPoint.get_ssid() == null)
 
1186
            return -1;
 
1187
 
1183
1188
        for (let i = 0; i < this._networks.length; i++) {
1184
1189
            if (this._networkCompare(this._networks[i], accessPoint))
1185
1190
                return i;
1188
1193
    },
1189
1194
 
1190
1195
    _accessPointAdded: function(device, accessPoint) {
 
1196
        if (accessPoint.get_ssid() == null) {
 
1197
            // This access point is not visible yet
 
1198
            // Wait for it to get a ssid
 
1199
            accessPoint._notifySsidId = accessPoint.connect('notify::ssid', Lang.bind(this, this._notifySsidCb));
 
1200
            return;
 
1201
        }
 
1202
 
1191
1203
        let pos = this._findNetwork(accessPoint);
1192
1204
        let apObj;
1193
1205
        let needsupdate = false;
1210
1222
                      item: null,
1211
1223
                      accessPoints: [ accessPoint ]
1212
1224
                    };
1213
 
            apObj.ssidText = NetworkManager.utils_ssid_to_utf8(apObj.ssid);
 
1225
            apObj.ssidText = ssidToLabel(apObj.ssid);
1214
1226
            needsupdate = true;
1215
1227
        }
1216
1228
 
1423
1435
    },
1424
1436
 
1425
1437
    _createActiveConnectionItem: function() {
1426
 
        let activeAp = this.device.active_access_point;
1427
1438
        let icon, title;
1428
1439
        if (this._activeConnection._connection) {
1429
1440
            let connection = this._activeConnection._connection;
1430
 
            if (activeAp)
1431
 
                this._activeConnectionItem = new NMNetworkMenuItem([ activeAp ], undefined,
1432
 
                                                                       { reactive: false });
 
1441
            if (this._activeNetwork)
 
1442
                this._activeConnectionItem = new NMNetworkMenuItem(this._activeNetwork.accessPoints, undefined,
 
1443
                                                                   { reactive: false });
1433
1444
            else
1434
1445
                this._activeConnectionItem = new PopupMenu.PopupImageMenuItem(connection._name,
1435
1446
                                                                              'network-wireless-connected',
1437
1448
        } else {
1438
1449
            // We cannot read the connection (due to ACL, or API incompatibility), but we still show signal if we have it
1439
1450
            let menuItem;
1440
 
            if (activeAp)
1441
 
                this._activeConnectionItem = new NMNetworkMenuItem([ activeAp ], undefined,
1442
 
                                                                       { reactive: false });
 
1451
            if (this._activeNetwork)
 
1452
                this._activeConnectionItem = new NMNetworkMenuItem(this._activeNetwork.accessPoints, undefined,
 
1453
                                                                   { reactive: false });
1443
1454
            else
1444
1455
                this._activeConnectionItem = new PopupMenu.PopupImageMenuItem(_("Connected (private)"),
1445
1456
                                                                              'network-wireless-connected',
1486
1497
                let accessPoints = sortAccessPoints(apObj.accessPoints);
1487
1498
                if (   (accessPoints[0]._secType == NMAccessPointSecurity.WPA2_ENT)
1488
1499
                    || (accessPoints[0]._secType == NMAccessPointSecurity.WPA_ENT)) {
1489
 
                    // 802.1x-enabled APs get handled by nm-applet for now...
1490
 
                    this._applet_proxy.ConnectTo8021xNetworkRemote(this.device.get_path(),
1491
 
                                                                   accessPoints[0].dbus_path,
1492
 
                                                                   Lang.bind(this, function(results, err) {
1493
 
                                                                       if (err)
1494
 
                                                                           log(err);
1495
 
                                                                   }));
 
1500
                    // 802.1x-enabled APs require further configuration, so they're
 
1501
                    // handled in gnome-control-center
 
1502
                    Util.spawn(['gnome-control-center', 'network', 'connect-8021x-wifi',
 
1503
                                this.device.get_path(), accessPoints[0].dbus_path]);
1496
1504
                } else {
1497
1505
                    let connection = this._createAutomaticConnection(apObj);
1498
1506
                    this._client.add_and_activate_connection(connection, this.device, accessPoints[0].dbus_path, null)
1550
1558
        this._statusSection.addAction(_("Enable networking"), Lang.bind(this, function() {
1551
1559
            this._client.networking_enabled = true;
1552
1560
        }));
1553
 
        this._statusSection.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
1554
1561
        this._statusSection.actor.hide();
1555
1562
        this.menu.addMenuItem(this._statusSection);
 
1563
        this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
1556
1564
 
1557
1565
        this._devices = { };
1558
1566
 
1563
1571
        };
1564
1572
 
1565
1573
        this._devices.wired.section.addMenuItem(this._devices.wired.item);
1566
 
        this._devices.wired.section.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
1567
1574
        this._devices.wired.section.actor.hide();
1568
1575
        this.menu.addMenuItem(this._devices.wired.section);
 
1576
        this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
1569
1577
 
1570
1578
        this._devices.wireless = {
1571
1579
            section: new PopupMenu.PopupMenuSection(),
1573
1581
            item: this._makeToggleItem('wireless', _("Wireless"))
1574
1582
        };
1575
1583
        this._devices.wireless.section.addMenuItem(this._devices.wireless.item);
1576
 
        this._devices.wireless.section.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
1577
1584
        this._devices.wireless.section.actor.hide();
1578
1585
        this.menu.addMenuItem(this._devices.wireless.section);
 
1586
        this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
1579
1587
 
1580
1588
        this._devices.wwan = {
1581
1589
            section: new PopupMenu.PopupMenuSection(),
1583
1591
            item: this._makeToggleItem('wwan', _("Mobile broadband"))
1584
1592
        };
1585
1593
        this._devices.wwan.section.addMenuItem(this._devices.wwan.item);
1586
 
        this._devices.wwan.section.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
1587
1594
        this._devices.wwan.section.actor.hide();
1588
1595
        this.menu.addMenuItem(this._devices.wwan.section);
 
1596
        this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
1589
1597
 
1590
1598
        this._devices.vpn = {
1591
1599
            section: new PopupMenu.PopupMenuSection(),
1598
1606
        this._devices.vpn.item.updateForDevice(this._devices.vpn.device);
1599
1607
        this._devices.vpn.section.addMenuItem(this._devices.vpn.item);
1600
1608
        this._devices.vpn.section.addMenuItem(this._devices.vpn.device.section);
1601
 
        this._devices.vpn.section.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
1602
1609
        this._devices.vpn.section.actor.hide();
1603
1610
        this.menu.addMenuItem(this._devices.vpn.section);
1604
 
 
1605
 
        this.menu.addAction(_("Network Settings"), function() {
1606
 
            Main.overview.hide();
1607
 
            let app = Shell.AppSystem.get_default().get_app('gnome-network-panel.desktop');
1608
 
            app.activate(-1);
1609
 
        });
 
1611
        this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
 
1612
        this.menu.addSettingsAction(_("Network Settings"), 'gnome-network-panel.desktop');
1610
1613
 
1611
1614
        this._activeConnections = [ ];
1612
1615
        this._connections = [ ];
1660
1663
    _ensureSource: function() {
1661
1664
        if (!this._source) {
1662
1665
            this._source = new NMMessageTraySource();
1663
 
            this._source._destroyId = this._source.connect('destroy', Lang.bind(this, function() {
1664
 
                this._source._destroyId = 0;
 
1666
            this._source.connect('destroy', Lang.bind(this, function() {
1665
1667
                this._source = null;
1666
1668
            }));
1667
1669
            Main.messageTray.add(this._source);
1709
1711
        }
1710
1712
    },
1711
1713
 
 
1714
    _notifyForDevice: function(device, iconName, title, text, urgency) {
 
1715
        if (device._notification)
 
1716
            device._notification.destroy();
 
1717
 
 
1718
        /* must call after destroying previous notification,
 
1719
           or this._source will be cleared */
 
1720
        this._ensureSource();
 
1721
 
 
1722
        let icon = new St.Icon({ icon_name: iconName,
 
1723
                                 icon_type: St.IconType.SYMBOLIC,
 
1724
                                 icon_size: this._source.ICON_SIZE
 
1725
                               });
 
1726
        device._notification = new MessageTray.Notification(this._source, title, text,
 
1727
                                                            { icon: icon });
 
1728
        device._notification.setUrgency(urgency);
 
1729
        device._notification.setTransient(true);
 
1730
        device._notification.connect('destroy', function() {
 
1731
            device._notification = null;
 
1732
        });
 
1733
        this._source.notify(device._notification);
 
1734
    },
 
1735
 
1712
1736
    _deviceAdded: function(client, device) {
1713
1737
        if (device._delegate) {
1714
1738
            // already seen, not adding again
1718
1742
        if (wrapperClass) {
1719
1743
            let wrapper = new wrapperClass(this._client, device, this._connections);
1720
1744
 
1721
 
            // FIXME: these notifications are duplicate with those exposed by nm-applet
1722
 
            // uncomment this code in 3.2, when we'll conflict with and kill nm-applet
1723
 
            /* wrapper._networkLostId = wrapper.connect('network-lost', Lang.bind(this, function(emitter) {
1724
 
                this._ensureSource();
1725
 
                let icon = new St.Icon({ icon_name: 'network-offline',
1726
 
                                         icon_type: St.IconType.SYMBOLIC,
1727
 
                                         icon_size: this._source.ICON_SIZE
1728
 
                                       });
1729
 
                let notification = new MessageTray.Notification(this._source,
1730
 
                                                                _("Connectivity lost"),
1731
 
                                                                _("You're no longer connected to the network"),
1732
 
                                                                { icon: icon });
1733
 
                this._source.notify(notification);
 
1745
            wrapper._networkLostId = wrapper.connect('network-lost', Lang.bind(this, function(device) {
 
1746
                this._notifyForDevice(device, 'network-offline',
 
1747
                                      _("Connectivity lost"),
 
1748
                                      _("You're no longer connected to the network"),
 
1749
                                      // set critical urgency to popup the notification automatically
 
1750
                                      MessageTray.Urgency.CRITICAL);
1734
1751
            }));
1735
 
            wrapper._activationFailedId = wrapper.connect('activation-failed', Lang.bind(this, function(wrapper, reason) {
1736
 
                this._ensureSource();
1737
 
                let icon = new St.Icon({ icon_name: 'network-error',
1738
 
                                         icon_type: St.IconType.SYMBOLIC,
1739
 
                                         icon_size: this._source.ICON_SIZE,
1740
 
                                       });
1741
 
                let banner;
 
1752
            wrapper._activationFailedId = wrapper.connect('activation-failed', Lang.bind(this, function(device, reason) {
1742
1753
                // XXX: nm-applet has no special text depending on reason
1743
1754
                // but I'm not sure of this generic message
1744
 
                let notification = new MessageTray.Notification(this._source,
1745
 
                                                                _("Connection failed"),
1746
 
                                                                _("Activation of network connection failed"),
1747
 
                                                                { icon: icon });
1748
 
                this._source.notify(notification);
1749
 
            })); */
 
1755
                this._notifyForDevice(device, 'network-error',
 
1756
                                      _("Connection failed"),
 
1757
                                      _("Activation of network connection failed"),
 
1758
                                     MessageTray.Urgency.HIGH);
 
1759
            }));
1750
1760
            wrapper._deviceStateChangedId = wrapper.connect('state-changed', Lang.bind(this, function(dev) {
1751
1761
                this._syncSectionTitle(dev.category);
1752
1762
            }));
1753
1763
            wrapper._destroyId = wrapper.connect('destroy', function(wrapper) {
1754
 
                //wrapper.disconnect(wrapper._networkLostId);
1755
 
                //wrapper.disconnect(wrapper._activationFailedId);
 
1764
                wrapper.disconnect(wrapper._networkLostId);
 
1765
                wrapper.disconnect(wrapper._activationFailedId);
1756
1766
                wrapper.disconnect(wrapper._deviceStateChangedId);
 
1767
                wrapper.disconnect(wrapper._destroyId);
1757
1768
            });
1758
1769
            let section = this._devices[wrapper.category].section;
1759
1770
            let devices = this._devices[wrapper.category].devices;
1798
1809
                active._primaryDevice.setActiveConnection(null);
1799
1810
                active._primaryDevice = null;
1800
1811
            }
1801
 
            if (active._notifyStateId) {
 
1812
            if (active._inited) {
1802
1813
                active.disconnect(active._notifyStateId);
1803
 
                active._notifyStateId = 0;
1804
 
            }
1805
 
            if (active._inited) {
1806
1814
                active.disconnect(active._notifyDefaultId);
1807
1815
                active.disconnect(active._notifyDefault6Id);
1808
1816
                active._inited = false;
1820
1828
            if (!a._inited) {
1821
1829
                a._notifyDefaultId = a.connect('notify::default', Lang.bind(this, this._updateIcon));
1822
1830
                a._notifyDefault6Id = a.connect('notify::default6', Lang.bind(this, this._updateIcon));
1823
 
                if (a.state == NetworkManager.ActiveConnectionState.ACTIVATING) // prepare to notify to the user
1824
 
                    a._notifyStateId = a.connect('notify::state', Lang.bind(this, this._notifyActiveConnection));
1825
 
                else {
1826
 
                    // notify as soon as possible
1827
 
                    Mainloop.idle_add(Lang.bind(this, function() {
1828
 
                        this._notifyActiveConnection(a);
1829
 
                    }));
1830
 
                }
 
1831
                a._notifyStateId = a.connect('notify::state', Lang.bind(this, this._updateIcon));
1831
1832
 
1832
1833
                a._inited = true;
1833
1834
            }
1877
1878
        this._mainConnection = activating || default_ip4 || default_ip6 || this._activeConnections[0] || null;
1878
1879
    },
1879
1880
 
1880
 
    _notifyActiveConnection: function(active) {
1881
 
        // FIXME: duplicate notifications when nm-applet is running
1882
 
        // This code will come back when nm-applet is killed
1883
 
        this._syncNMState();
1884
 
        return;
1885
 
 
1886
 
        if (active.state == NetworkManager.ActiveConnectionState.ACTIVATED) {
1887
 
 
1888
 
            // notify only connections that are visible
1889
 
            if (active._connection) {
1890
 
                this._ensureSource();
1891
 
 
1892
 
                let icon;
1893
 
                let banner;
1894
 
                switch (active._section) {
1895
 
                case NMConnectionCategory.WWAN:
1896
 
                    icon = 'network-cellular-signal-excellent';
1897
 
                    banner = _("You're now connected to mobile broadband connection '%s'").format(active._connection._name);
1898
 
                    break;
1899
 
                case NMConnectionCategory.WIRELESS:
1900
 
                    icon = 'network-wireless-signal-excellent';
1901
 
                    banner = _("You're now connected to wireless network '%s'").format(active._connection._name);
1902
 
                    break;
1903
 
                case NMConnectionCategory.WIRED:
1904
 
                    icon = 'network-wired';
1905
 
                    banner = _("You're now connected to wired network '%s'").format(active._connection._name);
1906
 
                    break;
1907
 
                case NMConnectionCategory.VPN:
1908
 
                    icon = 'network-vpn';
1909
 
                    banner = _("You're now connected to VPN network '%s'").format(active._connection._name);
1910
 
                    break;
1911
 
                default:
1912
 
                    // a fallback for a generic 'connected' icon
1913
 
                    icon = 'network-transmit-receive';
1914
 
                    banner = _("You're now connected to '%s'").format(active._connection._name);
1915
 
                }
1916
 
 
1917
 
                let iconActor = new St.Icon({ icon_name: icon,
1918
 
                                              icon_type: St.IconType.SYMBOLIC,
1919
 
                                              icon_size: this._source.ICON_SIZE
1920
 
                                            });
1921
 
                let notification = new MessageTray.Notification(this._source,
1922
 
                                                                _("Connection established"),
1923
 
                                                                banner,
1924
 
                                                                { icon: iconActor });
1925
 
                this._source.notify(notification);
1926
 
            }
1927
 
 
1928
 
            if (active._stateChangeId) {
1929
 
                active.disconnect(active._stateChangeId);
1930
 
                active._stateChangeId = 0;
1931
 
            }
1932
 
        }
1933
 
 
1934
 
        this._syncNMState();
1935
 
    },
1936
 
 
1937
1881
    _readConnections: function() {
1938
1882
        let connections = this._settings.list_connections();
1939
1883
        for (let i = 0; i < connections.length; i++) {