/*	Author:	Dan Clark <dan@gzero.org>
 *
 *	Project Homepage: http://gzero.org/gtk-ssh-keymanager
 *	
 *	License:
 *		obligatory GPL'd <http://www.gnu.org/copyleft/gpl.html>
 *
 *	Info:
 *		This is a GUI front end for ssh-add. 
 *		You must have GTK2 and OpenSSh for this program to work.
 *		All the same caveats of ssh-add apply to this program
 *	
 *	Compile:
 *		gcc -o gtk-ssh-keymanager gtk-ssh-keymanager.c `pkg-config --cflags --libs gtk+-2.0`
 * 
 */

#include <gtk/gtk.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

/* Globals */
GtkWidget * file_selector;
GtkWidget * treeview1;
GtkListStore * store;

/* Prototypes */
void get_ssh_key_list(void);
void strip_newline(char *str, int size);
GtkWidget * create_window1(void);
void on_add_btn_clicked(GtkButton * button, gpointer user_data);
void on_rem_btn_clicked(GtkButton * button, gpointer user_data);
void got_new_filename(GtkFileSelection * selector, gpointer user_data);

int main (int argc, char * argv[])
{
    GtkWidget *window1;

    gtk_set_locale();
    gtk_init(&argc, &argv);
    
    window1 = create_window1();

	/* Populates the list */
	get_ssh_key_list();
    
	gtk_widget_show(window1);
    
    gtk_main();
    
    return 0;
}

/*
 * I am forking of an instance of ssh-add 
 * and using pipes to grab its output
 *
 */
void get_ssh_key_list(void)
{
    pid_t pid;
    int   rv;
    int   commpipe[2];
    char  string[1024];
    
    if(pipe(commpipe)){fprintf(stderr,"Pipe error!\n"); exit(1);}
    if((pid = fork()) == -1){fprintf(stderr,"Fork error!\n"); exit(1);}
    
    if(pid) /* Parent */
    {
        dup2(commpipe[0],0);
        close(commpipe[1]);
       
        GtkTreeIter    iter;
        gtk_list_store_clear(store);
       
		int no = 1;
		
        while(fgets(string,1024,stdin) != 0)
        {
			no = 0;
		
			/* Do not add anything to the list if ssh-add reports no identities */
            if(strstr(string,"The agent has no identities.")){ break; }
            
            strip_newline(string,1024);
            
            gtk_list_store_append(store, &iter);
            gtk_list_store_set(store, &iter,0, strtok(string," "),-1);
            
            char * sub_str;
            int i;
            for(i = 1; i < 4; i++)
            {
                gtk_list_store_set(store, &iter, i, strtok(NULL," "),-1);
            }

        }
        
		if(no == 1){fprintf(stderr,"ssh-add borked for unknown reasons. ssh-agent not running?\n"); exit(1);}
	
        wait(&rv);
    }
    else    /* Child */
    {
        dup2(commpipe[1],1);
        close(commpipe[0]);
        if(execl("/usr/bin/ssh-add","/usr/bin/ssh-add","-l",NULL) == -1)
        {
			fprintf(stderr,"execl error!\n");
            exit(1);
        }
    }
}

/*
 * Used to strip new lines off of stdin to place in the list
 */
void strip_newline( char *str, int size )
{
    int i;
    for (  i = 0; i < size; ++i )
    {
        if ( str[i] == '\n' )
        {
            str[i] = '\0';
            return;   
        }
    }
}

/*
 * All the GTK stuff needed to make the GUI.
 * Most of this code I generated with GLADE
 * Nothing fancy happens here.
 */

GtkWidget * create_window1(void)
{
    GtkWidget *window1;
    GtkWidget *vbox1;
    GtkWidget *scrolledwindow1;
    GtkWidget *hbuttonbox1;
    GtkWidget *button1;
    GtkWidget *button2;
    GtkCellRenderer * renderer;
   
    window1 = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_widget_set_size_request(window1, 640, 256);
    gtk_window_set_title(GTK_WINDOW(window1), "gtk-ssh-keymanager");
    gtk_window_set_resizable(GTK_WINDOW(window1), FALSE);
    gtk_window_set_icon_name(GTK_WINDOW(window1), "stock_lock");
    
    vbox1 = gtk_vbox_new(FALSE, 0);
    gtk_widget_show(vbox1);
    gtk_container_add(GTK_CONTAINER(window1), vbox1);

    scrolledwindow1 = gtk_scrolled_window_new(NULL, NULL);
    gtk_widget_show(scrolledwindow1);
    gtk_box_pack_start(GTK_BOX(vbox1), scrolledwindow1, TRUE, TRUE, 0);
    gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwindow1), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
    gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwindow1), GTK_SHADOW_IN);

    treeview1 = gtk_tree_view_new();
    gtk_widget_show(treeview1);
    gtk_container_add(GTK_CONTAINER(scrolledwindow1), treeview1);
    //gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeview1), FALSE);
        
    renderer = gtk_cell_renderer_text_new();
    gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(treeview1), -1, "Size", renderer, "text", 0, NULL);
    renderer = gtk_cell_renderer_text_new();
    gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(treeview1), -1, "Fingerprint", renderer, "text", 1, NULL);
    renderer = gtk_cell_renderer_text_new();
    gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(treeview1), -1, "Filename", renderer, "text", 2, NULL);
    renderer = gtk_cell_renderer_text_new();
    gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(treeview1), -1, "Type", renderer, "text", 3, NULL);
    store = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
    gtk_tree_view_set_model(GTK_TREE_VIEW(treeview1), GTK_TREE_MODEL(store));

    hbuttonbox1 = gtk_hbutton_box_new();
    gtk_widget_show(hbuttonbox1);
    gtk_box_pack_start(GTK_BOX(vbox1), hbuttonbox1, FALSE, TRUE, 5);
    gtk_button_box_set_layout(GTK_BUTTON_BOX(hbuttonbox1), GTK_BUTTONBOX_SPREAD);

    button1 = gtk_button_new_from_stock("gtk-add");
    gtk_widget_show(button1);
    gtk_container_add(GTK_CONTAINER(hbuttonbox1), button1);

    button2 = gtk_button_new_from_stock("gtk-remove");
    gtk_widget_show(button2);
    gtk_container_add(GTK_CONTAINER(hbuttonbox1), button2);
 	
	/* call back to kill the gtk app on exit */
    g_signal_connect((gpointer) window1, "destroy", G_CALLBACK(gtk_main_quit), NULL);
  
	/* call backs for the apps buttons */
    g_signal_connect((gpointer) button1, "clicked", G_CALLBACK(on_add_btn_clicked), NULL);
    g_signal_connect((gpointer) button2, "clicked", G_CALLBACK(on_rem_btn_clicked), NULL);
    
    return window1;
}

/*
 * When the add button is pressed you can select your private keyfile
 * Defaults to $HOME/.ssh
 */
void on_add_btn_clicked(GtkButton *button, gpointer user_data)
{
    file_selector = gtk_file_selection_new("Please select a private key.");
    
    gtk_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(file_selector)->ok_button), "clicked", GTK_SIGNAL_FUNC(got_new_filename), NULL);
    gtk_signal_connect_object(GTK_OBJECT(GTK_FILE_SELECTION(file_selector)->ok_button), "clicked", GTK_SIGNAL_FUNC(gtk_widget_destroy), (gpointer) file_selector);
    gtk_signal_connect_object(GTK_OBJECT(GTK_FILE_SELECTION(file_selector)->cancel_button), "clicked", GTK_SIGNAL_FUNC(gtk_widget_destroy), (gpointer) file_selector);

    gtk_file_selection_hide_fileop_buttons(GTK_FILE_SELECTION(file_selector));
  	gtk_file_selection_set_select_multiple(GTK_FILE_SELECTION(file_selector), FALSE);
	
	char temp[512] = "\0";
	strncat(temp,getenv("HOME"),512);
	if(temp != NULL){ strncat(temp, "/.ssh/", 512); } 
	gtk_file_selection_set_filename(GTK_FILE_SELECTION(file_selector),temp);
	
    gtk_widget_show(file_selector);
    
}

/*
 * Find the selected key, remove it with a call to ssh-add
 * then update the list
 */
void on_rem_btn_clicked(GtkButton *button, gpointer user_data)
{
    GtkTreeSelection *selection;
    GtkTreeModel     *model;
    GtkTreeIter       iter;

    selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview1));

    if(gtk_tree_selection_get_selected(selection, &model, &iter))
    {
        gchar *name;
        gtk_tree_model_get (model, &iter, 2, &name, -1);

        char temp[1024];
        snprintf(temp, 1024, "`which ssh-add` -d %s\n", name);
        system(temp);

        g_free(name);
    }

    get_ssh_key_list();    
}

/*
 * When the file dialog comes back try to add the file with
 * a call to ssh-add then refresh the list
 */
void got_new_filename(GtkFileSelection * selector, gpointer user_data)
{
    const gchar * filename;
    filename = gtk_file_selection_get_filename(GTK_FILE_SELECTION(file_selector));
    
    char temp[1024];
    snprintf(temp, 1024, "`which ssh-add` %s", filename);
    system(temp);

    get_ssh_key_list();    
}
