/*
 * getfn v0.6
 *
 * Alan Ford <ajf101@ecs.soton.ac.uk> for CSLib 04/09/2002
 *
 * Will search passwd file (NIS if available, otherwise using getpwent)
 * for the specified string(s), as part of the username or full name.
 * If the username exists, by default that is all that is printed.
 * Output suitable for use for address book queries in mutt.
 *
 *     This program is free software; you can redistribute it and/or
 *     modify it under teh 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).
 *
 * ChangeLog:
 * 0.1 04/09/2002 - Initial Release
 * 0.2 17/09/2002 - Offers YP Search capability
 * 0.2.1 16/02/2003 - Fix for commas in gecos field; multiple NIS searches
 * 0.2.2 22/03/2003 - Improved searching (only if in pw_name || pw_gecos)
 * 0.2.3 22/06/2003 & 11/08/2003 - Ignore case (-i) option added
 * 0.3 17/09/2002 - Non-NIS (getpwent) version. Pretty slow, disregarded.
 * 0.5 16/02/2003 - Initial attempt at merging stuff together.
 * 0.5.1 28/03/2003 - Maybe better than last one!
 * 0.5.2 02/04/2003 - Better domain handling
 * 0.5.3 11/08/2003 - Ignore case (-i) option added
 * 0.6 13/08/2003 - Unified branches, now uses getopt, domain name option
 *
 * Compile: gcc -o getfn getfn.c -lnsl /usr/lib/librpcsvc.a
 * Options:
 * -DNO_NIS for no NIS support (then the -lnsl above is also not required)
 * -DDOMAIN="domain.dom" to set custom default domain
 */
 
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <pwd.h>
#include <sys/types.h>
#include <unistd.h>
#include <netdb.h>
#include <getopt.h>
#ifndef NO_NIS
#include <rpcsvc/ypclnt.h>
#include <rpcsvc/yp_prot.h>
#include <rpc/rpc.h>
#endif

#ifndef DOMAIN
#define DOMAIN "ecs.soton.ac.uk"
#endif

static char *search;
static int ignorecase=0, quiet=0, forcesearch=0;
char *domain;

void display_help(char *argv[]) {
	printf("getfn 0.6 (2003-08-13)\n");
	printf("Usage:\t%s [-i] [-q] [-f] [-d DOMAIN] user [...]\n", argv[0]);
	printf("Looks up the full name of user.\n\n");
	printf("\t-i, --ignore-case\tIgnore case in searching.\n");
	printf("\t-q, --quiet\t\tDo not print first line (not for use with mutt).\n");
	printf("\t-d, --domain=DOMAIN\tUse specified domain name.\n");
	printf("\t-f, --force-search\tForce searching, even if username exists.\n");
	printf("\t-h, --help\t\tThis help message.\n\n");
	printf("Report bugs to <ajf101@ecs.soton.ac.uk>\n");
	exit(1);
}

void printaddr (char *u, char *fn) {
	char *comma = strchr(fn, ',');
	if (comma)
		*comma = '\0';
	printf("%s@%s\t%s\n", u, domain, fn);
}

void strlow (char *s) {
	for (;*s != '\0';s++)
		*s = tolower(*s);
}

static int searchandprint (int status, char *inkey, int inkeylen, char *inval, int invallen, char *indata __attribute__ ((unused))) {
	struct passwd *sp_pw;
	char *name, *gecos;

	if (status != 1)
		return status;

	if (ignorecase)
		strlow(inval);

	if (inkeylen > 0 && strstr(inval, search)) {
		sp_pw = getpwnam(inkey);
		name = strdup(sp_pw->pw_name);
		gecos = strdup(sp_pw->pw_gecos);

		if (ignorecase) {
			strlow(name);
			strlow(gecos);
		}
		
		if (strstr(name, search) || strstr(gecos, search))
			printaddr(sp_pw->pw_name, sp_pw->pw_gecos);
	}
	return 0;
}

int main(int argc, char *argv[])
{
	int i=1, args_left, c;
	struct passwd *pw;
	struct hostent *hp;
	char name[128];
	char *p;
	char *domainname = NULL;
	char *user, *uname, *gecos;

	while (1) {
		static struct option long_options[] = {
			{"quiet", no_argument, 0, 'q'},
			{"ignore-case", no_argument, 0, 'i'},
			{"force-search", no_argument, 0, 'f'},
			{"domain", required_argument, 0, 'd'},
			{"help", no_argument, 0, 'h'},
			{0, 0, 0, 0}
		};

		int option_index = 0;

		c = getopt_long(argc, argv, "qifhd:", long_options, &option_index);

		if (c == -1)
			break;

		switch (c) {
			case 'q':
				quiet = 1;
				break;

			case 'i':
				ignorecase = 1;
				break;
			
			case 'd':
				domain = optarg;
				break;

			case 'f':
				forcesearch = 1;
				break;

			default:
				display_help(argv);
		}
	}

	args_left = argc - optind;
	
	if (args_left < 1) {
		printf("Too Few Arguments.\n");
		display_help(argv);
	}

	if (!domain) {
		gethostname(name, 128);
		hp = gethostbyname(name);
		if (p = strchr(hp->h_name, '.'))
			domain = ++p;
		else
			domain = DOMAIN;
	}

	if (!quiet)
		printf("Querying passwd file...\n");

#ifndef NO_NIS
	yp_get_default_domain(&domainname);
#endif

	for (i=optind;i<argc;i++) {
		char *at;
		user = argv[i];
		at = strchr(user, '@');
		if (at) {
			*at = '\0';
			domain = ++at;
		}

		if (!forcesearch)
			pw = getpwnam(user);
		else
			pw = NULL;

		if (pw)
			printaddr(user, pw->pw_gecos);
#ifndef NO_NIS
		else if (!domainname) {
#else
		else {
#endif
			while (pw = getpwent()) {
				uname = strdup(pw->pw_name);
				gecos = strdup(pw->pw_gecos);
				if (ignorecase) {
					strlow(uname);
					strlow(gecos);
					strlow(user);
				}
				if (strstr(uname, user) || strstr(gecos, user))
					printaddr(pw->pw_name, pw->pw_gecos);
			}
#ifndef NO_NIS
		} else {
			struct ypall_callback ypcb;
			int res;
			search = user;
			if (ignorecase)
				strlow(search);
			ypcb.foreach = searchandprint;
			ypcb.data = NULL;
			res = yp_all(domainname, "passwd.byname", &ypcb);
#endif
		}
	}
}

