/* * Copyright 2016 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * Authors: * Charles Kerr */ #include #include #include #include /*** **** ***/ class UsbSnap::Impl { public: explicit Impl(const std::string& fingerprint): m_fingerprint{fingerprint}, m_cancellable{g_cancellable_new()} { g_bus_get (G_BUS_TYPE_SESSION, m_cancellable, on_bus_ready_static, this); } ~Impl() { g_cancellable_cancel(m_cancellable); g_clear_object(&m_cancellable); if (m_subscription_id != 0) g_dbus_connection_signal_unsubscribe (m_bus, m_subscription_id); if (m_notification_id != 0) { GError* error {}; g_dbus_connection_call_sync(m_bus, DBusNames::Notify::NAME, DBusNames::Notify::PATH, DBusNames::Notify::INTERFACE, "CloseNotification", g_variant_new("(u)", m_notification_id), nullptr, G_DBUS_CALL_FLAGS_NONE, -1, nullptr, &error); if (error != nullptr) { g_warning("Error closing notification: %s", error->message); g_clear_error(&error); } } g_clear_object(&m_bus); } core::Signal& on_user_response() { return m_on_user_response; } private: static void on_bus_ready_static(GObject* /*source*/, GAsyncResult* res, gpointer gself) { GError* error {}; auto bus = g_bus_get_finish (res, &error); if (error != nullptr) { if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) g_warning("UsbSnap: Error getting session bus: %s", error->message); g_clear_error(&error); } else { static_cast(gself)->on_bus_ready(bus); } g_clear_object(&bus); } void on_bus_ready(GDBusConnection* bus) { m_bus = G_DBUS_CONNECTION(g_object_ref(G_OBJECT(bus))); m_subscription_id = g_dbus_connection_signal_subscribe(m_bus, DBusNames::Notify::NAME, DBusNames::Notify::INTERFACE, nullptr, DBusNames::Notify::PATH, nullptr, G_DBUS_SIGNAL_FLAGS_NONE, on_notification_signal_static, this, nullptr); auto body = g_strdup_printf(_("The computer's RSA key fingerprint is: %s"), m_fingerprint.c_str()); GVariantBuilder actions_builder; g_variant_builder_init(&actions_builder, G_VARIANT_TYPE_STRING_ARRAY); g_variant_builder_add(&actions_builder, "s", ACTION_ALLOW); g_variant_builder_add(&actions_builder, "s", _("Allow")); g_variant_builder_add(&actions_builder, "s", ACTION_DENY); g_variant_builder_add(&actions_builder, "s", _("Don't Allow")); GVariantBuilder hints_builder; g_variant_builder_init(&hints_builder, G_VARIANT_TYPE_VARDICT); g_variant_builder_add(&hints_builder, "{sv}", "x-lomiri-non-shaped-icon", g_variant_new_string("true")); g_variant_builder_add(&hints_builder, "{sv}", "x-lomiri-snap-decisions", g_variant_new_string("true")); g_variant_builder_add(&hints_builder, "{sv}", "x-lomiri-private-affirmative-tint", g_variant_new_string("true")); auto args = g_variant_new("(susssasa{sv}i)", "", uint32_t(0), "computer-symbolic", _("Allow USB Debugging?"), body, &actions_builder, &hints_builder, -1); g_dbus_connection_call(m_bus, DBusNames::Notify::NAME, DBusNames::Notify::PATH, DBusNames::Notify::INTERFACE, "Notify", args, G_VARIANT_TYPE("(u)"), G_DBUS_CALL_FLAGS_NONE, -1, // timeout m_cancellable, on_notify_reply_static, this); g_clear_pointer(&body, g_free); } static void on_notify_reply_static(GObject* obus, GAsyncResult* res, gpointer gself) { GError* error {}; auto reply = g_dbus_connection_call_finish (G_DBUS_CONNECTION(obus), res, &error); if (error != nullptr) { if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) g_warning("UsbSnap: Error calling Notify: %s", error->message); g_clear_error(&error); } else { uint32_t id {}; g_variant_get(reply, "(u)", &id); static_cast(gself)->on_notify_reply(id); } g_clear_pointer(&reply, g_variant_unref); } void on_notify_reply(uint32_t id) { m_notification_id = id; } static void on_notification_signal_static(GDBusConnection* /*connection*/, const gchar* /*sender_name*/, const gchar* object_path, const gchar* interface_name, const gchar* signal_name, GVariant* parameters, gpointer gself) { g_return_if_fail(!g_strcmp0(object_path, DBusNames::Notify::PATH)); g_return_if_fail(!g_strcmp0(interface_name, DBusNames::Notify::INTERFACE)); auto self = static_cast(gself); if (!g_strcmp0(signal_name, DBusNames::Notify::ActionInvoked::NAME)) { uint32_t id {}; const char* action_name {}; g_variant_get(parameters, "(u&s)", &id, &action_name); if (id == self->m_notification_id) self->on_action_invoked(action_name); } else if (!g_strcmp0(signal_name, DBusNames::Notify::NotificationClosed::NAME)) { uint32_t id {}; uint32_t close_reason {}; g_variant_get(parameters, "(uu)", &id, &close_reason); if (id == self->m_notification_id) self->on_notification_closed(close_reason); } } void on_action_invoked(const char* action_name) { const auto response = !g_strcmp0(action_name, ACTION_ALLOW) ? AdbdClient::PKResponse::ALLOW : AdbdClient::PKResponse::DENY; // FIXME: the current default is to cover the most common use case. // We need to get the notification ui's checkbox working ASAP so // that the user can provide this flag const bool remember_this_choice = response == AdbdClient::PKResponse::ALLOW; m_on_user_response(response, remember_this_choice); m_notification_id = 0; } void on_notification_closed(uint32_t close_reason) { if (close_reason == DBusNames::Notify::NotificationClosed::Reason::EXPIRED) m_on_user_response(AdbdClient::PKResponse::DENY, false); m_notification_id = 0; } static constexpr char const * ACTION_ALLOW {"allow"}; static constexpr char const * ACTION_DENY {"deny"}; const std::string m_fingerprint; core::Signal m_on_user_response; GCancellable* m_cancellable {}; GDBusConnection* m_bus {}; uint32_t m_notification_id {}; unsigned int m_subscription_id {}; }; /*** **** ***/ UsbSnap::UsbSnap(const std::string& public_key): impl{new Impl{public_key}} { } UsbSnap::~UsbSnap() { } core::Signal& UsbSnap::on_user_response() { return impl->on_user_response(); }