#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <rsbac/types.h>
#include <rsbac/syscalls.h>
#include <rsbac/error.h>
#include <rsbac/helpers.h>

#include "rbac.h"

#define ROOM 10

char * progname;

void use(void)
    {
      printf("%s (RSBAC)\n***\n", progname);
      printf("Use: %s [-vp] option\n", progname);
      printf("RBAC administration\n");
      printf("Parent = 0 means no parent\n");
      printf(" -v = verbose\n");
      printf(" -p = print all rights in list\n");
      printf(" -e = show effective rights (also inherited from parents)\n");
      printf(" -l = list roles\n");
      printf(" -s id = show single role\n");
      printf(" -d id = delete role\n");
      printf(" -c id name = create role\n");
      printf(" -P id parent-id = set parent role\n");
      printf(" -f id right-list = set role's FD rights\n");
    }

int main(int argc, char ** argv)
{
  int res = 0;
  int verbose = 0;
  int printall = 0;
  int effective = FALSE;

  progname = argv[0];

  while((argc > 1) && (argv[1][0] == '-'))
    {
      char * pos = argv[1];
      pos++;
      while(*pos)
        {
          switch(*pos)
            {
              case 'h':
                use();
                return 0;
              case 'v':
                verbose++;
                break;
              case 'p':
                printall = 1;
                break;
              case 'e':
                effective = 1;
                break;
              case 'd':
                if(argc > 2)
                  {
                    __u32 role;

                    role = strtoul(argv[2], 0, 10);
                    argc--;
                    argv++;
                    if (verbose)
                      printf("Deleting role %u!\n", role);
                    res = rsbac_reg(RBAC_sys_del_role, &role);
                  }
                else
                  {
                    fprintf(stderr, "%s: missing role number value for parameter %c\n", progname, *pos);
                    exit(1);
                  }
                break;
              case 's':
                if(argc > 2)
                  {
                    struct rbac_sys_get_role_arg get_role_arg;
                    struct rbac_role_entry_t entry;
                    char tmp[RSBAC_MAXNAMELEN];
                    u_int j;

                    get_role_arg.role = strtoul(argv[2], 0, 10);
                    argc--;
                    argv++;
                    get_role_arg.entry_p = &entry;
                    if (verbose)
                      {
                        if (effective)
                          printf("Show role %u (effective rights)\n", get_role_arg.role);
                        else
                          printf("Show role %u\n", get_role_arg.role);
                      }
                    res = rsbac_reg(RBAC_sys_get_role, &get_role_arg);
                    error_exit(res);
                    if (effective)
                      {
                        struct rbac_role_entry_t parent_entry;
                        int inherit = MAXINHERIT;

                        get_role_arg.role = entry.parent;
                        get_role_arg.entry_p = &parent_entry;
                        while (   inherit
                               && get_role_arg.role
                               && !rsbac_reg(RBAC_sys_get_role, &get_role_arg)
                              )
                          {
                            entry.fd_rights |= parent_entry.fd_rights;
                            get_role_arg.role = parent_entry.parent;
                            inherit--;
                          }
                      }
                    if (printall)
                      {
                        printf("Role: %u\nParent: %u\nName: %s\nfd_rights:",
                               get_role_arg.role,
                               entry.parent,
                               entry.name,
                               u64tostrlog(tmp, entry.fd_rights));
                        for (j=0; j<R_NONE; j++)
                          if(entry.fd_rights & RSBAC_REQUEST_VECTOR(j))
                            printf(" %s", get_request_name(tmp,j));
                        printf("\n");
                      }
                        else
                          printf("Role: %u\nParent: %u\nName: %s\nfd_rights: %s\n",
                                 get_role_arg.role,
                                 entry.parent,
                                 entry.name,
                                 u64tostrlog(tmp, entry.fd_rights));
                  }
                else
                  {
                    fprintf(stderr, "%s: missing role number value for parameter %c\n", progname, *pos);
                    exit(1);
                  }
                break;
              case 'c':
                if(argc > 3)
                  {
                    struct rbac_sys_get_role_arg get_role_arg;
                    struct rbac_sys_set_role_arg set_role_arg;

                    get_role_arg.role = strtoul(argv[2], 0, 10);
                    argc--;
                    argv++;
                    get_role_arg.entry_p = &set_role_arg.entry;
                    if (verbose)
                      printf("Create role %u %s:\n", get_role_arg.role, argv[2]);
                    res = rsbac_reg(RBAC_sys_get_role, &get_role_arg);
                    if (!res)
                      error_exit(-RSBAC_EEXISTS);
                    set_role_arg.entry.parent = 0;
                    set_role_arg.entry.fd_rights = 0;
                    strncpy(set_role_arg.entry.name, argv[2], RBAC_NAMELEN - 1);
                    set_role_arg.entry.name[RBAC_NAMELEN - 1] = 0;
                    argc--;
                    argv++;
                    set_role_arg.role = get_role_arg.role;
                    res = rsbac_reg(RBAC_sys_set_role, &set_role_arg);
                    error_exit(res);
                  }
                else
                  {
                    fprintf(stderr, "%s: missing role number and/or name for parameter %c\n", progname, *pos);
                    exit(1);
                  }
                break;
              case 'P':
                if(argc > 3)
                  {
                    struct rbac_sys_get_role_arg get_role_arg;
                    struct rbac_sys_set_role_arg set_role_arg;
                    __u32 parent;

                    get_role_arg.role = strtoul(argv[2], 0, 10);
                    argc--;
                    argv++;
                    get_role_arg.entry_p = &set_role_arg.entry;
                    parent = strtoul(argv[2], 0, 10);;
                    argc--;
                    argv++;
                    if (verbose)
                      printf("Set parent of role %u to %u:\n", get_role_arg.role, parent);
                    res = rsbac_reg(RBAC_sys_get_role, &get_role_arg);
                    error_exit(res);
                    set_role_arg.role = get_role_arg.role;
                    set_role_arg.entry.parent = parent;
                    res = rsbac_reg(RBAC_sys_set_role, &set_role_arg);
                    error_exit(res);
                  }
                else
                  {
                    fprintf(stderr, "%s: missing role and/or parent number for parameter %c\n", progname, *pos);
                    exit(1);
                  }
                break;
              case 'f':
                if(argc > 2)
                  {
                    struct rbac_sys_get_role_arg get_role_arg;
                    struct rbac_sys_set_role_arg set_role_arg;
                    rsbac_request_vector_t rights_vector = 0;
                    u_int right;
                    u_int j;

                    get_role_arg.role = strtoul(argv[2], 0, 10);
                    argc--;
                    argv++;
                    get_role_arg.entry_p = &set_role_arg.entry;
                    while (argc > 2)
                      {
                        right = get_request_nr(argv[2]);
                        if(right == R_NONE)
                          {
                            right = strtol(argv[2],0,10);
                            if(   (   (right >= R_NONE)
                                   && (right < RSBAC_RC_SPECIAL_RIGHT_BASE)
                                  )
                               || (right >= RCR_NONE)
                               || (   (right == 0)
                                   && strcmp(argv[2],"0")
                                  )
                              )
                              {
                                if(!strcmp(argv[2],"W"))
                                  {
                                    rights_vector |= RSBAC_WRITE_REQUEST_VECTOR;
                                  }
                                else
                                if(!strcmp(argv[2],"RW"))
                                  {
                                    rights_vector |= RSBAC_READ_WRITE_REQUEST_VECTOR;
                                  }
                                else
                                if(!strcmp(argv[2],"SY"))
                                  {
                                    rights_vector |= RSBAC_SYSTEM_REQUEST_VECTOR;
                                  }
                                else
                                if(!strcmp(argv[2],"SE"))
                                  {
                                    rights_vector |= RSBAC_SECURITY_REQUEST_VECTOR;
                                  }
                                else
                                if(!strcmp(argv[2],"S"))
                                  {
                                    rights_vector |= RSBAC_RC_SPECIAL_RIGHTS_VECTOR;
                                  }
                                else
                                if(!strcmp(argv[2],"R"))
                                  {
                                    rights_vector |= RSBAC_READ_REQUEST_VECTOR;
                                  }
                                else
                                if(!strcmp(argv[2],"UA"))
                                  {
                                    rights_vector &= RSBAC_RC_SPECIAL_RIGHTS_VECTOR;
                                  }
                                else
                                if(!strcmp(argv[2],"A"))
                                  {
                                    rights_vector |= RSBAC_ALL_REQUEST_VECTOR;
                                  }
                                else
                                  { /* end of rights */
                                    break;
                                  }
                              }
                          }
                        else
                          {
                            rights_vector |= RSBAC_RC_RIGHTS_VECTOR(right);
                          }
                        argc--;
                        argv++;
                      }
                    rights_vector &= RSBAC_FD_REQUEST_VECTOR;
                    if (verbose)
                      {
                        char tmp[RSBAC_MAXNAMELEN];

                        printf("Set fd rights of role %u to",
                               get_role_arg.role);
                        if (printall)
                          {
                            for (j=0; j<R_NONE; j++)
                              if(rights_vector & RSBAC_REQUEST_VECTOR(j))
                                printf(" %s", get_request_name(tmp,j));
                          }
                        else
                          printf(" %s", u64tostrlog(tmp, rights_vector));
                        printf("\n");
                      }
                    res = rsbac_reg(RBAC_sys_get_role, &get_role_arg);
                    error_exit(res);
                    set_role_arg.role = get_role_arg.role;
                    set_role_arg.entry.fd_rights = rights_vector;
                    res = rsbac_reg(RBAC_sys_set_role, &set_role_arg);
                    error_exit(res);
                  }
                else
                  {
                    fprintf(stderr, "%s: missing role and/or parent number for parameter %c\n", progname, *pos);
                    exit(1);
                  }
                break;
              case 'l':
                {
                  /* list roles */
                  struct rbac_sys_list_roles_arg arg;

                  if (verbose)
                    printf("List of RBAC roles:\n");
                  arg.maxnum = 0;
                  arg.role_array = NULL;
                  res = rsbac_reg(RBAC_sys_list_roles, &arg);
                  error_exit(res);
                  if (res > 0)
                    {
                      arg.role_array = malloc(sizeof(__u32) * (res + ROOM));
                      if (!arg.role_array)
                        error_exit(-RSBAC_ENOMEM);
                      arg.maxnum = res + ROOM;
                      res = rsbac_reg(RBAC_sys_list_roles, &arg);
                      error_exit(res);
                      if (res > 0)
                        {
                          int err;
                          struct rbac_sys_get_role_arg get_role_arg;
                          struct rbac_role_entry_t entry;
                          u_int i;

                          get_role_arg.entry_p = &entry;
                          for (i=0; i<res; i++)
                            {
                              get_role_arg.role = i;
                              err = rsbac_reg(RBAC_sys_get_role, &get_role_arg);
                              if (!err)
                                printf("%u %s\n",
                                        arg.role_array[i],
                                        entry.name);
                            }
                        }
                    }
                }
                break;
              default:
                fprintf(stderr, "%s: unknown parameter %c\n", progname, *pos);
                exit(1);
            }
          pos++;
        }
      argv++;
      argc--;
    }

  return 0;
}
