/* kim/lib/kim_ui_plugin.c */
/*
 * Copyright 2008 Massachusetts Institute of Technology.
 * All Rights Reserved.
 *
 * Export of this software from the United States of America may
 * require a specific license from the United States Government.
 * It is the responsibility of any person or organization contemplating
 * export to obtain such a license before exporting.
 *
 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
 * distribute this software and its documentation for any purpose and
 * without fee is hereby granted, provided that the above copyright
 * notice appear in all copies and that both that copyright notice and
 * this permission notice appear in supporting documentation, and that
 * the name of M.I.T. not be used in advertising or publicity pertaining
 * to distribution of the software without specific, written prior
 * permission.  Furthermore if you modify this software you must label
 * your software as modified software and not distribute it in such a
 * fashion that it might be confused with the original M.I.T. software.
 * M.I.T. makes no representations about the suitability of
 * this software for any purpose.  It is provided "as is" without express
 * or implied warranty.
 */

#include "k5-int.h"

#include "kim_private.h"


const char * const *kim_ui_plugin_files = NULL;
#if TARGET_OS_MAC
static const char *kim_ui_plugin_dirs[] = { KRB5_KIM_UI_PLUGIN_BUNDLE_DIR, LIBDIR "/krb5/plugins/kimui", NULL };
#else
static const char *kim_ui_plugin_dirs[] = { LIBDIR "/krb5/plugins/kimui", NULL };
#endif


struct kim_ui_plugin_context {
    krb5_context kcontext;
    struct plugin_dir_handle plugins;
    struct kim_ui_plugin_ftable_v0 *ftable;
    void **ftables;
    void *plugin_context;
};


/* ------------------------------------------------------------------------ */

static void kim_ui_plugin_context_free (kim_ui_plugin_context *io_context)
{
    if (io_context && *io_context) {
        if ((*io_context)->ftables) {
            krb5int_free_plugin_dir_data ((*io_context)->ftables);
        }
        if (PLUGIN_DIR_OPEN (&(*io_context)->plugins)) {
            krb5int_close_plugin_dirs (&(*io_context)->plugins);
        }
        if ((*io_context)->kcontext) {
            krb5_free_context ((*io_context)->kcontext);
        }
        free (*io_context);
        *io_context = NULL;
    }
}

/* ------------------------------------------------------------------------ */

static kim_error kim_ui_plugin_context_allocate (kim_ui_plugin_context *out_context)
{
    kim_error err = KIM_NO_ERROR;
    kim_ui_plugin_context context = NULL;

    if (!err && !out_context) { err = check_error (KIM_NULL_PARAMETER_ERR); }

    if (!err) {
        context = malloc (sizeof (*context));
        if (!context) { err = KIM_OUT_OF_MEMORY_ERR; }
    }

    if (!err) {
        err = krb5_error (NULL, krb5_init_context (&context->kcontext));
    }

    if (!err) {
        PLUGIN_DIR_INIT(&context->plugins);
        context->ftable = NULL;
        context->ftables = NULL;
        context->plugin_context = NULL;

        *out_context = context;
        context = NULL;
    }

    kim_ui_plugin_context_free (&context);

    return check_error (err);
}

#pragma mark -

/* ------------------------------------------------------------------------ */

kim_error kim_ui_plugin_init (kim_ui_context *io_context)
{
    kim_error err = KIM_NO_ERROR;
    kim_ui_plugin_context context = NULL;

    if (!err && !io_context) { err = check_error (KIM_NULL_PARAMETER_ERR); }

    if (!err) {
        err = kim_ui_plugin_context_allocate (&context);
    }

    if (!err) {
        PLUGIN_DIR_INIT(&context->plugins);

        err = krb5_error (context->kcontext,
                          krb5int_open_plugin_dirs (kim_ui_plugin_dirs,
                                                    kim_ui_plugin_files,
                                                    &context->plugins,
                                                    &context->kcontext->err));
    }

    if (!err) {
        err = krb5_error (context->kcontext,
                          krb5int_get_plugin_dir_data (&context->plugins,
                                                       "kim_ui_0",
                                                       &context->ftables,
                                                       &context->kcontext->err));
    }

    if (!err && context->ftables) {
        int i;

        for (i = 0; context->ftables[i]; i++) {
            struct kim_ui_plugin_ftable_v0 *ftable = context->ftables[i];
            context->plugin_context = NULL;

            err = ftable->init (&context->plugin_context);

            if (!err) {
                context->ftable = ftable;
                break; /* use first plugin that initializes correctly */
            }

            err = KIM_NO_ERROR; /* ignore failed plugins */
        }
    }

    if (!err && !context->ftable) {
        err = check_error (KRB5_PLUGIN_NO_HANDLE);
    }

    if (!err) {
        io_context->tcontext = context;
        context = NULL;
    }

    kim_ui_plugin_context_free (&context);

    return check_error (err);
}

/* ------------------------------------------------------------------------ */

kim_error kim_ui_plugin_enter_identity (kim_ui_context *in_context,
                                        kim_options     io_options,
                                        kim_identity   *out_identity,
                                        kim_boolean    *out_change_password)
{
    kim_error err = KIM_NO_ERROR;

    if (!err && !in_context         ) { err = check_error (KIM_NULL_PARAMETER_ERR); }
    if (!err && !io_options         ) { err = check_error (KIM_NULL_PARAMETER_ERR); }
    if (!err && !out_identity       ) { err = check_error (KIM_NULL_PARAMETER_ERR); }
    if (!err && !out_change_password) { err = check_error (KIM_NULL_PARAMETER_ERR); }

    if (!err) {
        kim_ui_plugin_context context = (kim_ui_plugin_context) in_context->tcontext;

        err = context->ftable->enter_identity (context->plugin_context,
                                               io_options,
                                               out_identity,
                                               out_change_password);
    }

    return check_error (err);
}

/* ------------------------------------------------------------------------ */

kim_error kim_ui_plugin_select_identity (kim_ui_context      *in_context,
                                         kim_selection_hints  io_hints,
                                         kim_identity        *out_identity,
                                         kim_boolean         *out_change_password)
{
    kim_error err = KIM_NO_ERROR;

    if (!err && !in_context         ) { err = check_error (KIM_NULL_PARAMETER_ERR); }
    if (!err && !io_hints           ) { err = check_error (KIM_NULL_PARAMETER_ERR); }
    if (!err && !out_identity       ) { err = check_error (KIM_NULL_PARAMETER_ERR); }
    if (!err && !out_change_password) { err = check_error (KIM_NULL_PARAMETER_ERR); }

    if (!err) {
        kim_ui_plugin_context context = (kim_ui_plugin_context) in_context->tcontext;

        err = context->ftable->select_identity (context->plugin_context,
                                                io_hints,
                                                out_identity,
                                                out_change_password);
    }

    return check_error (err);
}

/* ------------------------------------------------------------------------ */

kim_error kim_ui_plugin_auth_prompt (kim_ui_context      *in_context,
                                     kim_identity         in_identity,
                                     kim_prompt_type      in_type,
                                     kim_boolean          in_allow_save_reply,
                                     kim_boolean          in_hide_reply,
                                     kim_string           in_title,
                                     kim_string           in_message,
                                     kim_string           in_description,
                                     char               **out_reply,
                                     kim_boolean         *out_save_reply)
{
    kim_error err = KIM_NO_ERROR;

    if (!err && !in_context ) { err = check_error (KIM_NULL_PARAMETER_ERR); }
    if (!err && !in_identity) { err = check_error (KIM_NULL_PARAMETER_ERR); }
    if (!err && !out_reply  ) { err = check_error (KIM_NULL_PARAMETER_ERR); }
    /* in_title, in_message or in_description may be NULL */

    if (!err) {
        kim_ui_plugin_context context = (kim_ui_plugin_context) in_context->tcontext;

        err = context->ftable->auth_prompt (context->plugin_context,
                                            in_identity,
                                            in_type,
                                            in_allow_save_reply,
                                            in_hide_reply,
                                            in_title,
                                            in_message,
                                            in_description,
                                            out_reply,
                                            out_save_reply);
    }

    return check_error (err);
}

/* ------------------------------------------------------------------------ */

kim_error kim_ui_plugin_change_password (kim_ui_context  *in_context,
                                         kim_identity     in_identity,
                                         kim_boolean      in_old_password_expired,
                                         char           **out_old_password,
                                         char           **out_new_password,
                                         char           **out_verify_password)
{
    kim_error err = KIM_NO_ERROR;

    if (!err && !in_context         ) { err = check_error (KIM_NULL_PARAMETER_ERR); }
    if (!err && !in_identity        ) { err = check_error (KIM_NULL_PARAMETER_ERR); }
    if (!err && !out_old_password   ) { err = check_error (KIM_NULL_PARAMETER_ERR); }
    if (!err && !out_new_password   ) { err = check_error (KIM_NULL_PARAMETER_ERR); }
    if (!err && !out_verify_password) { err = check_error (KIM_NULL_PARAMETER_ERR); }

    if (!err) {
        kim_ui_plugin_context context = (kim_ui_plugin_context) in_context->tcontext;

        err = context->ftable->change_password (context->plugin_context,
                                                in_identity,
                                                in_old_password_expired,
                                                out_old_password,
                                                out_new_password,
                                                out_verify_password);
    }

    return check_error (err);
}

/* ------------------------------------------------------------------------ */

kim_error kim_ui_plugin_handle_error (kim_ui_context *in_context,
                                      kim_identity    in_identity,
                                      kim_error       in_error,
                                      kim_string      in_error_message,
                                      kim_string      in_error_description)
{
    kim_error err = KIM_NO_ERROR;

    if (!err && !in_context          ) { err = check_error (KIM_NULL_PARAMETER_ERR); }
    if (!err && !in_error_message    ) { err = check_error (KIM_NULL_PARAMETER_ERR); }
    if (!err && !in_error_description) { err = check_error (KIM_NULL_PARAMETER_ERR); }

    if (!err) {
        kim_ui_plugin_context context = (kim_ui_plugin_context) in_context->tcontext;

        err = context->ftable->handle_error (context->plugin_context,
                                             in_identity,
                                             in_error,
                                             in_error_message,
                                             in_error_description);
    }

    return check_error (err);
}

/* ------------------------------------------------------------------------ */

void kim_ui_plugin_free_string (kim_ui_context  *in_context,
                                char           **io_string)
{
    kim_error err = KIM_NO_ERROR;

    if (!err && !in_context) { err = check_error (KIM_NULL_PARAMETER_ERR); }
    if (!err && !io_string ) { err = check_error (KIM_NULL_PARAMETER_ERR); }

    if (!err) {
        kim_ui_plugin_context context = (kim_ui_plugin_context) in_context->tcontext;

        context->ftable->free_string (context->plugin_context,
                                      io_string);
    }
 }

/* ------------------------------------------------------------------------ */

kim_error kim_ui_plugin_fini (kim_ui_context *io_context)
{
    kim_error err = KIM_NO_ERROR;

    if (!err && !io_context) { err = check_error (KIM_NULL_PARAMETER_ERR); }

    if (!err) {
        kim_ui_plugin_context context = (kim_ui_plugin_context) io_context->tcontext;

        if (context) {
            err = context->ftable->fini (context->plugin_context);
        }

        if (!err) {
            kim_ui_plugin_context_free (&context);
            io_context->tcontext = NULL;
        }
    }

    return check_error (err);
}
