/*
 *  Copyright (C) 2004 Morten Fjord-Larsen
 *
 *  $Id: generate.c,v 1.1.1.1 2004/04/26 23:58:50 fjord Exp $
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program 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 Library General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#include <gnome.h>
#include <glade/glade.h>

#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#define GENERATE_MAX_LENGTH (20+1)


static int generate_chars = 8;
static char generate_password_generated[GENERATE_MAX_LENGTH];

static gboolean generate_use_lowercase = TRUE;
static gboolean generate_use_uppercase = TRUE;
static gboolean generate_use_numbers = TRUE;
static gboolean generate_use_special = TRUE;


void generate_on_option_changed(GtkCheckButton *checkbutton)
{
    gboolean *option;
    g_assert(checkbutton != NULL);
    option = gtk_object_get_user_data(GTK_OBJECT(checkbutton));
    g_assert(option != NULL);
    *option = !*option;
}

void generate_on_number_changed(GtkSpinButton *spinbutton)
{
    g_assert(spinbutton != NULL);
    generate_chars = gtk_spin_button_get_value(spinbutton);
}

void generate_password(GtkEntry *entry)
{
    int len, i, fd, r;

    static char lowercase[] = "abcdefgkijklmnopqrstuvwxyz";
    static char uppercase[] = "ABCDEFGKIJKLMNOPQRSTUVWXYZ";
    static char numbers[] = "0123456789";
    static char special[] = "!@#$%^&*()_+=-'\"\{}[]?><,.|";

    static char used[sizeof(lowercase) + sizeof(uppercase) +
		     sizeof(numbers) + sizeof(special) + 1];

    used[0] = '\0';
    if (generate_use_lowercase)
	strcat(used, lowercase);
    if (generate_use_uppercase)
	strcat(used, uppercase);
    if (generate_use_numbers)
	strcat(used, numbers);
    if (generate_use_special)
	strcat(used, special);

    len = strlen(used);
    if (len == 0) {
	generate_password_generated[0] = '\0';
	return;
    }

    g_assert(generate_chars < sizeof(generate_password_generated));

    // We relie on /dev/random for proper random numbers.
    fd = open("/dev/random", O_RDONLY, 0);
    if (fd == -1) 
	return;

    // We actually steal more random bits than we need to here.
    r = read(fd, generate_password_generated, generate_chars);
    if (r != generate_chars)
	return;
    close(fd);

    for (i = 0; i < generate_chars; i++) {
	unsigned idx = ((unsigned char)generate_password_generated[i]) % len;
	generate_password_generated[i] = used[idx];
    }
    generate_password_generated[generate_chars] = '\0';
    gtk_entry_set_text(entry, generate_password_generated);
}


const char *generate_display_dialog()
{
    GladeXML *xml;
    GtkWidget *dialog;
    char *result = NULL;
    GtkWidget *checkbutton;
    int r;

    xml = glade_xml_new(PACKAGE_DATA_DIR"/generate-password-dialog.glade", 
	    		NULL, NULL);
    g_assert(xml != NULL);
    glade_xml_signal_autoconnect(xml);

    generate_use_lowercase = TRUE;
    generate_use_uppercase = TRUE;
    generate_use_numbers = TRUE;
    generate_use_special = TRUE;

    // let checkbuttons' user data fields to point to the corresponding
    // boolean
    checkbutton = glade_xml_get_widget(xml, "lowercase_checkbutton");
    g_assert(checkbutton != NULL);
    gtk_object_set_user_data(GTK_OBJECT(checkbutton), &generate_use_lowercase);
    checkbutton = glade_xml_get_widget(xml, "uppercase_checkbutton");
    g_assert(checkbutton != NULL);
    gtk_object_set_user_data(GTK_OBJECT(checkbutton), &generate_use_uppercase);
    checkbutton = glade_xml_get_widget(xml, "numbers_checkbutton");
    g_assert(checkbutton != NULL);
    gtk_object_set_user_data(GTK_OBJECT(checkbutton), &generate_use_numbers);
    checkbutton = glade_xml_get_widget(xml, "special_checkbutton");
    g_assert(checkbutton != NULL);
    gtk_object_set_user_data(GTK_OBJECT(checkbutton), &generate_use_special);

    dialog = glade_xml_get_widget(xml, "generate_password_dialog");
    g_assert(dialog != NULL);

    r = gtk_dialog_run(GTK_DIALOG(dialog));
    switch (r) {
	case GTK_RESPONSE_OK :
	    result = generate_password_generated;
	    break;
    }
    gtk_widget_destroy(dialog);

    return result;
}
