File source

import * as dbus from "dbus-native";
import { isArray } from "util";
import { Service, ItemStatusListener } from ".";

export default class NetworkManagerService implements Service {

  private sysbus = dbus.systemBus();
  private nmservice = this.sysbus.getService("org.freedesktop.NetworkManager")
  
  private connectionMap = {};
  private activeConnectionMap = {};
  private deviceMap = {};
  private connectionStateMap = {};
  private connStatusListeners:{[connId:string]:ItemStatusListener[]} = {};
  private activeConnectionPropertyChangeListenerMap = {};

  constructor(){
    this.init();
  }

  init(){
    this.nmservice.getInterface("/org/freedesktop/NetworkManager/Settings", "org.freedesktop.NetworkManager.Settings", (err, settings) => {
        settings.ListConnections((err, settingList) => {
            for(let setting of settingList){
                this.nmservice.getInterface(setting, "org.freedesktop.NetworkManager.Settings.Connection", (err, nmconn) => {
                    nmconn.GetSettings((err, connsettings) => {
                        let connId = mapDbusDict(mapDbusDict(connsettings).connection).id[1][0]; 
                        this.connectionMap[connId] = setting;
                    })
                })
            }
        })
    })

    this.nmservice.getInterface("/org/freedesktop/NetworkManager", "org.freedesktop.DBus.Properties", (err, nmprops) => {
      nmprops.on("PropertiesChanged", (ifname, changed, invalidated) => {
        if(ifname === "org.freedesktop.NetworkManager"){
          let changedProps = mapDbusDict(changed);
          if(changedProps.ActiveConnections){
            this.updateActiveConnections(changedProps.ActiveConnections[1][0])
          }
        }
      })

      nmprops.Get("org.freedesktop.NetworkManager", "ActiveConnections", (err, list) => {
        this.updateActiveConnections(list[1][0]);
      })
    })
  }

  updateActiveConnections(list:string[]){
    let n = 0;
    for(let item of list){
      this.nmservice.getInterface(item, "org.freedesktop.DBus.Properties", (err, connprops) => {
        if(err) return;
        if(!this.activeConnectionPropertyChangeListenerMap[item]){
          n++;
          connprops.Get("org.freedesktop.NetworkManager.Connection.Active", "Id", (err, id) => {
            if(err) {n--;return;}
            this.activeConnectionMap[id[1][0]] = item;
            n--;
            if(n === 0) this.onConnectionStatusChange();
          })
          n++;
          connprops.Get("org.freedesktop.NetworkManager.Connection.Active", "State", (err, state) => {
            if(err){n--;return;}
            this.connectionStateMap[item] = state[1][0];
            n--;
            if(n === 0) this.onConnectionStatusChange();
          })
          connprops.on("PropertiesChanged", this.activeConnectionPropertyChangeListenerMap[item] = (ifname, changed, invalidated) => {
            if(ifname === "org.freedesktop.NetworkManager.Connection.Active"){
              let changedProps = mapDbusDict(changed);
              if(changedProps.State){
                this.connectionStateMap[item] = changedProps.State[1][0];
                this.onConnectionStatusChange();
              } 
            }
          })
        }
      });
    }
    let connectionsDisappeared = 0;
    for(let item of Object.keys(this.activeConnectionPropertyChangeListenerMap)){
      if(list.indexOf(item) < 0){
        connectionsDisappeared++;
        this.activeConnectionPropertyChangeListenerMap[item] = undefined;
        this.activeConnectionMap[item] = undefined;
      }
    }
    if(connectionsDisappeared) this.onConnectionStatusChange();
  }

  activateConnection(connId:string, devId:string){
    this.nmservice.getInterface("/org/freedesktop/NetworkManager", "org.freedesktop.NetworkManager", (err, nm) => {
      nm.ActivateConnection(this.connectionMap[connId]||"/", this.deviceMap[devId]||"/", "/");
    })
  }

  deactivateConnection(connId:string){
    this.nmservice.getInterface("/org/freedesktop/NetworkManager", "org.freedesktop.NetworkManager", (err, nm) => {
      nm.DeactivateConnection(this.activeConnectionMap[connId]||"/");
    })
  }

  private onConnectionStatusChange(){
    for(let o of Object.keys(this.connStatusListeners)){
      for(let l of this.connStatusListeners[o]){
        if(this.activeConnectionMap[o]){
          let status = this.connectionStateMap[this.activeConnectionMap[o]];
            l({
              0: "down",
              1: "warning",
              2: "up",
              3: "warning",
              4: "down",
            }[status||0])
        }else{
          l("down");
        }
      }
    }
  }

  addStatusListener(type: string, item: string, listener:ItemStatusListener){
    if(type === "connection"){
      this.connStatusListeners[item] = this.connStatusListeners[item] || [];
      this.connStatusListeners[item].push(listener);
    }
  }

  handleEvent(type: string, item: string, event: string){
    if(type === "connection"){
      if(event === "up"){
        this.activateConnection(item, null);
      }else if(event === "down"){
        this.deactivateConnection(item);
      }
    }
  }


}


function mapDbusDict(input){
    if(!isArray(input)) return input;
    let output = {};
    for(let item of input){
        output[item[0]] = item[1]
    }
    return output;
}