/*
 * Copyright (C) 2023 KylinSoftCo., 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 3, 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<security/pam_appl.h>
#include<security/pam_misc.h>
#include<stdio.h>
#include<string.h>
#include <termios.h>
#define BUF_SIZE 128
#define DISABLE_ECHO 0
#define ENABLE_ECHO 1
char * getpasswd(int  enable_echo);
int pam_conversation(int num_msg, const struct pam_message **msg,
		struct pam_response **resp, void *appdata_ptr);

struct pam_conv conv = {
	pam_conversation,
	NULL
};

int pam_conversation(int num_msg, const struct pam_message **msg,
		struct pam_response **resp, void *appdata_ptr)
{
	int count = num_msg;
	char *password = 0;
	*resp = (struct pam_response *)malloc(num_msg *
			sizeof(struct pam_response));
	/*指针备份，下面的循环需要指针后移*/
	struct pam_response *tmp_save = (struct pam_response *)(*resp);
	memset(*resp, 0, num_msg * sizeof(struct pam_response));
	while(count-- >= 1){
		switch((*msg)->msg_style){
			case PAM_PROMPT_ECHO_OFF:
				printf("%s",(*msg)->msg);
				password = getpasswd(DISABLE_ECHO);
				(*resp)->resp = password;
				(*resp)->resp_retcode = 0;
				break;
			case PAM_PROMPT_ECHO_ON:
				printf("%s",(*msg)->msg);
				password = getpasswd(ENABLE_ECHO);
				(*resp)->resp = password;
				(*resp)->resp_retcode = 0;
				break;
			case PAM_ERROR_MSG:
				printf("%s",(*msg)->msg);
				break;
			case PAM_TEXT_INFO:
				printf("%s",(*msg)->msg);
				break;
			default:
				printf("Should not reach here!\n");
		}
		if(count != 0){
			msg++;
			(*resp)++;
		}
	}

	/*指针归位 方法1*/
	/*
	unsigned long address_value = (unsigned long)(*resp) -
		(num_msg -1) * sizeof(struct pam_response);
	(*resp) = (struct pam_response *)address_value;
	*/

	/*指针归位 方法2*/
	(*resp) = tmp_save;

	return PAM_SUCCESS;
}



int main(int argc, char *argv[])
{
	pam_handle_t *pamh = NULL;
	int retval;
	char *user = NULL;
	if(argc == 2){
		user = argv[1];
	} else {
		printf("Usage: You must specify a username.\n");
		exit(1);
	}
	retval = pam_start("biotest", user, &conv, &pamh);
	if(retval == PAM_SUCCESS){
		printf("PAM started Success.\n");
	} else {
		printf("PAM started Failed.\n");
	}

	int auth_status = pam_authenticate(pamh,0);
	if(auth_status != PAM_SUCCESS){
		const char * error_msg = pam_strerror(pamh, auth_status);
		printf("Auth Status : %s\n",error_msg);
	} else {
		printf("Authenticate Success!\n");
		auth_status =  pam_acct_mgmt(pamh, 0);
	}

	if(pam_end(pamh, retval) != PAM_SUCCESS){
		pamh = NULL;
		printf("Failed to terminate PAM.");
		exit(1);
	}
	return (retval == PAM_SUCCESS ? 0:1);
}

char * getpasswd(int enable_echo)
{
	struct termios tp, save;
	char *password = (char *)malloc(BUF_SIZE * sizeof(char));
	/*
	printf("Input Password:");
	fflush(stdout);
	*/


	if (tcgetattr(STDIN_FILENO, &tp) == -1){
		printf("tcgetattr error\n");
		exit(1);
	}
	save = tp;/* So we can restore settings later */
	if(enable_echo)
		;/*Default is echo. Do nothing*/
	else
		tp.c_lflag &= ~ECHO;/* ECHO off, other bits unchanged */
	if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &tp) == -1){
		printf("tcgetattr error\n");
		exit(1);
	}

	/* Read some input */

	if (fgets(password, BUF_SIZE, stdin) == NULL)
		printf("Got end-of-file/error on fgets()\n");
	else
		;

	/* Restore original terminal settings */

	if (tcsetattr(STDIN_FILENO, TCSANOW, &save) == -1){
		printf("tcgetattr error\n");
		exit(1);
	}

	/*fgets读入的字符串末尾有回车符，在此替换为\0*/
	password[strnlen(password,128)-1] = '\0';
	return password;
}
