/*
 * mars-nwe-dosutils - NetWare/DOS utility tools.
 *
 * Copyright (C) 2026 Mario Fetka
 *
 * 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
 * of the License, 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, see <http://www.gnu.org/licenses/>.
 */

/*
 * Purpose: RIGHTS utility for displaying effective NetWare rights.
 * Depends on: net.h, ncpapi.h, netcall.c requester helpers, ncpapi.c namespace/NCP helpers, tools.c shared utility routines.
 */

#include "net.h"
#include "ncpapi.h"
#include <dos.h>
#include <sys/types.h>
#include <sys/stat.h>

#ifndef S_IFDIR
#define S_IFDIR 0040000
#endif

#define NW_RIGHT_S 0x01
#define NW_RIGHT_R 0x02
#define NW_RIGHT_W 0x04
#define NW_RIGHT_C 0x08
#define NW_RIGHT_E 0x10
#define NW_RIGHT_M 0x20
#define NW_RIGHT_F 0x40
#define NW_RIGHT_A 0x80

/* NCP effective-rights bits returned by NCP87 subfunction 29. */
#define NCP_RIGHT_READ   0x0001
#define NCP_RIGHT_WRITE  0x0002
#define NCP_RIGHT_OPEN   0x0004
#define NCP_RIGHT_CREATE 0x0008
#define NCP_RIGHT_DELETE 0x0010
#define NCP_RIGHT_OWNER  0x0020
#define NCP_RIGHT_SEARCH 0x0040
#define NCP_RIGHT_MODIFY 0x0080
#define NCP_RIGHT_SUPER  0x0100

static uint8 rights_map_ncp_mask(uint16 ncp_rights);
static void rights_ncp_path(char *dst, char *src, int max);


static void rights_usage(void)
{
  fprintf(stdout, "\nUsage:  RIGHTS [path]\n \n");
  fprintf(stdout, "  Rights = All | Supervisor | Read | Write | Create | Erase\n");
  fprintf(stdout, "               | Modify | File scan | Access Control\n");
}


static void rights_ncp_path(char *dst, char *src, int max)
{
  char up[260];
  char *p;

  if (tool_is_current_path(src)) {
    dst[0] = '\0';
    return;
  }

  tool_upcopy(up, src, sizeof(up));
  p = up;
  if (p[0] && p[1] == ':') {
    p += 2;
    if (*p == '\\') p++;
  }
  while (*p == '\\') p++;

  strmaxcpy(dst, p, max - 1);
}


static int rights_get_ncp_info(char *path, NCP_NDIR_INFO *info)
{
  uint8 connid = 0;
  uint8 dhandle = 0;
  char npath[260];

  if (!info)
    return(1);

  memset(info, 0, sizeof(*info));

  if (tool_current_dhandle(&connid, &dhandle))
    return(1);

  rights_ncp_path(npath, path, sizeof(npath));
  return(ncp87_06_obtain_ndir_info(npath, (uint16)dhandle,
                                    info, NULL, NULL, NULL));
}

static int rights_path_exists(char *path, int *is_dir)
{
  NCP_NDIR_INFO info;

  if (rights_get_ncp_info(path, &info))
    return(0);

  if (is_dir)
    *is_dir = (info.attributes & 0x10) ? 1 : 0;

  return(1);
}


static int rights_effective_mask(char *path, int is_dir, uint8 *mask)
{
  uint8 connid = 0;
  uint8 dhandle = 0;
  uint8 eff = 0;
  char usepath[260];
  int newhandle;
  uint16 ncp_rights = 0;

  if (mask) *mask = 0;

  if (tool_current_dhandle(&connid, &dhandle))
    return(-1);

  /*
   * Prefer the explicit Client32 NCP87 effective-rights request.  This is
   * the DOS Client32 equivalent of ncpfs ncp_get_eff_directory_rights().
   * It works for both files and directories, so pass the requested path
   * itself instead of mapping files to their parent directory.
   */
  rights_ncp_path(usepath, path, sizeof(usepath));
  if (!ncp87_1d_get_effective_rights(usepath,
                                      (uint16)dhandle,
                                      &ncp_rights,
                                      NULL, NULL, NULL)) {
    if (mask) *mask = rights_map_ncp_mask(ncp_rights);
    return(0);
  }

  /*
   * Fallback for older requesters/paths: allocate a temporary directory
   * handle and use the returned old-style effective-rights byte.
   * This cannot directly target a file, so files are mapped to their parent.
   */
  if (tool_is_current_path(path)) {
    usepath[0] = '\0';
  } else if (is_dir) {
    rights_ncp_path(usepath, path, sizeof(usepath));
  } else {
    char npath[260];
    rights_ncp_path(npath, path, sizeof(npath));
    tool_parent_path(usepath, npath, sizeof(usepath));
  }

  newhandle = alloc_temp_dir_handle(dhandle, usepath, 0, &eff);
  if (newhandle < 0) {
    if (usepath[0]) {
      int subdir = 1;
      int r = ncp16_02_get_directory_entry(dhandle, (uint8 *)usepath, &subdir,
                        NULL, NULL, NULL);
      if (r >= 0) {
        eff = (uint8)r;
      } else {
        return(-1);
      }
    } else {
      eff = 0xff;
    }
  } else {
    dealloc_dir_handle(newhandle);
  }

  if (mask) *mask = eff;
  return(0);
}


static uint8 rights_map_ncp_mask(uint16 ncp_rights)
{
  uint8 mask = 0;

  if (ncp_rights & NCP_RIGHT_SUPER)  mask |= NW_RIGHT_S;
  if (ncp_rights & NCP_RIGHT_READ)   mask |= NW_RIGHT_R;
  if (ncp_rights & NCP_RIGHT_WRITE)  mask |= NW_RIGHT_W;
  if (ncp_rights & NCP_RIGHT_CREATE) mask |= NW_RIGHT_C;
  if (ncp_rights & NCP_RIGHT_DELETE) mask |= NW_RIGHT_E;
  if (ncp_rights & NCP_RIGHT_MODIFY) mask |= NW_RIGHT_M;
  if (ncp_rights & NCP_RIGHT_SEARCH) mask |= NW_RIGHT_F;
  if (ncp_rights & NCP_RIGHT_OWNER)  mask |= NW_RIGHT_A;

  return(mask);
}


static void rights_mask_string(uint8 mask, char *out)
{
  out[0] = (mask & NW_RIGHT_S) ? 'S' : ' ';
  out[1] = (mask & NW_RIGHT_R) ? 'R' : ' ';
  out[2] = (mask & NW_RIGHT_W) ? 'W' : ' ';
  out[3] = (mask & NW_RIGHT_C) ? 'C' : ' ';
  out[4] = (mask & NW_RIGHT_E) ? 'E' : ' ';
  out[5] = (mask & NW_RIGHT_M) ? 'M' : ' ';
  out[6] = (mask & NW_RIGHT_F) ? 'F' : ' ';
  out[7] = (mask & NW_RIGHT_A) ? 'A' : ' ';
  out[8] = '\0';
}

/*
 * Novell RIGHTS layout:
 *   - lines with star:    two leading blanks, star, blank, text
 *   - lines without star: four leading blanks, text
 * Keep the right-column letter at a fixed-ish DOS-screen position.
 */
static void rights_print_line(int have, int star, char *text, char letter)
{
  (void)have;

  if (star)
    fprintf(stdout, "  * %-43s(%c)\n", text, letter);
  else
    fprintf(stdout, "    %-43s(%c)\n", text, letter);
}

static void rights_display(char *path, int is_dir, uint8 mask)
{
  char hdr[300];
  char mstr[10];

  tool_header_path(hdr, path, sizeof(hdr));
  rights_mask_string(mask, mstr);

  fprintf(stdout, "%s\n", hdr);

  if (is_dir)
    fprintf(stdout, "Your Effective Rights for this directory are [%s]\n", mstr);
  else
    fprintf(stdout, "Your Effective Rights for this file are [%s]\n", mstr);

  if (is_dir) {
    rights_print_line(mask & NW_RIGHT_S, 0, "You have Supervisor Rights to Directory.", 'S');
    rights_print_line(mask & NW_RIGHT_R, 1, "May Read from File.", 'R');
    rights_print_line(mask & NW_RIGHT_W, 1, "May Write to File.", 'W');
    rights_print_line(mask & NW_RIGHT_C, 0, "May Create Subdirectories and Files.", 'C');
    rights_print_line(mask & NW_RIGHT_E, 0, "May Erase Directory.", 'E');
    rights_print_line(mask & NW_RIGHT_M, 0, "May Modify Directory.", 'M');
    rights_print_line(mask & NW_RIGHT_F, 0, "May Scan for Files.", 'F');
    rights_print_line(mask & NW_RIGHT_A, 0, "May Change Access Control.", 'A');

    fprintf(stdout, "\n");
    fprintf(stdout, "* Has no effect on directory.\n\n");
    fprintf(stdout, "    Entries in Directory May Inherit [%s] rights.\n", mstr);
    if (mask == 0xff)
      fprintf(stdout, "    You have ALL RIGHTS to Directory Entry.\n");
  } else {
    rights_print_line(mask & NW_RIGHT_S, 0, "You have Supervisor Rights to File.", 'S');
    rights_print_line(mask & NW_RIGHT_R, 0, "May Read from File.", 'R');
    rights_print_line(mask & NW_RIGHT_W, 0, "May Write to File.", 'W');
    rights_print_line(mask & NW_RIGHT_C, 1, "May Create Subdirectories and Files.", 'C');
    rights_print_line(mask & NW_RIGHT_E, 0, "May Erase File.", 'E');
    rights_print_line(mask & NW_RIGHT_M, 0, "May Modify File.", 'M');
    rights_print_line(mask & NW_RIGHT_F, 0, "May Scan for File.", 'F');
    rights_print_line(mask & NW_RIGHT_A, 0, "May Change Access Control.", 'A');

    fprintf(stdout, "\n");
    fprintf(stdout, "* Create is necessary to salvage a file that has been deleted.\n\n");
    if (mask == 0xff)
      fprintf(stdout, "    You have ALL RIGHTS to Directory Entry.\n");
  }
}

int func_rights(int argc, char *argv[], int mode)
{
  char *path = ".";
  uint8 mask = 0;
  int is_dir;

  (void)mode;

  if (argc > 2) {
    fprintf(stdout, "Too many parameters on command line.\n");
    rights_usage();
    return(1);
  }

  if (argc == 2) {
    if (tool_is_help_arg(argv[1])) {
      rights_usage();
      return(0);
    }
    path = argv[1];
  }

  if (!rights_path_exists(path, &is_dir)) {
    fprintf(stdout, "Specified path not locatable.\n");
    rights_usage();
    return(1);
  }

  if (rights_effective_mask(path, is_dir, &mask)) {
    fprintf(stdout, "Specified path not locatable.\n");
    rights_usage();
    return(1);
  }

  rights_display(path, is_dir, mask);
  return(0);
}
