/* Copyright (c) 1997, 1998, 1999 Free Software Foundation, Inc.
   Author: Thorsten Kukuk <kukuk@suse.de>

   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 2, 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, write to the Free Software Foundation,
   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#define _GNU_SOURCE

#ifdef HAVE_GETOPT_H
#include <getopt.h>
#else
#include "lib/compat/getopt.h"
#endif
#include <locale.h>
#include <libintl.h>
#include <string.h>
#include <rpcsvc/nis.h>

#ifndef _
#define _(String) gettext (String)
#endif

static nis_error
list_group_members (char *group, int master_only, int silent)
{
  unsigned long mem_exp_cnt = 0, mem_imp_cnt = 0, mem_rec_cnt = 0;
  unsigned long nomem_exp_cnt = 0, nomem_imp_cnt = 0, nomem_rec_cnt = 0;
  char **mem_exp, **mem_imp, **mem_rec;
  char **nomem_exp, **nomem_imp, **nomem_rec;
  char buffer[strlen (group) + 50];
  nis_result *res;
  char *cp, *cp2;
  u_int i;

  cp = stpcpy (buffer, nis_leaf_of (group));
  cp = stpcpy (cp, ".groups_dir");
  cp2 = nis_domain_of (group);
  if (cp2 != NULL && strlen (cp2) > 0)
    {
      cp = stpcpy (cp, ".");
      strcpy (cp, cp2);
    }
  if (master_only)
    res = nis_lookup (buffer, FOLLOW_LINKS|EXPAND_NAME|MASTER_ONLY);
  else
    res = nis_lookup (buffer, FOLLOW_LINKS|EXPAND_NAME);

  if (res->status != NIS_SUCCESS && res->status != NIS_S_SUCCESS)
    return res->status;

  if ((NIS_RES_NUMOBJ (res) != 1) ||
      (__type_of (NIS_RES_OBJECT (res)) != NIS_GROUP_OBJ))
    return res->status;

  if (silent)
    return NIS_SUCCESS;

  mem_exp = malloc (sizeof (char *) * NIS_RES_NUMOBJ (res));
  mem_imp = malloc (sizeof (char *) * NIS_RES_NUMOBJ (res));
  mem_rec = malloc (sizeof (char *) * NIS_RES_NUMOBJ (res));
  nomem_exp = malloc (sizeof (char *) * NIS_RES_NUMOBJ (res));
  nomem_imp = malloc (sizeof (char *) * NIS_RES_NUMOBJ (res));
  nomem_rec = malloc (sizeof (char *) * NIS_RES_NUMOBJ (res));

  for (i = 0; i < NIS_RES_OBJECT (res)->GR_data.gr_members.gr_members_len; ++i)
    {
      char *grmem = NIS_RES_OBJECT (res)->GR_data.gr_members.gr_members_val[i];
      int neg = grmem[0] == '-';

      switch (grmem[neg])
	{
	case '*':
	  if (neg)
	    {
	      nomem_imp[nomem_imp_cnt] = grmem;
	      ++nomem_imp_cnt;
	    }
	  else
	    {
	      mem_imp[mem_imp_cnt] = grmem;
	      ++mem_imp_cnt;
	    }
	  break;
	case '@':
	  if (neg)
	    {
	      nomem_rec[nomem_rec_cnt] = grmem;
	      ++nomem_rec_cnt;
	    }
	  else
	    {
	      mem_rec[mem_rec_cnt] = grmem;
	      ++mem_rec_cnt;
	    }
	  break;
	default:
	  if (neg)
	    {
	      nomem_exp[nomem_exp_cnt] = grmem;
	      ++nomem_exp_cnt;
	    }
	  else
	    {
	      mem_exp[mem_exp_cnt] = grmem;
	      ++mem_exp_cnt;
	    }
	  break;
	}
    }
  {
    char buf[strlen (NIS_RES_OBJECT (res)->zo_domain) + 10];
    printf (_("Group entry for \"%s.%s\" group:\n"),
	    NIS_RES_OBJECT (res)->zo_name,
	    nis_domain_of_r (NIS_RES_OBJECT (res)->zo_domain,
			     buf, strlen (NIS_RES_OBJECT (res)->zo_domain)
			     + 10));
  }
  if (mem_exp_cnt)
    {
      fputs (_("    Explicit members:\n"), stdout);
      for (i = 0; i < mem_exp_cnt; ++i)
	printf ("\t%s\n", mem_exp[i]);
    }
  else
    fputs (_("    No explicit members\n"), stdout);
  if (mem_imp_cnt)
    {
      fputs (_("    Implicit members:\n"), stdout);
      for (i = 0; i < mem_imp_cnt; ++i)
	printf ("\t%s\n", &mem_imp[i][2]);
    }
  else
    fputs (_("    No implicit members\n"), stdout);
  if (mem_rec_cnt)
    {
      fputs (_("    Recursive members:\n"), stdout);
      for (i = 0; i < mem_rec_cnt; ++i)
	printf ("\t%s\n", &mem_rec[i][1]);
    }
  else
    fputs (_("    No recursive members\n"), stdout);
  if (nomem_exp_cnt)
    {
      fputs (_("    Explicit nonmembers:\n"), stdout);
      for (i = 0; i < nomem_exp_cnt; ++i)
	printf ("\t%s\n", &nomem_exp[i][1]);
    }
  else
    fputs (_("    No explicit nonmembers\n"), stdout);
  if (nomem_imp_cnt)
    {
      fputs (_("    Implicit nonmembers:\n"), stdout);
      for (i = 0; i < nomem_imp_cnt; ++i)
	printf ("\t%s\n", &mem_imp[i][3]);
    }
  else
    fputs (_("    No implicit nonmembers\n"), stdout);
  if (nomem_rec_cnt)
    {
      fputs (_("    Explicit nonmembers:\n"), stdout);
      for (i = 0; i < nomem_rec_cnt; ++i)
	printf ("\t%s=n", &nomem_rec[i][2]);
    }
  else
    fputs (_("    No recursive nonmembers\n"), stdout);

  free (mem_exp);
  free (mem_imp);
  free (mem_rec);
  free (nomem_exp);
  free (nomem_imp);
  free (nomem_rec);
  nis_freeresult (res);

  return NIS_SUCCESS;
}


/* Print the version information.  */
static inline void
print_version (void)
{
  fprintf (stdout, "nisgrpadm (%s) %s\n", PACKAGE, VERSION);
  fprintf (stdout, gettext ("\
Copyright (C) %s Thorsten Kukuk.\n\
This is free software; see the source for copying conditions.  There is NO\n\
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
"), "1998");
  /* fprintf (stdout, _("Written by %s.\n"), "Thorsten Kukuk"); */
}

static inline void
print_usage (void)
{
  fputs (_("Usage: nisgrpadm -a|-r|-t [-s] group principal ...\n"), stdout);
  fputs (_("       nisgrpadm -d|-l [-M] [-s] group\n"), stdout);
  fputs (_("       nisgrpadm -c [-M] [-s] group\n"), stdout);
}

static void
print_help (void)
{
  print_usage ();
  fputs (_("nisgrpadm - NIS+ group administration\n\n"), stdout);

  fputs (_("  -a             Add specified list of NIS+ principals to group\n"),
         stdout);
  fputs (_("  -c             Create group in the NIS+ namespace\n"), stdout);
  fputs (_("  -d             Destroy (remove) group from the NIS+ namespace\n"),
	 stdout);
  fputs (_("  -l             List members of the specified group\n"), stdout);
  fputs (_("  -M             Query master server only\n"), stdout);
  fputs (_("  -r             Remove the list of specified principals\n"),
	 stdout);
  fputs (_("  -s             Work silently\n"), stdout);
  fputs (_("  -t             Check if specified principals are members of group\n"),
	 stdout);
  fputs (_("  --help         Give this help list\n"), stdout);
  fputs (_("  --usage        Give a short usage message\n"), stdout);
  fputs (_("  --version      Print program version\n"), stdout);
}

static inline void
print_error (void)
{
  const char *program = "nisgrpadm";

  fprintf (stderr,
           _("Try `%s --help' or `%s --usage' for more information.\n"),
           program, program);
}

int
main (int argc, char *argv[])
{
  int silent_flag = 0;
  int add_flag = 0;
  int create_flag = 0;
  int destroy_flag = 0;
  int list_flag = 0;
  int master_only = 0;
  int remove_flag = 0;
  int member_flag = 0;
  int error_flag = 0;
  nis_error status = 0;

  setlocale (LC_MESSAGES, "");
  bindtextdomain (PACKAGE, LOCALEDIR);
  textdomain (PACKAGE);

  while (1)
    {
      int c;
      int option_index = 0;
      static struct option long_options[] =
      {
        {"version", no_argument, NULL, '\255'},
        {"usage", no_argument, NULL, '\254'},
        {"help", no_argument, NULL, '\253'},
        {NULL, 0, NULL, '\0'}
      };

      c = getopt_long (argc, argv, "sacdlrtM", long_options, &option_index);
      if (c == (-1))
        break;
      switch (c)
        {
        case 's':
          silent_flag = 1;
          break;
        case 'a':
	  add_flag = 1;
          break;
        case 'c':
	  create_flag = 1;
          break;
        case 'd':
	  destroy_flag = 1;
          break;
        case 'l':
	  list_flag = 1;
          break;
        case 'M':
          master_only = 1;
          break;
        case 'r':
          remove_flag = 1;
          break;
	case 't':
	  member_flag = 1;
	  break;
        case '\253':
          print_help ();
          return 0;
        case '\255':
          print_version ();
          return 0;
        case '\254':
          print_usage ();
          return 0;
        default:
          print_error ();
          return 1;
        }
    }

  argc -= optind;
  argv += optind;

  if (!error_flag)
    {
      if (add_flag + remove_flag + member_flag + create_flag + destroy_flag +
	  list_flag != 1)
	error_flag = 1;
      else
	if (master_only && !list_flag)
	  error_flag = 1;
	else
	  if (add_flag + remove_flag + member_flag == 1 && argc < 2)
	    error_flag = 1;
	  else
	    if (create_flag + destroy_flag + list_flag == 1 && argc != 1)
	      error_flag = 1;
    }

  if (error_flag)
    {
      if (silent_flag)
	return NIS_BADREQUEST;
      else
	{
	  if (master_only && !list_flag)
	    fputs (_("The '-M' flag is only allowed with '-l'\n"), stderr);
	  if (add_flag + remove_flag + member_flag == 1 && argc < 2)
	    fprintf (stderr, _("%s: To few arguments\n"), "nisgrpadm");
	  if (create_flag + destroy_flag + list_flag == 1 && argc != 1)
	    fputs (_("Missing group name.\n"), stderr);
	  print_error ();
	  return 1;
	}
    }

  if (list_flag)
    {
      status = list_group_members (argv[0], master_only, silent_flag);
      if (status && !silent_flag)
	nis_perror (status, argv[0]);
    }

  if (member_flag)
    {
      int i;

      for (i = 1; i < argc; ++i)
	{
	  nis_error retval = nis_ismember (argv[i], argv[0]) ? 0 : 2;

	  if (silent_flag)
	    {
	      if (retval != 0)
		status = retval;
	    }
	  else
	    {
	      if (retval == 0)
		fprintf (stdout,
			 _("Principal \"%s\" is a member of group \"%s\"\n"),
			 argv[i], argv[0]);
	      else
		{
		  fprintf (stdout,
		    _("Principal \"%s\" is not a member of group \"%s\"\n"),
			   argv[i], argv[0]);
		  status = retval;
		}
	    }
	}
    }

  if (remove_flag)
    {
      int i;
      for (i = 1; i < argc; ++i)
	{
	  nis_error retval = nis_removemember (argv[i], argv[0]);
	  if (silent_flag)
	    {
	      if (retval)
		status = retval;
	    }
	  else
	    if (retval)
	      {
		status = retval;
		nis_perror (retval, argv[0]);
	      }
	    else
	      fprintf (stdout, _("Removed \"%s\" from group \"%s\"\n"),
		       argv[i], argv[0]);
	}
    }

  if (add_flag)
    {
      int i;
      for (i = 1; i < argc; ++i)
	{
	  nis_error retval = nis_addmember (argv[i], argv[0]);
	  if (silent_flag)
	    {
	      if (retval)
		status = retval;
	    }
	  else
	    if (retval)
	      {
		status = retval;
		nis_perror (retval, argv[0]);
	      }
	    else
	      fprintf (stdout, _("Added \"%s\" to group \"%s\"\n"), argv[i],
		       argv[0]);
	}
    }

  if (create_flag)
    {
      if (argv[0][strlen (argv[0]) - 1] != '.')
	{
	  if (!silent_flag)
	    fputs (_("Group name must be fully qualified.\n"), stderr);
	}
      else
	{
	  status = nis_creategroup (argv[0], 0);
	  if (!silent_flag)
	    {
	      if (status)
		nis_perror (status, argv[0]);
	      else
		fprintf (stdout, _("Created group \"%s\"\n"), argv[0]);
	    }
	}
    }

  if (destroy_flag)
    {
      status = nis_destroygroup (argv[0]);
      if (!silent_flag)
	{
	  if (status)
	    nis_perror (status, argv[0]);
	  else
	    fprintf (stdout, _("Destroyed group \"%s\"\n"), argv[0]);
	}
    }

  return status;
}
