#!/usr/bin/pike
/*
 * usage: gdbmpasswd [-f passwdfile] add|delete username 
 *            [password] [home] [shell]
 *
 * This code is (C) 1997 Francesco Chemolli <kinkie@comedia.it>
 * It can be used, copied and modified under the terms of the GNU General
 * Public License, version 2 or later.
 *
 * This code comes with NO WARRANTY OF ANY KIND, implicit or explicit.
 * Use at your own risk.
 */
#define DEFAULT_DB "passwd.db"

#if efun(roxen)
inherit "roxenlib";
#endif

#include <stdio.h>

mixed * options = ({
	({
		"passwdfile",
		Getopt.HAS_ARG,
		({ "-f","--file" }),
		"GDBMPASSWDFILE",
		"passwd.db"
	}),
	({
		"help",
		Getopt.NO_ARG,
		"-h"
	}),
});

void help() {
	write(
		"gdbmpasswd handles roxen GDBM authorization databases.\n"
		"syntax:\n"
		"gdbmpasswd [-f passwdfile] add username password [home] [shell]\n"
		"gdbmpasswd [-f passwdfile] delete username\n"
		"gdbmpasswd [-f passwdfile] show username\n"
		"gdbmpasswd [-f passwdfile] list\n"
		"-f allows to specify a password database (default is " DEFAULT_DB ")\n"
		"add: add or change an user's information\n"
		"delete: delete an user\n"
		"list: list the usernames currently in the database\n"
		"show: show an user's entry contents\n"
		);
	exit(0);
}

mapping make_options_sane (mixed * getopt_results) {
	mixed tmp;
	mapping retval=([]);
	foreach (getopt_results,tmp)
		retval[tmp[0]]=tmp[1];
	return retval;
}

void add(string dbname, void|string username,
	void|string passwd, void|string home, void|string shell) 
{
	string *data;
	if (!username || !strlen(username)|| !passwd || !strlen(passwd))
		throw( ({"Missing argument"}) );

	passwd=crypt(passwd);

	//erase shell if unspecified
	if (shell&&!strlen(shell))
		shell=0;
	if (home&&!strlen(home))
		home=0;
	//make sure that if shell is specified, home is too.
	if (!home&&shell)
		home="";
	
	data=({passwd});
	if(home)
		data+=({home});
	if(shell)
		data+=({shell});

	object db=Gdbm.gdbm(dbname,"wc");
	db->store(username,data*":");
}

void delete(string dbname, void|string username)
{
	if(!username||!strlen(username))
		throw( ({"Missing argument"}) );
	object db=Gdbm.gdbm(dbname,"w");
	db->delete (username);
}

string* list(string dbname)
{
	object db=Gdbm.gdbm(dbname,"r");
	string key,*retval=({});
	for(key=db->firstkey();key;key=db->nextkey(key))
		retval += ({key});
	return retval;
}

string* show(string dbname, void|string username)
{
	if(!username||!strlen(username))
		throw( ({"Missing argument"}) );
	object db=Gdbm.gdbm(dbname,"r");
	string data;
	string* retval=({username});
	data=db->fetch(username);
	if (!data)
		return ({"No such user\n"});
	return retval+data/":"; 
}

int main(int argc, string * argv)
{
	mixed * args;
	object db;
	mapping arguments;
	string dbname=DEFAULT_DB;

	// find options
	args=Getopt.find_all_options(argv,options);
	arguments=make_options_sane(args);

//	write(sprintf("arguments: %O\n argv=%O\n",arguments,argv));

	if(arguments->help)
		help();
	if (arguments->passwdfile)
		dbname=arguments->passwdfile;

	//let's trim the arguments array to have the commands in a sane state.
	argv=argv[1..];
	while (sizeof(argv)>0 && !argv[0])
		argv=argv[1..];

	// no argument was given
	if (!sizeof(argv))
		help();
	
	//let's handle things
	switch(argv[0]) {
		string* tmp;
		case "add": add(dbname,@argv[1..]); break;
		case "delete": delete(dbname,@argv[1..]); break;
		case "list": write("\""+list(dbname,@argv[1..])*"\" \""+"\"\n"); break;
		case "show": 
			tmp=show(dbname,@argv[1..]);
			write (tmp[0]+": "+tmp[1..]*","+"\n");
			break;
		default: help();
	}
}

#if efun(roxen)
/*
 * Operations are based on prestates:
 * config
 * list
 * new
 * act (delete, change, show) //change is unimplemented
 */
string|mapping parse (object id) {
	string db=DEFAULT_DB;
	mixed retval;

	if (id->cookies->db&&strlen(id->cookies->db))
		db=id->cookies->db;

	if (id->prestate->config) {
		retval=http_redirect(id->not_query);
		retval->extra_heads["Set-Cookie"]="db="+
			http_encode_cookie(id->variables->db)+
			"; expires=Sun, 29-Dec-99 23:59:59 GMT"
			"; path=/";
		return retval;
	}
	if (id->prestate->act) {
		if (!id->variables->action||!strlen(id->variables->action))
			return http_redirect(add_pre_state(id->not_query,(<"list">)));
		switch (id->variables->action) {
			mixed tmp;
			case "show":
				tmp=show(db,id->variables->what||"");
				return sprintf(
						"<TITLE>Show user data</TITLE>"
						"<A href=\"%s\">Go to the listing</A><P>"
						"User: %s<BR>\n"
						"Crypted password: %s<BR>\n"
						"%s<BR>%s<BR>"
						,add_pre_state(id->not_query,(<"list">))
						,tmp[0],tmp[1]
						,sizeof(tmp)>2?"Home directory: "+tmp[2]:""
						,sizeof(tmp)>3?"Login shell: "+tmp[3]:""
						);
//			case "change":
//				return http_redirect (add_pre_state(id->not_query,(<"change">)));
			case "delete":
				delete(db,id->variables->what);
				return http_redirect(add_pre_state(id->not_query,(<"list">)));
			default:
		}
		return http_redirect(add_pre_state(id->not_query,(<"list">)));
	}
	if(id->prestate->new) {
		add(db,id->variables->name,id->variables->passwd,id->variables->home,
				id->variables->shell);
		return http_redirect(add_pre_state(id->not_query,(<"list">)));
	}
	if(id->prestate->list) {
		string *dbcontents, *lines,tmp;
		int j;
		mixed err;
		retval="<HTML><TITLE>Password database administration</TITLE>";
		retval += "<A href=\""+id->not_query+"\">Configure</A>\n";
		retval += sprintf(
				"<FORM action=%s>New user/change values:<BR>name <INPUT name=name><BR>"
				"password: <INPUT name=passwd><BR>"
				"home directory: <INPUT name=home><BR>"
				"login shell: <INPUT name=shell><BR>"
				"<INPUT type=submit value=\"Add it\"></FORM><BR>"
				"<FORM action=%s>"
				"<INPUT type=submit name=action value=show>"
				"<INPUT type=submit name=action value=delete>"
				"<OL>"
				,add_pre_state(id->not_query,(<"new">))
				,add_pre_state(id->not_query,(<"act">))
				);
		err=catch{
		dbcontents=sort(list(db));
		};
		if (err)
			dbcontents=({});
		lines=allocate(sizeof(dbcontents));
		for (j=0; j<sizeof(dbcontents); j++) {
			lines[j]=sprintf(
					"<LI><INPUT type=radio name=what value=\"%s\">%s",
					dbcontents[j],dbcontents[j]
					);
		}
		return retval +lines*"\n"+
			"</OL></FORM></HTML>";
	}
	return sprintf(
		"<HTML><TITLE>Password database administration</TITLE>"
		"<FORM action=\"%s\">"
		"Database location: <INPUT name=\"db\" value=%s size=50><BR>"
		"<INPUT type=submit value=Configure><INPUT type=reset><BR>"
		"</FORM>"
		"<A href=%s>Begin working on the database</A>"
		,add_pre_state(id->not_query,(<"config">))
		,db
		,add_pre_state(id->not_query,(<"list">))
		);
}
#endif
