/*
 * Copyright (C) 2008 Holger Hans Peter Freyther <zecke@openmoko.org>
 * Copyrgith (C) 2008 Openmoko Inc.
 *
 * 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 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 <http://www.gnu.org/licenses/>.
 *
 */

#include <pwd.h>
#include <shadow.h>
#include <stdio.h>
#include <time.h>

#define _XOPEN_SOURCE
#include <unistd.h>

#include <gtk/gtk.h>

// This function is taken from 'busybox'.
static int i64c(int i) {
    if (i <= 0)
        return ('.');
    if (i == 1)
        return ('/');
    if (i >= 2 && i < 12)
        return ('0' - 2 + i);
    if (i >= 12 && i < 38)
        return ('A' - 12 + i);
    if (i >= 38 && i < 63)
        return ('a' - 38 + i);
    return ('z');
}   
    
// This function is taken from 'busybox'.
static const char *crypt_make_salt() {
    time_t now;
    static unsigned long x;
    static char result[3];
    
    time(&now);
    x += now + getpid() + clock();
    result[0] = i64c(((x >> 18) ^ (x >> 6)) & 077);
    result[1] = i64c(((x >> 12) ^ x) & 077);
    result[2] = '\0';
    return result;
}

#define CRYPT_FAILED        -1
#define NEW_FILE_FAILED     -2
#define RENAME_FAILED       -3

static int write_password(const char* password_input)
{
   char* password = crypt(password_input, crypt_make_salt());

    if (!password)
        return CRYPT_FAILED; 

    /* rewind and rewrite the password file */
    setpwent();

    FILE* file = fopen("/etc/passwd.new", "w");
    if (!file)
        return NEW_FILE_FAILED;

    struct passwd* pass;
    while ((pass = getpwent()) != 0l) {
        /* no  shadow password support */
        if (pass->pw_uid == 0)
            pass->pw_passwd = password;

        putpwent(pass, file);
    }

    fclose( file );
    endpwent();
    if (rename("/etc/passwd.new","/etc/passwd") == -1)
        return RENAME_FAILED;

    return 0;
}

// TODO use bindtext...gettext
#define _(x) (x)

GtkWidget* s_password;
GtkWidget* s_confirm;
GtkWidget* s_window;

static void run_message(const char* message, gboolean clear)
{
    if (clear) {
        gtk_entry_set_text (GTK_ENTRY(s_password), "");
        gtk_entry_set_text (GTK_ENTRY(s_confirm), "");
    }

    /* show the message */
    GtkWidget* dialog = gtk_message_dialog_new (GTK_WINDOW(s_window),
                                  GTK_DIALOG_DESTROY_WITH_PARENT,
                                  GTK_MESSAGE_ERROR,
                                  GTK_BUTTONS_OK,
                                  message);
    gtk_dialog_run (GTK_DIALOG (dialog));
    gtk_widget_destroy (dialog);
}

static void set_password()
{
    const char* password = gtk_entry_get_text (GTK_ENTRY(s_password));
    const char* confirm  = gtk_entry_get_text (GTK_ENTRY(s_confirm));

    if (strcmp(password, confirm) != 0)
        return run_message (_("The passwords do not match. Please try again."), TRUE);

    int result = write_password (password);
    if (result == CRYPT_FAILED)
        return run_message (_("The crypting of the password failed. Please try again."), FALSE);
    else if (result == RENAME_FAILED || result == NEW_FILE_FAILED)
        return run_message (_("The writing of the new password file failed. Check permissions and please try again."), FALSE);
    else
        return gtk_main_quit();
}


int main(int argc, char** argv)
{
    gtk_init(&argc, &argv);

    /* Create the UI */
    s_window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    g_signal_connect (s_window, "delete-event", G_CALLBACK(gtk_main_quit), NULL); 
    gtk_window_set_title(GTK_WINDOW(s_window), _("Set root password"));

    GtkWidget* intro = gtk_label_new (_("Please set a root password."));

    GtkWidget* password_label = gtk_label_new (_("Password:"));
    s_password = gtk_entry_new ();
    gtk_entry_set_visibility (GTK_ENTRY(s_password), FALSE);

    GtkWidget* confirm_label = gtk_label_new (_("Confirm:"));
    gtk_misc_set_alignment (GTK_MISC(intro), 0, 0);
    s_confirm = gtk_entry_new ();
    gtk_entry_set_visibility (GTK_ENTRY(s_confirm), FALSE);

    GtkWidget* button = gtk_button_new_with_label ("Set password");
    g_signal_connect(button, "clicked", G_CALLBACK(set_password), NULL);

    /* box packing */
    GtkWidget* main_box = gtk_vbox_new (FALSE, 0);

    GtkWidget* intro_box = gtk_hbox_new (FALSE, 0);
    gtk_box_pack_start (GTK_BOX(intro_box), intro, TRUE, FALSE, 0);
    gtk_box_pack_start (GTK_BOX(main_box), intro_box, FALSE, FALSE, 0);

    GtkWidget* entry_table = gtk_table_new (2, 2, FALSE);
    gtk_table_attach (GTK_TABLE(entry_table), password_label, 0, 1, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0);
    gtk_table_attach_defaults (GTK_TABLE(entry_table), s_password,     1, 2, 0, 1);
    gtk_table_attach (GTK_TABLE(entry_table), confirm_label,  0, 1, 1, 2, GTK_SHRINK, GTK_SHRINK, 0, 0);
    gtk_table_attach_defaults (GTK_TABLE(entry_table), s_confirm,      1, 2, 1, 2);
    gtk_box_pack_start (GTK_BOX(main_box), entry_table, FALSE, FALSE, 0);

    GtkWidget* button_box = gtk_hbox_new (FALSE, 0);
    gtk_box_pack_start (GTK_BOX(button_box), button, TRUE, FALSE, 0);
    gtk_box_pack_start (GTK_BOX(main_box), button_box, FALSE, FALSE, 0); 

    gtk_container_add (GTK_CONTAINER(s_window), main_box);
    gtk_widget_show_all (GTK_WIDGET(s_window));
    gtk_main();

    return 0;
}
