/*
 *
 *   pamhelper.cc
 *
 *   PAM helper app for writing network scripts, etc., that need to authenticate users.
 *
 *   Copyright (C) 2003 by Matt Roberts, KK5JY,
 *   All rights reserved.
 *
 *
 */


#include <unistd.h>
#include <vector>
#include <iostream>
#include <string>
#include <my++/auth.h>
#include <my++/stype.h>


/*
 *
 *   int main (int argc, char **argv)
 *
 */

using namespace my;
using namespace std;


/*
 *
 *   class helper_session
 *
 */

class helper_session : public auth::pam::base_session {
	private:
		std::string hs_app_name, hs_user_name, hs_user_pass;

	public:
		bool doAuth(void);
		helper_session(
				const std::string &app_name,
				const std::string &user_name,
				const std::string &user_pass) :
			base_session(app_name),
			hs_app_name(app_name),
			hs_user_name(user_name),
			hs_user_pass(user_pass) { /* noop */ };
		~helper_session(void) throw() { /* noop */ };

	protected:
		bool conversation (auth::pam::base_session::message_list &ml);
};


bool helper_session::doAuth(void) {
	try {
		/* start the authentication sequence */
		if (!start(hs_user_name)) return false;

		/* set some parameters for the conversation */
		set_user_prompt ("Username: ");

		/* authenticate the user */
		if (!authenticate()) return false;

		/* do account management if needed */
		if (!manage_account()) {
			/* change the user's auth token if needed */
			if (!change_token())
				return false;
		}

		/* open the user's session */
		//if (!open_session()) return false;
	} catch (...) {
		try {
			end();
		} catch (...) {
		}

		/* rethrow the exception */
		throw;
	}

	return true;
}


bool helper_session::conversation(auth::pam::base_session::message_list &ml) {
	for (message_list::iterator mi = ml.begin(); mi != ml.end(); ++mi) {
		switch (mi->style) {
			case message::prompt_echo:
				mi->setResponse(hs_user_name);
				break;
			case message::prompt_noecho:
				mi->setResponse(hs_user_pass);
				break;
			default:
				mi->setResponse(0);
				break;
		}
	}

	return true;
}


int main (int argc, char **argv) {
	std::string appname;
	std::string authreq;
	std::string user, pass;
	const size_t maxtokens = 2;
	size_t auth_fail_delay = 0;
	std::string *tokens = new std::string[maxtokens];
	size_t i = 0;

	if (!tokens) {
		cerr << "Insufficient memory." << endl;
		return 1;
	}

	if (argc < 2) {
		cerr << "Must specify a service name." << endl;
		return 1;
	} else {
		appname = ltrim(rtrim(argv[1]));
	}

	if (argc > 2) {
		auth_fail_delay = atoi(argv[2]);
	}

	for(;;) {
		authreq.erase();
		user.erase();
		pass.erase();

		if (!getline(cin, authreq))
			return 0;

		for (i = 0; i < maxtokens; ++i)
			tokens[i].erase();

		std::string::iterator ch;

		for (
				i = 0,
				ch = authreq.begin();
				(i < maxtokens) && (ch != authreq.end());
				++ch
			) {
			switch (*ch) {
				case ':':
					++i;
					break;
				default:
					tokens[i] += *ch;
			}
		}

		user = ltrim(rtrim(tokens[0]));
		pass = ltrim(rtrim(tokens[1]));

		if (!user.size()) {
			cout << "ERR No USER specified" << endl;
			continue;
		}

		//cerr << "Authenticate:" << appname << ':' << user << ':' << pass << ':' << endl;

		/* create a PAM session */
		helper_session a (appname, user, pass);
	
		/* check the authenticity of the user */
		try {
			if (!a.doAuth()) {
				if (auth_fail_delay)
					sleep(auth_fail_delay);
				cout << "ERR Authentication Failure" << endl;
			} else {
				cout << "OK" << endl;
			}
		} catch (const exception &e) {
			cerr << e.what() << endl;
			return 1;
		} catch (...) {
			cerr << "Undefined error." << endl;
			return 1;
		}
	}

	return 0;
}
