[hotssh] Parse ~/.ssh/known_hosts, use for entry completion



commit 31367e50c55efa9eb0b7715a4563f02615aa81b0
Author: Colin Walters <walters verbum org>
Date:   Tue Nov 26 17:07:29 2013 -0500

    Parse ~/.ssh/known_hosts, use for entry completion

 Makefile-src.am     |    2 +
 src/hotssh-hostdb.c |  202 +++++++++++++++++++++++++++++++++++++++++++++++++++
 src/hotssh-hostdb.h |   36 +++++++++
 src/hotssh-tab.c    |   30 ++++++++
 4 files changed, 270 insertions(+), 0 deletions(-)
---
diff --git a/Makefile-src.am b/Makefile-src.am
index ed2cf73..071dbab 100644
--- a/Makefile-src.am
+++ b/Makefile-src.am
@@ -18,6 +18,7 @@ CLEANFILES += hotssh-search-glue.h hotssh-search-glue.c
 hotssh_headers = $(addprefix src/, \
        hotssh-app.h \
        hotssh-search-provider.h \
+       hotssh-hostdb.h \
        hotssh-tab.h \
        hotssh-password-interaction.h \
        hotssh-win.h \
@@ -30,6 +31,7 @@ hotssh_SOURCES = $(hotssh_headers) \
        src/main.c \
        src/hotssh-app.c \
        src/hotssh-search-provider.c \
+       src/hotssh-hostdb.c \
        src/hotssh-tab.c \
        src/hotssh-password-interaction.c \
        src/hotssh-win.c \
diff --git a/src/hotssh-hostdb.c b/src/hotssh-hostdb.c
new file mode 100644
index 0000000..8dbb687
--- /dev/null
+++ b/src/hotssh-hostdb.c
@@ -0,0 +1,202 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2013 Colin Walters <walters verbum org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include "hotssh-hostdb.h"
+#include "hotssh-win.h"
+#include "hotssh-prefs.h"
+
+#include "libgsystem.h"
+
+struct _HotSshHostDB
+{
+  GObject parent;
+};
+
+struct _HotSshHostDBClass
+{
+  GObjectClass parent_class;
+};
+
+typedef struct _HotSshHostDBPrivate HotSshHostDBPrivate;
+
+struct _HotSshHostDBPrivate
+{
+  GtkListStore *model;
+  GFile *openssh_dir;
+  GFile *openssh_knownhosts_path;
+  GFile *hotssh_extra;
+  GFileMonitor *knownhosts_monitor;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE(HotSshHostDB, hotssh_hostdb, G_TYPE_OBJECT)
+
+static void
+on_knownhosts_changed (GFileMonitor        *monitor,
+                       GFile               *src,
+                       GFile               *other,
+                       GFileMonitorEvent    event,
+                       gpointer             user_data)
+{
+  HotSshHostDB *self = user_data;
+  G_GNUC_UNUSED HotSshHostDBPrivate *priv = hotssh_hostdb_get_instance_private (self);
+  GError *local_error = NULL;
+  gs_free char *contents = NULL;
+  GtkTreeIter modeliter;
+  gsize len;
+  char *iter;
+  char *eol;
+  char *carriage;
+
+  if (!g_file_load_contents (priv->openssh_knownhosts_path, NULL,
+                             &contents, &len, NULL,
+                             &local_error))
+    goto out;
+
+  gtk_list_store_clear (priv->model);
+  
+  iter = contents;
+  while (TRUE)
+    {
+      gs_strfreev char **parts = NULL;
+      char *comma;
+
+      eol = strchr (iter, '\n');
+      if (eol)
+        *eol = '\0';
+      
+      carriage = strrchr (iter, '\r');
+      if (carriage)
+        *carriage = '\0';
+      
+      if (contents[0] == '#')
+        goto next;
+
+      parts = g_strsplit (iter, " ", -1);
+      if (!parts || g_strv_length (parts) < 3)
+        goto next;
+      
+      comma = strchr (parts[0], ',');
+      if (comma)
+        *comma = '\0';
+      gtk_list_store_append (priv->model, &modeliter);
+      gtk_list_store_set (priv->model, &modeliter, 0, parts[0], 1, 0, -1);
+
+    next:
+      if (eol)
+        iter = eol + 1;
+      else
+        break;
+    }
+
+  g_debug ("Read %d known hosts", gtk_tree_model_iter_n_children ((GtkTreeModel*)priv->model, NULL));
+  
+ out:
+  if (local_error)
+    {
+      g_debug ("Failed to read '%s': %s", gs_file_get_path_cached (priv->openssh_knownhosts_path),
+               local_error->message);
+      g_clear_error (&local_error);
+    }
+}
+
+static void
+hotssh_hostdb_init (HotSshHostDB *self)
+{
+  HotSshHostDBPrivate *priv = hotssh_hostdb_get_instance_private (self);
+  const char *homedir;
+  GError *local_error = NULL;
+  gs_free char *knownhosts_path = NULL;
+  gs_free char *openssh_path = NULL;
+
+  priv->model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_UINT64);
+  homedir = g_get_home_dir ();
+  g_assert (homedir);
+
+  openssh_path = g_build_filename (homedir, ".ssh", NULL);
+  knownhosts_path = g_build_filename (openssh_path, "known_hosts", NULL);
+
+  priv->openssh_dir = g_file_new_for_path (openssh_path);
+  priv->openssh_knownhosts_path = g_file_new_for_path (knownhosts_path);
+
+  if (!g_file_query_exists (priv->openssh_dir, NULL))
+    {
+      if (!g_file_make_directory (priv->openssh_dir, NULL, &local_error))
+        {
+          g_error ("Failed to make '%s' directory: %s",
+                   gs_file_get_path_cached (priv->openssh_dir),
+                   local_error->message);
+        }
+    }
+  
+  priv->knownhosts_monitor = g_file_monitor (priv->openssh_knownhosts_path, 0, NULL,
+                                             &local_error);
+  if (!priv->knownhosts_monitor)
+    {
+      g_error ("Failed to monitor '%s': %s",
+               gs_file_get_path_cached (priv->openssh_knownhosts_path),
+               local_error->message);
+    }
+  
+  g_signal_connect (priv->knownhosts_monitor, "changed",
+                    G_CALLBACK (on_knownhosts_changed), self);
+  on_knownhosts_changed (priv->knownhosts_monitor, NULL, NULL, 0, self);
+}
+
+static void
+hotssh_hostdb_dispose (GObject *object)
+{
+  HotSshHostDBPrivate *priv = hotssh_hostdb_get_instance_private ((HotSshHostDB*)object);
+
+  g_clear_object (&priv->model);
+
+  G_OBJECT_CLASS (hotssh_hostdb_parent_class)->dispose (object);
+}
+
+static void
+hotssh_hostdb_class_init (HotSshHostDBClass *class)
+{
+  G_OBJECT_CLASS (class)->dispose = hotssh_hostdb_dispose;
+}
+
+HotSshHostDB *
+hotssh_hostdb_get_instance (void)
+{
+  static gsize initialized;
+  static HotSshHostDB *instance = NULL;
+  
+  if (g_once_init_enter (&initialized))
+    {
+      instance = g_object_new (HOTSSH_TYPE_HOSTDB, NULL);
+
+      g_once_init_leave (&initialized, 1);
+    }
+  return g_object_ref (instance);
+}
+
+GtkTreeModel *
+hotssh_hostdb_get_model (HotSshHostDB *self)
+{
+  HotSshHostDBPrivate *priv = hotssh_hostdb_get_instance_private (self);
+  return g_object_ref (priv->model);
+}
diff --git a/src/hotssh-hostdb.h b/src/hotssh-hostdb.h
new file mode 100644
index 0000000..cc5cdd3
--- /dev/null
+++ b/src/hotssh-hostdb.h
@@ -0,0 +1,36 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2013 Colin Walters <walters verbum org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#pragma once
+
+#include <gtk/gtk.h>
+
+#define HOTSSH_TYPE_HOSTDB (hotssh_hostdb_get_type ())
+#define HOTSSH_HOSTDB(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), HOTSSH_TYPE_HOSTDB, HotSshHostDB))
+
+typedef struct _HotSshHostDB          HotSshHostDB;
+typedef struct _HotSshHostDBClass     HotSshHostDBClass;
+
+GType                   hotssh_hostdb_get_type     (void);
+HotSshHostDB           *hotssh_hostdb_get_instance (void);
+
+GtkTreeModel           *hotssh_hostdb_get_model    (HotSshHostDB *self);
+
+
diff --git a/src/hotssh-tab.c b/src/hotssh-tab.c
index ce1bccc..a05b6be 100644
--- a/src/hotssh-tab.c
+++ b/src/hotssh-tab.c
@@ -19,6 +19,7 @@
  */
 
 #include "hotssh-tab.h"
+#include "hotssh-hostdb.h"
 #include "hotssh-password-interaction.h"
 #include "gssh.h"
 
@@ -92,6 +93,7 @@ struct _HotSshTabPrivate
   guint authmechanism_index;
 
   char *hostname;
+  GtkEntryCompletion *host_completion;
   GSocketConnectable *address;
   GSshConnection *connection;
   GSshChannel *channel;
@@ -736,6 +738,20 @@ hotssh_tab_style_updated (GtkWidget      *widget)
   vte_terminal_set_color_background_rgba ((VteTerminal*)priv->terminal, &bg);
 }
 
+static gboolean
+host_entry_match (GtkEntryCompletion *completion,
+                  const char         *key,
+                  GtkTreeIter        *iter,
+                  gpointer            user_data)
+{
+  gs_free char *host = NULL;
+  GtkTreeModel *model = gtk_entry_completion_get_model (completion);
+
+  gtk_tree_model_get (model, iter, 0, &host, -1);
+
+  return g_str_has_prefix (host, key);
+}
+
 static void
 hotssh_tab_grab_focus (GtkWidget *widget)
 {
@@ -793,15 +809,29 @@ hotssh_tab_init (HotSshTab *self)
   gtk_widget_show_all (priv->terminal_box);
 
   g_queue_init (&priv->write_queue);
+
+  {
+    gs_unref_object HotSshHostDB *hostdb = hotssh_hostdb_get_instance ();
+    gs_unref_object GtkTreeModel *hostdb_model = hotssh_hostdb_get_model (hostdb);
+    priv->host_completion = gtk_entry_completion_new ();
+    gtk_entry_completion_set_match_func (priv->host_completion, host_entry_match, self, NULL);
+    gtk_entry_completion_set_model (priv->host_completion, hostdb_model);
+    gtk_entry_completion_set_text_column (priv->host_completion, 0);
+    gtk_entry_completion_set_inline_completion (priv->host_completion, TRUE);
+    gtk_entry_set_completion ((GtkEntry*)priv->host_entry, priv->host_completion);
+  }
 }
 
 static void
 hotssh_tab_dispose (GObject *object)
 {
   HotSshTab *self = HOTSSH_TAB (object);
+  HotSshTabPrivate *priv = hotssh_tab_get_instance_private (self);
 
   page_transition (self, HOTSSH_TAB_PAGE_NEW_CONNECTION);
 
+  g_clear_object (&priv->host_completion);
+
   G_OBJECT_CLASS (hotssh_tab_parent_class)->dispose (object);
 }
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]