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.

197 lines
5.1 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. using MConn;
  21. /**
  22. * Device communication channel
  23. *
  24. * Automatically handle channel encoding.
  25. */
  26. class DeviceChannel : Object {
  27. public signal void connected();
  28. public signal void disconnected();
  29. public signal void packet_received(Packet pkt);
  30. private InetSocketAddress _isa = null;
  31. private SocketConnection _conn = null;
  32. private DataOutputStream _dout = null;
  33. private DataInputStream _din = null;
  34. private uint _srcid = 0;
  35. // channel encryption method
  36. private Crypt _crypt = null;
  37. public DeviceChannel(InetAddress host, uint port, Crypt crypt) {
  38. _isa = new InetSocketAddress(host, (uint16) port);
  39. _crypt = crypt;
  40. }
  41. public async void open() {
  42. assert(this._isa != null);
  43. var client = new SocketClient();
  44. try {
  45. _conn = yield client.connect_async(_isa);
  46. } catch (Error e) {
  47. //
  48. critical("failed to connect to %s:%u: %s",
  49. _isa.address.to_string(), _isa.port,
  50. e.message);
  51. return;
  52. // TODO emit disconnected signal?
  53. }
  54. debug("connected to %s:%u", _isa.address.to_string(), _isa.port);
  55. // use data streams
  56. _dout = new DataOutputStream(_conn.output_stream);
  57. _din = new DataInputStream(_conn.input_stream);
  58. // messages end with \n\n
  59. _din.set_newline_type(DataStreamNewlineType.LF);
  60. // setup socket monitoring
  61. Socket sock = _conn.get_socket();
  62. // enable keepalive
  63. sock.set_keepalive(true);
  64. // prep source for monitoring events
  65. SocketSource source = sock.create_source(IOCondition.IN | IOCondition.ERR |
  66. IOCondition.HUP);
  67. source.set_callback((src, cond) => {
  68. this._io_ready();
  69. return true;
  70. });
  71. // attach source
  72. _srcid = source.attach(null);
  73. connected();
  74. }
  75. public async void close() {
  76. debug("closing connection");
  77. if (_srcid > 0) {
  78. Source.remove(_srcid);
  79. _srcid = 0;
  80. }
  81. _din.close();
  82. _dout.close();
  83. _conn.close();
  84. _din = null;
  85. _dout = null;
  86. _conn = null;
  87. }
  88. /**
  89. * send:
  90. * Possibly blocking
  91. *
  92. * @param: instance of Packet
  93. **/
  94. public async void send(Packet pkt) {
  95. string to_send = pkt.to_string() + "\n";
  96. debug("send data: %s", to_send);
  97. // _dout.put_string(data);
  98. try {
  99. _dout.put_string(to_send);
  100. } catch (IOError e) {
  101. critical("failed to send message: %s", e.message);
  102. // TODO disconnect?
  103. }
  104. }
  105. public async void receive() throws Error {
  106. size_t line_len;
  107. // read line up to newline
  108. string data = yield _din.read_upto_async("\n", -1,
  109. Priority.DEFAULT,
  110. null,
  111. out line_len);
  112. debug("received line: %s", data);
  113. // expecting \n\n
  114. _din.read_byte();
  115. _din.read_byte();
  116. Packet pkt = Packet.new_from_data(data);
  117. if (pkt == null) {
  118. critical("failed to build packet from data");
  119. return;
  120. }
  121. handle_packet(pkt);
  122. }
  123. private async void _io_ready() {
  124. debug("check for IO");
  125. try {
  126. debug("try read");
  127. this.receive();
  128. } catch (Error e) {
  129. critical("error occurred: %d: %s", e.code, e.message);
  130. }
  131. }
  132. private void handle_packet(Packet pkt) {
  133. debug("handle packet of type: %s", pkt.pkt_type);
  134. if (pkt.pkt_type == Packet.ENCRYPTED) {
  135. handle_encrypted_packet(pkt);
  136. } else {
  137. // signal that we got a packet
  138. packet_received(pkt);
  139. }
  140. }
  141. private void handle_encrypted_packet(Packet pkt) {
  142. // Ecypted packets have 'data' member in body. The 'data'
  143. // member is an array of strings, each string is base64
  144. // encoded data, of length appropriate for channel ecryption
  145. // method.
  146. Json.Array arr = pkt.body.get_array_member("data");
  147. if (arr == null) {
  148. critical("missing data member in encrypted packet");
  149. return;
  150. }
  151. var msgbytes = new ByteArray();
  152. arr.foreach_element((a, i, node) => {
  153. debug("node data: %s", node.get_string());
  154. // encrypted data is base64 encoded
  155. uchar[] data = Base64.decode(node.get_string());
  156. var dbytes = new Bytes.take(data);
  157. ByteArray decrypted = this._crypt.decrypt(dbytes);
  158. debug("data length: %zu", decrypted.data.length);
  159. msgbytes.append(decrypted.data);
  160. });
  161. // data should be complete now
  162. debug("total length of packet data: %zu", msgbytes.len);
  163. // make sure there is \0 at the end
  164. msgbytes.append({'\0'});
  165. string decrypted_data = ((string)msgbytes.data).dup();
  166. debug("decrypted data: %s", decrypted_data);
  167. Packet dec_pkt = Packet.new_from_data(decrypted_data);
  168. if (dec_pkt == null) {
  169. critical("failed to parse decrypted packet");
  170. } else {
  171. packet_received(dec_pkt);
  172. }
  173. }
  174. }