/* ex:ts=4:sw=4:sts=4:et */ /* -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ /** * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * AUTHORS * Maciek Borzecki */ /** * General device wrapper. */ class Device : Object { public signal void paired(bool pair); public signal void connected(); public signal void disconnected(); public signal void message(Packet pkt); public string device_id { get; private set; default = ""; } public string device_name { get; private set; default = ""; } public string device_type { get; private set; default = ""; } public uint protocol_version {get; private set; default = 5; } public uint tcp_port {get; private set; default = 1714; } public InetAddress host { get; private set; default = null; } public bool is_paired { get; private set; default = false; } // set to true if pair request was sent private bool _pair_in_progress = false; private DeviceChannel _channel = null; private Device() { } /** * Constructs a new Device wrapper based on identity packet. * * @param pkt identity packet * @param host source host that the packet came from */ public Device.from_identity(Packet pkt, InetAddress host) { debug("got packet: %s", pkt.to_string()); var body = pkt.body; this.host = host; this.device_name = body.get_string_member("deviceName"); this.device_id = body.get_string_member("deviceId"); this.device_type = body.get_string_member("deviceType"); this.protocol_version = (int) body.get_int_member("protocolVersion"); this.tcp_port = (uint) body.get_int_member("tcpPort"); debug("added new device: %s", this.to_string()); } ~Device() { } /** * Generates a unique string for this device */ public string to_unique_string() { return this.to_string().replace(" ", "-"); } public string to_string() { return "%s-%s-%s-%u".printf(this.device_id, this.device_name, this.device_type, this.protocol_version); } private async void greet() { string[] interfaces = {"kdeconnect.notification", "kdeconnect.battery", "kdeconnect.ping"}; yield _channel.send(Packet.new_identity("test-laptop", "dadada", interfaces, interfaces)); this.pair_if_needed(); } /** * pair: sent pair request * * Internally changes pair requests state tracking. * * @param expect_response se to true if expecting a response */ public async void pair(bool expect_response = true) { if (this.host != null) { debug("start pairing"); var core = Core.instance(); string pubkey = core.crypt.get_public_key_pem(); debug("public key: %s", pubkey); if (expect_response == true) _pair_in_progress = true; yield _channel.send(Packet.new_pair(pubkey)); } } public void pair_if_needed() { if (is_paired == false && _pair_in_progress == false) this.pair(); } public void activate() { assert(_channel == null); var core = Core.instance(); _channel = new DeviceChannel(this.host, this.tcp_port, core.crypt); _channel.connected.connect((c) => { this.greet(); }); _channel.disconnected.connect((c) => { this.handle_disconnect(); }); _channel.packet_received.connect((c, pkt) => { this.packet_received(pkt); }); _channel.open(); debug("open finished"); } public void deactivate() { if (_channel == null) _channel.close(); _channel = null; } /** * activate_from_device: * * Try to activate using a newly discovered device. If device is * already active, compare the host address to see if it * changed. If so, close the current connection and activate with * new address. * * @param dev device */ public void activate_from_device(Device dev) { if (host == null) { activate(); } else if (dev.host.to_string() != host.to_string()) { // same host, assuming no activation needed deactivate(); activate(); } else { debug("device %s already active", dev.to_string()); } } private void packet_received(Packet pkt) { debug("got packet"); if (pkt.pkt_type == Packet.PAIR) { // pairing handle_pair_packet(pkt); } } private void handle_pair_packet(Packet pkt) { assert(pkt.pkt_type == Packet.PAIR); bool pair = pkt.body.get_boolean_member("pair"); if (_pair_in_progress == true) { // response to host initiated pairing if (pair == true) { debug("device is paired, pairing complete"); this.is_paired = true; } else { critical("pairing rejected by device"); this.is_paired = false; } // pair completed _pair_in_progress = false; } else { debug("unsolicited pair change from device"); if (pair == false) { // unpair from device this.is_paired = false; } else { // pair request from device this.pair(false); } } // emit signal paired(is_paired); } private void handle_disconnect() { // channel got disconnected debug("channel disconnected"); } }