/*  Copyright (c) 2002, 2003, 2006 The Regents of the University of Michigan.
 *  All rights reserved.
 *
 *  Andy Adamson <andros@citi.umich.edu>
 *  David M. Richter <richterd@citi.umich.edu>
 *  Alexis Mackenzie <allamack@citi.umich.edu>
 *  Alex Soule <soule@umich.edu>
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions
 *  are met:
 *
 *  1. Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *  2. Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *  3. Neither the name of the University nor the names of its
 *     contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
 *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 *  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 *  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 *  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */


#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <libgen.h>
#include "libacl_nfs4.h"
#include <ftw.h>
#include <getopt.h>

#define OPT_DACL	0x98
#define OPT_SACL	0x99

static void usage(int);
static void more_help();
static char *execname;
static void print_acl_from_path(const char *, enum acl_type);
static int ignore_comment = 0;

static int print_acl(const char *fpath, const struct stat *sb, int tflag, struct FTW *ftwbuf)
{
	print_acl_from_path(fpath, ACL_TYPE_ACL);
	return 0;
}

static int print_dacl(const char *fpath, const struct stat *sb, int tflag, struct FTW *ftwbuf)
{
	print_acl_from_path(fpath, ACL_TYPE_DACL);
	return 0;
}

static int print_sacl(const char *fpath, const struct stat *sb, int tflag, struct FTW *ftwbuf)
{
	print_acl_from_path(fpath, ACL_TYPE_SACL);
	return 0;
}

static struct option long_options[] = {
        {"more-help",    0, 0, 'H' },
        {"help",         0, 0, 'h' },
        {"recursive",     0, 0, 'R' },
        {"omit-header",  0, 0, 'c'},
        {"dacl",         0, 0, OPT_DACL},
        {"sacl",         0, 0, OPT_SACL},
        { NULL,          0, 0, 0,  },
};

int main(int argc, char **argv)
{
	int opt, res = 1;
        int do_recursive = 0;
	int (*recursive)(const char *fpath, const struct stat *sb,
			 int tflag, struct FTW *ftwbuf) = print_acl;
	enum acl_type type = ACL_TYPE_ACL;
	
	execname = basename(argv[0]);

	if (argc < 2) {
		fprintf(stderr, "%s: you must specify a path.\n", execname);
		usage(0);
		goto out;
	}

	while ((opt = getopt_long(argc, argv, "HR?hc", long_options, NULL)) != -1) {
		switch(opt) {
			case 'H':
				more_help();
				res = 0;
				goto out;

			case 'R':
				do_recursive = 1;
				break;
			case 'c':
				ignore_comment = 1;
				break;
			case OPT_DACL:
				type = ACL_TYPE_DACL;
				recursive = print_dacl;
				break;
			case OPT_SACL:
				type = ACL_TYPE_SACL;
				recursive = print_sacl;
				break;
			case 'h':
				usage(1);
				res = 0;
				goto out;
			case '?':
			default:
				usage(0);
				goto out;
		}
	}

	if (optind >= argc) {
		fprintf(stderr, "%s: you must specify a path after options.\n", execname);
		usage(0);
		goto out;
	}

	for(; optind < argc; optind++) {
		if(do_recursive) {
			if (nftw(argv[optind], recursive, 20, 0) == -1)
				printf("Invalid filename: %s\n", argv[optind]);
		}
		else
			print_acl_from_path(argv[optind], type);
		res = 0;
	}
out:
	return res;
}

static void print_acl_from_path(const char *fpath, enum acl_type type)
{
	struct nfs4_acl *acl;

	switch (type) {
	case ACL_TYPE_ACL:
		acl = nfs4_getacl(fpath);
		break;
	case ACL_TYPE_DACL:
		acl = nfs4_getdacl(fpath);
		break;
	case ACL_TYPE_SACL:
		acl = nfs4_getsacl(fpath);
		break;
	}

	if (acl != NULL) {
		if (ignore_comment == 0)
			printf("# file: %s\n", fpath);
		nfs4_print_acl(stdout, acl);
		printf("\n");
		nfs4_free_acl(acl);
	} else {
		switch (errno) {
		case ENODATA:
			fprintf(stderr,"Attribute not found on file: %s\n",
				fpath);
			break;
		case EREMOTEIO:
			fprintf(stderr,"An NFS server error occurred.\n");
			break;
		case EOPNOTSUPP:
                        fprintf(stderr,"Operation to request attribute not "
				       "supported: %s\n", fpath);
			break;
		default:
			perror("Failed operation");
		}
	}
}

static void usage(int label)
{
	if (label)
		fprintf(stderr, "%s %s -- get NFSv4 file or directory access control lists.\n", execname, VERSION);

	static const char *gfusage = \
	"Usage: %s [OPTIONS] file ...\n"
	" .. where OPTIONS is any (or none) of:\n"
	"   -H, --more-help	 display ACL format information\n"
	"   -h, --help		 display this help text\n"
	"   -R, --recursive	 recurse into subdirectories\n"
	"   -c, --omit-header	 Do not display the comment header (Do not print filename)\n"
	"       --dacl           display the NFSv4.1 dacl\n"
	"       --sacl           display the NFSv4.1 sacl\n";

	fprintf(stderr, gfusage, execname);
}

static void more_help()
{
	const char *info = \
	"%s %s -- get NFSv4 file or directory access control lists.\n\n"
	"An NFSv4 ACL consists of one or more NFSv4 ACEs, each delimited by commas or whitespace.\n"
	"An NFSv4 ACE is written as a colon-delimited, 4-field string in the following format:\n"
	"\n"
	"    <type>:<flags>:<principal>:<permissions>\n"
	"\n"
	"    * <type> - one of:\n"
	"        'A'  allow\n"
	"        'D'  deny\n"
	"        'U'  audit\n"
	"        'L'  alarm\n"
	"\n"
	"    * <flags> - zero or more (depending on <type>) of:\n"
	"        'f'  file-inherit\n"
	"        'd'  directory-inherit\n"
	"        'p'  no-propagate-inherit\n"
	"        'i'  inherit-only\n"
	"        'S'  successful-access\n"
	"        'F'  failed-access\n"
	"        'g'  group (denotes that <principal> is a group)\n"
	"        'I'  inherited\n"
	"\n"
	"    * <principal> - named user or group, or one of: \"OWNER@\", \"GROUP@\", \"EVERYONE@\"\n"
	"\n"
	"    * <permissions> - one or more of:\n"
	"        'r'  read-data / list-directory \n"
	"        'w'  write-data / create-file \n"
	"        'a'  append-data / create-subdirectory \n"
	"        'x'  execute \n"
	"        'd'  delete\n"
	"        'D'  delete-child (directories only)\n"
	"        't'  read-attrs\n"
	"        'T'  write-attrs\n"
	"        'n'  read-named-attrs\n"
	"        'N'  write-named-attrs\n"
	"        'c'  read-ACL\n"
	"        'C'  write-ACL\n"
	"        'o'  write-owner\n"
	"        'y'  synchronize\n"
	"\n"
	"For more information and examples, please refer to the nfs4_acl(5) manpage.\n";

	printf(info, execname, VERSION); 
}
