/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * Copyright (C) 2024 KylinSoft  Co., Ltd.
 *
 * 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, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 */
#include <QCoreApplication>
#include <glib.h>

/*using pam change password*/
#include <stdio.h>
#include <security/pam_appl.h>
#include <security/pam_misc.h>
#include <string.h>

static const char *non_interactive_password = "";
static const char *new_password = "";
static int isCurrentPwd = 0;
static char *error = "";

static int changeCurrent_conv (int num_msg, const struct pam_message **msg, struct pam_response **resp, void *appdata_ptr)
{
    struct pam_response *responses;
    int count;

//    assert (NULL != non_interactive_password);

    if (num_msg <= 0 || num_msg >= PAM_MAX_NUM_MSG) {
        printf("bad number of messages %d <= 0 || >= %d\n", num_msg, PAM_MAX_NUM_MSG);
        *resp = NULL;
        return PAM_CONV_ERR;
    }

    responses = (struct pam_response *) calloc ((size_t) num_msg, sizeof (*responses));
    if (NULL == responses) {
        return PAM_BUF_ERR;
    }

    //循环每个消息
    for (count=0; count < num_msg; count++) {
        //初始化响应变量
        responses[count].resp_retcode = 0;

//        printf("message[%d]: %d %s\n", count, msg[count]->msg_style, msg[count]->msg);

        //根据消息类型处理消息
        switch (msg[count]->msg_style) {

        //回显消息，从标准输入获取数据并显示在屏幕上，一般是交互的名称信息，如用户名等
        case PAM_PROMPT_ECHO_ON:
            fprintf (stderr, "PAM modules requesting echoing are not supported.\n");
            goto failed_conversation;

        //从标准输入获取不回显数据，一般是输入密码
        case PAM_PROMPT_ECHO_OFF:
            if (isCurrentPwd == 0){
                isCurrentPwd = 1;
                responses[count].resp = strdup (non_interactive_password);
            } else {
                responses[count].resp = strdup (new_password);
            }

            if (NULL == responses[count].resp) {
                goto failed_conversation;
            }
            break;

        //回显PAM模块传递的错误消息
        case PAM_ERROR_MSG:
            if ((NULL == msg[count]->msg)) {
                goto failed_conversation;
            } else {
                error = strdup(msg[count]->msg);
            }
            responses[count].resp = NULL;
            break;

        //回显PAM模块传递的文本消息
        case PAM_TEXT_INFO:
            if ((NULL == msg[count]->msg)) {
                goto failed_conversation;
            } else {
                error = strdup(msg[count]->msg);
            }
            responses[count].resp = NULL;
            break;

        default:
            (void) fprintf (stderr, ("conversation type %d not supported.\n"), msg[count]->msg_style);
            goto failed_conversation;
        }
    }

    *resp = responses;

    return PAM_SUCCESS;

failed_conversation:
    for (count=0; count < num_msg; count++) {
        if (NULL != responses[count].resp) {
            memset (responses[count].resp, 0, strnlen(responses[count].resp, sizeof(responses[count].resp)));
            free (responses[count].resp);
            responses[count].resp = NULL;
        }
    }

    free (responses);
    *resp = NULL;

    return PAM_CONV_ERR;
}


int main(int argc, char *argv[])
{
    struct pam_conv conv;
    int ret = 0;
    pam_handle_t * pamh = NULL;
    const char *username = g_get_user_name();
    if (argc == 3) {
        non_interactive_password = argv[1];
        new_password = argv[2];
    } else {
        printf("missing parameter!\n");
        return -1;
    }

    //会话函数传递到PAM模块中，在模块中通过pam_get_item获取并调用
    conv.conv = changeCurrent_conv;
    conv.appdata_ptr = NULL;

    //
    if ((pam_start("passwd", username, &conv, &pamh)) != PAM_SUCCESS){
        return 0;
    }

    //
    ret = pam_chauthtok(pamh, 0);
    if (ret == PAM_SUCCESS){
        if (error != "") {
            printf("%s\n", error);
            free(error);
        }
    } else {
        if (error == "") {
            printf("Unable to modify password!\n");
        } else {
            printf("%s\n", error);
            free(error);
        }
    }

    //结束PAM
    ret = pam_end(pamh, ret);

    return ret;
}

/*end pam*/
