/*
 * gcc -g -o fwmon fwmon.c `pkg-config --cflags --libs gio-unix-2.0 libnm-glib`
 */

#include <gio/gio.h>
#include <gio/gdesktopappinfo.h>
#include "nm-client.h"
#include "nm-remote-settings.h"

static GApplication *app;

static gboolean shown;

static GSettings *vino_settings;

static NMRemoteSettings *settings;

static gboolean
sharing_is_enabled (void)
{
  return g_settings_get_boolean (vino_settings, "enabled");
}

static void
disable_sharing (void)
{
  g_settings_set_boolean (vino_settings, "enabled", FALSE);
}

static void
set_connection_trusted (const gchar *uuid, gboolean trusted)
{
  NMRemoteConnection *rc;
  NMSettingConnection *sc;
  const gchar *zone;

  if (trusted)
    zone = "trusted";
  else
    zone = "public";

  rc = nm_remote_settings_get_connection_by_uuid (settings, uuid);
  sc = nm_connection_get_setting_connection (NM_CONNECTION (rc));
  g_object_set (sc, NM_SETTING_CONNECTION_ZONE, zone, NULL);
  nm_remote_connection_commit_changes (rc, NULL, NULL); 
}

static void
get_connection_trusted (const gchar *uuid, gboolean *trusted, gboolean *untrusted)
{
  NMRemoteConnection *rc;
  NMSettingConnection *sc;
  const gchar *zone;

  *trusted = FALSE;
  *untrusted = FALSE;

  rc = nm_remote_settings_get_connection_by_uuid (settings, uuid);
  sc = nm_connection_get_setting_connection (NM_CONNECTION (rc));
  zone = nm_setting_connection_get_zone (sc);

  if (zone == NULL)
    return;

  if (g_strcmp0 (zone, "trusted") == 0)
    *trusted = TRUE;
  else
    *untrusted = TRUE;
}

static void
notify_untrusted_network (void)
{
      GNotification *n;

      shown = TRUE;

      disable_sharing ();

      n = g_notification_new ("Pants down alert!");
      g_notification_set_body (n, "Sharing services have been disabled because you are on an untrusted network.");
      g_notification_add_button (n, "Sharing Settings...", "app.show-sharing-settings");
      g_notification_set_default_action (n, "app.show-sharing-settings");
      g_application_send_notification (app, "untrusted-net", n);
      g_object_unref (n);
}

static void
notify_unknown_network (const gchar *uuid)
{
  GNotification *n;

  shown = TRUE;

  n = g_notification_new ("Pants down alert!");
  g_notification_set_body (n, "You have connected to an unknown network. If this is a public network, you may want to stop sharing services that expose your information on the local network.");
  g_notification_add_button_with_target (n, "Keep sharing", "app.trust-network", "s", uuid);
  g_notification_add_button_with_target (n, "Stop sharing", "app.distrust-network", "s", uuid);
  g_notification_add_button (n, "Sharing Settings...", "app.show-sharing-settings");
  g_notification_set_default_action (n, "app.show-sharing-settings");
  g_application_send_notification (app, "unknown-net", n);
  g_object_unref (n);
}

static void
connection_state_changed (NMActiveConnection *ac, GParamSpec *pspec, gpointer data)
{
  const gchar *uuid;
  gboolean trusted;
  gboolean untrusted;

  if (nm_active_connection_get_state (ac) != NM_ACTIVE_CONNECTION_STATE_ACTIVATED)
    return;

  uuid = nm_active_connection_get_uuid (ac);
  if (!nm_remote_settings_get_connection_by_uuid (settings, uuid))
    return; 
  get_connection_trusted (uuid, &trusted, &untrusted);

  if (!trusted && sharing_is_enabled ())
    {
      if (untrusted)
        notify_untrusted_network ();
      else
        notify_unknown_network (uuid);
    }
}

static void
active_connections_changed (NMClient *client, GParamSpec *pspec, gpointer data)
{
  const GPtrArray *connections;
  gint i;
  gboolean unknown_network_seen;
  gboolean untrusted_network_seen;
  const gchar *first_unknown_uuid;

  g_print ("Active connections changed\n");
  if (sharing_is_enabled ())
    g_print ("Sharing enabled\n");
  else
    g_print ("Sharing not enabled\n");

  unknown_network_seen = FALSE;
  untrusted_network_seen = FALSE;
  first_unknown_uuid = NULL;

  connections = nm_client_get_active_connections (client);
  for (i = 0; connections && (i < connections->len); i++)
    {
      NMActiveConnection *ac;
      NMRemoteConnection *rc;
      NMSettingConnection *sc;
      const gchar *zone;
      const gchar *uuid;
      gboolean trusted;
      gboolean untrusted;
 
      ac = g_ptr_array_index (connections, i);
      if (!g_object_get_data (G_OBJECT (ac), "has-state-changed-handler"))
        {
          g_signal_connect (ac, "notify::state", G_CALLBACK (connection_state_changed), NULL);
          g_object_set_data (G_OBJECT (ac), "has-state-changed-handler", GINT_TO_POINTER (1));
        }

      if (nm_active_connection_get_state (ac) != NM_ACTIVE_CONNECTION_STATE_ACTIVATED)
        continue;

      uuid = nm_active_connection_get_uuid (ac);
      rc = nm_remote_settings_get_connection_by_uuid (settings, uuid);
      if (rc == NULL)
        continue;

      get_connection_trusted (uuid, &trusted, &untrusted);

      if (trusted)
        break;

      if (untrusted)
        {
          untrusted_network_seen = TRUE;
        }
      else
        {
          unknown_network_seen = TRUE;
          if (first_unknown_uuid == NULL)
            first_unknown_uuid = uuid;
        }
    }

#if 0
  if (shown)
    return;
#endif

  if (sharing_is_enabled ())
    {
      if (untrusted_network_seen)
        notify_untrusted_network ();
      else if (unknown_network_seen)
        notify_unknown_network (first_unknown_uuid);
    }
}

static void
activate (GApplication *app)
{
  NMClient *client;
  DBusGConnection *bus = NULL; 
 
  g_application_hold (app);

  client = nm_client_new ();
  bus = dbus_g_bus_get (DBUS_BUS_SYSTEM, NULL);
  settings = nm_remote_settings_new (bus);

  vino_settings = g_settings_new ("org.gnome.Vino");
  
  g_signal_connect (client, "notify::active-connections", G_CALLBACK (active_connections_changed), settings);
}

static void
trust_network (GSimpleAction *action,
               GVariant      *parameter,
               gpointer       user_data)
{
  const gchar *uuid;

  shown = FALSE;

  uuid = g_variant_get_string (parameter, NULL);
  g_print ("trusting network: %s\n", uuid);
  set_connection_trusted (uuid, TRUE);
}

static void
distrust_network (GSimpleAction *action,
                  GVariant      *parameter,
                  gpointer       user_data)
{
  const gchar *uuid;

  shown = FALSE;

  uuid = g_variant_get_string (parameter, NULL);
  g_print ("distrusting network: %s\n", uuid);
  disable_sharing ();
  set_connection_trusted (uuid, FALSE);
}

static void
show_sharing_settings (GSimpleAction *action,
                       GVariant      *parameter,
                       gpointer       user_data)
{
  GDesktopAppInfo *app;

  app = g_desktop_app_info_new ("gnome-sharing-panel.desktop");
  g_app_info_launch_uris (G_APP_INFO (app), NULL, NULL, NULL);
  g_object_unref (app);
  shown = FALSE;
}

static GActionEntry entries[] = {
  { "trust-network", trust_network, "s", NULL, NULL },
  { "distrust-network", distrust_network, "s", NULL, NULL },
  { "show-sharing-settings", show_sharing_settings, NULL, NULL, NULL },
};

int main (int argc, char *argv[])
{
  app = g_application_new ("org.gnome.fwmon", 0);
  g_action_map_add_action_entries (G_ACTION_MAP (app), entries, G_N_ELEMENTS (entries), NULL);

  g_signal_connect (app, "activate", G_CALLBACK (activate), NULL);
  return g_application_run (app, argc, argv);
}
