/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#pragma ident	"%Z%%M%	%I%	%E% SMI"

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <stdarg.h>
#include <unistd.h>
#include <limits.h>
#include <string.h>
#include <syslog.h>
#include <errno.h>
#include <locale.h>
#ifndef SUNOS_4
#include <libintl.h>
#endif
#include <pwd.h>

#include <ns.h>
#include <list.h>

extern char *optarg;
extern int optind, opterr, optopt;
extern char *getenv(const char *);

static void _decode_ldapResult(int result, char *printerName);

static int
authorized()
{
	struct passwd *pw;
	uid_t uid;
	gid_t list[NGROUPS_MAX];
	int len;

	if ((uid = getuid()) == 0)
		return (1);	/* "root" is authorized */

	if (((pw = getpwnam("lp")) != NULL) && (uid == pw->pw_uid))
		return (1);	/* "lp" is authorized */

	if ((pw = getpwuid(uid)) == NULL)
		return (0);	/* intruders are not authorized */

	if (chkauthattr("solaris.print.admin", pw->pw_name) == 1)
		return (1);	/* "solaris.print.admin" is authorized */

	if ((len = getgroups(sizeof (list), list)) != -1)
		for (; len >= 0; len--)
			if (list[len] == 14)
				return (1);	/* group 14 is authorized */

	return (0);	/* nobody else is authorized */
}

static void
Usage(char *name)
{
	(void) fprintf(stderr,
		gettext("Usage: %s [-n files | nisplus | ldap] [-x] "
			"[-h ldaphost] [-D binddn] [-w passwd] "
			"[-a key=value] [-d key] (printer)\n"),
		name);
	exit(1);
}


/*
 *  main() calls the appropriate routine to parse the command line arguments
 *	and then calls the local remove routine, followed by the remote remove
 *	routine to remove jobs.
 */
int
main(int ac, char *av[])
{
	int result = 0;
	int delete_printer = 0;
	int c;
	char	*program = NULL,
		*printer = NULL,
		*host = NULL,
		*binddn = NULL,
		*passwd = NULL,
		*ins = NULL,
		*ons = "files";
	char	**changes = NULL;
	ns_cred_t	*cred = NULL;
	ns_printer_t 	*printer_obj = NULL;

	(void) setlocale(LC_ALL, "");

#if	!defined(TEXT_DOMAIN)
#define	TEXT_DOMAIN "SYS_TEST"
#endif
	(void) textdomain(TEXT_DOMAIN);

	if ((program = strrchr(av[0], '/')) == NULL)
		program = av[0];
	else
		program++;

	openlog(program, LOG_PID, LOG_LPR);

	if (ac < 2)
		Usage(program);

	while ((c = getopt(ac, av, "a:d:D:h:n:r:w:x")) != EOF)
		switch (c) {
		case 'd':
			if (strchr(optarg, '=') != NULL)
				Usage(program);
			/* FALLTHRU */
		case 'a':
			changes = (char **)list_append((void**)changes,
						(void *)strdup(optarg));
			break;
		case 'D':
			binddn = optarg;
			break;
		case 'h':
			host = optarg;
			break;
		case 'n':
			ons = optarg;
			break;
		case 'r':
			ins = optarg;
			break;
		case 'w':
			passwd = optarg;
			break;
		case 'x':
			delete_printer++;
			break;
		default:
			Usage(program);
		}

	if (optind != ac-1)
		Usage(program);

	/*
	 * Check required options have been given: [ -x | [ -a | -d ]]
	 */
	if ((changes == NULL) && (delete_printer == 0)) {
		Usage(program);
	}

	printer = av[optind];

	if (strchr(printer, ':') != NULL) {
		(void) fprintf(stderr, gettext(
			"POSIX-Style names are not valid destinations (%s)\n"),
			printer);
		return (1);
	}

	ins = normalize_ns_name(ins);
	ons = normalize_ns_name(ons);
	if (ins == NULL)
		ins = ons;

	/* check / set the name service for writing */
	if (strcasecmp("user", ons) == 0) {
		(void) setuid(getuid());
		ons = "user";
	} else if (strcasecmp("files", ons) == 0) {
		if (authorized() == 0) {
			(void) fprintf(stderr, gettext(
				"Permission denied: not authorized\n"));
			return (1);
		}
		ons = "files";
	} else if (strcasecmp("nisplus", ons) == 0) {
		ons = "nisplus";
	} else if (strcasecmp("ldap", ons) == 0) {
		if ((cred = calloc(1, sizeof (*cred))) == NULL) {
			(void) fprintf(stderr,
				gettext("could not initialize credential\n"));
			return (1);
		}

		if (binddn == NULL) {
			(void) fprintf(stderr,
			    gettext("Distinguished Name is required.\n"));
			return (1);
		}

		if (passwd == NULL) {
			passwd = getpassphrase(gettext("Bind Password:"));
		}

		/*
		 * Setup LDAP bind credentials, so that it uses
		 * the default ldap port, and the NS domain for this
		 * ldapclient box. Note: passwdType is currently not
		 * used but once the ldap native function can select
		 * secure or insure password it will pass the user selected
		 * security type.
		 */
		cred->passwd = passwd;
		cred->passwdType = NS_PW_INSECURE; /* use default */
		cred->binddn = binddn;
		cred->host = host;
		cred->port = 0;		/* use default */
		cred->domainDN = NULL;	/* use default */

		ons = "ldap";
		(void) setuid(getuid());
	} else {
		(void) fprintf(stderr,
			gettext("%s is not a supported name service.\n"),
			ons);
		return (1);
	}

	if (strcasecmp(NS_SVC_LDAP, ons) != 0) {

	    /* Naming Service is not LDAP */

	    /* get the printer object */
	    if ((printer_obj = ns_printer_get_name(printer, ins)) == NULL) {
		if (delete_printer != 0) {
			(void) fprintf(stderr, gettext("%s: unknown printer\n"),
				printer);
			return (1);
		}
		if ((printer_obj = calloc(1, sizeof (*printer_obj))) == NULL) {
			(void) fprintf(stderr, gettext(
				"could not initialize printer object\n"));
			return (1);
		}
		printer_obj->name = strdup(printer);
	    }

	    printer_obj->source = ons;

	    if (cred != NULL) {
		printer_obj->cred = cred;
	    }

	    /* make the changes to it */
	    while (changes != NULL && *changes != NULL) {
		int has_equals = (strchr(*changes, '=') != NULL);
		char *p, *key = NULL, *value = NULL;

		key = *(changes++);

		for (p = key; ((p != NULL) && (*p != NULL)); p++)
			if (*p == '=') {
				*p = NULL;
				value = ++p;
				break;
			} else if (*p == '\\')
				p++;

		if ((value != NULL) && (*value == NULL))
			value = NULL;

		if ((key != NULL) && (key[0] != NULL)) {
			if ((value == NULL) &&
			    (ns_get_value(key, printer_obj) == NULL) &&
			    (has_equals == 0)) {
				fprintf(stderr,
					gettext("%s: unknown attribute\n"),
					key);
				result = 1;
			} else
			(void) ns_set_value_from_string(key, value,
				printer_obj);
		}
	    }
	    if (delete_printer != 0)
		printer_obj->attributes = NULL;

	    /* write it back */
	    if (ns_printer_put(printer_obj) != 0) {
		(void) fprintf(stderr,
				gettext("Failed to write into %s database\n"),
				ons);
		result = 1;
	    }
	}

	else {
		/*
		 * Naming Service is LDAP
		 *
		 * Action the request by calling ns ldap functions to
		 * add, modify or delete the printer object.
		 */

		if ((printer_obj = calloc(1, sizeof (*printer_obj))) == NULL) {
			(void) fprintf(stderr, gettext(
				"could not initialize printer object\n"));
			return (1);
		}

		if ((cred != NULL) && (printer_obj != NULL)) {
			printer_obj->name = strdup(printer);
			printer_obj->cred = cred;
			printer_obj->cred->domainDN = NULL; /* use default */
			printer_obj->source = ons;
			printer_obj->nsdata = malloc(sizeof (NS_LDAPDATA));

			if (printer_obj->nsdata != NULL) {
				/*
				 * Update the LDAP directory for this printer
				 */

				if (delete_printer != 0) {
					/* Delete the printer object */
					((NS_LDAPDATA *)
					(printer_obj->nsdata))->attrList = NULL;
				} else {
					/* Add or modify the printer object */
					((NS_LDAPDATA *)
					(printer_obj->nsdata))->attrList =
									changes;
				}

				result = ns_printer_put(printer_obj);
				if (result != 0) {
					/* display LDAP specific message */
					_decode_ldapResult(result, printer);

					(void) fprintf(stderr, gettext(
					"Failed to update %s database\n"), ons);
					result = 1;
				}

				free(printer_obj->nsdata);
			}

			else {
				_decode_ldapResult(NSL_ERR_MEMORY, NULL);
				result = 1;
			}
		}

		else {
			result = 1;
			(void) fprintf(stderr,
				gettext("Error - no LDAP credentials\n"));
		}

		if (printer_obj != NULL) {
			if (printer_obj->name != NULL) {
				free(printer_obj->name);
			}
			free(printer_obj);
		}

	}

	return (result);
} /* main */




/*
 * *****************************************************************************
 *
 * Function:    _decode_ldapResult()
 *
 * Description: Decode the ldap_put_printer specific error codes and display
 *              the appropriate error message.
 *
 * Parameters:
 * Input:       int result - contains the NSL_RESULT codes
 *              char *printerName - name of printer
 * Output:      None
 *
 * Returns:     void
 *
 * *****************************************************************************
 */

static void
_decode_ldapResult(int result, char *printerName)

{
	NSL_RESULT lresult = (NSL_RESULT)result;

	/* ------------- */

	switch (lresult)
	{
		case NSL_OK:
		{
			break;
		}

		case NSL_ERR_INTERNAL:
		{
			(void) fprintf(stderr,
				gettext("Unexpected software error\n"));
			break;
		}

		case NSL_ERR_ADD_FAILED:
		{
			(void) fprintf(stderr, "%s %s\n",
				gettext("Failed to add printer:"), printerName);
			break;
		}

		case NSL_ERR_MOD_FAILED:
		{
			(void) fprintf(stderr, "%s %s\n",
				gettext("Failed to modify printer:"),
					printerName);
			break;
		}

		case NSL_ERR_DEL_FAILED:
		{
			(void) fprintf(stderr, "%s %s\n",
				gettext("Failed to delete printer:"),
					printerName);
			break;
		}


		case NSL_ERR_UNKNOWN_PRINTER:
		{
			(void) fprintf(stderr, "%s %s\n",
				gettext("Unknown printer:"), printerName);
			break;
		}

		case NSL_ERR_CREDENTIALS:
		{
			(void) fprintf(stderr, "%s\n",
		gettext("Missing LDAP credential information for printer:"));
			break;
		}

		case NSL_ERR_CONNECT:
		{
			(void) fprintf(stderr, "%s\n",
				gettext("Failed to connect to LDAP server"));
			break;
		}

		case NSL_ERR_BIND:
		{
			(void) fprintf(stderr, gettext("LDAP bind failed\n"));
			break;
		}

		case NSL_ERR_RENAME:
		{
			(void) fprintf(stderr, "%s %s\n",
			    gettext("Object rename not allowed for printer:"),
			    printerName);
			break;
		}

		case NSL_ERR_KVP:
		{
			(void) fprintf(stderr, "%s",
			    gettext("Setting sun-printer-kvp attribute is "
				"not supported through this command.\n"));
			break;
		}

		case NSL_ERR_BSDADDR:
		{
			(void) fprintf(stderr, "%s",
			    gettext("Setting sun-printer-bsdaddr attribute is "
				"not supported through this command.\n"
				"Use the bsaddr attribute instead.\n"));
			break;
		}

		case NSL_ERR_PNAME:
		{
			(void) fprintf(stderr, "%s",
			    gettext("Setting printer-name attribute is "
				"not supported through this command.\n"));
			break;
		}

		case NSL_ERR_MEMORY:
		{
			(void) fprintf(stderr,
					gettext("Memory allocation error\n"));
			break;
		}

		case NSL_ERR_MULTIOP:
		{
			(void) fprintf(stderr,
				gettext("Delete and add operation on the "
					"same key attribute is not allowed\n"));
			break;
		}

		case NSL_ERR_NOTALLOWED:
		{
			(void) fprintf(stderr,
				gettext("KVP attribute is not allowed\n"));
			break;
		}

		default:
		{
			(void) fprintf(stderr,
					gettext("Error code = %d\n"), result);
			break;
		}
	}

} /* _decode_ldapResult */