/**********
 * Copyright (c) 2004 Greg Parker.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY GREG PARKER ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 **********/

#include "includes.h"
#include "formutils.h"
#include "data/connectionlist.h"
#include "data/prefs.h"
#include "rsrc/rsrc.h"
#include "ssh/ssh.h"

#include "passphraseform.h"

static char *PassphraseFormKeyName = ""; // in
static char *PassphraseFormPassphrase = NULL; // out - must be freed elsewhere


static void RetrievePassphrase(void) FORMS_SEGMENT;
static void ForgetPassphrase(void) FORMS_SEGMENT;
static void SetPassphraseEcho(Boolean echo) FORMS_SEGMENT;
static Boolean LoadPassphraseForm(void) FORMS_SEGMENT;

extern struct ssh_session_t *ss;


static void RetrievePassphrase(void)
{
    FieldPtr passphraseFld = PrvGetObjectByID(PassphraseFormPassphraseFieldID);
    MemHandle passphraseH;
    char *passphrase;

    FldReleaseFocus(passphraseFld);
    FldCompactText(passphraseFld);
    passphraseH = FldGetTextHandle(passphraseFld);
    FldSetTextHandle(passphraseFld, NULL);

    if (passphraseH) {
        passphrase = MemHandleLock(passphraseH);
    } else {
        passphrase = "";
    }

    PassphraseFormPassphrase = arena_strdup(passphrase);

    if (passphraseH) {
        MemSet(passphrase, MemHandleSize(passphraseH), 0);
        MemHandleUnlock(passphraseH);
        MemHandleFree(passphraseH);
    }
}


static void ForgetPassphrase(void)
{
    FieldPtr passphraseFld = PrvGetObjectByID(PassphraseFormPassphraseFieldID);
    MemHandle passphraseH;

    FldReleaseFocus(passphraseFld);
    FldCompactText(passphraseFld);
    passphraseH = FldGetTextHandle(passphraseFld);
    FldSetTextHandle(passphraseFld, NULL);

    if (passphraseH) {
        MemSet(MemHandleLock(passphraseH), MemHandleSize(passphraseH), 0);
        MemHandleUnlock(passphraseH);
        MemHandleFree(passphraseH);
    }
}


static void SetPassphraseEcho(Boolean echo)
{
    PrvSetControlValue(PassphraseFormEchoCheckboxID, echo);
    FldSetFont(PrvGetObjectByID(PassphraseFormPassphraseFieldID), 
               echo ? stdFont : (FontID)PasswordFontID);
}


static Boolean LoadPassphraseForm(void)
{
    Boolean ok = true;
    char *message;
    char *msgStart = "Enter passphrase for key '";
    char *msgEnd = "':";

    message = arena_malloc(strlen(msgStart) + strlen(msgEnd) + 
                           strlen(PassphraseFormKeyName) + 1);
    strcpy(message, msgStart);
    strncat(message, PassphraseFormKeyName, 80);
    strcat(message, msgEnd);

    if (ok) ok = PrvSetFieldToValueByID(PassphraseFormMessageFieldID, message);
    arena_free(message);
    if (!ok) complain("bad 1");

    if (ok) PrvSetFocusByID(PassphraseFormPassphraseFieldID);

    if (ok) SetPassphraseEcho(PrefsGetInt(prefEchoPassword, 0));

    return ok;
}


Boolean PassphraseFormHandleEvent(EventPtr e)
{
    FormPtr frmP = FrmGetActiveForm();

    switch (e->eType) {
    case frmOpenEvent:
        LoadPassphraseForm();
        FrmDrawForm(frmP);
        return true;

    case ctlSelectEvent:
        switch (e->data.ctlSelect.controlID) {
        case PassphraseFormOKButtonID:
            // stash passphrase in global
            RetrievePassphrase();
            // return FALSE so FrmDoDialog will return
            return false;

        case PassphraseFormCancelButtonID:
            // return FALSE so FrmDoDialog will return
            return false;

        case PassphraseFormEchoCheckboxID:
            // change password field echoing
            PrefsPutInt(prefEchoPassword, e->data.ctlSelect.on);
            SetPassphraseEcho(e->data.ctlSelect.on);
            return true;

        default:
            return false;
        }

    case frmCloseEvent:
        // clear entered passphrase, if any
        ForgetPassphrase();
        return false;

    default:
        return false;
    }
}


char *GetPassphrase(char *comment) 
{
    FormPtr frm;
    char *result;
    EventType event;

    PassphraseFormKeyName = comment;

    frm = FrmInitForm(PassphraseFormID);
    FrmSetEventHandler(frm, PassphraseFormHandleEvent);

    // send frmOpenEvent
    event.eType = frmOpenEvent;
    event.data.frmLoad.formID = PassphraseFormID;
    EvtAddEventToQueue(&event);

    FrmDoDialog(frm);
    FrmDeleteForm(frm);

    // result will be NULL on cancel and "" on empty passphrase
    result = PassphraseFormPassphrase;
    PassphraseFormKeyName = NULL;
    PassphraseFormPassphrase = NULL;

    return result;
}
