Browse Source

global: applied uncrustify

bboozzoo/uncrustify
Maciek Borzecki 7 years ago
parent
commit
349750105a
45 changed files with 6029 additions and 4281 deletions
  1. +167
    -158
      src/crypt/certificate.vala
  2. +70
    -70
      src/mconnect/application.vala
  3. +69
    -65
      src/mconnect/battery-proxy.vala
  4. +29
    -30
      src/mconnect/battery.vala
  5. +95
    -94
      src/mconnect/config.vala
  6. +106
    -99
      src/mconnect/core.vala
  7. +260
    -229
      src/mconnect/device-proxy.vala
  8. +594
    -571
      src/mconnect/device.vala
  9. +273
    -272
      src/mconnect/devicechannel.vala
  10. +155
    -153
      src/mconnect/devicemanager-proxy.vala
  11. +242
    -247
      src/mconnect/devicemanager.vala
  12. +63
    -48
      src/mconnect/discovereddevice.vala
  13. +64
    -66
      src/mconnect/discovery.vala
  14. +47
    -45
      src/mconnect/io-job.vala
  15. +11
    -13
      src/mconnect/logging.vala
  16. +7
    -8
      src/mconnect/main.vala
  17. +132
    -134
      src/mconnect/mousepad.vala
  18. +117
    -122
      src/mconnect/notification.vala
  19. +155
    -147
      src/mconnect/packet.vala
  20. +2
    -2
      src/mconnect/packethandlerinterface-proxy.vala
  21. +3
    -4
      src/mconnect/packethandlerinterface.vala
  22. +24
    -24
      src/mconnect/packethandlers-proxy.vala
  23. +48
    -45
      src/mconnect/packethandlers.vala
  24. +28
    -28
      src/mconnect/ping-proxy.vala
  25. +25
    -26
      src/mconnect/ping.vala
  26. +59
    -59
      src/mconnect/property-proxy.vala
  27. +31
    -31
      src/mconnect/share-proxy.vala
  28. +188
    -188
      src/mconnect/share.vala
  29. +18
    -18
      src/mconnect/telephony-proxy.vala
  30. +104
    -106
      src/mconnect/telephony.vala
  31. +166
    -166
      src/mconnect/transfer-download.vala
  32. +6
    -7
      src/mconnect/transfer-interface.vala
  33. +88
    -89
      src/mconnect/transfer-manager-proxy.vala
  34. +30
    -31
      src/mconnect/transfer-manager.vala
  35. +29
    -27
      src/mconnect/transfer-proxy.vala
  36. +165
    -165
      src/mconnect/transfer-upload.vala
  37. +173
    -173
      src/mconnect/utils.vala
  38. +39
    -16
      src/mconnectctl/device-iface.vala
  39. +6
    -6
      src/mconnectctl/device-manager-iface.vala
  40. +351
    -352
      src/mconnectctl/main.vala
  41. +6
    -7
      src/mconnectctl/share-iface.vala
  42. +5
    -6
      src/mconnectctl/telephony-iface.vala
  43. +100
    -98
      test/mconn-crypt-vala-test.vala
  44. +34
    -36
      test/mconn-utils-test.vala
  45. +1645
    -0
      uncrustify.cfg

+ 167
- 158
src/crypt/certificate.vala View File

@ -20,162 +20,171 @@
namespace Mconn {
namespace Crypt {
private GnuTLS.X509.PrivateKey generate_private_key() {
var key = GnuTLS.X509.PrivateKey.create();
key.generate(GnuTLS.PKAlgorithm.RSA, 2048);
// size_t sz = 4096;
// var buf = GnuTLS.malloc(sz);
// key.export_pkcs8(GnuTLS.X509.CertificateFormat.PEM, "",
// GnuTLS.X509.PKCSEncryptFlags.PLAIN,
// buf, ref sz);
// stdout.printf("private key:\n");
// stdout.printf("%s", (string)buf);
// GnuTLS.free(buf);
return key;
}
private struct dn_setting {
string oid;
string name;
}
GnuTLS.X509.Certificate generate_self_signed_cert(GnuTLS.X509.PrivateKey key, string common_name) {
var cert = GnuTLS.X509.Certificate.create();
var start_time = new DateTime.now_local();
var end_time = start_time.add_years(10);
cert.set_key(key);
cert.set_version(1);
cert.set_activation_time((time_t)start_time.to_unix());
cert.set_expiration_time((time_t)end_time.to_unix());
uint32 serial = Posix.htonl(10);
cert.set_serial(&serial, sizeof(uint32));
dn_setting[] dn = {
dn_setting() { oid=GnuTLS.OID.X520_ORGANIZATION_NAME,
name="mconnect"},
dn_setting() { oid=GnuTLS.OID.X520_ORGANIZATIONAL_UNIT_NAME,
name="mconnect"},
dn_setting() { oid=GnuTLS.OID.X520_COMMON_NAME,
name=common_name},
};
foreach (var dn_val in dn) {
var err = cert.set_dn_by_oid(dn_val.oid, 0,
dn_val.name.data, dn_val.name.length);
if (err != GnuTLS.ErrorCode.SUCCESS ) {
warning("set dn failed for OID %s - %s, err: %d\n",
dn_val.oid, dn_val.name, err);
}
}
var err = cert.sign(cert, key);
GLib.assert(err == GnuTLS.ErrorCode.SUCCESS);
// size_t sz = 8192;
// var buf = GnuTLS.malloc(sz);
// err = cert.export(GnuTLS.X509.CertificateFormat.PEM, buf, ref sz);
// if (err != GnuTLS.ErrorCode.SUCCESS) {
// if (err == GnuTLS.ErrorCode.SHORT_MEMORY_BUFFER) {
// stdout.printf("too short\n");
// } else {
// stdout.printf("other error: %d\n", err);
// }
// } else {
// stdout.printf("certificate:\n");
// stdout.printf("size: %zu\n", sz);
// stdout.printf("%s", (string)buf);
// }
// GnuTLS.free(buf);
return cert;
}
private uint8[] export_certificate(GnuTLS.X509.Certificate cert) {
var buf = new uint8[8192];
size_t sz = buf.length;
var err = cert.export(GnuTLS.X509.CertificateFormat.PEM, buf, ref sz);
assert(err == GnuTLS.ErrorCode.SUCCESS);
debug("actual certificate PEM size: %zu", sz);
debug("certificate PEM:\n%s", (string)buf);
// TODO: figure out if this is valid at all
buf.length = (int) sz;
return buf;
}
private uint8[] export_private_key(GnuTLS.X509.PrivateKey key) {
var buf = new uint8[8192];
size_t sz = buf.length;
var err = key.export_pkcs8(GnuTLS.X509.CertificateFormat.PEM, "",
GnuTLS.X509.PKCSEncryptFlags.PLAIN,
buf, ref sz);
assert(err == GnuTLS.ErrorCode.SUCCESS);
debug("actual private key PEM size: %zu", sz);
debug("private key PEM:\n%s", (string)buf);
// TODO: figure out if this is valid at all
buf.length = (int) sz;
return buf;
}
private void export_to_file(string path, uint8[] data) throws Error {
var f = File.new_for_path(path);
f.replace_contents(data, "", false,
FileCreateFlags.PRIVATE | FileCreateFlags.REPLACE_DESTINATION,
null);
}
public void generate_key_cert(string key_path, string cert_path, string name) throws Error {
var key = generate_private_key();
var cert = generate_self_signed_cert(key, name);
export_to_file(cert_path, export_certificate(cert));
export_to_file(key_path, export_private_key(key));
}
private GnuTLS.X509.Certificate cert_from_pem(string certificate_pem) {
var datum = GnuTLS.Datum() { data=certificate_pem.data,
size=certificate_pem.data.length };
var cert = GnuTLS.X509.Certificate.create();
var res = cert.import(ref datum, GnuTLS.X509.CertificateFormat.PEM);
assert(res == GnuTLS.ErrorCode.SUCCESS);
return cert;
}
/**
* fingerprint_certificate:
* Produce a SHA1 fingerprint of the certificate
*
* @param certificate_pem PEM encoded certificate
* @return SHA1 fingerprint as bytes
*/
public uint8[] fingerprint_certificate(string certificate_pem) {
var cert = cert_from_pem(certificate_pem);
// TOOD: make digest configurable, for now assume it's SHA1
var data = new uint8[20];
size_t sz = data.length;
var res = cert.get_fingerprint(GnuTLS.DigestAlgorithm.SHA1,
data, ref sz);
assert(res == GnuTLS.ErrorCode.SUCCESS);
assert(sz == data.length);
return data;
}
}
namespace Crypt {
private GnuTLS.X509.PrivateKey generate_private_key () {
var key = GnuTLS.X509.PrivateKey.create ();
key.generate (GnuTLS.PKAlgorithm.RSA, 2048);
// size_t sz = 4096;
// var buf = GnuTLS.malloc(sz);
// key.export_pkcs8(GnuTLS.X509.CertificateFormat.PEM, "",
// GnuTLS.X509.PKCSEncryptFlags.PLAIN,
// buf, ref sz);
// stdout.printf("private key:\n");
// stdout.printf("%s", (string)buf);
// GnuTLS.free(buf);
return key;
}
private struct dn_setting {
string oid;
string name;
}
GnuTLS.X509.Certificate generate_self_signed_cert (GnuTLS.X509.PrivateKey key,
string common_name) {
var cert = GnuTLS.X509.Certificate.create ();
var start_time = new DateTime.now_local ();
var end_time = start_time.add_years (10);
cert.set_key (key);
cert.set_version (1);
cert.set_activation_time ((time_t) start_time.to_unix ());
cert.set_expiration_time ((time_t) end_time.to_unix ());
uint32 serial = Posix.htonl (10);
cert.set_serial (&serial, sizeof (uint32));
dn_setting[] dn = {
dn_setting () {
oid = GnuTLS.OID.X520_ORGANIZATION_NAME,
name = "mconnect"
},
dn_setting () {
oid = GnuTLS.OID.X520_ORGANIZATIONAL_UNIT_NAME,
name = "mconnect"
},
dn_setting () {
oid = GnuTLS.OID.X520_COMMON_NAME,
name = common_name
},
};
foreach (var dn_val in dn) {
var err = cert.set_dn_by_oid (dn_val.oid, 0,
dn_val.name.data, dn_val.name.length);
if (err != GnuTLS.ErrorCode.SUCCESS) {
warning ("set dn failed for OID %s - %s, err: %d\n",
dn_val.oid, dn_val.name, err);
}
}
var err = cert.sign (cert, key);
GLib.assert (err == GnuTLS.ErrorCode.SUCCESS);
// size_t sz = 8192;
// var buf = GnuTLS.malloc(sz);
// err = cert.export(GnuTLS.X509.CertificateFormat.PEM, buf, ref sz);
// if (err != GnuTLS.ErrorCode.SUCCESS) {
// if (err == GnuTLS.ErrorCode.SHORT_MEMORY_BUFFER) {
// stdout.printf("too short\n");
// } else {
// stdout.printf("other error: %d\n", err);
// }
// } else {
// stdout.printf("certificate:\n");
// stdout.printf("size: %zu\n", sz);
// stdout.printf("%s", (string)buf);
// }
// GnuTLS.free(buf);
return cert;
}
private uint8[] export_certificate (GnuTLS.X509.Certificate cert) {
var buf = new uint8[8192];
size_t sz = buf.length;
var err = cert.export (GnuTLS.X509.CertificateFormat.PEM, buf, ref sz);
assert (err == GnuTLS.ErrorCode.SUCCESS);
debug ("actual certificate PEM size: %zu", sz);
debug ("certificate PEM:\n%s", (string) buf);
// TODO: figure out if this is valid at all
buf.length = (int) sz;
return buf;
}
private uint8[] export_private_key (GnuTLS.X509.PrivateKey key) {
var buf = new uint8[8192];
size_t sz = buf.length;
var err = key.export_pkcs8 (GnuTLS.X509.CertificateFormat.PEM, "",
GnuTLS.X509.PKCSEncryptFlags.PLAIN,
buf, ref sz);
assert (err == GnuTLS.ErrorCode.SUCCESS);
debug ("actual private key PEM size: %zu", sz);
debug ("private key PEM:\n%s", (string) buf);
// TODO: figure out if this is valid at all
buf.length = (int) sz;
return buf;
}
private void export_to_file (string path, uint8[] data) throws Error {
var f = File.new_for_path (path);
f.replace_contents (data, "", false,
FileCreateFlags.PRIVATE | FileCreateFlags.REPLACE_DESTINATION,
null);
}
public void generate_key_cert (string key_path, string cert_path, string name) throws Error {
var key = generate_private_key ();
var cert = generate_self_signed_cert (key, name);
export_to_file (cert_path, export_certificate (cert));
export_to_file (key_path, export_private_key (key));
}
private GnuTLS.X509.Certificate cert_from_pem (string certificate_pem) {
var datum = GnuTLS.Datum () {
data = certificate_pem.data,
size = certificate_pem.data.length
};
var cert = GnuTLS.X509.Certificate.create ();
var res = cert.import (ref datum, GnuTLS.X509.CertificateFormat.PEM);
assert (res == GnuTLS.ErrorCode.SUCCESS);
return cert;
}
/**
* fingerprint_certificate:
* Produce a SHA1 fingerprint of the certificate
*
* @param certificate_pem PEM encoded certificate
* @return SHA1 fingerprint as bytes
*/
public uint8[] fingerprint_certificate (string certificate_pem) {
var cert = cert_from_pem (certificate_pem);
// TOOD: make digest configurable, for now assume it's SHA1
var data = new uint8[20];
size_t sz = data.length;
var res = cert.get_fingerprint (GnuTLS.DigestAlgorithm.SHA1,
data, ref sz);
assert (res == GnuTLS.ErrorCode.SUCCESS);
assert (sz == data.length);
return data;
}
}
}

+ 70
- 70
src/mconnect/application.vala View File

@ -20,98 +20,98 @@
namespace Mconn {
public class Application : GLib.Application {
public class Application : GLib.Application {
private Core core = null;
private Core core = null;
private static bool log_debug = false;
private static bool log_debug_verbose = false;
private static bool log_debug = false;
private static bool log_debug_verbose = false;
private const GLib.OptionEntry[] options = {
{"debug", 'd', 0, OptionArg.NONE, ref log_debug,
"Show debug output", null},
{"verbose-debug", 0, 0, OptionArg.NONE, ref log_debug_verbose,
"Show verbose debug output", null},
{null}
};
private const GLib.OptionEntry[] options = {
{ "debug", 'd', 0, OptionArg.NONE, ref log_debug,
"Show debug output", null },
{ "verbose-debug", 0, 0, OptionArg.NONE, ref log_debug_verbose,
"Show verbose debug output", null },
{ null }
};
private Discovery discovery = null;
private DeviceManager manager = null;
private DeviceManagerDBusProxy bus_manager = null;
private TransferManager transfer = null;
private TransferManagerDBusProxy bus_transfer = null;
private Discovery discovery = null;
private DeviceManager manager = null;
private DeviceManagerDBusProxy bus_manager = null;
private TransferManager transfer = null;
private TransferManagerDBusProxy bus_transfer = null;
public Application() {
Object(application_id: "org.mconnect");
add_main_option_entries(options);
public Application () {
Object (application_id: "org.mconnect");
add_main_option_entries (options);
discovery = new Discovery();
manager = new DeviceManager();
transfer = new TransferManager();
}
discovery = new Discovery ();
manager = new DeviceManager ();
transfer = new TransferManager ();
}
protected override void startup() {
debug("startup");
protected override void startup () {
debug ("startup");
base.startup();
base.startup ();
if (log_debug == true)
Environment.set_variable("G_MESSAGES_DEBUG", "all", false);
if (log_debug == true)
Environment.set_variable ("G_MESSAGES_DEBUG", "all", false);
if (log_debug_verbose == true)
Logging.enable_vdebug();
if (log_debug_verbose == true)
Logging.enable_vdebug ();
core = Core.instance();
if (core == null)
error("cannot initialize core");
core = Core.instance ();
if (core == null)
error ("cannot initialize core");
core.transfer_manager = this.transfer;
core.transfer_manager = this.transfer;
if (core.config.is_debug_on() == true)
Environment.set_variable("G_MESSAGES_DEBUG", "all", false);
if (core.config.is_debug_on () == true)
Environment.set_variable ("G_MESSAGES_DEBUG", "all", false);
Notify.init("mconnect");
Notify.init ("mconnect");
discovery.device_found.connect((disc, discdev) => {
manager.handle_discovered_device(discdev);
});
discovery.device_found.connect ((disc, discdev) => {
manager.handle_discovered_device (discdev);
});
try {
discovery.listen();
} catch (Error e) {
message("failed to setup device listener: %s", e.message);
}
}
try {
discovery.listen ();
} catch (Error e) {
message ("failed to setup device listener: %s", e.message);
}
}
protected override void activate() {
debug("activate");
// reload devices from cache
manager.load_cache();
hold();
}
protected override void activate () {
debug ("activate");
// reload devices from cache
manager.load_cache ();
hold ();
}
public override bool dbus_register(DBusConnection conn,
string object_path) throws Error {
public override bool dbus_register (DBusConnection conn,
string object_path) throws Error {
this.bus_manager = new DeviceManagerDBusProxy.with_manager(conn,
this.manager);
this.bus_manager.publish();
this.bus_manager = new DeviceManagerDBusProxy.with_manager (conn,
this.manager);
this.bus_manager.publish ();
this.bus_transfer = new TransferManagerDBusProxy.with_manager(conn,
this.transfer);
this.bus_transfer.publish();
this.bus_transfer = new TransferManagerDBusProxy.with_manager (conn,
this.transfer);
this.bus_transfer.publish ();
base.dbus_register(conn, object_path);
debug("dbus register, path %s", object_path);
base.dbus_register (conn, object_path);
debug ("dbus register, path %s", object_path);
return true;
}
return true;
}
public override void dbus_unregister(DBusConnection conn,
string object_path) {
public override void dbus_unregister (DBusConnection conn,
string object_path) {
base.dbus_unregister(conn, object_path);
debug("dbus unregister, path %s", object_path);
}
}
base.dbus_unregister (conn, object_path);
debug ("dbus unregister, path %s", object_path);
}
}
}

+ 69
- 65
src/mconnect/battery-proxy.vala View File

@ -21,69 +21,73 @@
[DBus (name = "org.mconnect.Device.Battery")]
class BatteryHandlerProxy : Object, PacketHandlerInterfaceProxy {
private Device device = null;
private BatteryHandler battery_handler = null;
private uint register_id = 0;
private ulong notify_id = 0;
private DBusPropertyNotifier prop_notifier = null;
public uint level { get; private set; default = 0; }
public bool charging { get; private set; default = false; }
public BatteryHandlerProxy.for_device_handler(Device dev,
PacketHandlerInterface iface) {
this.device = dev;
this.battery_handler = (BatteryHandler) iface;
this.battery_handler.battery.connect(this.battery_change);
}
private void battery_change(Device dev, uint level, bool charging) {
if (this.device != dev)
return;
this.level = level;
this.charging = charging;
}
[DBus (visible = false)]
public void bus_register(DBusConnection conn, string path) throws IOError {
if (this.register_id == 0)
this.register_id = conn.register_object(path, this);
this.prop_notifier = new DBusPropertyNotifier(conn,
"org.mconnect.Device.Battery",
path);
this.notify.connect(this.send_property_change);
}
[DBus (visible = false)]
public void bus_unregister(DBusConnection conn) throws IOError {
if (this.register_id != 0)
conn.unregister_object(this.register_id);
this.register_id = 0;
this.notify.disconnect(this.send_property_change);
this.notify_id = 0;
}
private void send_property_change(ParamSpec p) {
assert(this.prop_notifier != null);
Variant v = null;
if (p.name == "level") {
v = this.level;
}
if (p.name == "charging") {
v = this.charging;
}
if (v == null)
return;
this.prop_notifier.queue_property_change(p.name, v);
}
private Device device = null;
private BatteryHandler battery_handler = null;
private uint register_id = 0;
private ulong notify_id = 0;
private DBusPropertyNotifier prop_notifier = null;
public uint level {
get; private set; default = 0;
}
public bool charging {
get; private set; default = false;
}
public BatteryHandlerProxy.for_device_handler (Device dev,
PacketHandlerInterface iface) {
this.device = dev;
this.battery_handler = (BatteryHandler) iface;
this.battery_handler.battery.connect (this.battery_change);
}
private void battery_change (Device dev, uint level, bool charging) {
if (this.device != dev)
return;
this.level = level;
this.charging = charging;
}
[DBus (visible = false)]
public void bus_register (DBusConnection conn, string path) throws IOError {
if (this.register_id == 0)
this.register_id = conn.register_object (path, this);
this.prop_notifier = new DBusPropertyNotifier (conn,
"org.mconnect.Device.Battery",
path);
this.notify.connect (this.send_property_change);
}
[DBus (visible = false)]
public void bus_unregister (DBusConnection conn) throws IOError {
if (this.register_id != 0)
conn.unregister_object (this.register_id);
this.register_id = 0;
this.notify.disconnect (this.send_property_change);
this.notify_id = 0;
}
private void send_property_change (ParamSpec p) {
assert (this.prop_notifier != null);
Variant v = null;
if (p.name == "level") {
v = this.level;
}
if (p.name == "charging") {
v = this.charging;
}
if (v == null)
return;
this.prop_notifier.queue_property_change (p.name, v);
}
}

+ 29
- 30
src/mconnect/battery.vala View File

@ -20,44 +20,43 @@
class BatteryHandler : Object, PacketHandlerInterface {
public const string BATTERY = "kdeconnect.battery";
public const string BATTERY = "kdeconnect.battery";
public string get_pkt_type() {
return BATTERY;
}
public string get_pkt_type () {
return BATTERY;
}
private BatteryHandler() {
private BatteryHandler () {
}
}
public static BatteryHandler instance () {
return new BatteryHandler ();
}
public static BatteryHandler instance() {
return new BatteryHandler();
}
public void use_device (Device dev) {
debug ("use device %s for battery status updates", dev.to_string ());
dev.message.connect (this.message);
}
public void use_device(Device dev) {
debug("use device %s for battery status updates", dev.to_string());
dev.message.connect(this.message);
}
public void release_device (Device dev) {
debug ("release device %s", dev.to_string ());
dev.message.disconnect (this.message);
}
public void release_device(Device dev) {
debug("release device %s", dev.to_string());
dev.message.disconnect(this.message);
}
public void message (Device dev, Packet pkt) {
if (pkt.pkt_type != BATTERY) {
return;
}
public void message(Device dev, Packet pkt) {
if (pkt.pkt_type != BATTERY) {
return;
}
debug ("got battery packet");
debug("got battery packet");
int64 level = pkt.body.get_int_member ("currentCharge");
bool charging = pkt.body.get_boolean_member ("isCharging");
int64 level = pkt.body.get_int_member("currentCharge");
bool charging = pkt.body.get_boolean_member("isCharging");
debug ("battery level: %u %s", (uint) level,
(charging == true) ? "charging" : "");
battery (dev, (uint) level, charging);
}
debug("battery level: %u %s", (uint) level,
(charging == true) ? "charging" : "");
battery(dev, (uint)level, charging);
}
public signal void battery(Device dev, uint level, bool charging);
public signal void battery (Device dev, uint level, bool charging);
}

+ 95
- 94
src/mconnect/config.vala View File

@ -19,98 +19,99 @@
*/
public class Config : Object {
public const string FILE = "mconnect.conf";
private KeyFile _kf = null;
public string path { get; private set; default = null; }
public static string[] config_search_dirs(string primary_dir) {
string[] dirs = {primary_dir};
string[] sysdirs = Environment.get_system_data_dirs();
foreach (string d in sysdirs) {
dirs += Path.build_path(Path.DIR_SEPARATOR_S,
d, "mconnect");
}
return dirs;
}
public Config(string base_config_dir) {
_kf = new KeyFile();
string[] dirs = config_search_dirs(base_config_dir);
string full_path = null;
foreach (string d in dirs) {
debug("config search dir: %s", d);
}
try {
bool found = _kf.load_from_dirs(Config.FILE, dirs,
out full_path,
KeyFileFlags.KEEP_COMMENTS);
path = full_path;
if (found == false) {
critical("configuration file %s was not found",
Config.FILE);
}
message("loaded configuration from %s", full_path);
} catch (KeyFileError ke) {
critical("failed to parse configuration file: %s", ke.message);
} catch (FileError fe) {
critical("failed to read configuration file: %s", fe.message);
}
}
public void dump_to_file(string path) {
if (_kf == null)
return;
string data = _kf.to_data();
try {
FileUtils.set_contents(path, data);
} catch (FileError e) {
critical("failed to save configuration to %s: %s",
path, e.message);
}
}
public bool is_device_allowed(string name, string type) {
debug("check if device %s type %s is allowed", name, type);
try {
string[] devices = _kf.get_string_list("main", "devices");
foreach (string dev in devices) {
debug("checking dev %s", dev);
//
if (_kf.has_group(dev) == false) {
debug("no group %s", dev);
continue;
}
if (_kf.get_string(dev, "name") == name &&
_kf.get_string(dev, "type") == type &&
_kf.get_boolean(dev, "allowed") == true)
{
return true;
}
}
} catch (KeyFileError ke) {
critical("failed to read entries from configuration file: %s",
ke.message);
}
return false;
}
public bool is_debug_on() {
try {
bool debug = _kf.get_boolean("main", "debug");
return debug;
} catch (KeyFileError ke) {
critical("failed to read config entry");
}
return false;
}
public const string FILE = "mconnect.conf";
private KeyFile _kf = null;
public string path {
get; private set; default = null;
}
public static string[] config_search_dirs (string primary_dir) {
string[] dirs = { primary_dir };
string[] sysdirs = Environment.get_system_data_dirs ();
foreach (string d in sysdirs) {
dirs += Path.build_path (Path.DIR_SEPARATOR_S,
d, "mconnect");
}
return dirs;
}
public Config (string base_config_dir) {
_kf = new KeyFile ();
string[] dirs = config_search_dirs (base_config_dir);
string full_path = null;
foreach (string d in dirs) {
debug ("config search dir: %s", d);
}
try {
bool found = _kf.load_from_dirs (Config.FILE, dirs,
out full_path,
KeyFileFlags.KEEP_COMMENTS);
path = full_path;
if (found == false) {
critical ("configuration file %s was not found",
Config.FILE);
}
message ("loaded configuration from %s", full_path);
} catch (KeyFileError ke) {
critical ("failed to parse configuration file: %s", ke.message);
} catch (FileError fe) {
critical ("failed to read configuration file: %s", fe.message);
}
}
public void dump_to_file (string path) {
if (_kf == null)
return;
string data = _kf.to_data ();
try {
FileUtils.set_contents (path, data);
} catch (FileError e) {
critical ("failed to save configuration to %s: %s",
path, e.message);
}
}
public bool is_device_allowed (string name, string type) {
debug ("check if device %s type %s is allowed", name, type);
try {
string[] devices = _kf.get_string_list ("main", "devices");
foreach (string dev in devices) {
debug ("checking dev %s", dev);
//
if (_kf.has_group (dev) == false) {
debug ("no group %s", dev);
continue;
}
if (_kf.get_string (dev, "name") == name &&
_kf.get_string (dev, "type") == type &&
_kf.get_boolean (dev, "allowed") == true) {
return true;
}
}
} catch (KeyFileError ke) {
critical ("failed to read entries from configuration file: %s",
ke.message);
}
return false;
}
public bool is_debug_on () {
try {
bool debug = _kf.get_boolean ("main", "debug");
return debug;
} catch (KeyFileError ke) {
critical ("failed to read config entry");
}
return false;
}
}

+ 106
- 99
src/mconnect/core.vala View File

@ -21,103 +21,110 @@ using Mconn;
class Core : Object {
public const string APP_NAME = "mconnect";
public TlsCertificate certificate { get; private set; }
public PacketHandlers handlers {get; private set; default = null; }
public Config config { get; private set; default = null; }
public TransferManager transfer_manager { get; set; default = null; }
private static Core _instance = null;
private Core() {
debug("init core");
}
public static Core? instance() {
if (Core._instance == null)
{
init_user_dirs();
var config = init_config();
var cert = init_crypto();
var handlers = new PacketHandlers();
var core = new Core();
core.config = config;
core.certificate = cert;
core.handlers = handlers;
info("supported interfaces: %s",
string.joinv(", ", handlers.interfaces));
Core._instance = core;
}
return Core._instance;
}
public static string get_storage_dir() {
return Path.build_filename(Environment.get_user_data_dir(),
APP_NAME);
}
public static string get_config_dir() {
return Path.build_filename(Environment.get_user_config_dir(),
APP_NAME);
}
public static string get_cache_dir() {
return Path.build_filename(Environment.get_user_cache_dir(),
APP_NAME);
}
private static void init_user_dirs() {
DirUtils.create_with_parents(get_storage_dir(), 0700);
DirUtils.create_with_parents(get_config_dir(), 0700);
}
private static TlsCertificate init_crypto() throws Error {
var key_file = File.new_for_path(Path.build_filename(get_storage_dir(),
"private.pem"));
var cert_file = File.new_for_path(Path.build_filename(get_storage_dir(),
"certificate.pem"));
if (key_file.query_exists() == false || cert_file.query_exists() == false) {
try {
string host_name = Environment.get_host_name();
string user = Environment.get_user_name();
Crypt.generate_key_cert(key_file.get_path(),
cert_file.get_path(),
@"$user@$host_name");
} catch (Error e) {
warning("failed to generate private key or certificate: %s", e.message);
throw e;
}
}
TlsCertificate tls_cert;
try {
tls_cert = new TlsCertificate.from_files(cert_file.get_path(),
key_file.get_path());
} catch (Error e) {
warning("failed to load certificate or key: %s", e.message);
throw e;
}
return tls_cert;
}
private static Config init_config() {
string user_config_path = get_config_dir() + "/" + Config.FILE;
var config = new Config(get_config_dir());
// write configuration to user config file if none is present
if (config.path != user_config_path) {
config.dump_to_file(user_config_path);
}
return config;
}
public const string APP_NAME = "mconnect";
public TlsCertificate certificate {
get; private set;
}
public PacketHandlers handlers {
get; private set; default = null;
}
public Config config {
get; private set; default = null;
}
public TransferManager transfer_manager {
get; set; default = null;
}
private static Core _instance = null;
private Core () {
debug ("init core");
}
public static Core ? instance () {
if (Core._instance == null) {
init_user_dirs ();
var config = init_config ();
var cert = init_crypto ();
var handlers = new PacketHandlers ();
var core = new Core ();
core.config = config;
core.certificate = cert;
core.handlers = handlers;
info ("supported interfaces: %s",
string.joinv (", ", handlers.interfaces));
Core._instance = core;
}
return Core._instance;
}
public static string get_storage_dir () {
return Path.build_filename (Environment.get_user_data_dir (),
APP_NAME);
}
public static string get_config_dir () {
return Path.build_filename (Environment.get_user_config_dir (),
APP_NAME);
}
public static string get_cache_dir () {
return Path.build_filename (Environment.get_user_cache_dir (),
APP_NAME);
}
private static void init_user_dirs () {
DirUtils.create_with_parents (get_storage_dir (), 0700);
DirUtils.create_with_parents (get_config_dir (), 0700);
}
private static TlsCertificate init_crypto () throws Error {
var key_file = File.new_for_path (Path.build_filename (get_storage_dir (),
"private.pem"));
var cert_file = File.new_for_path (Path.build_filename (get_storage_dir (),
"certificate.pem"));
if (key_file.query_exists () == false || cert_file.query_exists () == false) {
try {
string host_name = Environment.get_host_name ();
string user = Environment.get_user_name ();
Crypt.generate_key_cert (key_file.get_path (),
cert_file.get_path (),
@"$user@$host_name");
} catch (Error e) {
warning ("failed to generate private key or certificate: %s", e.message);
throw e;
}
}
TlsCertificate tls_cert;
try {
tls_cert = new TlsCertificate.from_files (cert_file.get_path (),
key_file.get_path ());
} catch (Error e) {
warning ("failed to load certificate or key: %s", e.message);
throw e;
}
return tls_cert;
}
private static Config init_config () {
string user_config_path = get_config_dir () + "/" + Config.FILE;
var config = new Config (get_config_dir ());
// write configuration to user config file if none is present
if (config.path != user_config_path) {
config.dump_to_file (user_config_path);
}
return config;
}
}

+ 260
- 229
src/mconnect/device-proxy.vala View File

@ -26,233 +26,264 @@ using Gee;
[DBus (name = "org.mconnect.Device")]
class DeviceDBusProxy : Object {
public string id {
get { return device.device_id; }
private set {}
default = "";
}
public string name {
get { return device.device_name; }
private set {}
default = "";
}
public string device_type {
get { return device.device_type; }
private set {}
default = "";
}
public uint protocol_version {
get { return device.protocol_version; }
private set {}
default = 5;
}
public string address { get; private set; default = ""; }
public bool is_paired {
get { return device.is_paired; }
private set {}
default = false;
}
public bool allowed {
get { return device.allowed; }
private set {}
default = false;
}
public bool is_active {
get { return device.is_active; }
private set {}
default = false;
}
public bool is_connected { get; private set; default = false; }
public string[] incoming_capabilities {
get;
private set;
}
public string[] outgoing_capabilities {
get;
private set;
}
public string certificate {
owned get { return device.certificate_pem; }
private set {}
}
public string certificate_fingerprint {
get { return device.certificate_fingerprint; }
private set {}
}
private HashMap<string,PacketHandlerInterfaceProxy> handlers;
private uint register_id = 0;
private DBusPropertyNotifier prop_notifier = null;
[DBus (visible = false)]
public ObjectPath object_path = null;
[DBus (visible = false)]
public Device device {get; private set; default = null; }
public DeviceDBusProxy.for_device_with_path(Device device, ObjectPath path) {
this.device = device;
this.object_path = path;
this.handlers = new HashMap<string, PacketHandlerInterfaceProxy>();
this.update_address();
this.update_capabilities();
this.device.notify.connect(this.param_changed);
this.device.connected.connect(() => {
this.is_connected = true;
});
this.device.disconnected.connect(() => {
this.is_connected = false;
});
this.notify.connect(this.update_properties);
}
private void update_capabilities() {
string[] caps = {};
foreach (var cap in device.incoming_capabilities) {
caps += cap;
}
this.incoming_capabilities = caps;
caps = {};
foreach (var cap in device.outgoing_capabilities) {
caps += cap;
}
this.outgoing_capabilities = caps;
}
private void update_address() {
this.address = "%s:%u".printf(device.host.to_string(),
device.tcp_port);
}
private void update_properties(ParamSpec param) {
debug("param %s changed", param.name);
string name = param.name;
Variant v = null;
switch (param.name) {
case "address":
v = this.address;
break;
case "id":
v = this.id;
break;
case "name":
v = this.name;
break;
case "device-type":
name = "DeviceType";
v = this.device_type;
break;
case "potocol-version":
name = "ProtocolVersion";
v = this.protocol_version;
break;
case "is-paired":
name = "IsPaired";
v = this.is_paired;
break;
case "allowed":
v = this.allowed;
break;
case "is-active":
name = "IsActive";
v = this.is_active;
break;
case "is-connected":
name = "IsConnected";
v = this.is_connected;
break;
case "certificate":
name = "certificate";
v = this.certificate;
break;
}
if (v == null)
return;
this.prop_notifier.queue_property_change(name, v);
}
private void param_changed(ParamSpec param) {
debug("parameter %s changed", param.name);
switch (param.name) {
case "host":
case "tcp-port":
this.update_address();
break;
case "allowed":
this.allowed = device.allowed;
break;
case "is-active":
this.is_active = device.is_active;
break;
case "is-paired":
this.is_paired = device.is_paired;
break;
case "incoming-capabilities":
case "outgoing-capabilities":
this.update_capabilities();
break;
}
}
[DBus (visible = false)]
public bool has_handler(string cap) {
return this.handlers.has_key(cap);
}
[DBus (visible = false)]
public void bus_register(DBusConnection conn) {
try {
this.register_id = conn.register_object(this.object_path, this);
this.prop_notifier = new DBusPropertyNotifier(conn,
"org.mconnect.Device",
this.object_path);
} catch (IOError err) {
warning("failed to register DBus object for device %s under path %s",
this.device.to_string(), this.object_path.to_string());
}
}
[DBus (visible = false)]
public void bus_unregister(DBusConnection conn) {
if (this.register_id != 0) {
conn.unregister_object(this.register_id);
}
this.register_id = 0;
this.prop_notifier = null;
}
[DBus (visible = false)]
public void bus_register_handler(DBusConnection conn,
string cap,
PacketHandlerInterfaceProxy handler) {
handler.bus_register(conn, this.object_path);
this.handlers.@set(cap, handler);
}
[DBus (visible = false)]
public void bus_unregister_handler(DBusConnection conn,
string cap) {
PacketHandlerInterfaceProxy handler;
this.handlers.@unset(cap, out handler);
if (handler != null) {
handler.bus_unregister(conn);
}
}
public string id {
get {
return device.device_id;
}
private set {
}
default = "";
}
public string name {
get {
return device.device_name;
}
private set {
}
default = "";
}
public string device_type {
get {
return device.device_type;
}
private set {
}
default = "";
}
public uint protocol_version {
get {
return device.protocol_version;
}
private set {
}
default = 5;
}
public string address {
get; private set; default = "";
}
public bool is_paired {
get {
return device.is_paired;
}
private set {
}
default = false;
}
public bool allowed {
get {
return device.allowed;
}
private set {
}
default = false;
}
public bool is_active {
get {
return device.is_active;
}
private set {
}
default = false;
}
public bool is_connected {
get; private set; default = false;
}
public string[] incoming_capabilities {
get;
private set;
}
public string[] outgoing_capabilities {
get;
private set;
}
public string certificate {
owned get {
return device.certificate_pem;
}
private set {
}
}
public string certificate_fingerprint {
get {
return device.certificate_fingerprint;
}
private set {
}
}
private HashMap<string, PacketHandlerInterfaceProxy> handlers;
private uint register_id = 0;
private DBusPropertyNotifier prop_notifier = null;
[DBus (visible = false)]
public ObjectPath object_path = null;
[DBus (visible = false)]
public Device device {
get; private set; default = null;
}
public DeviceDBusProxy.for_device_with_path (Device device, ObjectPath path) {
this.device = device;
this.object_path = path;
this.handlers = new HashMap<string, PacketHandlerInterfaceProxy>();
this.update_address ();
this.update_capabilities ();
this.device.notify.connect (this.param_changed);
this.device.connected.connect (() => {
this.is_connected = true;
});
this.device.disconnected.connect (() => {
this.is_connected = false;
});
this.notify.connect (this.update_properties);
}
private void update_capabilities () {
string[] caps = {};
foreach (var cap in device.incoming_capabilities) {
caps += cap;
}
this.incoming_capabilities = caps;
caps = {};
foreach (var cap in device.outgoing_capabilities) {
caps += cap;
}
this.outgoing_capabilities = caps;
}
private void update_address () {
this.address = "%s:%u".printf (device.host.to_string (),
device.tcp_port);
}
private void update_properties (ParamSpec param) {
debug ("param %s changed", param.name);
string name = param.name;
Variant v = null;
switch (param.name) {
case "address":
v = this.address;
break;
case "id":
v = this.id;
break;
case "name":
v = this.name;
break;
case "device-type":
name = "DeviceType";
v = this.device_type;
break;
case "potocol-version":
name = "ProtocolVersion";
v = this.protocol_version;
break;
case "is-paired":
name = "IsPaired";
v = this.is_paired;
break;
case "allowed":
v = this.allowed;
break;
case "is-active":
name = "IsActive";
v = this.is_active;
break;
case "is-connected":
name = "IsConnected";
v = this.is_connected;
break;
case "certificate":
name = "certificate";
v = this.certificate;
break;
}
if (v == null)
return;
this.prop_notifier.queue_property_change (name, v);
}
private void param_changed (ParamSpec param) {
debug ("parameter %s changed", param.name);
switch (param.name) {
case "host":
case "tcp-port":
this.update_address ();
break;
case "allowed":
this.allowed = device.allowed;
break;
case "is-active":
this.is_active = device.is_active;
break;
case "is-paired":
this.is_paired = device.is_paired;
break;
case "incoming-capabilities":
case "outgoing-capabilities":
this.update_capabilities ();
break;
}
}
[DBus (visible = false)]
public bool has_handler (string cap) {
return this.handlers.has_key (cap);
}
[DBus (visible = false)]
public void bus_register (DBusConnection conn) {
try {
this.register_id = conn.register_object (this.object_path, this);
this.prop_notifier = new DBusPropertyNotifier (conn,
"org.mconnect.Device",
this.object_path);
} catch (IOError err) {
warning ("failed to register DBus object for device %s under path %s",
this.device.to_string (), this.object_path.to_string ());
}
}
[DBus (visible = false)]
public void bus_unregister (DBusConnection conn) {
if (this.register_id != 0) {
conn.unregister_object (this.register_id);
}
this.register_id = 0;
this.prop_notifier = null;
}
[DBus (visible = false)]
public void bus_register_handler (DBusConnection conn,
string cap,
PacketHandlerInterfaceProxy handler) {
handler.bus_register (conn, this.object_path);
this.handlers.@set (cap, handler);
}
[DBus (visible = false)]
public void bus_unregister_handler (DBusConnection conn,
string cap) {
PacketHandlerInterfaceProxy handler;
this.handlers.@unset (cap, out handler);
if (handler != null) {
handler.bus_unregister (conn);
}
}
}

+ 594
- 571
src/mconnect/device.vala
File diff suppressed because it is too large
View File


+ 273
- 272
src/mconnect/devicechannel.vala View File

@ -27,276 +27,277 @@ using Mconn;
*/
class DeviceChannel : Object {
public signal void disconnected();
public signal void packet_received(Packet pkt);
private InetSocketAddress _isa = null;
private SocketConnection _sock_conn = null;
private TlsConnection _tls_conn = null;
private DataOutputStream _dout = null;
private DataInputStream _din = null;
private uint _srcid = 0;
private Socket _socket = null;
public TlsCertificate peer_certificate = null;
public DeviceChannel(InetAddress host, uint port) {
this._isa = new InetSocketAddress(host, (uint16) port);
}
~DeviceChannel() {
debug("channel destroyed");
}
private static void fixup_socket(Socket sock) {
Utils.socket_set_keepalive(sock);
}
private void replace_streams(InputStream input, OutputStream output) {
if (this._dout != null) {
try {
this._dout.close();
} catch (Error e) {
warning("failed to close output stream: %s", e.message);
}
}
this._dout = new DataOutputStream(output);
if (this._din != null) {
try {
this._din.close();
} catch (Error e) {
warning("failed to close input stream: %s", e.message);
}
}
this._din = new DataInputStream(input);
// messages end with \n\n
this._din.set_newline_type(DataStreamNewlineType.LF);
}
private void monitor_events() {
var source = _socket.create_source(IOCondition.IN);
source.set_callback((src, cond) => {
return this._io_ready(cond);
});
// attach source
this._srcid = source.attach(null);
}
private void unmonitor_events() {
if (this._srcid > 0) {
Source.remove(_srcid);
this._srcid = 0;
}
}
public async bool open() {
GLib.assert(this._isa != null);
debug("connect to %s:%u", this._isa.address.to_string(), this._isa.port);
var client = new SocketClient();
SocketConnection conn;
try {
conn = yield client.connect_async(_isa);
} catch (Error e) {
//
warning("failed to connect to %s:%u: %s",
this._isa.address.to_string(), this._isa.port,
e.message);
// emit disconnected
return false;
}
debug("connected to %s:%u", this._isa.address.to_string(), this._isa.port);
this._socket = conn.get_socket();
// fixup socket keepalive
fixup_socket(_socket);
this._sock_conn = conn;
// input/output streams will close underlying base stream when .close()
// is called on them, make sure that we pass Unix*Stream with which can
// skip closing the socket
this.replace_streams(new UnixInputStream(_socket.fd, false),
new UnixOutputStream(_socket.fd, false));
// start monitoring socket events
this.monitor_events();
return true;
}
/**
* secure:
* Switch channel to TLS mode
*
* When TLS was established, `peer_certificate` will store the remote client
* certificate. If `expected_peer` is null, the peer certificate will be
* accepted unconditionally during handshake and the caller must eventually
* decide if the client is to be trusted or not. However, if `expected_peer`
* was set, the received certificate and expected one will be compared
* during handshake and connection will be rejected if a mismatch is found.
*
* @param expected_peer the peer certificate we are expecting to see
* @return true if TLS negotiation was successful, false otherwise
*/
public async bool secure(TlsCertificate? expected_peer = null) {
GLib.assert(this._sock_conn != null);
// stop monitoring socket events
this.unmonitor_events();
var cert = Core.instance().certificate;
// wrap with TLS
var tls_conn = Utils.make_tls_connection(this._sock_conn,
cert,
expected_peer);
try {
info("attempt TLS handshake");
var res = yield tls_conn.handshake_async();
if (res) {
info("TLS handshare successful");
this.peer_certificate = tls_conn.peer_certificate;
} else {
warning("TLS handshake unsuccessful");
return false;
}
} catch (Error e) {
warning("TLS handshake failed: %s", e.message);
return false;
}
this._tls_conn = tls_conn;
// data will now pass through TLS stream wrapper
this.replace_streams(_tls_conn.input_stream,
_tls_conn.output_stream);
// monitor socket events
this.monitor_events();
return true;
}
public void close() {
debug("closing connection");
this.unmonitor_events();
try {
if (this._din != null)
this._din.close();
} catch (Error e) {
warning("failed to close data input: %s", e.message);
}
try {
if (this._dout != null)
this._dout.close();
} catch (Error e) {
warning("failed to close data output: %s", e.message);
}
try {
if (this._tls_conn != null)
this._tls_conn.close();
} catch (Error e) {
warning("failed to close TLS connection: %s", e.message);
}
try {
if (this._sock_conn != null)
this._sock_conn.close();
} catch (Error e) {
warning("failed to close connection: %s", e.message);
}
this._din = null;
this._dout = null;
this._sock_conn = null;
this._tls_conn = null;
this._socket = null;
this.peer_certificate = null;
}
/**
* send:
* Possibly blocking
*
* @param: instance of Packet
**/
public async void send(Packet pkt) {
string to_send = pkt.to_string() + "\n";
debug("send data: %s", to_send);
GLib.assert(this._dout != null);
try {
this._dout.put_string(to_send);
} catch (IOError e) {
warning("failed to send message: %s", e.message);
// TODO disconnect?
}
}
/**
* receive:
* Try to receive some data from channel
*
* @return false if channel was closed, true otherwise
*/
public bool receive() {
size_t line_len;
string data = null;
GLib.assert(this._din != null);
try {
// read line up to a newline
data = this._din.read_upto("\n", -1, out line_len, null);
// expecting \n
this._din.read_byte();
} catch (IOError ie) {
warning("I/O error: %s", ie.message);
}
if (data == null) {
debug("connection closed?");
return false;
}
vdebug("received line: %s", data);
Packet pkt = Packet.new_from_data(data);
if (pkt == null) {
warning("failed to build packet from data");
// data was received, hence connection is still alive
return true;
}
this.handle_packet(pkt);
return true;
}
private bool _io_ready(uint flags) {
debug("check for IO, conditions: 0x%x", flags);
bool res = this.receive();
if (res == false) {
// disconnected
this.disconnected();
}
return res;
}
private void handle_packet(Packet pkt) {
// debug("handle packet of type: %s", pkt.pkt_type);
if (pkt.pkt_type == Packet.ENCRYPTED) {
warning("received packet with eplicit encryption, this usually indicates a protocol version < 6 type packet, such pacckets are no longer supported, dropping..");
} else {
// signal that we got a packet
this.packet_received(pkt);
}
}
public signal void disconnected ();
public signal void packet_received (Packet pkt);
private InetSocketAddress _isa = null;
private SocketConnection _sock_conn = null;
private TlsConnection _tls_conn = null;
private DataOutputStream _dout = null;
private DataInputStream _din = null;
private uint _srcid = 0;
private Socket _socket = null;
public TlsCertificate peer_certificate = null;
public DeviceChannel (InetAddress host, uint port) {
this._isa = new InetSocketAddress (host, (uint16) port);
}
~DeviceChannel () {
debug ("channel destroyed");
}
private static void fixup_socket (Socket sock) {
Utils.socket_set_keepalive (sock);
}
private void replace_streams (InputStream input, OutputStream output) {
if (this._dout != null) {
try {
this._dout.close ();
} catch (Error e) {
warning ("failed to close output stream: %s", e.message);
}
}
this._dout = new DataOutputStream (output);
if (this._din != null) {
try {
this._din.close ();
} catch (Error e) {
warning ("failed to close input stream: %s", e.message);
}
}
this._din = new DataInputStream (input);
// messages end with \n\n
this._din.set_newline_type (DataStreamNewlineType.LF);
}
private void monitor_events () {
var source = _socket.create_source (IOCondition.IN);
source.set_callback ((src, cond) => {
return this._io_ready (cond);
});
// attach source
this._srcid = source.attach (null);
}
private void unmonitor_events () {
if (this._srcid > 0) {
Source.remove (_srcid);
this._srcid = 0;
}
}
public async bool open () {
GLib.assert (this._isa != null);
debug ("connect to %s:%u", this._isa.address.to_string (), this._isa.port);
var client = new SocketClient ();
SocketConnection conn;
try {
conn = yield client.connect_async (_isa);
} catch (Error e) {
//
warning ("failed to connect to %s:%u: %s",
this._isa.address.to_string (), this._isa.port,
e.message);
// emit disconnected
return false;
}
debug ("connected to %s:%u", this._isa.address.to_string (), this._isa.port);
this._socket = conn.get_socket ();
// fixup socket keepalive
fixup_socket (_socket);
this._sock_conn = conn;
// input/output streams will close underlying base stream when .close()
// is called on them, make sure that we pass Unix*Stream with which can
// skip closing the socket
this.replace_streams (new UnixInputStream (_socket.fd, false),
new UnixOutputStream (_socket.fd, false));
// start monitoring socket events
this.monitor_events ();
return true;
}
/**
* secure:
* Switch channel to TLS mode
*
* When TLS was established, `peer_certificate` will store the remote client
* certificate. If `expected_peer` is null, the peer certificate will be
* accepted unconditionally during handshake and the caller must eventually
* decide if the client is to be trusted or not. However, if `expected_peer`
* was set, the received certificate and expected one will be compared
* during handshake and connection will be rejected if a mismatch is found.
*
* @param expected_peer the peer certificate we are expecting to see
* @return true if TLS negotiation was successful, false otherwise
*/
public async bool secure (TlsCertificate ? expected_peer = null) {
GLib.assert (this._sock_conn != null);
// stop monitoring socket events
this.unmonitor_events ();
var cert = Core.instance ().certificate;
// wrap with TLS
var tls_conn = Utils.make_tls_connection (this._sock_conn,
cert,
expected_peer);
try {
info ("attempt TLS handshake");
var res = yield tls_conn.handshake_async ();
if (res) {
info ("TLS handshare successful");
this.peer_certificate = tls_conn.peer_certificate;
} else {
warning ("TLS handshake unsuccessful");
return false;
}
} catch (Error e) {
warning ("TLS handshake failed: %s", e.message);
return false;
}
this._tls_conn = tls_conn;
// data will now pass through TLS stream wrapper
this.replace_streams (_tls_conn.input_stream,
_tls_conn.output_stream);
// monitor socket events
this.monitor_events ();
return true;
}
public void close () {
debug ("closing connection");
this.unmonitor_events ();
try {
if (this._din != null)
this._din.close ();
} catch (Error e) {
warning ("failed to close data input: %s", e.message);
}
try {
if (this._dout != null)
this._dout.close ();
} catch (Error e) {
warning ("failed to close data output: %s", e.message);
}
try {
if (this._tls_conn != null)
this._tls_conn.close ();
} catch (Error e) {
warning ("failed to close TLS connection: %s", e.message);
}
try {
if (this._sock_conn != null)
this._sock_conn.close ();
} catch (Error e) {
warning ("failed to close connection: %s", e.message);
}
this._din = null;
this._dout = null;
this._sock_conn = null;
this._tls_conn = null;
this._socket = null;
this.peer_certificate = null;
}
/**
* send:
* Possibly blocking
*
* @param: instance of Packet
**/
public async void send (Packet pkt) {
string to_send = pkt.to_string () + "\n";
debug ("send data: %s", to_send);
GLib.assert (this._dout != null);
try {
this._dout.put_string (to_send);
} catch (IOError e) {
warning ("failed to send message: %s", e.message);
// TODO disconnect?
}
}
/**
* receive:
* Try to receive some data from channel
*
* @return false if channel was closed, true otherwise
*/
public bool receive () {
size_t line_len;
string data = null;
GLib.assert (this._din != null);
try {
// read line up to a newline
data = this._din.read_upto ("\n", -1, out line_len, null);
// expecting \n
this._din.read_byte ();
} catch (IOError ie) {
warning ("I/O error: %s", ie.message);
}
if (data == null) {
debug ("connection closed?");
return false;
}
vdebug ("received line: %s", data);
Packet pkt = Packet.new_from_data (data);
if (pkt == null) {
warning ("failed to build packet from data");
// data was received, hence connection is still alive
return true;
}
this.handle_packet (pkt);
return true;
}
private bool _io_ready (uint flags) {
debug ("check for IO, conditions: 0x%x", flags);
bool res = this.receive ();
if (res == false) {
// disconnected
this.disconnected ();
}
return res;
}
private void handle_packet (Packet pkt) {
// debug("handle packet of type: %s", pkt.pkt_type);
if (pkt.pkt_type == Packet.ENCRYPTED) {
warning ("received packet with eplicit encryption, this usually indicates a protocol version < 6 type packet, such pacckets are no longer supported, dropping..");
} else {
// signal that we got a packet
this.packet_received (pkt);
}
}
}

+ 155
- 153
src/mconnect/devicemanager-proxy.vala View File

@ -20,156 +20,158 @@
using Gee;
[DBus (name = "org.mconnect.DeviceManager")]
class DeviceManagerDBusProxy : Object
{
private DeviceManager manager;
public string certificate {
owned get { return Core.instance().certificate.certificate_pem; }
private set {}
}
public signal void device_added(string path);
public signal void device_removed(string path);
private const string DBUS_PATH = "/org/mconnect/manager";
private DBusConnection bus = null;
private HashMap<string, DeviceDBusProxy> devices;
private int device_idx = 0;
public DeviceManagerDBusProxy.with_manager(DBusConnection bus,
DeviceManager manager) {
this.manager = manager;
this.bus = bus;
this.devices = new HashMap<string, DeviceDBusProxy>();
manager.found_new_device.connect((d) => {
this.add_device(d);
});
manager.device_capability_added.connect(this.add_device_capability);
}
[DBus (visible = false)]
public void publish() throws IOError {
assert(this.bus != null);
this.bus.register_object(DBUS_PATH, this);
}
/**
* allow_device:
* @path: device object path
*
* Allow given device
*/
public void allow_device(string path) {
debug("allow device %s", path);
var dev_proxy = this.devices.@get(path);
if (dev_proxy == null) {
warning("no device under path %s", path);
return;
}
this.manager.allow_device(dev_proxy.device);
}
/**
* disallow_device:
* @path: device object path
*
* Disallow given device
*/
public void disallow_device(string path) {
debug("disallow device %s", path);
var dev_proxy = this.devices.@get(path);
if (dev_proxy == null) {
warning("no device under path %s", path);
return;
}
this.manager.disallow_device(dev_proxy.device);
}
/**
* list_devices:
*
* Returns a list of DBus paths of all known devices
*/
public ObjectPath[] list_devices() {
ObjectPath[] devices = {};
foreach (var path in this.devices.keys) {
devices += new ObjectPath(path);
}
return devices;
}
private void add_device(Device dev) {
var path = make_device_path();
var device_proxy = new DeviceDBusProxy.for_device_with_path(dev,
new ObjectPath(path));
this.devices.@set(path, device_proxy);
info("register device %s under path %s",
dev.to_string(), path);
device_proxy.bus_register(this.bus);
device_added(path);
}
private DeviceDBusProxy? find_proxy_for_device(Device dev) {
DeviceDBusProxy dp = null;
foreach (var entry in this.devices.entries) {
if (entry.value.device == dev) {
dp = entry.value;
break;
}
}
return dp;
}
private void add_device_capability(Device dev,
string capability,
PacketHandlerInterface iface) {
DeviceDBusProxy dp = find_proxy_for_device(dev);
if (dp == null) {
warning("no bus proxy for device %s", dev.to_string());
return;
}
if (dp.has_handler(capability)) {
return;
}
info("add capability handler %s for device at path %s",
capability, dp.object_path.to_string());
var h = PacketHandlersProxy.new_device_capability_handler(dev,
capability,
iface);
if (h != null) {
h.bus_register(this.bus, dp.object_path);
}
}
/**
* make_device_path:
*
* return device path string that can be used as ObjectPath
*/
private string make_device_path() {
var path = "/org/mconnect/device/%d".printf(this.device_idx);
// bump device index
this.device_idx++;
return path;
}
}
class DeviceManagerDBusProxy : Object {
private DeviceManager manager;
public string certificate {
owned get {
return Core.instance ().certificate.certificate_pem;
}
private set {
}
}
public signal void device_added (string path);
public signal void device_removed (string path);
private const string DBUS_PATH = "/org/mconnect/manager";
private DBusConnection bus = null;
private HashMap<string, DeviceDBusProxy> devices;
private int device_idx = 0;
public DeviceManagerDBusProxy.with_manager (DBusConnection bus,
DeviceManager manager) {
this.manager = manager;
this.bus = bus;
this.devices = new HashMap<string, DeviceDBusProxy>();
manager.found_new_device.connect ((d) => {
this.add_device (d);
});
manager.device_capability_added.connect (this.add_device_capability);
}
[DBus (visible = false)]
public void publish () throws IOError {
assert (this.bus != null);
this.bus.register_object (DBUS_PATH, this);
}
/**
* allow_device:
* @path: device object path
*
* Allow given device
*/
public void allow_device (string path) {
debug ("allow device %s", path);
var dev_proxy = this.devices.@get (path);
if (dev_proxy == null) {
warning ("no device under path %s", path);
return;
}
this.manager.allow_device (dev_proxy.device);
}
/**
* disallow_device:
* @path: device object path
*
* Disallow given device
*/
public void disallow_device (string path) {
debug ("disallow device %s", path);
var dev_proxy = this.devices.@get (path);
if (dev_proxy == null) {
warning ("no device under path %s", path);
return;
}
this.manager.disallow_device (dev_proxy.device);
}
/**
* list_devices:
*
* Returns a list of DBus paths of all known devices
*/
public ObjectPath[] list_devices () {
ObjectPath[] devices = {};
foreach (var path in this.devices.keys) {
devices += new ObjectPath (path);
}
return devices;
}
private void add_device (Device dev) {
var path = make_device_path ();
var device_proxy = new DeviceDBusProxy.for_device_with_path (dev,
new ObjectPath (path));
this.devices.@set (path, device_proxy);
info ("register device %s under path %s",
dev.to_string (), path);
device_proxy.bus_register (this.bus);
device_added (path);
}
private DeviceDBusProxy ? find_proxy_for_device (Device dev) {
DeviceDBusProxy dp = null;
foreach (var entry in this.devices.entries) {
if (entry.value.device == dev) {
dp = entry.value;
break;
}
}
return dp;
}
private void add_device_capability (Device dev,
string capability,
PacketHandlerInterface iface) {
DeviceDBusProxy dp = find_proxy_for_device (dev);
if (dp == null) {
warning ("no bus proxy for device %s", dev.to_string ());
return;
}
if (dp.has_handler (capability)) {
return;
}
info ("add capability handler %s for device at path %s",
capability, dp.object_path.to_string ());
var h = PacketHandlersProxy.new_device_capability_handler (dev,
capability,
iface);
if (h != null) {
h.bus_register (this.bus, dp.object_path);
}
}
/**
* make_device_path:
*
* return device path string that can be used as ObjectPath
*/
private string make_device_path () {
var path = "/org/mconnect/device/%d".printf (this.device_idx);
// bump device index
this.device_idx++;
return path;
}
}

+ 242
- 247
src/mconnect/devicemanager.vala View File

@ -19,250 +19,245 @@
*/
using Gee;
class DeviceManager : GLib.Object
{
public signal void found_new_device(Device dev);
public signal void device_capability_added(Device dev,
string capability,
PacketHandlerInterface handler);
public const string DEVICES_CACHE_FILE = "devices";
private HashMap<string, Device> devices;
public DeviceManager() {
debug("device manager..");
this.devices = new HashMap<string, Device>();
}
/**
* Obtain path to devices cache file
*/
private string get_cache_file() {
var cache_file = Path.build_filename(Core.get_cache_dir(),
DEVICES_CACHE_FILE);
vdebug("cache file: %s", cache_file);
// make sure that cache dir exists
DirUtils.create_with_parents(Core.get_cache_dir(),
0700);
return cache_file;
}
/**
* Load known devices from cache and attempt pairing.
*/
public void load_cache() {
var cache_file = get_cache_file();
debug("try loading devices from device cache %s", cache_file);
var kf = new KeyFile();
try {
kf.load_from_file(cache_file, KeyFileFlags.NONE);
string[] groups = kf.get_groups();
foreach (string group in groups) {
var dev = Device.new_from_cache(kf, group);
if (dev != null) {
debug("device %s from cache", dev.to_string());
handle_new_device(dev);
}
}
} catch (Error e) {
debug("error loading cache file: %s", e.message);
}
}
/**
* Update contents of device cache
*/
private void update_cache() {
// debug("update devices cache");
if (devices.size == 0)
return;
var kf = new KeyFile();
foreach (var dev in devices.values) {
dev.to_cache(kf, dev.device_name);
}
try {
// debug("saving to cache");
FileUtils.set_contents(get_cache_file(),
kf.to_data());
} catch (FileError e) {
warning("failed to save to cache file %s: %s",
get_cache_file(), e.message);
}
}
public void handle_discovered_device(DiscoveredDevice discovered_dev) {
debug("found device: %s", discovered_dev.to_string());
var new_dev = new Device.from_discovered_device(discovered_dev);
handle_new_device(new_dev);
}
public void handle_new_device(Device new_dev) {
var is_new = false;
string unique = new_dev.to_unique_string();
vdebug("device key: %s", unique);
if (this.devices.has_key(unique) == false) {
debug("adding new device with key: %s", unique);
this.devices.@set(unique, new_dev);
is_new = true;
} else {
debug("device %s already present", unique);
}
var dev = this.devices.@get(unique);
// notify everyone that a new device appeared
if (is_new) {
// make sure that this happens before we update device data so that
// all subscribeds of found_new_device() signal have a chance to
// setup eveything they need
found_new_device(dev);
}
if (is_new) {
dev.capability_added.connect(this.device_capability_added_cb);
dev.capability_removed.connect(this.device_capability_removed_cb);
}
// update device information
dev.update_from_device(new_dev);
debug("allowed? %s", dev.allowed.to_string());
// check if device is whitelisted in configuration
if (!dev.allowed && device_allowed_in_config(dev)) {
dev.allowed = true;
}
// update devices cache
update_cache();
if (dev.allowed) {
// device is allowed
activate_device(dev);
} else {
warning("skipping device %s activation, device not allowed",
dev.to_string());
}
}
private void activate_device(Device dev) {
info("activating device %s, active: %s", dev.to_string(),
dev.is_active.to_string());
if (!dev.is_active) {
dev.paired.connect(this.device_paired);
dev.disconnected.connect(this.device_disconnected);
dev.activate();
}
}
/**
* device_allowed_in_config:
* @dev device
*
* Returns true if a matching device is enabled via configuration file.
*/
private bool device_allowed_in_config(Device dev) {
if (dev.allowed)
return true;
var core = Core.instance();
var in_config = core.config.is_device_allowed(dev.device_name,
dev.device_type);
return in_config;
}
private void device_paired(Device dev, bool status) {
info("device %s pair status change: %s",
dev.to_string(), status.to_string());
update_cache();
if (status == false) {
// we're no longer interested in paired singnal
dev.paired.disconnect(this.device_paired);
// we're not paired anymore, deactivate if needed
dev.deactivate();
}
}
private void device_capability_added_cb(Device dev, string cap) {
info("capability %s added to device %s", cap, dev.to_string());
if (dev.has_capability_handler(cap)) {
return;
}
var core = Core.instance();
var h = core.handlers.get_capability_handler(cap);
if (h != null) {
dev.register_capability_handler(cap, h);
device_capability_added(dev, cap, h);
} else {
warning("no handler for capability %s", cap);
}
}
private void device_capability_removed_cb(Device dev, string cap) {
info("capability %s removed from device %s", cap, dev.to_string());
}
private void device_disconnected(Device dev) {
debug("device %s got disconnected", dev.to_string());
dev.paired.disconnect(this.device_paired);
dev.disconnected.disconnect(this.device_disconnected);
}
/**
* allow_device:
* @path: device object path
*
* Allow given device
*/
public void allow_device(Device dev) {
dev.allowed = true;
// update device cache
update_cache();
// maybe activate if needed
activate_device(dev);
}
/**
* disallow_device:
* @path: device object path
*
* Disallow given device
*/
public void disallow_device(Device dev) {
dev.allowed = false;
// update device cache
update_cache();
}
}
class DeviceManager : GLib.Object {
public signal void found_new_device (Device dev);
public signal void device_capability_added (Device dev,
string capability,
PacketHandlerInterface handler);
public const string DEVICES_CACHE_FILE = "devices";
private HashMap<string, Device> devices;
public DeviceManager () {
debug ("device manager..");
this.devices = new HashMap<string, Device>();
}
/**
* Obtain path to devices cache file
*/
private string get_cache_file () {
var cache_file = Path.build_filename (Core.get_cache_dir (),
DEVICES_CACHE_FILE);
vdebug ("cache file: %s", cache_file);
// make sure that cache dir exists
DirUtils.create_with_parents (Core.get_cache_dir (),
0700);
return cache_file;
}
/**
* Load known devices from cache and attempt pairing.
*/
public void load_cache () {
var cache_file = get_cache_file ();
debug ("try loading devices from device cache %s", cache_file);
var kf = new KeyFile ();
try {
kf.load_from_file (cache_file, KeyFileFlags.NONE);
string[] groups = kf.get_groups ();
foreach (string group in groups) {
var dev = Device.new_from_cache (kf, group);
if (dev != null) {
debug ("device %s from cache", dev.to_string ());
handle_new_device (dev);
}
}
} catch (Error e) {
debug ("error loading cache file: %s", e.message);
}
}
/**
* Update contents of device cache
*/
private void update_cache () {
// debug("update devices cache");
if (devices.size == 0)
return;
var kf = new KeyFile ();
foreach (var dev in devices.values) {
dev.to_cache (kf, dev.device_name);
}
try {
// debug("saving to cache");
FileUtils.set_contents (get_cache_file (),
kf.to_data ());
} catch (FileError e) {
warning ("failed to save to cache file %s: %s",
get_cache_file (), e.message);
}
}
public void handle_discovered_device (DiscoveredDevice discovered_dev) {
debug ("found device: %s", discovered_dev.to_string ());
var new_dev = new Device.from_discovered_device (discovered_dev);
handle_new_device (new_dev);
}
public void handle_new_device (Device new_dev) {
var is_new = false;
string unique = new_dev.to_unique_string ();
vdebug ("device key: %s", unique);
if (this.devices.has_key (unique) == false) {
debug ("adding new device with key: %s", unique);
this.devices.@set (unique, new_dev);
is_new = true;
} else {
debug ("device %s already present", unique);
}
var dev = this.devices.@get (unique);
// notify everyone that a new device appeared
if (is_new) {
// make sure that this happens before we update device data so that
// all subscribeds of found_new_device() signal have a chance to
// setup eveything they need
found_new_device (dev);
}
if (is_new) {
dev.capability_added.connect (this.device_capability_added_cb);
dev.capability_removed.connect (this.device_capability_removed_cb);
}
// update device information
dev.update_from_device (new_dev);
debug ("allowed? %s", dev.allowed.to_string ());
// check if device is whitelisted in configuration
if (!dev.allowed && device_allowed_in_config (dev)) {
dev.allowed = true;
}
// update devices cache
update_cache ();
if (dev.allowed) {
// device is allowed
activate_device (dev);
} else {
warning ("skipping device %s activation, device not allowed",
dev.to_string ());
}
}
private void activate_device (Device dev) {
info ("activating device %s, active: %s", dev.to_string (),
dev.is_active.to_string ());
if (!dev.is_active) {
dev.paired.connect (this.device_paired);
dev.disconnected.connect (this.device_disconnected);
dev.activate ();
}
}
/**
* device_allowed_in_config:
* @dev device
*
* Returns true if a matching device is enabled via configuration file.
*/
private bool device_allowed_in_config (Device dev) {
if (dev.allowed)
return true;
var core = Core.instance ();
var in_config = core.config.is_device_allowed (dev.device_name,
dev.device_type);
return in_config;
}
private void device_paired (Device dev, bool status) {
info ("device %s pair status change: %s",
dev.to_string (), status.to_string ());
update_cache ();
if (status == false) {
// we're no longer interested in paired singnal
dev.paired.disconnect (this.device_paired);
// we're not paired anymore, deactivate if needed
dev.deactivate ();
}
}
private void device_capability_added_cb (Device dev, string cap) {
info ("capability %s added to device %s", cap, dev.to_string ());
if (dev.has_capability_handler (cap)) {
return;
}
var core = Core.instance ();
var h = core.handlers.get_capability_handler (cap);
if (h != null) {
dev.register_capability_handler (cap, h);
device_capability_added (dev, cap, h);
} else {
warning ("no handler for capability %s", cap);
}
}
private void device_capability_removed_cb (Device dev, string cap) {
info ("capability %s removed from device %s", cap, dev.to_string ());
}
private void device_disconnected (Device dev) {
debug ("device %s got disconnected", dev.to_string ());
dev.paired.disconnect (this.device_paired);
dev.disconnected.disconnect (this.device_disconnected);
}
/**
* allow_device:
* @path: device object path
*
* Allow given device
*/
public void allow_device (Device dev) {
dev.allowed = true;
// update device cache
update_cache ();
// maybe activate if needed
activate_device (dev);
}
/**
* disallow_device:
* @path: device object path
*
* Disallow given device
*/
public void disallow_device (Device dev) {
dev.allowed = false;
// update device cache
update_cache ();
}
}

+ 63
- 48
src/mconnect/discovereddevice.vala View File

@ -23,60 +23,75 @@
*/
class DiscoveredDevice : Object {
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 string[] outgoing_capabilities { get; private set; default = null; }
public string[] incoming_capabilities { get; private set; default = null; }
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 string[] outgoing_capabilities {
get; private set; default = null;
}
public string[] incoming_capabilities {
get; private set; default = null;
}
/**
* Constructs DiscoveredDevice based on identity packet.
*
* @param pkt identity packet
* @param host source host that the packet came from
*/
public DiscoveredDevice.from_identity(Packet pkt, InetAddress host) {
/**
* Constructs DiscoveredDevice based on identity packet.
*
* @param pkt identity packet
* @param host source host that the packet came from
*/
public DiscoveredDevice.from_identity (Packet pkt, InetAddress host) {
debug("got packet: %s", pkt.to_string());
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");
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");
var incoming = body.get_array_member("incomingCapabilities");
var outgoing = body.get_array_member("outgoingCapabilities");
this.outgoing_capabilities = new string[outgoing.get_length()];
this.incoming_capabilities = new string[incoming.get_length()];
var incoming = body.get_array_member ("incomingCapabilities");
var outgoing = body.get_array_member ("outgoingCapabilities");
this.outgoing_capabilities = new string[outgoing.get_length ()];
this.incoming_capabilities = new string[incoming.get_length ()];
incoming.foreach_element((a, i, n) => {
this.incoming_capabilities[i] = n.get_string();
});
outgoing.foreach_element((a, i, n) => {
this.outgoing_capabilities[i] = n.get_string();
});
incoming.foreach_element ((a, i, n) => {
this.incoming_capabilities[i] = n.get_string ();
});
outgoing.foreach_element ((a, i, n) => {
this.outgoing_capabilities[i] = n.get_string ();
});
debug("discovered new device: %s", this.to_string());
}
debug ("discovered new device: %s", this.to_string ());
}
public string to_string() {
return "discovered-%s-%s-%s-%u".printf(this.device_id,
this.device_name,
this.device_type,
this.protocol_version);
}
public string to_unique_string() {
return Utils.make_unique_device_string(this.device_id,
this.device_name,
this.device_type,
this.protocol_version);
}
public string to_string () {
return "discovered-%s-%s-%s-%u".printf (this.device_id,
this.device_name,
this.device_type,
this.protocol_version);
}
public string to_unique_string () {
return Utils.make_unique_device_string (this.device_id,
this.device_name,
this.device_type,
this.protocol_version);
}
}

+ 64
- 66
src/mconnect/discovery.vala View File

@ -18,83 +18,81 @@
* Maciek Borzecki <maciek.borzecki (at] gmail.com>
*/
class Discovery : GLib.Object
{
private Socket socket = null;
class Discovery : GLib.Object {
private Socket socket = null;
public signal void device_found(DiscoveredDevice dev);
public signal void device_found (DiscoveredDevice dev);
public Discovery() {
}
public Discovery () {
}
~Discovery() {
debug("cleaning up discovery...");
if (this.socket != null) {
this.socket.close();
}
}
~Discovery () {
debug ("cleaning up discovery...");
if (this.socket != null) {
this.socket.close ();
}
}
public void listen() throws Error {
this.socket = new Socket(SocketFamily.IPV4,
SocketType.DATAGRAM,
SocketProtocol.UDP);
var sa = new InetSocketAddress(new InetAddress.any(SocketFamily.IPV4),
1714);
debug("start listening for new devices at: %s:%u",
sa.address.to_string(), sa.port);
public void listen () throws Error {
this.socket = new Socket (SocketFamily.IPV4,
SocketType.DATAGRAM,
SocketProtocol.UDP);
var sa = new InetSocketAddress (new InetAddress.any (SocketFamily.IPV4),
1714);
debug ("start listening for new devices at: %s:%u",
sa.address.to_string (), sa.port);
try {
socket.bind(sa, false);
} catch (Error e) {
this.socket.close();
this.socket = null;
throw e;
}
try {
socket.bind (sa, false);
} catch (Error e) {
this.socket.close ();
this.socket = null;
throw e;
}
var source = socket.create_source(IOCondition.IN);
source.set_callback((s, c) => {
this.incomingPacket();
return true;
});
source.attach(MainContext.default());
}
var source = socket.create_source (IOCondition.IN);
source.set_callback ((s, c) => {
this.incomingPacket ();
return true;
});
source.attach (MainContext.default ());
}
private void incomingPacket() {
vdebug("incoming packet");
private void incomingPacket () {
vdebug ("incoming packet");
uint8 buffer[4096];
SocketAddress sa;
InetSocketAddress isa;
uint8 buffer[4096];
SocketAddress sa;
InetSocketAddress isa;
try {
ssize_t read = this.socket.receive_from(out sa, buffer);
isa = (InetSocketAddress)sa;
vdebug("got %zd bytes from: %s:%u", read,
isa.address.to_string(), isa.port);
} catch (Error e) {
warning("failed to receive packet: %s", e.message);
return;
}
try {
ssize_t read = this.socket.receive_from (out sa, buffer);
isa = (InetSocketAddress) sa;
vdebug ("got %zd bytes from: %s:%u", read,
isa.address.to_string (), isa.port);
} catch (Error e) {
warning ("failed to receive packet: %s", e.message);
return;
}
vdebug("message data: %s", (string)buffer);
vdebug ("message data: %s", (string) buffer);
this.parsePacketFromHost((string) buffer, isa.address);
}
this.parsePacketFromHost ((string) buffer, isa.address);
}
private void parsePacketFromHost(string data, InetAddress host)
{
// expecing an identity packet
var pkt = Packet.new_from_data(data);
if (pkt.pkt_type != Packet.IDENTITY) {
message("unexpected packet type %s from device %s",
pkt.pkt_type, host.to_string());
return;
}
private void parsePacketFromHost (string data, InetAddress host) {
// expecing an identity packet
var pkt = Packet.new_from_data (data);
if (pkt.pkt_type != Packet.IDENTITY) {
message ("unexpected packet type %s from device %s",
pkt.pkt_type, host.to_string ());
return;
}
var dev = new DiscoveredDevice.from_identity(pkt, host);
message("connection from device: \'%s\', responds at: %s:%u",
dev.device_name, host.to_string(), dev.tcp_port);
var dev = new DiscoveredDevice.from_identity (pkt, host);
message ("connection from device: \'%s\', responds at: %s:%u",
dev.device_name, host.to_string (), dev.tcp_port);
device_found(dev);
}
}
device_found (dev);
}
}

+ 47
- 45
src/mconnect/io-job.vala View File

@ -19,55 +19,57 @@ using Logging;
class IOCopyJob : Object {
private InputStream from = null;
private OutputStream to = null;
private InputStream from = null;
private OutputStream to = null;
public IOCopyJob(InputStream from, OutputStream to) {
this.from = from;
this.to = to;
}
public IOCopyJob (InputStream from, OutputStream to) {
this.from = from;
this.to = to;
}
/**
* transfer_async:
* @cancel: cancellable
*
* Starty asynchronous transfer of data from @from stream to @to stream.
*
* @return number of bytes transferred if no error occurred
*/
public async uint64 start_async(Cancellable? cancel) throws Error {
uint64 bytes_done = 0;
var chunk_size = 4096;
var max_chunk_size = 64 * 1024;
while (true) {
var data = yield this.from.read_bytes_async(chunk_size,
Priority.DEFAULT,
cancel);
vdebug("read %d bytes", data.length);
if (data.length == 0) {
break;
}
yield this.to.write_bytes_async(data, Priority.DEFAULT,
cancel);
bytes_done += data.length;
this.progress(bytes_done);
/**
* transfer_async:
* @cancel: cancellable
*
* Starty asynchronous transfer of data from @from stream to @to stream.
*
* @return number of bytes transferred if no error occurred
*/
public async uint64 start_async (Cancellable ? cancel) throws Error {
uint64 bytes_done = 0;
var chunk_size = 4096;
var max_chunk_size = 64 * 1024;
while (true) {
var data = yield this.from.read_bytes_async (chunk_size,
Priority.DEFAULT,
cancel);
if (data.length == chunk_size)
chunk_size = 2 * chunk_size;
vdebug ("read %d bytes", data.length);
if (data.length == 0) {
break;
}
yield this.to.write_bytes_async (data, Priority.DEFAULT,
cancel);
if (chunk_size > max_chunk_size)
chunk_size = max_chunk_size;
}
bytes_done += data.length;
this.progress (bytes_done);
debug("transfer done, got %s bytes", format_size(bytes_done));
return bytes_done;
}
if (data.length == chunk_size)
chunk_size = 2 * chunk_size;
/**
* progress:
* @bytes_down: number of bytes transferred
*
* Indicate transfer progress
*/
public signal void progress(uint64 bytes_done);
if (chunk_size > max_chunk_size)
chunk_size = max_chunk_size;
}
debug ("transfer done, got %s bytes", format_size (bytes_done));
return bytes_done;
}
/**
* progress:
* @bytes_down: number of bytes transferred
*
* Indicate transfer progress
*/
public signal void progress (uint64 bytes_done);
}

+ 11
- 13
src/mconnect/logging.vala View File

@ -17,18 +17,17 @@
*/
namespace Logging {
public bool VERBOSE = false;
public bool VERBOSE = false;
/**
* enable_vdebug:
*
* Enable verbose debug logging
*/
void enable_vdebug() {
VERBOSE = true;
}
void enable_vdebug () {
VERBOSE = true;
}
}
/**
@ -37,10 +36,9 @@ void enable_vdebug() {
*
* Same as debug() but looks at verbose debug flag
*/
void vdebug(string format, ...) {
if (Logging.VERBOSE == true) {
var l = va_list();
logv(null, LogLevelFlags.LEVEL_DEBUG, format, l);
}
}
void vdebug (string format, ...) {
if (Logging.VERBOSE == true) {
var l = va_list ();
logv (null, LogLevelFlags.LEVEL_DEBUG, format, l);
}
}

+ 7
- 8
src/mconnect/main.vala View File

@ -17,15 +17,14 @@
* AUTHORS
* Maciek Borzecki <maciek.borzecki (at] gmail.com>
*/
public static int main(string[] args)
{
var app = new Mconn.Application();
public static int main (string[] args) {
var app = new Mconn.Application ();
// needed for mousepad protocol handler
Gdk.init(ref args);
// needed for mousepad protocol handler
Gdk.init (ref args);
// needed for clipboard sharing
Gtk.init(ref args);
// needed for clipboard sharing
Gtk.init (ref args);
return app.run(args);
return app.run (args);
}

+ 132
- 134
src/mconnect/mousepad.vala View File

@ -20,138 +20,136 @@
class MousepadHandler : Object, PacketHandlerInterface {
public const string MOUSEPAD = "kdeconnect.mousepad.request";
public const string MOUSEPAD_PACKET = "kdeconnect.mousepad";
private Gdk.Display _display;
public string get_pkt_type() {
return MOUSEPAD;
}
private MousepadHandler() {
}
public static MousepadHandler instance() {
var ms = new MousepadHandler();
if (Atspi.init() > 1) {
warning("failed to initialize AT-SPI");
}
ms._display = Gdk.Display.get_default();
if (ms._display == null) {
warning("failed to obtain display");
}
return ms;
}
public void use_device(Device dev) {
debug("use device %s for mouse/keyboard input", dev.to_string());
dev.message.connect(this.message);
}
public void release_device(Device dev) {
debug("release device %s ", dev.to_string());
dev.message.disconnect(this.message);
}
private void message(Device dev, Packet pkt) {
if (pkt.pkt_type != MOUSEPAD_PACKET && pkt.pkt_type != MOUSEPAD) {
return;
}
debug("got mousepad packet");
if (_display == null) {
warning("display not initialized");
return;
}
if (pkt.body.has_member("singleclick")) {
// single click
debug("single click");
send_click(1);
} else if (pkt.body.has_member("doubleclick")) {
send_click(1, true);
} else if (pkt.body.has_member("rightclick")) {
send_click(3);
} else if (pkt.body.has_member("middleclick")) {
send_click(2);
} else if (pkt.body.has_member("dx") && pkt.body.has_member("dy")) {
// motion/position
double dx = pkt.body.get_double_member("dx");
double dy = pkt.body.get_double_member("dy");
debug("position: %f x %f", dx, dy);
move_cursor_relative(dx, dy);
} else if (pkt.body.has_member("key")) {
string key = pkt.body.get_string_member("key");
debug("got key: %s", key);
send_key(key);
} else if (pkt.body.has_member("specialKey")) {
var keynum = pkt.body.get_int_member("specialKey");
debug("got special key: %s", keynum.to_string());
send_keysym((uint) keynum);
}
}
private void move_cursor_relative(double dx, double dy) {
try {
Atspi.generate_mouse_event((long)dx, (long)dy, "rel");
} catch (Error e) {
warning("failed to generate mouse move event: %s", e.message);
}
}
private void send_click(int button, bool doubleclick = false) {
var etype = "b1c";
if (button == 2) {
etype = "b2c";
} else if (button == 3) {
etype = "b3c";
}
try {
int x, y;
_display.get_pointer(null, out x, out y, null);
Atspi.generate_mouse_event(x, y, etype);
if (doubleclick) {
Atspi.generate_mouse_event(x, y, etype);
}
} catch (Error e) {
warning("failed to generate mouse click event: %s", e.message);
}
}
private void send_key(string key) {
try {
Atspi.generate_keyboard_event(0, key,
Atspi.KeySynthType.STRING);
} catch (Error e) {
warning("failed to generate keyboard event: %s", e.message);
}
}
private void send_keysym(uint key) {
uint keyval = 0;
if (key == 12) {
keyval = Gdk.keyval_from_name("Return");
} else if (key == 1) {
keyval = Gdk.keyval_from_name("BackSpace");
}
if (keyval == 0) {
warning("could not identify key %u", key);
return;
}
debug("keyval %x %s", keyval, Gdk.keyval_name(keyval));
try {
Atspi.generate_keyboard_event(keyval, null,
Atspi.KeySynthType.PRESSRELEASE
| Atspi.KeySynthType.SYM);
} catch (Error e) {
warning("failed to generate keyboard event: %s", e.message);
}
}
public const string MOUSEPAD = "kdeconnect.mousepad.request";
public const string MOUSEPAD_PACKET = "kdeconnect.mousepad";
private Gdk.Display _display;
public string get_pkt_type () {
return MOUSEPAD;
}
private MousepadHandler () {
}
public static MousepadHandler instance () {
var ms = new MousepadHandler ();
if (Atspi.init () > 1) {
warning ("failed to initialize AT-SPI");
}
ms._display = Gdk.Display.get_default ();
if (ms._display == null) {
warning ("failed to obtain display");
}
return ms;
}
public void use_device (Device dev) {
debug ("use device %s for mouse/keyboard input", dev.to_string ());
dev.message.connect (this.message);
}
public void release_device (Device dev) {
debug ("release device %s ", dev.to_string ());
dev.message.disconnect (this.message);
}
private void message (Device dev, Packet pkt) {
if (pkt.pkt_type != MOUSEPAD_PACKET && pkt.pkt_type != MOUSEPAD) {
return;
}
debug ("got mousepad packet");
if (_display == null) {
warning ("display not initialized");
return;
}
if (pkt.body.has_member ("singleclick")) {
// single click
debug ("single click");
send_click (1);
} else if (pkt.body.has_member ("doubleclick")) {
send_click (1, true);
} else if (pkt.body.has_member ("rightclick")) {
send_click (3);
} else if (pkt.body.has_member ("middleclick")) {
send_click (2);
} else if (pkt.body.has_member ("dx") && pkt.body.has_member ("dy")) {
// motion/position
double dx = pkt.body.get_double_member ("dx");
double dy = pkt.body.get_double_member ("dy");
debug ("position: %f x %f", dx, dy);
move_cursor_relative (dx, dy);
} else if (pkt.body.has_member ("key")) {
string key = pkt.body.get_string_member ("key");
debug ("got key: %s", key);
send_key (key);
} else if (pkt.body.has_member ("specialKey")) {
var keynum = pkt.body.get_int_member ("specialKey");
debug ("got special key: %s", keynum.to_string ());
send_keysym ((uint) keynum);
}
}
private void move_cursor_relative (double dx, double dy) {
try {
Atspi.generate_mouse_event ((long) dx, (long) dy, "rel");
} catch (Error e) {
warning ("failed to generate mouse move event: %s", e.message);
}
}
private void send_click (int button, bool doubleclick = false) {
var etype = "b1c";
if (button == 2) {
etype = "b2c";
} else if (button == 3) {
etype = "b3c";
}
try {
int x, y;
_display.get_pointer (null, out x, out y, null);
Atspi.generate_mouse_event (x, y, etype);
if (doubleclick) {
Atspi.generate_mouse_event (x, y, etype);
}
} catch (Error e) {
warning ("failed to generate mouse click event: %s", e.message);
}
}
private void send_key (string key) {
try {
Atspi.generate_keyboard_event (0, key,
Atspi.KeySynthType.STRING);
} catch (Error e) {
warning ("failed to generate keyboard event: %s", e.message);
}
}
private void send_keysym (uint key) {
uint keyval = 0;
if (key == 12) {
keyval = Gdk.keyval_from_name ("Return");
} else if (key == 1) {
keyval = Gdk.keyval_from_name ("BackSpace");
}
if (keyval == 0) {
warning ("could not identify key %u", key);
return;
}
debug ("keyval %x %s", keyval, Gdk.keyval_name (keyval));
try {
Atspi.generate_keyboard_event (keyval, null,
Atspi.KeySynthType.PRESSRELEASE
| Atspi.KeySynthType.SYM);
} catch (Error e) {
warning ("failed to generate keyboard event: %s", e.message);
}
}
}

+ 117
- 122
src/mconnect/notification.vala View File

@ -1,4 +1,4 @@
/* ex:ts=4:sw=4:sts=4:et */
/* 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
@ -21,125 +21,120 @@ using Gee;
class NotificationHandler : Object, PacketHandlerInterface {
private const string NOTIFICATION = "kdeconnect.notification";
private HashMap<string, Notify.Notification> _pending_notifications;
public string get_pkt_type() {
return NOTIFICATION;
}
private NotificationHandler() {
_pending_notifications = new HashMap<string, Notify.Notification>();
}
public static NotificationHandler instance() {
return new NotificationHandler();
}
public void use_device(Device dev) {
dev.message.connect(this.message);
}
public void release_device(Device dev) {
dev.message.disconnect(this.message);
}
public void message(Device dev, Packet pkt) {
if (pkt.pkt_type != NOTIFICATION) {
return;
}
debug("got notification packet");
// get application ID
string id = pkt.body.get_string_member("id");
// dialer notifications are handled by telephony plugin
if (id.match_string("com.android.dialer", false) == true)
return;
// maybe it's a notification about a notification being
// cancelled
if (pkt.body.has_member("isCancel") == true &&
pkt.body.get_boolean_member("isCancel") == true)
{
debug("message cancels notification %s", id);
if (_pending_notifications.has_key(id) == true)
{
// cancel out pending notifications
Notify.Notification notif = _pending_notifications.@get(id);
if (notif != null)
{
_pending_notifications.unset(id);
try {
notif.close();
} catch (Error e) {
critical("error closing notification: %s", e.message);
}
}
}
return;
}
// check if notification is already known, if so don't show
// anything
if (_pending_notifications.has_key(id) == true)
{
debug("notification %s is known, ignore", id);
return;
}
debug("new notification %s", id);
// other notifications
if (pkt.body.has_member("appName") == false ||
pkt.body.has_member("ticker") == false)
return;
string app = pkt.body.get_string_member("appName");
string ticker = pkt.body.get_string_member("ticker");
// skip empty notifications
if (ticker.length == 0)
return;
DateTime time = null;
// check if time was provided, prepend to ticker
if (pkt.body.has_member("time") == true) {
string timestr = pkt.body.get_string_member("time");
int64 t = int64.parse(timestr);
// time in ms since Epoch, convert to seconds
t /= 1000;
time = new DateTime.from_unix_local(t);
} else {
time = new DateTime.now_local();
}
// format the body of notification, so that a time information
// is included
if (time != null)
ticker = "%s %s".printf(time.format("%X"),
ticker);
GLib.message("notification from %s: %s", app, ticker);
var notif = new Notify.Notification(app, ticker,
"phone");
try {
// react to closed signal
notif.closed.connect((n) => {
this.handle_closed_notification(id);
});
notif.show();
_pending_notifications.@set(id, notif);
} catch (Error e) {
critical("failed to show notification: %s", e.message);
}
}
private void handle_closed_notification(string id) {
debug("notification %s closed by user", id);
if (_pending_notifications.has_key(id) == true)
{
_pending_notifications.unset(id);
}
}
private const string NOTIFICATION = "kdeconnect.notification";
private HashMap<string, Notify.Notification> _pending_notifications;
public string get_pkt_type () {
return NOTIFICATION;
}
private NotificationHandler () {
_pending_notifications = new HashMap<string, Notify.Notification>();
}
public static NotificationHandler instance () {
return new NotificationHandler ();
}
public void use_device (Device dev) {
dev.message.connect (this.message);
}
public void release_device (Device dev) {
dev.message.disconnect (this.message);
}
public void message (Device dev, Packet pkt) {
if (pkt.pkt_type != NOTIFICATION) {
return;
}
debug ("got notification packet");
// get application ID
string id = pkt.body.get_string_member ("id");
// dialer notifications are handled by telephony plugin
if (id.match_string ("com.android.dialer", false) == true)
return;
// maybe it's a notification about a notification being
// cancelled
if (pkt.body.has_member ("isCancel") == true &&
pkt.body.get_boolean_member ("isCancel") == true) {
debug ("message cancels notification %s", id);
if (_pending_notifications.has_key (id) == true) {
// cancel out pending notifications
Notify.Notification notif = _pending_notifications.@get (id);
if (notif != null) {
_pending_notifications.unset (id);
try {
notif.close ();
} catch (Error e) {
critical ("error closing notification: %s", e.message);
}
}
}
return;
}
// check if notification is already known, if so don't show
// anything
if (_pending_notifications.has_key (id) == true) {
debug ("notification %s is known, ignore", id);
return;
}
debug ("new notification %s", id);
// other notifications
if (pkt.body.has_member ("appName") == false ||
pkt.body.has_member ("ticker") == false)
return;
string app = pkt.body.get_string_member ("appName");
string ticker = pkt.body.get_string_member ("ticker");
// skip empty notifications
if (ticker.length == 0)
return;
DateTime time = null;
// check if time was provided, prepend to ticker
if (pkt.body.has_member ("time") == true) {
string timestr = pkt.body.get_string_member ("time");
int64 t = int64.parse (timestr);
// time in ms since Epoch, convert to seconds
t /= 1000;
time = new DateTime.from_unix_local (t);
} else {
time = new DateTime.now_local ();
}
// format the body of notification, so that a time information
// is included
if (time != null)
ticker = "%s %s".printf (time.format ("%X"),
ticker);
GLib.message ("notification from %s: %s", app, ticker);
var notif = new Notify.Notification (app, ticker,
"phone");
try {
// react to closed signal
notif.closed.connect ((n) => {
this.handle_closed_notification (id);
});
notif.show ();
_pending_notifications.@set (id, notif);
} catch (Error e) {
critical ("failed to show notification: %s", e.message);
}
}
private void handle_closed_notification (string id) {
debug ("notification %s closed by user", id);
if (_pending_notifications.has_key (id) == true) {
_pending_notifications.unset (id);
}
}
}

+ 155
- 147
src/mconnect/packet.vala View File

@ -21,155 +21,163 @@
using Json;
public errordomain PacketError {
MALFORMED
MALFORMED
}
class Packet : GLib.Object {
/**
* Payload:
* Wrapper for payload transfer information
*/
public struct Payload {
public uint64 size;
public uint port;
}
public const int PROTOCOL_VERSION = 7;
public const string IDENTITY = "kdeconnect.identity";
public const string PAIR = "kdeconnect.pair";
public const string ENCRYPTED = "kdeconnect.encrypted";
public string pkt_type { get; private set; default = ""; }
public int64 id { get; private set; default = 0; }
public Json.Object body { get; private set; default = null; }
public Payload? payload { get; set; default = null; }
public Packet(string type, Json.Object body, int64 id = 0) {
this.pkt_type = type;
this.body = body;
if (id == 0) {
this.id = get_real_time() / 1000;
} else {
this.id = id;
}
}
public static Packet? new_from_data(string data) {
Json.Parser jp = new Json.Parser();
try {
jp.load_from_data(data, -1);
// there should be an object at root node
Json.Object root_obj = jp.get_root().get_object();
if (root_obj == null)
throw new PacketError.MALFORMED("Missing root object");
// object needs to have these fields
string[] required_members = {"type", "id", "body"};
foreach (string m in required_members) {
if (root_obj.has_member(m) == false)
throw new PacketError.MALFORMED(@"Missing $m member");
}
string type = root_obj.get_string_member("type");
int64 id = root_obj.get_int_member("id");
Json.Object body = root_obj.get_object_member("body");
vdebug("packet type: %s", type);
var pkt = new Packet(type, body, id);
// ignore payload info for encrypted packets
if (type != ENCRYPTED) {
if (root_obj.has_member("payloadSize") &&
root_obj.has_member("payloadTransferInfo")) {
var size = root_obj.get_int_member("payloadSize");
var pti = root_obj.get_object_member("payloadTransferInfo");
int64 port = 0;
if (pti == null) {
warning("no payload transfer info?");
} else {
port = (int) pti.get_int_member("port");
}
if (size != 0 && port != 0) {
pkt.payload = {(uint64) size, (uint) port};
}
}
}
return pkt;
} catch (Error e) {
message("failed to parse message: \'%s\', error: %s",
data, e.message);
}
return null;
}
public static Packet new_pair(bool pair = true) {
var builder = new Json.Builder();
builder.begin_object();
builder.set_member_name("pair");
builder.add_boolean_value(pair);
builder.end_object();
var data_obj = builder.get_root().get_object();
return new Packet(PAIR, data_obj);
}
public static Packet new_identity(string name,
string device_id,
string[] in_interfaces,
string[] out_interfaces,
string device_type = "desktop") {
var builder = new Json.Builder();
builder.begin_object();
builder.set_member_name("deviceName");
builder.add_string_value(name);
builder.set_member_name("deviceId");
builder.add_string_value(device_id);
builder.set_member_name("deviceType");
builder.add_string_value(device_type);
builder.set_member_name("SupportedIncomingInterfaces");
builder.add_string_value(string.joinv(",", in_interfaces));
builder.set_member_name("SupportedOutgoingInterfaces");
builder.add_string_value(string.joinv(",", out_interfaces));
builder.set_member_name("protocolVersion");
builder.add_int_value(PROTOCOL_VERSION);
builder.end_object();
Json.Object data_obj = builder.get_root().get_object();
return new Packet(IDENTITY, data_obj);
}
public string to_string() {
var gen = new Json.Generator();
// root node
var root = new Json.Node(Json.NodeType.OBJECT);
var root_obj = new Json.Object();
root_obj.set_string_member("type", pkt_type);
root_obj.set_int_member("id", id);
root_obj.set_object_member("body", body);
if (this.payload != null) {
root_obj.set_int_member("payloadSize", (int64) this.payload.size);
var pti = new Json.Object();
pti.set_int_member("port", this.payload.port);
root_obj.set_object_member("payloadTransferInfo", pti);
}
root.set_object(root_obj);
gen.set_root(root);
gen.set_pretty(false);
string data = gen.to_data(null);
return data;
}
/**
* Payload:
* Wrapper for payload transfer information
*/
public struct Payload {
public uint64 size;
public uint port;
}
public const int PROTOCOL_VERSION = 7;
public const string IDENTITY = "kdeconnect.identity";
public const string PAIR = "kdeconnect.pair";
public const string ENCRYPTED = "kdeconnect.encrypted";
public string pkt_type {
get; private set; default = "";
}
public int64 id {
get; private set; default = 0;
}
public Json.Object body {
get; private set; default = null;
}
public Payload ? payload {
get; set; default = null;
}
public Packet (string type, Json.Object body, int64 id = 0) {
this.pkt_type = type;
this.body = body;
if (id == 0) {
this.id = get_real_time () / 1000;
} else {
this.id = id;
}
}
public static Packet ? new_from_data (string data) {
Json.Parser jp = new Json.Parser ();
try {
jp.load_from_data (data, -1);
// there should be an object at root node
Json.Object root_obj = jp.get_root ().get_object ();
if (root_obj == null)
throw new PacketError.MALFORMED ("Missing root object");
// object needs to have these fields
string[] required_members = { "type", "id", "body" };
foreach (string m in required_members) {
if (root_obj.has_member (m) == false)
throw new PacketError.MALFORMED (@"Missing $m member");
}
string type = root_obj.get_string_member ("type");
int64 id = root_obj.get_int_member ("id");
Json.Object body = root_obj.get_object_member ("body");
vdebug ("packet type: %s", type);
var pkt = new Packet (type, body, id);
// ignore payload info for encrypted packets
if (type != ENCRYPTED) {
if (root_obj.has_member ("payloadSize") &&
root_obj.has_member ("payloadTransferInfo")) {
var size = root_obj.get_int_member ("payloadSize");
var pti = root_obj.get_object_member ("payloadTransferInfo");
int64 port = 0;
if (pti == null) {
warning ("no payload transfer info?");
} else {
port = (int) pti.get_int_member ("port");
}
if (size != 0 && port != 0) {
pkt.payload = { (uint64) size, (uint) port };
}
}
}
return pkt;
} catch (Error e) {
message ("failed to parse message: \'%s\', error: %s",
data, e.message);
}
return null;
}
public static Packet new_pair (bool pair = true) {
var builder = new Json.Builder ();
builder.begin_object ();
builder.set_member_name ("pair");
builder.add_boolean_value (pair);
builder.end_object ();
var data_obj = builder.get_root ().get_object ();
return new Packet (PAIR, data_obj);
}
public static Packet new_identity (string name,
string device_id,
string[] in_interfaces,
string[] out_interfaces,
string device_type = "desktop") {
var builder = new Json.Builder ();
builder.begin_object ();
builder.set_member_name ("deviceName");
builder.add_string_value (name);
builder.set_member_name ("deviceId");
builder.add_string_value (device_id);
builder.set_member_name ("deviceType");
builder.add_string_value (device_type);
builder.set_member_name ("SupportedIncomingInterfaces");
builder.add_string_value (string.joinv (",", in_interfaces));
builder.set_member_name ("SupportedOutgoingInterfaces");
builder.add_string_value (string.joinv (",", out_interfaces));
builder.set_member_name ("protocolVersion");
builder.add_int_value (PROTOCOL_VERSION);
builder.end_object ();
Json.Object data_obj = builder.get_root ().get_object ();
return new Packet (IDENTITY, data_obj);
}
public string to_string () {
var gen = new Json.Generator ();
// root node
var root = new Json.Node (Json.NodeType.OBJECT);
var root_obj = new Json.Object ();
root_obj.set_string_member ("type", pkt_type);
root_obj.set_int_member ("id", id);
root_obj.set_object_member ("body", body);
if (this.payload != null) {
root_obj.set_int_member ("payloadSize", (int64) this.payload.size);
var pti = new Json.Object ();
pti.set_int_member ("port", this.payload.port);
root_obj.set_object_member ("payloadTransferInfo", pti);
}
root.set_object (root_obj);
gen.set_root (root);
gen.set_pretty (false);
string data = gen.to_data (null);
return data;
}
}

+ 2
- 2
src/mconnect/packethandlerinterface-proxy.vala View File

@ -22,6 +22,6 @@
* PacketHandlerInterfaceProxy: interface of DBus exported packet handler
*/
interface PacketHandlerInterfaceProxy : Object {
public abstract void bus_register(DBusConnection conn, string path) throws IOError;
public abstract void bus_unregister(DBusConnection conn) throws IOError;
public abstract void bus_register (DBusConnection conn, string path) throws IOError;
public abstract void bus_unregister (DBusConnection conn) throws IOError;
}

+ 3
- 4
src/mconnect/packethandlerinterface.vala View File

@ -20,10 +20,9 @@
interface PacketHandlerInterface : Object {
public abstract string get_pkt_type();
public abstract string get_pkt_type ();
public abstract void use_device(Device dev);
public abstract void release_device(Device dev);
public abstract void use_device (Device dev);
public abstract void release_device (Device dev);
}

+ 24
- 24
src/mconnect/packethandlers-proxy.vala View File

@ -20,29 +20,29 @@
class PacketHandlersProxy : Object {
public static PacketHandlerInterfaceProxy? new_device_capability_handler(
Device dev,
string cap,
PacketHandlerInterface iface) {
public static PacketHandlerInterfaceProxy ? new_device_capability_handler (
Device dev,
string cap,
PacketHandlerInterface iface) {
switch (iface.get_pkt_type()) {
case BatteryHandler.BATTERY: {
return new BatteryHandlerProxy.for_device_handler(dev, iface);
}
case PingHandler.PING: {
return new PingHandlerProxy.for_device_handler(dev, iface);
}
case ShareHandler.SHARE: {
return new ShareHandlerProxy.for_device_handler(dev, iface);
}
case TelephonyHandler.TELEPHONY: {
return new TelephonyHandlerProxy.for_device_handler(dev, iface);
}
default:
warning("cannot register bus handler for %s",
iface.get_pkt_type());
break;
}
return null;
}
switch (iface.get_pkt_type ()) {
case BatteryHandler.BATTERY: {
return new BatteryHandlerProxy.for_device_handler (dev, iface);
}
case PingHandler.PING: {
return new PingHandlerProxy.for_device_handler (dev, iface);
}
case ShareHandler.SHARE: {
return new ShareHandlerProxy.for_device_handler (dev, iface);
}
case TelephonyHandler.TELEPHONY: {
return new TelephonyHandlerProxy.for_device_handler (dev, iface);
}
default:
warning ("cannot register bus handler for %s",
iface.get_pkt_type ());
break;
}
return null;
}
}

+ 48
- 45
src/mconnect/packethandlers.vala View File

@ -21,58 +21,61 @@ using Gee;
class PacketHandlers : Object {
private HashMap<string, PacketHandlerInterface> _handlers;
private HashMap<string, PacketHandlerInterface> _handlers;
public string[] interfaces {
owned get { return _handlers.keys.to_array(); }
private set {}
}
public string[] interfaces {
owned get {
return _handlers.keys.to_array ();
}
private set {
}
}
public PacketHandlers() {
_handlers = load_handlers();
}
public PacketHandlers () {
_handlers = load_handlers ();
}
private static HashMap<string, PacketHandlerInterface> load_handlers() {
HashMap<string, PacketHandlerInterface> hnd =
new HashMap<string, PacketHandlerInterface>();
private static HashMap<string, PacketHandlerInterface> load_handlers () {
HashMap<string, PacketHandlerInterface> hnd =
new HashMap<string, PacketHandlerInterface>();
var notification = NotificationHandler.instance();
var battery = BatteryHandler.instance();
var telephony = TelephonyHandler.instance();
var mousepad = MousepadHandler.instance();
var ping = PingHandler.instance();
var share = ShareHandler.instance();
var notification = NotificationHandler.instance ();
var battery = BatteryHandler.instance ();
var telephony = TelephonyHandler.instance ();
var mousepad = MousepadHandler.instance ();
var ping = PingHandler.instance ();
var share = ShareHandler.instance ();
hnd.@set(notification.get_pkt_type(), notification);
hnd.@set(battery.get_pkt_type(), battery);
hnd.@set(telephony.get_pkt_type(), telephony);
hnd.@set(mousepad.get_pkt_type(), mousepad);
hnd.@set(ping.get_pkt_type(), ping);
hnd.@set(share.get_pkt_type(), share);
hnd.@set (notification.get_pkt_type (), notification);
hnd.@set (battery.get_pkt_type (), battery);
hnd.@set (telephony.get_pkt_type (), telephony);
hnd.@set (mousepad.get_pkt_type (), mousepad);
hnd.@set (ping.get_pkt_type (), ping);
hnd.@set (share.get_pkt_type (), share);
return hnd;
}
return hnd;
}
/**
* SupportedCapabilityFunc:
* @capability: capability name
* @handler: packet handler
*
* User provided callback called when enabling @capability handled
* by @handler for a particular device.
*/
public delegate void SupportedCapabilityFunc(string capability,
PacketHandlerInterface handler);
/**
* SupportedCapabilityFunc:
* @capability: capability name
* @handler: packet handler
*
* User provided callback called when enabling @capability handled
* by @handler for a particular device.
*/
public delegate void SupportedCapabilityFunc (string capability,
PacketHandlerInterface handler);
public PacketHandlerInterface? get_capability_handler(string cap) {
// all handlers are singletones for now
var h = this._handlers.@get(cap);
return h;
}
public PacketHandlerInterface ? get_capability_handler (string cap) {
// all handlers are singletones for now
var h = this._handlers.@get (cap);
return h;
}
public static string to_capability(string pkttype) {
if (pkttype.has_suffix(".request"))
return pkttype.replace(".request", "");
return pkttype;
}
public static string to_capability (string pkttype) {
if (pkttype.has_suffix (".request"))
return pkttype.replace (".request", "");
return pkttype;
}
}

+ 28
- 28
src/mconnect/ping-proxy.vala View File

@ -21,32 +21,32 @@
[DBus (name = "org.mconnect.Device.Ping")]
class PingHandlerProxy : Object, PacketHandlerInterfaceProxy {
private Device device = null;
private PingHandler ping_handler = null;
public PingHandlerProxy.for_device_handler(Device dev,
PacketHandlerInterface iface) {
this.device = dev;
this.ping_handler = (PingHandler) iface;
this.ping_handler.ping.connect(this.ping_cb);
}
[DBus (visible = false)]
public void bus_register(DBusConnection conn, string path) throws IOError {
conn.register_object(path, this);
}
[DBus (visible = false)]
public void bus_unregister(DBusConnection conn) throws IOError {
//conn.unregister_object(this);
}
private void ping_cb(Device dev) {
if (this.device != dev)
return;
ping();
}
public signal void ping();
private Device device = null;
private PingHandler ping_handler = null;
public PingHandlerProxy.for_device_handler (Device dev,
PacketHandlerInterface iface) {
this.device = dev;
this.ping_handler = (PingHandler) iface;
this.ping_handler.ping.connect (this.ping_cb);
}
[DBus (visible = false)]
public void bus_register (DBusConnection conn, string path) throws IOError {
conn.register_object (path, this);
}
[DBus (visible = false)]
public void bus_unregister (DBusConnection conn) throws IOError {
// conn.unregister_object(this);
}
private void ping_cb (Device dev) {
if (this.device != dev)
return;
ping ();
}
public signal void ping ();
}

+ 25
- 26
src/mconnect/ping.vala View File

@ -20,38 +20,37 @@
class PingHandler : Object, PacketHandlerInterface {
public const string PING = "kdeconnect.ping";
public const string PING = "kdeconnect.ping";
public string get_pkt_type() {
return PING;
}
public string get_pkt_type () {
return PING;
}
private PingHandler() {
private PingHandler () {
}
}
public static PingHandler instance () {
return new PingHandler ();
}
public static PingHandler instance() {
return new PingHandler();
}
public void use_device (Device dev) {
debug ("use device %s for ping", dev.to_string ());
dev.message.connect (this.message);
}
public void use_device(Device dev) {
debug("use device %s for ping", dev.to_string());
dev.message.connect(this.message);
}
public void release_device (Device dev) {
debug ("release device %s", dev.to_string ());
dev.message.disconnect (this.message);
}
public void release_device(Device dev) {
debug("release device %s", dev.to_string());
dev.message.disconnect(this.message);
}
public void message (Device dev, Packet pkt) {
if (pkt.pkt_type != PING) {
return;
}
public void message(Device dev, Packet pkt) {
if (pkt.pkt_type != PING) {
return;
}
GLib.message ("ping from device %s", dev.to_string ());
ping (dev);
}
GLib.message("ping from device %s", dev.to_string());
ping(dev);
}
public signal void ping(Device dev);
public signal void ping (Device dev);
}

+ 59
- 59
src/mconnect/property-proxy.vala View File

@ -24,78 +24,78 @@
*/
class DBusPropertyNotifier : Object {
private DBusConnection conn = null;
private string iface = "";
private string path = "";
private VariantBuilder builder = null;
private uint timeout_src = 0;
private DBusConnection conn = null;
private string iface = "";
private string path = "";
private VariantBuilder builder = null;
private uint timeout_src = 0;
public const uint TIMEOUT = 300;
public const uint TIMEOUT = 300;
public DBusPropertyNotifier(DBusConnection conn,
string iface,
string path) {
this.conn = conn;
this.iface = iface;
this.path = path;
}
public DBusPropertyNotifier (DBusConnection conn,
string iface,
string path) {
this.conn = conn;
this.iface = iface;
this.path = path;
}
/**
* queue_property_change:
*
* @name: property name (will be automatically capitalized if needed)
* @val: Variant holding property value
*
* This method will queue up property notifications for sending. By default
* it waits @TIMEOUT ms before sending the actual signal.
*/
public void queue_property_change(string name, Variant val) {
if (this.builder == null) {
this.builder = new VariantBuilder(VariantType.ARRAY);
}
/**
* queue_property_change:
*
* @name: property name (will be automatically capitalized if needed)
* @val: Variant holding property value
*
* This method will queue up property notifications for sending. By default
* it waits @TIMEOUT ms before sending the actual signal.
*/
public void queue_property_change (string name, Variant val) {
if (this.builder == null) {
this.builder = new VariantBuilder (VariantType.ARRAY);
}
string nm = name;
if (name.get_char(0).islower()) {
nm = name.get_char(0).toupper().to_string() + name.substring(1);
}
string nm = name;
if (name.get_char (0).islower ()) {
nm = name.get_char (0).toupper ().to_string () + name.substring (1);
}
this.builder.add("{sv}", nm, val);
this.builder.add ("{sv}", nm, val);
if (this.timeout_src == 0) {
this.timeout_src = Timeout.add(300,
this.send_property_change);
}
}
if (this.timeout_src == 0) {
this.timeout_src = Timeout.add (300,
this.send_property_change);
}
}
/**
* send_property_change:
*
* Send out actual PropertiesChanged signals
*/
private bool send_property_change() {
this.timeout_src = 0;
/**
* send_property_change:
*
* Send out actual PropertiesChanged signals
*/
private bool send_property_change () {
this.timeout_src = 0;
if (this.builder == null)
return false;;
if (this.builder == null)
return false; ;
try {
var invalid_builder = new VariantBuilder(new VariantType ("as"));
var invalid_builder = new VariantBuilder (new VariantType ("as"));
this.conn.emit_signal(null,
this.path,
"org.freedesktop.DBus.Properties",
"PropertiesChanged",
new Variant ("(sa{sv}as)",
this.iface,
builder,
invalid_builder)
);
this.conn.emit_signal (null,
this.path,
"org.freedesktop.DBus.Properties",
"PropertiesChanged",
new Variant ("(sa{sv}as)",
this.iface,
builder,
invalid_builder)
);
} catch (Error e) {
warning("%s\n", e.message);
warning ("%s\n", e.message);
}
this.builder = null;
this.builder = null;
return false;
}
return false;
}
}

+ 31
- 31
src/mconnect/share-proxy.vala View File

@ -21,35 +21,35 @@
[DBus (name = "org.mconnect.Device.Share")]
class ShareHandlerProxy : Object, PacketHandlerInterfaceProxy {
private Device device = null;
private ShareHandler share_handler = null;
public ShareHandlerProxy.for_device_handler(Device dev,
PacketHandlerInterface iface) {
this.device = dev;
this.share_handler = (ShareHandler) iface;
}
[DBus (visible = false)]
public void bus_register(DBusConnection conn, string path) throws IOError {
conn.register_object(path, this);
}
[DBus (visible = false)]
public void bus_unregister(DBusConnection conn) throws IOError {
//conn.unregister_object(this);
}
public void share_file(string path) throws IOError {
this.share_handler.share_file(this.device, path);
}
public void share_url(string url) throws IOError {
debug("share url %s", url);
this.share_handler.share_url(this.device, url);
}
public void share_text(string text) throws IOError {
this.share_handler.share_text(this.device, text);
}
private Device device = null;
private ShareHandler share_handler = null;
public ShareHandlerProxy.for_device_handler (Device dev,
PacketHandlerInterface iface) {
this.device = dev;
this.share_handler = (ShareHandler) iface;
}
[DBus (visible = false)]
public void bus_register (DBusConnection conn, string path) throws IOError {
conn.register_object (path, this);
}
[DBus (visible = false)]
public void bus_unregister (DBusConnection conn) throws IOError {
// conn.unregister_object(this);
}
public void share_file (string path) throws IOError {
this.share_handler.share_file (this.device, path);
}
public void share_url (string url) throws IOError {
debug ("share url %s", url);
this.share_handler.share_url (this.device, url);
}
public void share_text (string text) throws IOError {
this.share_handler.share_text (this.device, text);
}
}

+ 188
- 188
src/mconnect/share.vala View File

@ -18,192 +18,192 @@
class ShareHandler : Object, PacketHandlerInterface {
public const string SHARE = "kdeconnect.share.request";
public const string SHARE_PKT = "kdeconnect.share";
private static string DOWNLOADS = null;
public void use_device(Device dev) {
debug("use device %s for sharing", dev.to_string());
dev.message.connect(this.message);
}
private ShareHandler() {
}
public static ShareHandler instance() {
if (ShareHandler.DOWNLOADS == null) {
ShareHandler.DOWNLOADS = Path.build_filename(
Environment.get_user_special_dir(UserDirectory.DOWNLOAD),
"mconnect");
if (DirUtils.create_with_parents(ShareHandler.DOWNLOADS,
0700) == -1) {
warning("failed to create downloads directory: %s",
Posix.strerror(Posix.errno));
}
}
info("downloads will be saved to %s", ShareHandler.DOWNLOADS);
return new ShareHandler();
}
private static string make_downloads_path(string name) {
return Path.build_filename(ShareHandler.DOWNLOADS, name);
}
public string get_pkt_type() {
return SHARE;
}
public void release_device(Device dev) {
debug("release device %s", dev.to_string());
dev.message.disconnect(this.message);
}
private void message(Device dev, Packet pkt) {
if (pkt.pkt_type != SHARE_PKT && pkt.pkt_type != SHARE) {
return;
}
if (pkt.body.has_member("filename")) {
this.handle_file(dev, pkt);
} else if (pkt.body.has_member("url")) {
this.handle_url(dev, pkt);
} else if (pkt.body.has_member("text")) {
this.handle_text(dev, pkt);
}
}
private void handle_file(Device dev, Packet pkt) {
if (pkt.payload == null) {
warning("missing payload info");
return;
}
string name = pkt.body.get_string_member("filename");
debug("file: %s size: %s", name, format_size(pkt.payload.size));
var t = new DownloadTransfer(
dev,
new InetSocketAddress(dev.host,
(uint16) pkt.payload.port),
pkt.payload.size,
make_downloads_path(name));
Core.instance().transfer_manager.push_job(t);
t.start_async.begin();
}
private void handle_url(Device dev, Packet pkt) {
var url_msg = pkt.body.get_string_member("url");
var urls = Utils.find_urls(url_msg);
if (urls.length > 0) {
var url = urls[0];
debug("got URL: %s, launching...", url);
Utils.show_own_notification("Launching shared URL",
dev.device_name);
AppInfo.launch_default_for_uri(url, null);
}
}
private void handle_text(Device dev, Packet pkt) {
var text = pkt.body.get_string_member("text");
debug("shared text '%s'", text);
var display = Gdk.Display.get_default();
if (display != null) {
var cb = Gtk.Clipboard.get_default(display);
cb.set_text(text, -1);
Utils.show_own_notification("Text copied to clipboard",
dev.device_name);
}
}
private Packet make_share_packet(string name, string data) {
var builder = new Json.Builder();
builder.begin_object();
builder.set_member_name(name);
builder.add_string_value(data);
builder.end_object();
return new Packet(SHARE,
builder.get_root().get_object());
}
private Packet make_file_share_packet(string filename, uint64 size,
uint16 port) {
var builder = new Json.Builder();
builder.begin_object();
builder.set_member_name("filename");
builder.add_string_value(filename);
builder.end_object();
var pkt = new Packet(SHARE,
builder.get_root().get_object());
pkt.payload = Packet.Payload(){
size=size,
port=port
};
return pkt;
}
public void share_url(Device dev, string url) {
debug("share url %s to device %s", url, dev.to_string());
dev.send(make_share_packet("url", url));
}
public void share_text(Device dev, string text) {
debug("share text %s to device %s", text, dev.to_string());
dev.send(make_share_packet("text", text));
}
public void share_file(Device dev, string path) {
debug("share file %s to device %s", path, dev.to_string());
var file = File.new_for_path(path);
uint64 size = 0;
try {
var fi = file.query_info(FileAttribute.STANDARD_SIZE,
FileQueryInfoFlags.NONE);
size = fi.get_size();
} catch (Error e) {
warning("failed to obtain file size: %s", e.message);
return;
}
debug("file size: %llu", size);
if (size == 0) {
warning("trying to share empty file %s", path);
return;
}
FileInputStream input;
try {
input = file.read();
} catch (Error e) {
warning("failed to open source file at path %s: %s",
file.get_path(), e.message);
throw e;
}
uint16 port;
var listener = Core.instance().transfer_manager.make_listener(out port);
if (listener == null) {
warning("coult not allodate a listener");
return;
}
debug("allocated listener on port %u", port);
var t = new UploadTransfer(dev, listener, input, size);
Core.instance().transfer_manager.push_job(t);
t.start_async.begin();
dev.send(make_file_share_packet(file.get_basename(), size, port));
}
public const string SHARE = "kdeconnect.share.request";
public const string SHARE_PKT = "kdeconnect.share";
private static string DOWNLOADS = null;
public void use_device (Device dev) {
debug ("use device %s for sharing", dev.to_string ());
dev.message.connect (this.message);
}
private ShareHandler () {
}
public static ShareHandler instance () {
if (ShareHandler.DOWNLOADS == null) {
ShareHandler.DOWNLOADS = Path.build_filename (
Environment.get_user_special_dir (UserDirectory.DOWNLOAD),
"mconnect");
if (DirUtils.create_with_parents (ShareHandler.DOWNLOADS,
0700) == -1) {
warning ("failed to create downloads directory: %s",
Posix.strerror (Posix.errno));
}
}
info ("downloads will be saved to %s", ShareHandler.DOWNLOADS);
return new ShareHandler ();
}
private static string make_downloads_path (string name) {
return Path.build_filename (ShareHandler.DOWNLOADS, name);
}
public string get_pkt_type () {
return SHARE;
}
public void release_device (Device dev) {
debug ("release device %s", dev.to_string ());
dev.message.disconnect (this.message);
}
private void message (Device dev, Packet pkt) {
if (pkt.pkt_type != SHARE_PKT && pkt.pkt_type != SHARE) {
return;
}
if (pkt.body.has_member ("filename")) {
this.handle_file (dev, pkt);
} else if (pkt.body.has_member ("url")) {
this.handle_url (dev, pkt);
} else if (pkt.body.has_member ("text")) {
this.handle_text (dev, pkt);
}
}
private void handle_file (Device dev, Packet pkt) {
if (pkt.payload == null) {
warning ("missing payload info");
return;
}
string name = pkt.body.get_string_member ("filename");
debug ("file: %s size: %s", name, format_size (pkt.payload.size));
var t = new DownloadTransfer (
dev,
new InetSocketAddress (dev.host,
(uint16) pkt.payload.port),
pkt.payload.size,
make_downloads_path (name));
Core.instance ().transfer_manager.push_job (t);
t.start_async.begin ();
}
private void handle_url (Device dev, Packet pkt) {
var url_msg = pkt.body.get_string_member ("url");
var urls = Utils.find_urls (url_msg);
if (urls.length > 0) {
var url = urls[0];
debug ("got URL: %s, launching...", url);
Utils.show_own_notification ("Launching shared URL",
dev.device_name);
AppInfo.launch_default_for_uri (url, null);
}
}
private void handle_text (Device dev, Packet pkt) {
var text = pkt.body.get_string_member ("text");
debug ("shared text '%s'", text);
var display = Gdk.Display.get_default ();
if (display != null) {
var cb = Gtk.Clipboard.get_default (display);
cb.set_text (text, -1);
Utils.show_own_notification ("Text copied to clipboard",
dev.device_name);
}
}
private Packet make_share_packet (string name, string data) {
var builder = new Json.Builder ();
builder.begin_object ();
builder.set_member_name (name);
builder.add_string_value (data);
builder.end_object ();
return new Packet (SHARE,
builder.get_root ().get_object ());
}
private Packet make_file_share_packet (string filename, uint64 size,
uint16 port) {
var builder = new Json.Builder ();
builder.begin_object ();
builder.set_member_name ("filename");
builder.add_string_value (filename);
builder.end_object ();
var pkt = new Packet (SHARE,
builder.get_root ().get_object ());
pkt.payload = Packet.Payload () {
size = size,
port = port
};
return pkt;
}
public void share_url (Device dev, string url) {
debug ("share url %s to device %s", url, dev.to_string ());
dev.send (make_share_packet ("url", url));
}
public void share_text (Device dev, string text) {
debug ("share text %s to device %s", text, dev.to_string ());
dev.send (make_share_packet ("text", text));
}
public void share_file (Device dev, string path) {
debug ("share file %s to device %s", path, dev.to_string ());
var file = File.new_for_path (path);
uint64 size = 0;
try {
var fi = file.query_info (FileAttribute.STANDARD_SIZE,
FileQueryInfoFlags.NONE);
size = fi.get_size ();
} catch (Error e) {
warning ("failed to obtain file size: %s", e.message);
return;
}
debug ("file size: %llu", size);
if (size == 0) {
warning ("trying to share empty file %s", path);
return;
}
FileInputStream input;
try {
input = file.read ();
} catch (Error e) {
warning ("failed to open source file at path %s: %s",
file.get_path (), e.message);
throw e;
}
uint16 port;
var listener = Core.instance ().transfer_manager.make_listener (out port);
if (listener == null) {
warning ("coult not allodate a listener");
return;
}
debug ("allocated listener on port %u", port);
var t = new UploadTransfer (dev, listener, input, size);
Core.instance ().transfer_manager.push_job (t);
t.start_async.begin ();
dev.send (make_file_share_packet (file.get_basename (), size, port));
}
}

+ 18
- 18
src/mconnect/telephony-proxy.vala View File

@ -19,26 +19,26 @@
[DBus (name = "org.mconnect.Device.Telephony")]
class TelephonyHandlerProxy : Object, PacketHandlerInterfaceProxy {
private Device device = null;
private TelephonyHandler telephony = null;
private Device device = null;
private TelephonyHandler telephony = null;
public TelephonyHandlerProxy.for_device_handler(Device dev,
PacketHandlerInterface iface) {
this.device = dev;
this.telephony = (TelephonyHandler) iface;
}
public TelephonyHandlerProxy.for_device_handler (Device dev,
PacketHandlerInterface iface) {
this.device = dev;
this.telephony = (TelephonyHandler) iface;
}
[DBus (visible = false)]
public void bus_register(DBusConnection conn, string path) throws IOError {
conn.register_object(path, this);
}
[DBus (visible = false)]
public void bus_register (DBusConnection conn, string path) throws IOError {
conn.register_object (path, this);
}
[DBus (visible = false)]
public void bus_unregister(DBusConnection conn) throws IOError {
// conn.unregister_object(this);
}
[DBus (visible = false)]
public void bus_unregister (DBusConnection conn) throws IOError {
// conn.unregister_object(this);
}
public void send_sms(string number, string message) {
this.telephony.send_sms(this.device, number, message);
}
public void send_sms (string number, string message) {
this.telephony.send_sms (this.device, number, message);
}
}

+ 104
- 106
src/mconnect/telephony.vala View File

@ -1,4 +1,4 @@
/* ex:ts=4:sw=4:sts=4:et */
/* 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
@ -21,109 +21,107 @@ using Mconn;
class TelephonyHandler : Object, PacketHandlerInterface {
public const string TELEPHONY = "kdeconnect.telephony";
public const string SMS_REQUEST = "kdeconnect.sms.request";
public string get_pkt_type() {
return TELEPHONY;
}
private TelephonyHandler() {
}
public static TelephonyHandler instance() {
return new TelephonyHandler();
}
public void use_device(Device dev) {
dev.message.connect(this.message);
}
public void release_device(Device dev) {
dev.message.disconnect(this.message);
}
public void message(Device dev, Packet pkt) {
if (pkt.pkt_type != TELEPHONY) {
return;
}
debug("got telephony packet");
if (pkt.body.has_member("phoneNumber") == false ||
pkt.body.has_member("event") == false)
return;
string number = pkt.body.get_string_member("phoneNumber");
string ev = pkt.body.get_string_member("event");
// string ticker = convert_to_utf8(raw_ticker);
GLib.message("call from %s, status %s", number, ev);
// handle only missed call and ringing events
const string[] accepted_events = {"ringing", "missedCall"};
if (ev in accepted_events) {
string summary = "Other event";
if (ev == "ringing")
summary = "Incoming call";
if (ev == "missedCall")
summary = "Missed call";
// check if ringing was cancelled
if (ev == "missedCall" && pkt.body.has_member("isCancel")) {
bool cancelled = pkt.body.get_boolean_member("isCancel");
if (cancelled == true) {
debug("call cancelled");
return;
}
}
// telephony packets have no time information
var time = new DateTime.now_local();
number = "%s %s".printf(time.format("%X"), number);
var notif = new Notify.Notification(summary, number,
"phone");
try {
notif.show();
} catch (Error e) {
critical("failed to show notification: %s", e.message);
}
}
}
/**
* make_sms_packet:
* @number: recipient's number
* @message: message
*
* @return allocated packet
*/
private Packet make_sms_packet(string number, string message) {
var builder = new Json.Builder();
builder.begin_object();
builder.set_member_name("sendSms");
builder.add_boolean_value(true);
builder.set_member_name("phoneNumber");
builder.add_string_value(number);
builder.set_member_name("messageBody");
builder.add_string_value(message);
builder.end_object();
return new Packet(SMS_REQUEST,
builder.get_root().get_object());
}
/**
* send_sms:
*
* Reques to send an SMS to @number with message @message.
*/
public void send_sms(Device dev, string number, string message) {
dev.send(make_sms_packet(number, message));
}
public const string TELEPHONY = "kdeconnect.telephony";
public const string SMS_REQUEST = "kdeconnect.sms.request";
public string get_pkt_type () {
return TELEPHONY;
}
private TelephonyHandler () {
}
public static TelephonyHandler instance () {
return new TelephonyHandler ();
}
public void use_device (Device dev) {
dev.message.connect (this.message);
}
public void release_device (Device dev) {
dev.message.disconnect (this.message);
}
public void message (Device dev, Packet pkt) {
if (pkt.pkt_type != TELEPHONY) {
return;
}
debug ("got telephony packet");
if (pkt.body.has_member ("phoneNumber") == false ||
pkt.body.has_member ("event") == false)
return;
string number = pkt.body.get_string_member ("phoneNumber");
string ev = pkt.body.get_string_member ("event");
// string ticker = convert_to_utf8(raw_ticker);
GLib.message ("call from %s, status %s", number, ev);
// handle only missed call and ringing events
const string[] accepted_events = { "ringing", "missedCall" };
if (ev in accepted_events) {
string summary = "Other event";
if (ev == "ringing")
summary = "Incoming call";
if (ev == "missedCall")
summary = "Missed call";
// check if ringing was cancelled
if (ev == "missedCall" && pkt.body.has_member ("isCancel")) {
bool cancelled = pkt.body.get_boolean_member ("isCancel");
if (cancelled == true) {
debug ("call cancelled");
return;
}
}
// telephony packets have no time information
var time = new DateTime.now_local ();
number = "%s %s".printf (time.format ("%X"), number);
var notif = new Notify.Notification (summary, number,
"phone");
try {
notif.show ();
} catch (Error e) {
critical ("failed to show notification: %s", e.message);
}
}
}
/**
* make_sms_packet:
* @number: recipient's number
* @message: message
*
* @return allocated packet
*/
private Packet make_sms_packet (string number, string message) {
var builder = new Json.Builder ();
builder.begin_object ();
builder.set_member_name ("sendSms");
builder.add_boolean_value (true);
builder.set_member_name ("phoneNumber");
builder.add_string_value (number);
builder.set_member_name ("messageBody");
builder.add_string_value (message);
builder.end_object ();
return new Packet (SMS_REQUEST,
builder.get_root ().get_object ());
}
/**
* send_sms:
*
* Reques to send an SMS to @number with message @message.
*/
public void send_sms (Device dev, string number, string message) {
dev.send (make_sms_packet (number, message));
}
}

+ 166
- 166
src/mconnect/transfer-download.vala View File

@ -18,170 +18,170 @@
class DownloadTransfer : TransferInterface, Object {
private InetSocketAddress isa = null;
private File file = null;
private FileOutputStream foutstream = null;
private Cancellable cancellable = null;
private SocketConnection conn = null;
private TlsConnection tls_conn = null;
public uint64 size = 0;
public uint64 transferred = 0;
public string destination = "";
private IOCopyJob job = null;
private Device device = null;
public DownloadTransfer(Device dev, InetSocketAddress isa,
uint64 size, string dest) {
this.isa = isa;
this.cancellable = new Cancellable();
this.destination = dest;
this.size = size;
this.device = dev;
}
public async bool start_async() {
try {
this.file = File.new_for_path(this.destination + ".part");
this.foutstream = this.file.replace(null, false,
FileCreateFlags.PRIVATE | FileCreateFlags.REPLACE_DESTINATION);
} catch (Error e) {
warning("failed to open destination path %s: %s",
this.destination, e.message);
return false;
}
debug("start transfer from %s:%u",
this.isa.address.to_string(), this.isa.port);
var client = new SocketClient();
try {
this.conn = yield client.connect_async(this.isa);
debug("connected");
} catch (Error e) {
var err ="failed to connect: %s".printf(e.message);
warning(err);
this.cleanup_error(err);
return false;
}
var sock = this.conn.get_socket();
Utils.socket_set_keepalive(sock);
// enable TLS
this.tls_conn = Utils.make_tls_connection(this.conn,
Core.instance().certificate,
this.device.certificate,
Utils.TlsConnectionMode.CLIENT);
try {
debug("attempt TLS handshake");
var tls_res = yield this.tls_conn.handshake_async();
debug("TLS handshake complete");
} catch (Error e) {
var err ="TLS handshake failed: %s".printf(e.message);
warning(err);
this.cleanup_error(err);
return false;
}
this.start_transfer();
return true;
}
private void start_transfer() {
debug("connected, start transfer");
this.job = new IOCopyJob(this.tls_conn.input_stream,
this.foutstream);
this.job.progress.connect((t, done) => {
int percent = (int) (100.0 * ((double)done / (double)this.size));
debug("progress: %s/%s %d%%",
format_size(done), format_size(this.size), percent);
this.transferred = done;
});
this.started();
this.job.start_async.begin(this.cancellable,
this.job_complete);
}
private void job_complete(Object? obj, AsyncResult res) {
info("transfer finished");
try {
var rcvd_bytes = this.job.start_async.end(res);
debug("transfer done, got %s", format_size(rcvd_bytes));
this.cleanup_success();
} catch (Error err) {
warning("transfer failed: %s", err.message);
this.cleanup_error(err.message);
}
}
private void cleanup() {
if (this.foutstream != null) {
try {
this.foutstream.close();
} catch (IOError e) {
warning("failed to close file output: %s",
e.message);
}
}
if (this.tls_conn != null) {
try {
this.tls_conn.close();
} catch (IOError e) {
warning("failed to close TLS connection: %s",
e.message);
}
}
if (this.conn != null) {
try {
this.conn.close();
} catch (IOError e) {
warning("failed to close connection: %s",
e.message);
}
}
this.file = null;
this.foutstream = null;
this.conn = null;
this.tls_conn = null;
this.job = null;
}
private void cleanup_error(string reason) {
this.file.@delete();
this.cleanup();
this.error(reason);
}
private void cleanup_success() {
try {
var dest = File.new_for_path(this.destination);
this.file.move(dest, FileCopyFlags.OVERWRITE);
this.cleanup();
this.finished();
} catch (Error e) {
var err = "failed to rename temporary file %s to %s: %s".printf(this.file.get_path(),
this.destination,
e.message);
warning(err);
this.cleanup_error(err);
}
}
public void cancel() {
debug("cancel called");
this.cancellable.cancel();
}
private InetSocketAddress isa = null;
private File file = null;
private FileOutputStream foutstream = null;
private Cancellable cancellable = null;
private SocketConnection conn = null;
private TlsConnection tls_conn = null;
public uint64 size = 0;
public uint64 transferred = 0;
public string destination = "";
private IOCopyJob job = null;
private Device device = null;
public DownloadTransfer (Device dev, InetSocketAddress isa,
uint64 size, string dest) {
this.isa = isa;
this.cancellable = new Cancellable ();
this.destination = dest;
this.size = size;
this.device = dev;
}
public async bool start_async () {
try {
this.file = File.new_for_path (this.destination + ".part");
this.foutstream = this.file.replace (null, false,
FileCreateFlags.PRIVATE | FileCreateFlags.REPLACE_DESTINATION);
} catch (Error e) {
warning ("failed to open destination path %s: %s",
this.destination, e.message);
return false;
}
debug ("start transfer from %s:%u",
this.isa.address.to_string (), this.isa.port);
var client = new SocketClient ();
try {
this.conn = yield client.connect_async (this.isa);
debug ("connected");
} catch (Error e) {
var err = "failed to connect: %s".printf (e.message);
warning (err);
this.cleanup_error (err);
return false;
}
var sock = this.conn.get_socket ();
Utils.socket_set_keepalive (sock);
// enable TLS
this.tls_conn = Utils.make_tls_connection (this.conn,
Core.instance ().certificate,
this.device.certificate,
Utils.TlsConnectionMode.CLIENT);
try {
debug ("attempt TLS handshake");
var tls_res = yield this.tls_conn.handshake_async ();
debug ("TLS handshake complete");
} catch (Error e) {
var err = "TLS handshake failed: %s".printf (e.message);
warning (err);
this.cleanup_error (err);
return false;
}
this.start_transfer ();
return true;
}
private void start_transfer () {
debug ("connected, start transfer");
this.job = new IOCopyJob (this.tls_conn.input_stream,
this.foutstream);
this.job.progress.connect ((t, done) => {
int percent = (int) (100.0 * ((double) done / (double) this.size));
debug ("progress: %s/%s %d%%",
format_size (done), format_size (this.size), percent);
this.transferred = done;
});
this.started ();
this.job.start_async.begin (this.cancellable,
this.job_complete);
}
private void job_complete (Object ? obj, AsyncResult res) {
info ("transfer finished");
try {
var rcvd_bytes = this.job.start_async.end (res);
debug ("transfer done, got %s", format_size (rcvd_bytes));
this.cleanup_success ();
} catch (Error err) {
warning ("transfer failed: %s", err.message);
this.cleanup_error (err.message);
}
}
private void cleanup () {
if (this.foutstream != null) {
try {
this.foutstream.close ();
} catch (IOError e) {
warning ("failed to close file output: %s",
e.message);
}
}
if (this.tls_conn != null) {
try {
this.tls_conn.close ();
} catch (IOError e) {
warning ("failed to close TLS connection: %s",
e.message);
}
}
if (this.conn != null) {
try {
this.conn.close ();
} catch (IOError e) {
warning ("failed to close connection: %s",
e.message);
}
}
this.file = null;
this.foutstream = null;
this.conn = null;
this.tls_conn = null;
this.job = null;
}
private void cleanup_error (string reason) {
this.file.@delete ();
this.cleanup ();
this.error (reason);
}
private void cleanup_success () {
try {
var dest = File.new_for_path (this.destination);
this.file.move (dest, FileCopyFlags.OVERWRITE);
this.cleanup ();
this.finished ();
} catch (Error e) {
var err = "failed to rename temporary file %s to %s: %s".printf (this.file.get_path (),
this.destination,
e.message);
warning (err);
this.cleanup_error (err);
}
}
public void cancel () {
debug ("cancel called");
this.cancellable.cancel ();
}
}

+ 6
- 7
src/mconnect/transfer-interface.vala View File

@ -18,12 +18,11 @@
interface TransferInterface : Object {
public abstract async bool start_async();
public abstract async bool start_async ();
public abstract void cancel();
public abstract void cancel ();
public signal void started();
public signal void finished();
public signal void error(string reason);
}
public signal void started ();
public signal void finished ();
public signal void error (string reason);
}

+ 88
- 89
src/mconnect/transfer-manager-proxy.vala View File

@ -21,92 +21,91 @@ using Gee;
[DBus (name = "org.mconnect.TransferManager")]
class TransferManagerDBusProxy : Object {
private TransferManager manager;
private DBusConnection bus;
private int job_idx = 0;
private const string DBUS_PATH = "/org/mconnect/transfer";
private HashMap<string, TransferDBusProxy> jobs;
public TransferManagerDBusProxy.with_manager(DBusConnection conn,
TransferManager manager) {
this.jobs = new HashMap<string, TransferDBusProxy>();
this.bus = conn;
manager.new_transfer.connect(this.handle_new_transfer);
}
[DBus (visible = false)]
public void publish() throws IOError {
assert(this.bus != null);
this.bus.register_object(DBUS_PATH, this);
}
/**
* list_jobs:
*
* Returns a list of DBus paths of all known transfer jobs
*/
public ObjectPath[] list_jobs() {
ObjectPath[] jobs = {};
foreach (var path in this.jobs.keys) {
jobs += new ObjectPath(path);
}
return jobs;
}
private void handle_new_transfer(Object? mgr, TransferInterface job) {
var path = make_transfer_path();
var tproxy = new TransferDBusProxy.for_transfer_with_path(job,
new ObjectPath(path));
this.jobs.@set(path, tproxy);
tproxy.bus_register(this.bus);
job.started.connect((_) => {
this.transfer_started(path);
});
job.finished.connect((o) => {
this.handle_transfer_done(path);
});
job.error.connect((o, err) => {
this.handle_transfer_failed(path, err);
});
}
private string make_transfer_path() {
var path = "/org/mconnect/transfer/%d".printf(this.job_idx);
// bump jobs index
this.job_idx++;
return path;
}
private void handle_transfer_done(string path) {
// var jp = this.find_proxy_for_job(TransferInterface(obj));
var jp = this.jobs.@get(path);
assert(jp != null);
jp.bus_unregister(this.bus);
this.transfer_finished(path);
}
private void handle_transfer_failed(string path, string err) {
// var jp = this.find_proxy_for_job(TransferInterface(obj));
var jp = this.jobs.@get(path);
assert(jp != null);
jp.bus_unregister(this.bus);
this.transfer_failed(path, err);
}
public signal void transfer_finished(string path);
public signal void transfer_failed(string path, string reason);
public signal void transfer_started(string path);
}
private TransferManager manager;
private DBusConnection bus;
private int job_idx = 0;
private const string DBUS_PATH = "/org/mconnect/transfer";
private HashMap<string, TransferDBusProxy> jobs;
public TransferManagerDBusProxy.with_manager (DBusConnection conn,
TransferManager manager) {
this.jobs = new HashMap<string, TransferDBusProxy>();
this.bus = conn;
manager.new_transfer.connect (this.handle_new_transfer);
}
[DBus (visible = false)]
public void publish () throws IOError {
assert (this.bus != null);
this.bus.register_object (DBUS_PATH, this);
}
/**
* list_jobs:
*
* Returns a list of DBus paths of all known transfer jobs
*/
public ObjectPath[] list_jobs () {
ObjectPath[] jobs = {};
foreach (var path in this.jobs.keys) {
jobs += new ObjectPath (path);
}
return jobs;
}
private void handle_new_transfer (Object ? mgr, TransferInterface job) {
var path = make_transfer_path ();
var tproxy = new TransferDBusProxy.for_transfer_with_path (job,
new ObjectPath (path));
this.jobs.@set (path, tproxy);
tproxy.bus_register (this.bus);
job.started.connect ((_) => {
this.transfer_started (path);
});
job.finished.connect ((o) => {
this.handle_transfer_done (path);
});
job.error.connect ((o, err) => {
this.handle_transfer_failed (path, err);
});
}
private string make_transfer_path () {
var path = "/org/mconnect/transfer/%d".printf (this.job_idx);
// bump jobs index
this.job_idx++;
return path;
}
private void handle_transfer_done (string path) {
// var jp = this.find_proxy_for_job(TransferInterface(obj));
var jp = this.jobs.@get (path);
assert (jp != null);
jp.bus_unregister (this.bus);
this.transfer_finished (path);
}
private void handle_transfer_failed (string path, string err) {
// var jp = this.find_proxy_for_job(TransferInterface(obj));
var jp = this.jobs.@get (path);
assert (jp != null);
jp.bus_unregister (this.bus);
this.transfer_failed (path, err);
}
public signal void transfer_finished (string path);
public signal void transfer_failed (string path, string reason);
public signal void transfer_started (string path);
}

+ 30
- 31
src/mconnect/transfer-manager.vala View File

@ -18,39 +18,38 @@
class TransferManager : Object {
public const uint16 PORT_MIN = 9970;
public const uint16 PORT_MAX = 9975;
public const uint16 PORT_MIN = 9970;
public const uint16 PORT_MAX = 9975;
public signal void new_transfer(TransferInterface job);
public signal void new_transfer (TransferInterface job);
public TransferManager() {
public TransferManager () {
}
}
public void push_job (TransferInterface job) {
debug ("new transfer job");
new_transfer (job);
}
public void push_job(TransferInterface job) {
debug("new transfer job");
new_transfer(job);
}
public SocketService? make_listener(out uint16 listen_port) {
var ss = new SocketService();
for (var port = PORT_MIN; port <= PORT_MAX; port++) {
var added = false;
try {
added = ss.add_inet_port(port, null);
} catch (Error e) {
if (e is IOError.ADDRESS_IN_USE) {
warning("port %u in use, trying another", port);
}
}
if (added == true) {
debug("allocated listener on port %u", port);
listen_port = port;
return ss;
}
}
ss.close();
warning("could not find a free port to listen on");
return null;
}
public SocketService ? make_listener (out uint16 listen_port) {
var ss = new SocketService ();
for (var port = PORT_MIN; port <= PORT_MAX; port++) {
var added = false;
try {
added = ss.add_inet_port (port, null);
} catch (Error e) {
if (e is IOError.ADDRESS_IN_USE) {
warning ("port %u in use, trying another", port);
}
}
if (added == true) {
debug ("allocated listener on port %u", port);
listen_port = port;
return ss;
}
}
ss.close ();
warning ("could not find a free port to listen on");
return null;
}
}

+ 29
- 27
src/mconnect/transfer-proxy.vala View File

@ -17,36 +17,38 @@
*/
[DBus (name = "org.mconnect.Transfer")]
class TransferDBusProxy: Object {
class TransferDBusProxy : Object {
[DBus (visible = false)]
public TransferInterface transfer { get; private set; default = null; }
[DBus (visible = false)]
public TransferInterface transfer {
get; private set; default = null;
}
private ObjectPath object_path = null;
private uint register_id = 0;
private ObjectPath object_path = null;
private uint register_id = 0;
public TransferDBusProxy.for_transfer_with_path(TransferInterface transfer,
ObjectPath path) {
this.transfer = transfer;
this.object_path = path;
}
public TransferDBusProxy.for_transfer_with_path (TransferInterface transfer,
ObjectPath path) {
this.transfer = transfer;
this.object_path = path;
}
[DBus (visible = false)]
public void bus_register(DBusConnection conn) {
debug("register transfer at path %s", this.object_path.to_string());
this.register_id = conn.register_object(this.object_path, this);
}
[DBus (visible = false)]
public void bus_register (DBusConnection conn) {
debug ("register transfer at path %s", this.object_path.to_string ());
this.register_id = conn.register_object (this.object_path, this);
}
[DBus (visible = false)]
public void bus_unregister(DBusConnection conn) {
if (this.register_id != 0) {
debug("unregister transfer at path %s", this.object_path.to_string());
conn.unregister_object(this.register_id);
}
}
[DBus (visible = false)]
public void bus_unregister (DBusConnection conn) {
if (this.register_id != 0) {
debug ("unregister transfer at path %s", this.object_path.to_string ());
conn.unregister_object (this.register_id);
}
}
public void cancel() {
debug("cancelling job");
this.transfer.cancel();
}
}
public void cancel () {
debug ("cancelling job");
this.transfer.cancel ();
}
}

+ 165
- 165
src/mconnect/transfer-upload.vala View File

@ -18,169 +18,169 @@
class UploadTransfer : TransferInterface, Object {
private FileInputStream finstream = null;
private Cancellable cancellable = null;
private Device device = null;
private TlsConnection tls_conn = null;
private SocketService listener = null;
private uint timeout_source = 0;
private IOCopyJob job = null;
private SocketConnection conn = null;
private uint64 transferred = 0;
private uint64 size;
private const int WAIT_TIMEOUT = 30;
public UploadTransfer(Device dev, SocketService listener,
FileInputStream source, uint64 size) {
this.listener = listener;
this.cancellable = new Cancellable();
this.device = dev;
this.finstream = source;
this.size = size;
}
public async bool start_async() {
debug("start transfer from to device %s",
this.device.to_string());
this.listener.incoming.connect(this.client_connected);
debug("wait for client");
this.timeout_source = Timeout.add_seconds(WAIT_TIMEOUT,
this.wait_timeout);
this.listener.start();
return true;
}
private bool wait_timeout() {
warning("timeout waiting for client");
this.listener.stop();
this.cleanup_error("timeout waiting for client");
return false;
}
private bool client_connected(SocketConnection conn, Object? source) {
if (this.timeout_source != 0) {
Source.remove(this.timeout_source);
this.timeout_source = 0;
}
this.handle_client.begin(conn);
return false;
}
private async void handle_client(SocketConnection conn) {
var isa = conn.get_remote_address() as InetSocketAddress;
debug("client connected: %s:%u", isa.address.to_string(),
isa.port);
this.conn = conn;
var sock = this.conn.get_socket();
Utils.socket_set_keepalive(sock);
// enable TLS
this.tls_conn = Utils.make_tls_connection(this.conn,
Core.instance().certificate,
this.device.certificate,
Utils.TlsConnectionMode.SERVER);
try {
debug("attempt TLS handshake");
var tls_res = yield this.tls_conn.handshake_async();
debug("TLS handshake complete");
} catch (Error e) {
var err ="TLS handshake failed: %s".printf(e.message);
warning(err);
this.cleanup_error(err);
return;
}
this.start_transfer();
}
private void start_transfer() {
debug("connected, start transfer");
this.job = new IOCopyJob(this.finstream,
this.tls_conn.output_stream);
this.job.progress.connect((t, done) => {
int percent = (int) (100.0 * ((double)done / (double)this.size));
debug("progress: %s/%s %d%%",
format_size(done), format_size(this.size), percent);
this.transferred = done;
});
this.started();
this.job.start_async.begin(this.cancellable,
this.job_complete);
}
private void job_complete(Object? obj, AsyncResult res) {
info("transfer finished");
try {
var rcvd_bytes = this.job.start_async.end(res);
debug("transfer done, got %s", format_size(rcvd_bytes));
this.cleanup_success();
} catch (Error err) {
warning("transfer failed: %s", err.message);
this.cleanup_error(err.message);
}
}
private void cleanup() {
if (this.finstream != null) {
try {
this.finstream.close();
} catch (IOError e) {
warning("failed to close file input: %s",
e.message);
}
}
if (this.tls_conn != null) {
try {
this.tls_conn.close();
} catch (IOError e) {
warning("failed to close TLS connection: %s",
e.message);
}
}
if (this.conn != null) {
try {
this.conn.close();
} catch (IOError e) {
warning("failed to close connection: %s",
e.message);
}
}
this.listener.stop();
this.listener.close();
this.finstream = null;
this.conn = null;
this.tls_conn = null;
this.job = null;
}
private void cleanup_error(string reason) {
this.cleanup();
this.error(reason);
}
private void cleanup_success() {
this.cleanup();
this.finished();
}
public void cancel() {
debug("cancel called");
this.cancellable.cancel();
}
private FileInputStream finstream = null;
private Cancellable cancellable = null;
private Device device = null;
private TlsConnection tls_conn = null;
private SocketService listener = null;
private uint timeout_source = 0;
private IOCopyJob job = null;
private SocketConnection conn = null;
private uint64 transferred = 0;
private uint64 size;
private const int WAIT_TIMEOUT = 30;
public UploadTransfer (Device dev, SocketService listener,
FileInputStream source, uint64 size) {
this.listener = listener;
this.cancellable = new Cancellable ();
this.device = dev;
this.finstream = source;
this.size = size;
}
public async bool start_async () {
debug ("start transfer from to device %s",
this.device.to_string ());
this.listener.incoming.connect (this.client_connected);
debug ("wait for client");
this.timeout_source = Timeout.add_seconds (WAIT_TIMEOUT,
this.wait_timeout);
this.listener.start ();
return true;
}
private bool wait_timeout () {
warning ("timeout waiting for client");
this.listener.stop ();
this.cleanup_error ("timeout waiting for client");
return false;
}
private bool client_connected (SocketConnection conn, Object ? source) {
if (this.timeout_source != 0) {
Source.remove (this.timeout_source);
this.timeout_source = 0;
}
this.handle_client.begin (conn);
return false;
}
private async void handle_client (SocketConnection conn) {
var isa = conn.get_remote_address () as InetSocketAddress;
debug ("client connected: %s:%u", isa.address.to_string (),
isa.port);
this.conn = conn;
var sock = this.conn.get_socket ();
Utils.socket_set_keepalive (sock);
// enable TLS
this.tls_conn = Utils.make_tls_connection (this.conn,
Core.instance ().certificate,
this.device.certificate,
Utils.TlsConnectionMode.SERVER);
try {
debug ("attempt TLS handshake");
var tls_res = yield this.tls_conn.handshake_async ();
debug ("TLS handshake complete");
} catch (Error e) {
var err = "TLS handshake failed: %s".printf (e.message);
warning (err);
this.cleanup_error (err);
return;
}
this.start_transfer ();
}
private void start_transfer () {
debug ("connected, start transfer");
this.job = new IOCopyJob (this.finstream,
this.tls_conn.output_stream);
this.job.progress.connect ((t, done) => {
int percent = (int) (100.0 * ((double) done / (double) this.size));
debug ("progress: %s/%s %d%%",
format_size (done), format_size (this.size), percent);
this.transferred = done;
});
this.started ();
this.job.start_async.begin (this.cancellable,
this.job_complete);
}
private void job_complete (Object ? obj, AsyncResult res) {
info ("transfer finished");
try {
var rcvd_bytes = this.job.start_async.end (res);
debug ("transfer done, got %s", format_size (rcvd_bytes));
this.cleanup_success ();
} catch (Error err) {
warning ("transfer failed: %s", err.message);
this.cleanup_error (err.message);
}
}
private void cleanup () {
if (this.finstream != null) {
try {
this.finstream.close ();
} catch (IOError e) {
warning ("failed to close file input: %s",
e.message);
}
}
if (this.tls_conn != null) {
try {
this.tls_conn.close ();
} catch (IOError e) {
warning ("failed to close TLS connection: %s",
e.message);
}
}
if (this.conn != null) {
try {
this.conn.close ();
} catch (IOError e) {
warning ("failed to close connection: %s",
e.message);
}
}
this.listener.stop ();
this.listener.close ();
this.finstream = null;
this.conn = null;
this.tls_conn = null;
this.job = null;
}
private void cleanup_error (string reason) {
this.cleanup ();
this.error (reason);
}
private void cleanup_success () {
this.cleanup ();
this.finished ();
}
public void cancel () {
debug ("cancel called");
this.cancellable.cancel ();
}
}

+ 173
- 173
src/mconnect/utils.vala View File

@ -1,5 +1,3 @@
/* 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
@ -20,182 +18,184 @@
namespace Utils {
using Posix;
/**
* make_unique_device_string:
* @id: device ID
* @name: device name
* @type: device type
* @pv: protocol version
*
* Generate device string that can be used as map index
*/
string make_unique_device_string(string id, string name,
string type, uint pv) {
return make_device_string(id, name, type, pv).replace(" ", "-");
}
/**
* make_device_string:
* @id: device ID
* @name: device name
* @type: device type
* @pv: protocol version
*
* Generate device string
*/
string make_device_string(string id, string name,
string type, uint pv) {
return "%s-%s-%s-%u".printf(id, name, type, pv);
}
/**
* socket_set_keepalive:
* @sock: socket
*
* Set keepalive counters on socket
*/
void socket_set_keepalive(Socket sock) {
using Posix;
/**
* make_unique_device_string:
* @id: device ID
* @name: device name
* @type: device type
* @pv: protocol version
*
* Generate device string that can be used as map index
*/
string make_unique_device_string (string id, string name,
string type, uint pv) {
return make_device_string (id, name, type, pv).replace (" ", "-");
}
/**
* make_device_string:
* @id: device ID
* @name: device name
* @type: device type
* @pv: protocol version
*
* Generate device string
*/
string make_device_string (string id, string name,
string type, uint pv) {
return "%s-%s-%s-%u".printf (id, name, type, pv);
}
/**
* socket_set_keepalive:
* @sock: socket
*
* Set keepalive counters on socket
*/
void socket_set_keepalive (Socket sock) {
#if 0
IPPROTO_TCP = 6, /* Transmission Control Protocol. */
IPPROTO_TCP = 6, /* Transmission Control Protocol. */
TCP_KEEPIDLE 4 /* Start keeplives after this period */
TCP_KEEPINTVL 5 /* Interval between keepalives */
TCP_KEEPCNT 6 /* Number of keepalives before death */
TCP_KEEPIDLE 4 /* Start keeplives after this period */
TCP_KEEPINTVL 5 /* Interval between keepalives */
TCP_KEEPCNT 6 /* Number of keepalives before death */
#endif
#if 0
int option = 10;
Posix.setsockopt(sock.fd, 6, 4, &option, (Posix.socklen_t) sizeof(int));
option = 5;
Posix.setsockopt(sock.fd, 6, 5, &option, (Posix.socklen_t) sizeof(int));
option = 3;
Posix.setsockopt(sock.fd, 6, 6, &option, (Posix.socklen_t) sizeof(int));
int option = 10;
Posix.setsockopt (sock.fd, 6, 4, &option, (Posix.socklen_t) sizeof (int));
option = 5;
Posix.setsockopt (sock.fd, 6, 5, &option, (Posix.socklen_t) sizeof (int));
option = 3;
Posix.setsockopt (sock.fd, 6, 6, &option, (Posix.socklen_t) sizeof (int));
#endif
int option = 10;
Posix.setsockopt(sock.fd, IPProto.TCP,
Posix.TCP_KEEPIDLE,
&option, (Posix.socklen_t) sizeof(int));
option = 5;
Posix.setsockopt(sock.fd, IPProto.TCP,
Posix.TCP_KEEPINTVL,
&option, (Posix.socklen_t) sizeof(int));
option = 3;
Posix.setsockopt(sock.fd, IPProto.TCP,
Posix.TCP_KEEPCNT,
&option, (Posix.socklen_t) sizeof(int));
// enable keepalive
sock.set_keepalive(true);
}
public enum TlsConnectionMode {
SERVER,
CLIENT,
}
/**
* make_tls_connection:
*
* Create a TLS connection around given connected socket.
* When @expected_peer is non-null, the handshake will be rejected if the
* certificate presented by peer is different from expected.
*
* @sock_conn: connected socket
* @self_cert: own certificate
* @expected_peer: expected peer certificate
* @is_client_connection: if true then TLS client side connection is prepared
*
* @return new TlsConnection
*/
TlsConnection make_tls_connection(SocketConnection sock_conn,
TlsCertificate self_cert,
TlsCertificate? expected_peer = null,
TlsConnectionMode mode = TlsConnectionMode.SERVER) {
TlsConnection tls_conn;
if (mode == TlsConnectionMode.SERVER) {
debug("creating TLS server connection");
var tls_serv = TlsServerConnection.@new(sock_conn, self_cert);
tls_serv.authentication_mode = TlsAuthenticationMode.REQUESTED;
tls_conn = tls_serv;
} else {
debug("creating TLS client connection");
tls_conn = TlsClientConnection.@new(sock_conn,
sock_conn.get_remote_address());
tls_conn.set_certificate(self_cert);
}
tls_conn.accept_certificate.connect((peer_cert, errors) => {
info("accept certificate, flags: 0x%x", errors);
info("certificate:\n%s\n", peer_cert.certificate_pem);
if (expected_peer != null) {
if (Logging.VERBOSE) {
vdebug("verify certificate, expecting: %s, got: %s",
expected_peer.certificate_pem,
peer_cert.certificate_pem);
}
if (expected_peer.is_same(peer_cert)) {
return true;
} else {
warning("rejecting handshare, peer certificate mismatch, got:\n%s",
peer_cert.certificate_pem);
return false;
}
}
return true;
});
return tls_conn;
}
/**
* find_urls:
*
* Locate and extract URL like patterns in the text. URLs are assumed to
* start with http or https.
*
* @text: input test
* @return array of matches, if there were none then array if of length 0
*/
string[] find_urls(string text) {
try {
// regex taken from SO
int option = 10;
Posix.setsockopt (sock.fd, IPProto.TCP,
Posix.TCP_KEEPIDLE,
&option, (Posix.socklen_t) sizeof (int));
option = 5;
Posix.setsockopt (sock.fd, IPProto.TCP,
Posix.TCP_KEEPINTVL,
&option, (Posix.socklen_t) sizeof (int));
option = 3;
Posix.setsockopt (sock.fd, IPProto.TCP,
Posix.TCP_KEEPCNT,
&option, (Posix.socklen_t) sizeof (int));
// enable keepalive
sock.set_keepalive (true);
}
public enum TlsConnectionMode {
SERVER,
CLIENT,
}
/**
* make_tls_connection:
*
* Create a TLS connection around given connected socket.
* When @expected_peer is non-null, the handshake will be rejected if the
* certificate presented by peer is different from expected.
*
* @sock_conn: connected socket
* @self_cert: own certificate
* @expected_peer: expected peer certificate
* @is_client_connection: if true then TLS client side connection is prepared
*
* @return new TlsConnection
*/
TlsConnection make_tls_connection (SocketConnection sock_conn,
TlsCertificate self_cert,
TlsCertificate ? expected_peer = null,
TlsConnectionMode mode = TlsConnectionMode.SERVER) {
TlsConnection tls_conn;
if (mode == TlsConnectionMode.SERVER) {
debug ("creating TLS server connection");
var tls_serv = TlsServerConnection.@new (sock_conn, self_cert);
tls_serv.authentication_mode = TlsAuthenticationMode.REQUESTED;
tls_conn = tls_serv;
} else {
debug ("creating TLS client connection");
tls_conn = TlsClientConnection.@new (sock_conn,
sock_conn.get_remote_address ());
tls_conn.set_certificate (self_cert);
}
tls_conn.accept_certificate.connect ((peer_cert, errors) => {
info ("accept certificate, flags: 0x%x", errors);
info ("certificate:\n%s\n", peer_cert.certificate_pem);
if (expected_peer != null) {
if (Logging.VERBOSE) {
vdebug ("verify certificate, expecting: %s, got: %s",
expected_peer.certificate_pem,
peer_cert.certificate_pem);
}
if (expected_peer.is_same (peer_cert)) {
return true;
} else {
warning ("rejecting handshare, peer certificate mismatch, got:\n%s",
peer_cert.certificate_pem);
return false;
}
}
return true;
});
return tls_conn;
}
/**
* find_urls:
*
* Locate and extract URL like patterns in the text. URLs are assumed to
* start with http or https.
*
* @text: input test
* @return array of matches, if there were none then array if of length 0
*/
string[] find_urls (string text) {
try {
// regex taken from SO
// uncrustify breaks the regex, so *INDENT-OFF*
Regex r = /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+,.~#?&\/=]*)/;
MatchInfo mi;
string[] matches = {};
if (r.match(text, RegexMatchFlags.NOTEMPTY, out mi)) {
while (mi.matches()) {
if (mi.is_partial_match() == false) {
var m = mi.fetch(0);
debug("found match %s", m);
matches += m;
}
mi.next();
}
} else {
debug("no match");
}
return matches;
} catch (RegexError e) {
warning("failed to compile regex: %s", e.message);
return null;
}
}
public void show_own_notification(string message,
string summary = "mconnect",
string icon = "dialog-information") {
try {
var notif = new Notify.Notification(summary, message,
"phone");
notif.show();
} catch (Error e) {
critical("failed to show notification: %s", e.message);
}
}
// *INDENT-ON*
MatchInfo mi;
string[] matches = {};
if (r.match (text, RegexMatchFlags.NOTEMPTY, out mi)) {
while (mi.matches ()) {
if (mi.is_partial_match () == false) {
var m = mi.fetch (0);
debug ("found match %s", m);
matches += m;
}
mi.next ();
}
} else {
debug ("no match");
}
return matches;
} catch (RegexError e) {
warning ("failed to compile regex: %s", e.message);
return null;
}
}
public void show_own_notification (string message,
string summary = "mconnect",
string icon = "dialog-information") {
try {
var notif = new Notify.Notification (summary, message,
"phone");
notif.show ();
} catch (Error e) {
critical ("failed to show notification: %s", e.message);
}
}
}

+ 39
- 16
src/mconnectctl/device-iface.vala View File

@ -17,21 +17,44 @@
*/
namespace Mconnect {
[DBus (name = "org.mconnect.Device")]
public interface DeviceIface : Object {
public abstract string id { owned get;}
public abstract string name { owned get;}
public abstract string device_type { owned get;}
public abstract uint protocol_version { owned get;}
public abstract string address { owned get;}
public abstract bool is_paired { owned get;}
public abstract bool allowed { owned get;}
public abstract bool is_active { owned get;}
public abstract bool is_connected { owned get;}
public abstract string[] outgoing_capabilities { owned get;}
public abstract string[] incoming_capabilities { owned get;}
public abstract string certificate { owned get;}
}
[DBus (name = "org.mconnect.Device")]
public interface DeviceIface : Object {
public abstract string id {
owned get;
}
public abstract string name {
owned get;
}
public abstract string device_type {
owned get;
}
public abstract uint protocol_version {
owned get;
}
public abstract string address {
owned get;
}
public abstract bool is_paired {
owned get;
}
public abstract bool allowed {
owned get;
}
public abstract bool is_active {
owned get;
}
public abstract bool is_connected {
owned get;
}
public abstract string[] outgoing_capabilities {
owned get;
}
public abstract string[] incoming_capabilities {
owned get;
}
public abstract string certificate {
owned get;
}
}
}

+ 6
- 6
src/mconnectctl/device-manager-iface.vala View File

@ -17,12 +17,12 @@
*/
namespace Mconnect {
[DBus (name = "org.mconnect.DeviceManager")]
public interface DeviceManagerIface : Object {
[DBus (name = "org.mconnect.DeviceManager")]
public interface DeviceManagerIface : Object {
public const string OBJECT_PATH = "/org/mconnect/manager";
public const string OBJECT_PATH = "/org/mconnect/manager";
public abstract ObjectPath[] ListDevices() throws IOError;
public abstract void AllowDevice(string path) throws IOError;
}
public abstract ObjectPath[] ListDevices () throws IOError;
public abstract void AllowDevice (string path) throws IOError;
}
}

+ 351
- 352
src/mconnectctl/main.vala View File

@ -20,53 +20,51 @@
namespace Mconnect {
public class Client {
private static bool log_debug = false;
private static bool verbose = false;
// some hints for valac about the array holding remaining args
[CCode (array_length = false, array_null_terminated = true)]
private static string[] remaining;
private BusType bus_type = BusType.SESSION;
private const OptionEntry[] options = {
{"debug", 'd', 0, OptionArg.NONE, ref log_debug,
"Show debug output", null},
{"verbose", 'v', 0, OptionArg.NONE, ref verbose,
"Be verbose", null},
// there's no Vala const for G_OPTION_REMAINING (which is a #define
// for "")
{"", 0, 0, OptionArg.STRING_ARRAY, ref remaining, null,
"[COMMAND ..]"},
{null}
};
/**
* Command:
*
* command line 'command' wrapper
*/
private struct Command {
string command; // textual command, ex. list, show, etc.
int arg_count; // number of required parameters, not including
// command
unowned CommandFunc clbk; // callback
Command(string command, int arg_count, CommandFunc clbk) {
this.command = command;
this.arg_count = arg_count;
this.clbk = clbk;
}
}
// command callback
private delegate int CommandFunc(string[] args);
public static int main(string[] args)
{
try {
var opt_context = new OptionContext();
opt_context.set_description(
"""Available commands:
public class Client {
private static bool log_debug = false;
private static bool verbose = false;
// some hints for valac about the array holding remaining args
[CCode (array_length = false, array_null_terminated = true)]
private static string[] remaining;
private BusType bus_type = BusType.SESSION;
private const OptionEntry[] options = {
{ "debug", 'd', 0, OptionArg.NONE, ref log_debug,
"Show debug output", null },
{ "verbose", 'v', 0, OptionArg.NONE, ref verbose,
"Be verbose", null },
// there's no Vala const for G_OPTION_REMAINING (which is a #define
// for "")
{ "", 0, 0, OptionArg.STRING_ARRAY, ref remaining, null,
"[COMMAND ..]" },
{ null }
};
/**
* Command:
*
* command line 'command' wrapper
*/
private struct Command {
string command; // textual command, ex. list, show, etc.
int arg_count; // number of required parameters, not including command
unowned CommandFunc clbk; // callback
Command (string command, int arg_count, CommandFunc clbk) {
this.command = command;
this.arg_count = arg_count;
this.clbk = clbk;
}
}
// command callback
private delegate int CommandFunc (string[] args);
public static int main (string[] args) {
try {
var opt_context = new OptionContext ();
opt_context.set_description (
"""Available commands:
list-devices List devices
allow-device <path> Allow device
show-device <path> Show device details
@ -77,308 +75,309 @@ namespace Mconnect {
send-sms <number> <message> Send SMS
"""
);
opt_context.set_help_enabled(true);
opt_context.add_main_entries(options, null);
opt_context.parse(ref args);
} catch (OptionError e) {
stdout.printf("error: %s\n", e.message);
stdout.printf("Run '%s --help' to see a full " +
"list of available command line options.\n",
args[0]);
return 1;
}
if (log_debug == true)
Environment.set_variable("G_MESSAGES_DEBUG", "all", false);
var cl = new Client();
Command[] commands = {
Command("list-devices", 0, cl.cmd_list_devices),
Command("allow-device", 1, cl.cmd_allow_device),
Command("show-device", 1, cl.cmd_show_device),
Command("share-url", 2, cl.cmd_share_url),
Command("share-text", 2, cl.cmd_share_text),
Command("share-file", 2, cl.cmd_share_file),
Command("send-sms", 3, cl.cmd_send_sms),
};
handle_command(remaining, commands);
return 0;
}
/**
* handle_command:
* @args: remaining command line arguments
* @commands: supported commands array
*
* @return exit status of command or -1 on error
*/
private static int handle_command(string[] args, Command[] commands) {
// extract command and it's arguments if any
string command = "list-devices";
if (args.length > 0)
command = remaining[0];
debug("command is: %s", command);
string[] command_args = {};
if (args.length > 1)
command_args = args[1:args.length];
foreach (var cmden in commands) {
if (cmden.command == command) {
debug("found match for %s, args expect: %zd, have: %zd",
command, cmden.arg_count, command_args.length);
if (command_args.length != cmden.arg_count) {
stderr.printf("Incorrect number of arguments " +
"for command %s, see --help\n",
command);
return -1;
}
debug("running callback");
return cmden.clbk(command_args);
}
}
stderr.printf("Incorrect command, see --help\n");
return -1;
}
private int cmd_list_devices(string[] args) {
return checked_dbus_call(() => {
var manager = get_manager();
debug("list devices");
var devs = manager.ListDevices();
print_paths(devs, "Devices",
(path) => {
try {
var dp = get_device(path);
return "%s - %s".printf(dp.id, dp.name);
} catch (IOError e) {
warning("error occurred: %s", e.message);
return "(error)";
}
});
return 0;
});
}
private int cmd_allow_device(string[] args) {
return checked_dbus_call(() => {
var dp = args[0];
var manager = get_manager();
debug("allow device device %s", dp);
manager.AllowDevice(new ObjectPath(dp));
return 0;
});
}
private int cmd_share_url(string[] args) {
return checked_dbus_call(() => {
var dp = args[0];
var share = get_share(new ObjectPath(dp));
share.share_url(args[1]);
return 0;
});
}
private int cmd_share_text(string[] args) {
return checked_dbus_call(() => {
var dp = args[0];
var share = get_share(new ObjectPath(dp));
share.share_text(args[1]);
return 0;
});
}
private int cmd_share_file(string[] args) {
return checked_dbus_call(() => {
var dp = args[0];
var share = get_share(new ObjectPath(dp));
var file = File.new_for_path(args[1]);
var path = file.get_path();
debug("share path: %s", path);
share.share_file(path);
return 0;
});
}
private int cmd_send_sms(string[] args) {
return checked_dbus_call(() => {
var dp = args[0];
var number = args[1];
var message = args[2];
var telephony = get_telephony(new ObjectPath(dp));
telephony.send_sms(number, message);
return 0;
});
}
private void print_sorted_caps(string[] caps, string format) {
qsort_with_data<string>(caps, sizeof(string),
(a, b) => GLib.strcmp(a, b));
foreach (var cap in caps) {
stdout.printf(format, cap);
}
}
private int cmd_show_device(string[] args) {
return checked_dbus_call(() => {
var dp = get_device(new ObjectPath(args[0]));
stdout.printf("Device\n" +
" Name: %s\n" +
" ID: %s\n" +
" Address: %s\n" +
" Type: %s\n" +
" Allowed: %s\n" +
" Paired: %s\n" +
" Active: %s\n" +
" Connected: %s\n",
dp.name,
dp.id,
dp.address,
dp.device_type,
dp.allowed.to_string(),
dp.is_paired.to_string(),
dp.is_active.to_string(),
dp.is_connected.to_string());
if (verbose) {
stdout.printf(" Capabilities (out):\n");
print_sorted_caps(dp.outgoing_capabilities, " %s\n");
stdout.printf(" Capabilities (in):\n");
print_sorted_caps(dp.incoming_capabilities, " %s\n");
stdout.printf(" Certificate:\n%s\n", dp.certificate);
}
return 0;
});
}
private delegate int CheckDBusCallFunc() throws Error;
/**
* checked_dbus_call:
* @clbk: function to wrap
*
* Catch any DBus errors and return appropriate status
*/
private static int checked_dbus_call(CheckDBusCallFunc clbk) {
try {
return clbk();
} catch (IOError e) {
warning("communication returned an error: %s", e.message);
return -1;
} catch (DBusError e) {
warning("communication with service failed: %s", e.message);
} catch (Error e) {
warning("error: %s", e.message);
}
return 0;
}
/**
* get_mconnect_obj_proxy:
* @path: DBus object path
*
* Obtain an interface to a DBus object avaialble at
* Mconnect service under @path.
*
* @return null or interface
*/
private T? get_mconnect_obj_proxy<T>(ObjectPath path) throws IOError {
T proxy_out = null;
try {
proxy_out = Bus.get_proxy_sync(bus_type,
"org.mconnect",
path);
} catch (IOError e) {
warning("failed to obtain proxy to mconnect service: %s",
e.message);
throw e;
}
return proxy_out;
}
/**
* get_manager:
*
* Obtain DBus interface to Device Manager
*
* @return interface or null
*/
private DeviceManagerIface? get_manager() throws IOError {
return get_mconnect_obj_proxy(
new ObjectPath(DeviceManagerIface.OBJECT_PATH));
}
/**
* get_device:
* @path device object path
*
* Obtain DBus interface to Device
*
* @return interface or null
*/
private DeviceIface? get_device(ObjectPath path) throws IOError {
return get_mconnect_obj_proxy(path);
}
/**
* get_share:
*
* Obtain DBus interface to Share of given device
*
* @return interface or null
*/
private ShareIface? get_share(ObjectPath path) throws IOError {
return get_mconnect_obj_proxy(path);
}
/**
* get_telephony:
*
* Obtain DBus interface to Telephony of given device
*
* @return interface or null
*/
private TelephonyIface? get_telephony(ObjectPath path) throws IOError {
return get_mconnect_obj_proxy(path);
}
/**
* print_paths:
* @objs: object paths
* @header: header for printing,
* @desc_clbk: callback for producing a meaningful description
*
* Print a list of object paths, possibly adding a description
*/
private static void print_paths(ObjectPath[] objs, string header,
GetDescFunc desc_clbk) {
if (objs.length == 0)
stdout.printf("No objects were found\n");
else {
stdout.printf(header + ":\n");
foreach (var o in objs) {
string desc = null;
if (desc_clbk != null) {
debug("calling description callback for obj: %s",
o.to_string());
desc = desc_clbk(o);
}
stdout.printf(" %s", o.to_string());
if (desc != null)
stdout.printf(" %s", desc);
stdout.printf("\n");
}
}
}
private delegate string GetDescFunc(ObjectPath obj_path);
}
}
);
opt_context.set_help_enabled (true);
opt_context.add_main_entries (options, null);
opt_context.parse (ref args);
} catch (OptionError e) {
stdout.printf ("error: %s\n", e.message);
stdout.printf ("Run '%s --help' to see a full " +
"list of available command line options.\n",
args[0]);
return 1;
}
if (log_debug == true)
Environment.set_variable ("G_MESSAGES_DEBUG", "all", false);
var cl = new Client ();
Command[] commands = {
Command ("list-devices", 0, cl.cmd_list_devices),
Command ("allow-device", 1, cl.cmd_allow_device),
Command ("show-device", 1, cl.cmd_show_device),
Command ("share-url", 2, cl.cmd_share_url),
Command ("share-text", 2, cl.cmd_share_text),
Command ("share-file", 2, cl.cmd_share_file),
Command ("send-sms", 3, cl.cmd_send_sms),
};
handle_command (remaining, commands);
return 0;
}
/**
* handle_command:
* @args: remaining command line arguments
* @commands: supported commands array
*
* @return exit status of command or -1 on error
*/
private static int handle_command (string[] args, Command[] commands) {
// extract command and it's arguments if any
string command = "list-devices";
if (args.length > 0)
command = remaining[0];
debug ("command is: %s", command);
string[] command_args = {};
if (args.length > 1)
command_args = args[1 : args.length];
foreach (var cmden in commands) {
if (cmden.command == command) {
debug ("found match for %s, args expect: %zd, have: %zd",
command, cmden.arg_count, command_args.length);
if (command_args.length != cmden.arg_count) {
stderr.printf ("Incorrect number of arguments " +
"for command %s, see --help\n",
command);
return -1;
}
debug ("running callback");
return cmden.clbk (command_args);
}
}
stderr.printf ("Incorrect command, see --help\n");
return -1;
}
private int cmd_list_devices (string[] args) {
return checked_dbus_call (() => {
var manager = get_manager ();
debug ("list devices");
var devs = manager.ListDevices ();
print_paths (devs, "Devices",
(path) => {
try {
var dp = get_device (path);
return "%s - %s".printf (dp.id, dp.name);
} catch (IOError e) {
warning ("error occurred: %s", e.message);
return "(error)";
}
});
return 0;
});
}
private int cmd_allow_device (string[] args) {
return checked_dbus_call (() => {
var dp = args[0];
var manager = get_manager ();
debug ("allow device device %s", dp);
manager.AllowDevice (new ObjectPath (dp));
return 0;
});
}
private int cmd_share_url (string[] args) {
return checked_dbus_call (() => {
var dp = args[0];
var share = get_share (new ObjectPath (dp));
share.share_url (args[1]);
return 0;
});
}
private int cmd_share_text (string[] args) {
return checked_dbus_call (() => {
var dp = args[0];
var share = get_share (new ObjectPath (dp));
share.share_text (args[1]);
return 0;
});
}
private int cmd_share_file (string[] args) {
return checked_dbus_call (() => {
var dp = args[0];
var share = get_share (new ObjectPath (dp));
var file = File.new_for_path (args[1]);
var path = file.get_path ();
debug ("share path: %s", path);
share.share_file (path);
return 0;
});
}
private int cmd_send_sms (string[] args) {
return checked_dbus_call (() => {
var dp = args[0];
var number = args[1];
var message = args[2];
var telephony = get_telephony (new ObjectPath (dp));
telephony.send_sms (number, message);
return 0;
});
}
private void print_sorted_caps (string[] caps, string format) {
qsort_with_data<string>(caps, sizeof (string),
(a, b) => GLib.strcmp (a, b));
foreach (var cap in caps) {
stdout.printf (format, cap);
}
}
private int cmd_show_device (string[] args) {
return checked_dbus_call (() => {
var dp = get_device (new ObjectPath (args[0]));
stdout.printf ("Device\n" +
" Name: %s\n" +
" ID: %s\n" +
" Address: %s\n" +
" Type: %s\n" +
" Allowed: %s\n" +
" Paired: %s\n" +
" Active: %s\n" +
" Connected: %s\n",
dp.name,
dp.id,
dp.address,
dp.device_type,
dp.allowed.to_string (),
dp.is_paired.to_string (),
dp.is_active.to_string (),
dp.is_connected.to_string ());
if (verbose) {
stdout.printf (" Capabilities (out):\n");
print_sorted_caps (dp.outgoing_capabilities, " %s\n");
stdout.printf (" Capabilities (in):\n");
print_sorted_caps (dp.incoming_capabilities, " %s\n");
stdout.printf (" Certificate:\n%s\n", dp.certificate);
}
return 0;
});
}
private delegate int CheckDBusCallFunc () throws Error;
/**
* checked_dbus_call:
* @clbk: function to wrap
*
* Catch any DBus errors and return appropriate status
*/
private static int checked_dbus_call (CheckDBusCallFunc clbk) {
try {
return clbk ();
} catch (IOError e) {
warning ("communication returned an error: %s", e.message);
return -1;
} catch (DBusError e) {
warning ("communication with service failed: %s", e.message);
} catch (Error e) {
warning ("error: %s", e.message);
}
return 0;
}
/**
* get_mconnect_obj_proxy:
* @path: DBus object path
*
* Obtain an interface to a DBus object avaialble at
* Mconnect service under @path.
*
* @return null or interface
*/
private T ? get_mconnect_obj_proxy<T>(ObjectPath path) throws IOError {
T proxy_out = null;
try {
proxy_out = Bus.get_proxy_sync (bus_type,
"org.mconnect",
path);
} catch (IOError e) {
warning ("failed to obtain proxy to mconnect service: %s",
e.message);
throw e;
}
return proxy_out;
}
/**
* get_manager:
*
* Obtain DBus interface to Device Manager
*
* @return interface or null
*/
private DeviceManagerIface ? get_manager () throws IOError {
return get_mconnect_obj_proxy (
new ObjectPath (DeviceManagerIface.OBJECT_PATH));
}
/**
* get_device:
* @path device object path
*
* Obtain DBus interface to Device
*
* @return interface or null
*/
private DeviceIface ? get_device (ObjectPath path) throws IOError {
return get_mconnect_obj_proxy (path);
}
/**
* get_share:
*
* Obtain DBus interface to Share of given device
*
* @return interface or null
*/
private ShareIface ? get_share (ObjectPath path) throws IOError {
return get_mconnect_obj_proxy (path);
}
/**
* get_telephony:
*
* Obtain DBus interface to Telephony of given device
*
* @return interface or null
*/
private TelephonyIface ? get_telephony (ObjectPath path) throws IOError {
return get_mconnect_obj_proxy (path);
}
/**
* print_paths:
* @objs: object paths
* @header: header for printing,
* @desc_clbk: callback for producing a meaningful description
*
* Print a list of object paths, possibly adding a description
*/
private static void print_paths (ObjectPath[] objs, string header,
GetDescFunc desc_clbk) {
if (objs.length == 0)
stdout.printf ("No objects were found\n");
else {
stdout.printf (header + ":\n");
foreach (var o in objs) {
string desc = null;
if (desc_clbk != null) {
debug ("calling description callback for obj: %s",
o.to_string ());
desc = desc_clbk (o);
}
stdout.printf (" %s", o.to_string ());
if (desc != null)
stdout.printf (" %s", desc);
stdout.printf ("\n");
}
}
}
private delegate string GetDescFunc (ObjectPath obj_path);
}
}

+ 6
- 7
src/mconnectctl/share-iface.vala View File

@ -18,12 +18,11 @@
namespace Mconnect {
[DBus (name = "org.mconnect.Device.Share")]
public interface ShareIface : Object {
public abstract void share_url(string url) throws IOError;
public abstract void share_text(string text) throws IOError;
public abstract void share_file(string path) throws IOError;
}
[DBus (name = "org.mconnect.Device.Share")]
public interface ShareIface : Object {
public abstract void share_url (string url) throws IOError;
public abstract void share_text (string text) throws IOError;
public abstract void share_file (string path) throws IOError;
}
}

+ 5
- 6
src/mconnectctl/telephony-iface.vala View File

@ -18,11 +18,10 @@
namespace Mconnect {
[DBus (name = "org.mconnect.Device.Telephony")]
public interface TelephonyIface : Object {
public abstract void send_sms(string number,
string message) throws IOError;
}
[DBus (name = "org.mconnect.Device.Telephony")]
public interface TelephonyIface : Object {
public abstract void send_sms (string number,
string message) throws IOError;
}
}

+ 100
- 98
test/mconn-crypt-vala-test.vala View File

@ -1,93 +1,95 @@
using Mconn;
void test_generate() {
string key_path = "/tmp/test-key-vala.pem";
string cert_path = "/tmp/test-cert-vala.pem";
FileUtils.remove(key_path);
FileUtils.remove(cert_path);
assert(FileUtils.test(key_path, FileTest.EXISTS) == false);
try {
Crypt.generate_key_cert(key_path, cert_path, "foo");
} catch (Error e) {
warning("generate failed: %s", e.message);
Test.fail();
}
assert(FileUtils.test(key_path, FileTest.EXISTS) == true);
assert(FileUtils.test(cert_path, FileTest.EXISTS) == true);
void test_generate () {
string key_path = "/tmp/test-key-vala.pem";
string cert_path = "/tmp/test-cert-vala.pem";
FileUtils.remove (key_path);
FileUtils.remove (cert_path);
assert (FileUtils.test (key_path, FileTest.EXISTS) == false);
try {
Crypt.generate_key_cert (key_path, cert_path, "foo");
} catch (Error e) {
warning ("generate failed: %s", e.message);
Test.fail ();
}
assert (FileUtils.test (key_path, FileTest.EXISTS) == true);
assert (FileUtils.test (cert_path, FileTest.EXISTS) == true);
}
void test_generate_load() {
string key_path = "/tmp/test-key-vala.pem";
string cert_path = "/tmp/test-cert-vala.pem";
FileUtils.remove(key_path);
FileUtils.remove(cert_path);
try {
Crypt.generate_key_cert(key_path, cert_path, "bar");
} catch (Error e) {
warning("generate failed: %s", e.message);
Test.fail();
}
try {
var cert = new TlsCertificate.from_files(cert_path,
key_path);
} catch (Error e) {
warning("load from files failed: %s", e.message);
Test.fail();
}
void test_generate_load () {
string key_path = "/tmp/test-key-vala.pem";
string cert_path = "/tmp/test-cert-vala.pem";
FileUtils.remove (key_path);
FileUtils.remove (cert_path);
try {
Crypt.generate_key_cert (key_path, cert_path, "bar");
} catch (Error e) {
warning ("generate failed: %s", e.message);
Test.fail ();
}
try {
var cert = new TlsCertificate.from_files (cert_path,
key_path);
} catch (Error e) {
warning ("load from files failed: %s", e.message);
Test.fail ();
}
}
void test_custom_cn() {
string key_path = "/tmp/test-key-vala.pem";
string cert_path = "/tmp/test-cert-vala.pem";
FileUtils.remove(key_path);
FileUtils.remove(cert_path);
try {
Crypt.generate_key_cert(key_path, cert_path, "custom-cn");
} catch (Error e) {
warning("generate failed: %s", e.message);
Test.fail();
}
uint8[] data;
try {
File.new_for_path(cert_path).load_contents(null, out data, null);
} catch (Error e) {
warning("load contents failed: %s", e.message);
Test.fail();
}
var datum = GnuTLS.Datum() { data=data, size=data.length };
var cert = GnuTLS.X509.Certificate.create();
var res = cert.import(ref datum, GnuTLS.X509.CertificateFormat.PEM);
assert(res == GnuTLS.ErrorCode.SUCCESS);
// verify DN
var dn = new uint8[1024];
size_t sz = dn.length;
cert.get_dn(dn, ref sz);
debug("dn: %s\n", (string)dn);
var issuer_dn = new uint8[1024];
sz = issuer_dn.length;
cert.get_issuer_dn(issuer_dn, ref sz);
debug("dn: %s\n", (string)issuer_dn);
var subject = (string)dn;
var issuer = (string)issuer_dn;
// verify that the certificate is self signed
assert(subject == issuer);
//
assert("CN=custom-cn" in subject);
void test_custom_cn () {
string key_path = "/tmp/test-key-vala.pem";
string cert_path = "/tmp/test-cert-vala.pem";
FileUtils.remove (key_path);
FileUtils.remove (cert_path);
try {
Crypt.generate_key_cert (key_path, cert_path, "custom-cn");
} catch (Error e) {
warning ("generate failed: %s", e.message);
Test.fail ();
}
uint8[] data;
try {
File.new_for_path (cert_path).load_contents (null, out data, null);
} catch (Error e) {
warning ("load contents failed: %s", e.message);
Test.fail ();
}
var datum = GnuTLS.Datum () {
data = data, size = data.length
};
var cert = GnuTLS.X509.Certificate.create ();
var res = cert.import (ref datum, GnuTLS.X509.CertificateFormat.PEM);
assert (res == GnuTLS.ErrorCode.SUCCESS);
// verify DN
var dn = new uint8[1024];
size_t sz = dn.length;
cert.get_dn (dn, ref sz);
debug ("dn: %s\n", (string) dn);
var issuer_dn = new uint8[1024];
sz = issuer_dn.length;
cert.get_issuer_dn (issuer_dn, ref sz);
debug ("dn: %s\n", (string) issuer_dn);
var subject = (string) dn;
var issuer = (string) issuer_dn;
// verify that the certificate is self signed
assert (subject == issuer);
//
assert ("CN=custom-cn" in subject);
}
void test_fingerprint() {
var pem = """-----BEGIN CERTIFICATE-----
void test_fingerprint () {
var pem = """-----BEGIN CERTIFICATE-----
MIIC8jCCAdoCAQowDQYJKoZIhvcNAQEFBQAwPzERMA8GA1UEChMIbWNvbm5lY3Qx
ETAPBgNVBAsTCG1jb25uZWN0MRcwFQYDVQQDDA5tYWNpZWtAY29yc2FpcjAeFw0x
NzA5MjQxOTU3NDVaFw0yNzA5MjQxOTU3NDVaMD8xETAPBgNVBAoTCG1jb25uZWN0
@ -105,26 +107,26 @@ sCmsSGcb9ZkEQfRNGTmFFthkcnfTU9mKh8oGc/a9r0DDgYcPSCgqERt2fgiBrt85
/PXFBB3q2nX2XXqFRhqeN9eOlHBQ5EoZh8GUp7vJyxp5eAS9g2KVtCBwTDElQt4D
4hu+QuzzEmoWY9w1R+hblNu/37mWkzFFrLqYlkNU2vbKkuWMOTg=
-----END CERTIFICATE-----""";
var expected = "eb2611a447085322b206fa61d4bc5869b4a55657";
var expected = "eb2611a447085322b206fa61d4bc5869b4a55657";
var fingerprint = Crypt.fingerprint_certificate(pem);
// SHA1
assert(fingerprint.length == 20);
var fingerprint = Crypt.fingerprint_certificate (pem);
// SHA1
assert (fingerprint.length == 20);
var sb = new StringBuilder.sized(20*2);
foreach(var b in fingerprint) {
sb.append_printf("%02x", b);
}
var sb = new StringBuilder.sized (20 * 2);
foreach (var b in fingerprint) {
sb.append_printf ("%02x", b);
}
assert(sb.str == expected);
assert (sb.str == expected);
}
public static void main(string[] args) {
Test.init(ref args);
public static void main (string[] args) {
Test.init (ref args);
Test.add_func("/mconn-crypt-vala/generated", test_generate);
Test.add_func("/mconn-crypt-vala/load", test_generate_load);
Test.add_func("/mconn-crypt-vala/verify-cn", test_custom_cn);
Test.add_func("/mconn-crypt-vala/fingerprint", test_fingerprint);
Test.run();
Test.add_func ("/mconn-crypt-vala/generated", test_generate);
Test.add_func ("/mconn-crypt-vala/load", test_generate_load);
Test.add_func ("/mconn-crypt-vala/verify-cn", test_custom_cn);
Test.add_func ("/mconn-crypt-vala/fingerprint", test_fingerprint);
Test.run ();
}

+ 34
- 36
test/mconn-utils-test.vala View File

@ -1,57 +1,55 @@
void test_find_urls_simple() {
var urls = Utils.find_urls("https://en.m.wikipedia.org/wiki/Isle_of_Man via DuckDuckGo for Android");
void test_find_urls_simple () {
var urls = Utils.find_urls ("https://en.m.wikipedia.org/wiki/Isle_of_Man via DuckDuckGo for Android");
assert(urls != null);
assert(urls.length == 1);
assert (urls != null);
assert (urls.length == 1);
assert(urls[0] == "https://en.m.wikipedia.org/wiki/Isle_of_Man");
assert (urls[0] == "https://en.m.wikipedia.org/wiki/Isle_of_Man");
}
void test_find_urls_extract() {
var urls = Utils.find_urls("Foo bar baz?\n\nhttp://foo.bar.com/123/345/abcd\n\nShared from my Google cards");
void test_find_urls_extract () {
var urls = Utils.find_urls ("Foo bar baz?\n\nhttp://foo.bar.com/123/345/abcd\n\nShared from my Google cards");
assert(urls != null);
assert(urls.length == 1);
assert (urls != null);
assert (urls.length == 1);
assert(urls[0] == "http://foo.bar.com/123/345/abcd");
assert (urls[0] == "http://foo.bar.com/123/345/abcd");
}
void test_find_urls_many() {
var urls = Utils.find_urls("https://foo.bar.com http://google.biz http://www.funny.io");
void test_find_urls_many () {
var urls = Utils.find_urls ("https://foo.bar.com http://google.biz http://www.funny.io");
assert(urls != null);
assert(urls.length == 3);
assert (urls != null);
assert (urls.length == 3);
assert(urls[0] == "https://foo.bar.com");
assert(urls[1] == "http://google.biz");
assert(urls[2] == "http://www.funny.io");
assert (urls[0] == "https://foo.bar.com");
assert (urls[1] == "http://google.biz");
assert (urls[2] == "http://www.funny.io");
}
void test_find_urls_none() {
var urls = Utils.find_urls("baz bar \nbar.com foo ");
void test_find_urls_none () {
var urls = Utils.find_urls ("baz bar \nbar.com foo ");
assert(urls != null);
assert(urls.length == 0);
assert (urls != null);
assert (urls.length == 0);
}
void test_find_urls_special() {
var urls = Utils.find_urls("http://foo.bar.com/123,345%20,,,/foo.html");
void test_find_urls_special () {
var urls = Utils.find_urls ("http://foo.bar.com/123,345%20,,,/foo.html");
assert(urls != null);
assert(urls.length == 1);
assert (urls != null);
assert (urls.length == 1);
assert(urls[0] == "http://foo.bar.com/123,345%20,,,/foo.html");
assert (urls[0] == "http://foo.bar.com/123,345%20,,,/foo.html");
}
public static void main (string[] args) {
Test.init (ref args);
public static void main(string[] args) {
Test.init(ref args);
Test.add_func("/mconn-utils/find-urls/simple", test_find_urls_simple);
Test.add_func("/mconn-utils/find-urls/extract", test_find_urls_extract);
Test.add_func("/mconn-utils/find-urls/many", test_find_urls_many);
Test.add_func("/mconn-utils/find-urls/none", test_find_urls_none);
Test.add_func("/mconn-utils/find-urls/special", test_find_urls_special);
Test.run();
Test.add_func ("/mconn-utils/find-urls/simple", test_find_urls_simple);
Test.add_func ("/mconn-utils/find-urls/extract", test_find_urls_extract);
Test.add_func ("/mconn-utils/find-urls/many", test_find_urls_many);
Test.add_func ("/mconn-utils/find-urls/none", test_find_urls_none);
Test.add_func ("/mconn-utils/find-urls/special", test_find_urls_special);
Test.run ();
}

+ 1645
- 0
uncrustify.cfg
File diff suppressed because it is too large
View File


Loading…
Cancel
Save