/*
 *  neercs        console-based window manager
 *  Copyright (c) 2006 Sam Hocevar <sam@zoy.org>
 *                2008 Jean-Yves Lamoureux <jylam@lnxscene.org>
 *                All Rights Reserved
 *
 *  $Id: lock.c 2360 2008-06-11 16:23:46Z jylam $
 *
 *  This program is free software. It comes without any warranty, to
 *  the extent permitted by applicable law. You can redistribute it
 *  and/or modify it under the terms of the Do What The Fuck You Want
 *  To Public License, Version 2, as published by Sam Hocevar. See
 *  http://sam.zoy.org/wtfpl/COPYING for more details.
 */

#include "config.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <caca.h>
#include <caca.h>
#include <time.h>
#include <sys/wait.h>
#include <sys/types.h>

#if defined USE_LOCK
#if defined(__APPLE__)
#   include <pam/pam_appl.h>
#   include <pam/pam_misc.h>
#else
#   include <security/pam_appl.h>
#   include <security/pam_misc.h>
#endif
#   include <pwd.h>
#endif

#include "neercs.h"

#if defined USE_LOCK
static int convpam(int num_msg, const struct pam_message **msg,
                   struct pam_response **resp, void *appdata_ptr);
#endif

int update_lock(int c, struct screen_list *screen_list)
{
    int refresh = 0;

#if defined USE_LOCK
    if(!screen_list->locked) return 0;

    if(c==0x08)      // BACKSPACE
    {
        if(screen_list->lock_offset)
        {
            screen_list->lockpass[screen_list->lock_offset-1] = 0;
            screen_list->lock_offset--;
        }
    }
    else if(c==0x0d) // RETURN
    {
        memset(screen_list->lockmsg, 0, 1024);
        if(validate_lock(screen_list, getenv("USER"), screen_list->lockpass))
        {
            memset(screen_list->lockpass, 0, 1024);
            screen_list->locked = 0;
            screen_list->lock_offset = 0;
            refresh = 1;
        }
        else
        {
            memset(screen_list->lockpass, 0, 1024);
            screen_list->lock_offset = 0;
            refresh = 1;
        }
    }
    else
    {
        if(screen_list->lock_offset < 1023)
        {
            screen_list->lockpass[screen_list->lock_offset++] = c;
            screen_list->lockpass[screen_list->lock_offset]   = 0;
        }
    }
#endif

    return refresh;
}

void draw_lock(struct screen_list *screen_list)
{
#if defined USE_LOCK
    unsigned int i;
    char buffer[1024];
    caca_canvas_t *cv = screen_list->cv;

    gethostname(buffer, sizeof(buffer)-1);

    int w = 65, h = 20;
    int x = (caca_get_canvas_width(cv) - w) / 2;
    int y = (caca_get_canvas_height(cv) - h) / 2;


    caca_set_color_ansi(cv, CACA_BLUE, CACA_BLUE);
    caca_fill_box(cv,
                   x, y,
                   w, h, '#');
    caca_set_color_ansi(cv, CACA_DEFAULT, CACA_BLUE);
    caca_draw_cp437_box(cv,
                         x, y,
                         w, h);

    x+=2;
    y++;
    caca_printf(cv,
                 (caca_get_canvas_width(cv) - strlen(PACKAGE_STRING " locked")) / 2,
                 y-1,
                 PACKAGE_STRING " locked");

    caca_printf(cv, x, y++, "Please type in your password for %s@%s :", getenv("USER"), buffer);
    y+=2;

    x = (caca_get_canvas_width(cv)/2) - ((strlen(screen_list->lockpass) / 2) + strlen("Password : "));
    caca_printf(cv, x, y, "Password : ");
    x+=strlen("Password : ");
    for(i=0; i<strlen(screen_list->lockpass); i++)
    {
        caca_put_str(cv, x, y, "*");
        x++;
    }


    if(strlen(screen_list->lockmsg))
    {
        x = ((caca_get_canvas_width(cv) - w) / 2) + (strlen(screen_list->lockmsg));
        y+=2;
        caca_set_color_ansi(cv, CACA_RED, CACA_BLUE);
        caca_printf(cv, x, y, "Error : %s", screen_list->lockmsg);
    }
#endif
}


#if defined USE_LOCK

/* FIXME, handle this without assuming this is a password auth */
static int convpam(int num_msg, const struct pam_message **msg,
                   struct pam_response **resp, void *appdata_ptr)
{

    struct pam_response *aresp;
    int i;
    aresp = calloc(num_msg, sizeof(*aresp));

    for (i = 0; i < num_msg; ++i)
    {
        switch(msg[i]->msg_style)
        {
        case PAM_PROMPT_ECHO_ON:
        case PAM_PROMPT_ECHO_OFF:
            aresp[i].resp = strdup(appdata_ptr);
            aresp[i].resp_retcode = 0;
            break;
        case PAM_ERROR_MSG:
            break;
        default :
            printf("Unknow message type from PAM\n");
            break;
        }
    }

    *resp = aresp;
    return (PAM_SUCCESS);
}
#endif

int validate_lock(struct screen_list *screen_list, char *user, char *pass)
{
#if USE_LOCK
    int ret;
    pam_handle_t *pamh=NULL;
    char buffer[100];
    const char *service="neercs";
    struct pam_conv conv = {
        convpam,
        pass,
    };

    ret = pam_start(service, user, &conv, &pamh);
    if(ret!=PAM_SUCCESS)
        return 0;
    pam_set_item(pamh, PAM_RUSER, user);

    ret = gethostname(buffer, sizeof(buffer)-1);
    if (ret)
    {
        perror("failed to look up hostname");
        ret = pam_end(pamh, PAM_ABORT);
        sprintf(screen_list->lockmsg, "Can't get hostname");
        pam_end(pamh, PAM_SUCCESS);
        return 0;
    }

    ret = pam_set_item(pamh, PAM_RHOST, buffer);
    if(ret!=PAM_SUCCESS)
    {
        sprintf(screen_list->lockmsg, "Can't set hostname");
        pam_end(pamh, PAM_SUCCESS);
        return 0;
    }

    ret = pam_authenticate(pamh, 0);
    if(ret!=PAM_SUCCESS)
    {
        sprintf(screen_list->lockmsg, "Can't authenticate");
        pam_end(pamh, PAM_SUCCESS);
        return 0;
    }

    ret = pam_end(pamh, PAM_SUCCESS);
#endif

    return 1;
}

