mconnect - KDE Connect protocol implementation in Vala/C
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

209 lines
5.4 KiB

  1. /* ex:ts=4:sw=4:sts=4:et */
  2. /* -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
  3. /**
  4. * This program is free software; you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License version 2 as
  6. * published by the Free Software Foundation.
  7. *
  8. * This program is distributed in the hope that it will be useful,
  9. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. * GNU General Public License for more details.
  12. *
  13. * You should have received a copy of the GNU General Public License along
  14. * with this program; if not, write to the Free Software Foundation, Inc.,
  15. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  16. *
  17. * AUTHORS
  18. * Maciek Borzecki <maciek.borzecki (at] gmail.com>
  19. */
  20. /**
  21. * General device wrapper.
  22. */
  23. class Device : Object {
  24. public signal void paired(bool pair);
  25. public signal void connected();
  26. public signal void disconnected();
  27. public signal void message(Packet pkt);
  28. public string device_id { get; private set; default = ""; }
  29. public string device_name { get; private set; default = ""; }
  30. public string device_type { get; private set; default = ""; }
  31. public uint protocol_version {get; private set; default = 5; }
  32. public uint tcp_port {get; private set; default = 1714; }
  33. public InetAddress host { get; private set; default = null; }
  34. public bool is_paired { get; private set; default = false; }
  35. // set to true if pair request was sent
  36. private bool _pair_in_progress = false;
  37. private DeviceChannel _channel = null;
  38. private Device() {
  39. }
  40. /**
  41. * Constructs a new Device wrapper based on identity packet.
  42. *
  43. * @param pkt identity packet
  44. * @param host source host that the packet came from
  45. */
  46. public Device.from_identity(Packet pkt, InetAddress host) {
  47. debug("got packet: %s", pkt.to_string());
  48. var body = pkt.body;
  49. this.host = host;
  50. this.device_name = body.get_string_member("deviceName");
  51. this.device_id = body.get_string_member("deviceId");
  52. this.device_type = body.get_string_member("deviceType");
  53. this.protocol_version = (int) body.get_int_member("protocolVersion");
  54. this.tcp_port = (uint) body.get_int_member("tcpPort");
  55. debug("added new device: %s", this.to_string());
  56. }
  57. ~Device() {
  58. }
  59. /**
  60. * Generates a unique string for this device
  61. */
  62. public string to_unique_string() {
  63. return this.to_string().replace(" ", "-");
  64. }
  65. public string to_string() {
  66. return "%s-%s-%s-%u".printf(this.device_id, this.device_name,
  67. this.device_type, this.protocol_version);
  68. }
  69. private async void greet() {
  70. string[] interfaces = {"kdeconnect.notification",
  71. "kdeconnect.battery",
  72. "kdeconnect.ping"};
  73. yield _channel.send(Packet.new_identity("test-laptop",
  74. "dadada",
  75. interfaces, interfaces));
  76. this.pair_if_needed();
  77. }
  78. /**
  79. * pair: sent pair request
  80. *
  81. * Internally changes pair requests state tracking.
  82. *
  83. * @param expect_response se to true if expecting a response
  84. */
  85. public async void pair(bool expect_response = true) {
  86. if (this.host != null) {
  87. debug("start pairing");
  88. var core = Core.instance();
  89. string pubkey = core.crypt.get_public_key_pem();
  90. debug("public key: %s", pubkey);
  91. if (expect_response == true)
  92. _pair_in_progress = true;
  93. yield _channel.send(Packet.new_pair(pubkey));
  94. }
  95. }
  96. public void pair_if_needed() {
  97. if (is_paired == false && _pair_in_progress == false)
  98. this.pair();
  99. }
  100. public void activate() {
  101. assert(_channel == null);
  102. var core = Core.instance();
  103. _channel = new DeviceChannel(this.host, this.tcp_port,
  104. core.crypt);
  105. _channel.connected.connect((c) => {
  106. this.greet();
  107. });
  108. _channel.disconnected.connect((c) => {
  109. this.handle_disconnect();
  110. });
  111. _channel.packet_received.connect((c, pkt) => {
  112. this.packet_received(pkt);
  113. });
  114. _channel.open();
  115. debug("open finished");
  116. }
  117. public void deactivate() {
  118. if (_channel == null)
  119. _channel.close();
  120. _channel = null;
  121. }
  122. /**
  123. * activate_from_device:
  124. *
  125. * Try to activate using a newly discovered device. If device is
  126. * already active, compare the host address to see if it
  127. * changed. If so, close the current connection and activate with
  128. * new address.
  129. *
  130. * @param dev device
  131. */
  132. public void activate_from_device(Device dev) {
  133. if (host == null) {
  134. activate();
  135. } else if (dev.host.to_string() != host.to_string()) {
  136. // same host, assuming no activation needed
  137. deactivate();
  138. activate();
  139. } else {
  140. debug("device %s already active", dev.to_string());
  141. }
  142. }
  143. private void packet_received(Packet pkt) {
  144. debug("got packet");
  145. if (pkt.pkt_type == Packet.PAIR) {
  146. // pairing
  147. handle_pair_packet(pkt);
  148. }
  149. }
  150. private void handle_pair_packet(Packet pkt) {
  151. assert(pkt.pkt_type == Packet.PAIR);
  152. bool pair = pkt.body.get_boolean_member("pair");
  153. if (_pair_in_progress == true) {
  154. // response to host initiated pairing
  155. if (pair == true) {
  156. debug("device is paired, pairing complete");
  157. this.is_paired = true;
  158. } else {
  159. critical("pairing rejected by device");
  160. this.is_paired = false;
  161. }
  162. // pair completed
  163. _pair_in_progress = false;
  164. } else {
  165. debug("unsolicited pair change from device");
  166. if (pair == false) {
  167. // unpair from device
  168. this.is_paired = false;
  169. } else {
  170. // pair request from device
  171. this.pair(false);
  172. }
  173. }
  174. // emit signal
  175. paired(is_paired);
  176. }
  177. private void handle_disconnect() {
  178. // channel got disconnected
  179. debug("channel disconnected");
  180. }
  181. }