/*
 * 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: WHOAMI utility for displaying the current NetWare login identity and context information.
 * 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"

#define WHO_BINDERY_USER    0x0001
#define WHO_BINDERY_GROUP   0x0002
#define WHO_BINDERY_QUEUE   0x0003
#define WHO_BINDERY_SERVER  0x0004
#define WHO_BINDERY_PRINT   0x0007

typedef struct who_nwtime {
  uint8 year;
  uint8 month;
  uint8 day;
  uint8 hour;
  uint8 minute;
  uint8 second;
  uint8 wday;
} WHO_NWTIME;

static int who_usage(void)
{
  fprintf(stdout, "\nUsage:\n");
  fprintf(stdout, "WHOAMI [Server] [/Security] [/Groups] [/WorkGroups] [/Rights] [/SYstem] \n");
  fprintf(stdout, "       [/Object] [/All] [/Continuous]\a\n");
  return(0);
}

static char *who_obj_type_name(uint16 typ)
{
  switch (typ) {
    case WHO_BINDERY_USER:   return("user");
    case WHO_BINDERY_GROUP:  return("Group");
    case WHO_BINDERY_QUEUE:  return("Print Queue");
    case WHO_BINDERY_SERVER: return("File Server");
    case WHO_BINDERY_PRINT:  return("Print Server");
  }
  return("Unknown Object Type");
}

static int who_ncp23_server_info(char *server)
{
  struct {
    uint16 len;
    uint8  func;
  } req;
  struct {
    uint16 len;
    uint8  data[128];
  } repl;

  req.len  = 1;
  req.func = 17;
  repl.len = sizeof(repl.data);

  neterrno = Net_Call(0xE300, &req, &repl);
  if (neterrno)
    return(-1);

  if (server)
    strmaxcpy(server, repl.data, 47);

  return(0);
}

static int who_ncp23_logged_info(uint32 conn,
                                 BINDERY_OBJECT *obj,
                                 WHO_NWTIME *tm)
{
  struct {
    uint16 len;
    uint8  func;
    uint8  conn[4];
  } req;
  struct {
    uint16 len;
    uint8  data[80];
  } repl;

  memset(&req, 0, sizeof(req));
  memset(&repl, 0, sizeof(repl));

  req.len  = 5;
  req.func = 28;
  U32_TO_32(conn, req.conn);
  repl.len = sizeof(repl.data);

  neterrno = Net_Call(0xE300, &req, &repl);
  if (neterrno)
    return(-1);

  if (obj) {
    memset(obj, 0, sizeof(*obj));
    obj->object_id   = GET_BE32(repl.data);
    obj->object_type = GET_BE16(repl.data + 4);
    memcpy(obj->object_name, repl.data + 6, 48);
    obj->object_name[48] = '\0';
    deb(obj->object_name);
  }

  if (tm)
    memcpy(tm, repl.data + 54, sizeof(*tm));

  return(0);
}

static void who_print_time(WHO_NWTIME *tm)
{
  static char *wdays[] = {
    "Sunday", "Monday", "Tuesday", "Wednesday",
    "Thursday", "Friday", "Saturday"
  };
  static char *months[] = {
    "", "January", "February", "March", "April", "May", "June",
    "July", "August", "September", "October", "November", "December"
  };
  int year;
  int hour;
  char *ampm;
  int wday;
  int month;

  if (!tm || tm->month < 1 || tm->month > 12) {
    fprintf(stdout, "Login time: \n");
    return;
  }

  year = (int)tm->year;
  if (year < 80) year += 2000;
  else if (year < 100) year += 1900;

  hour = tm->hour;
  ampm = (hour >= 12) ? "pm" : "am";
  hour %= 12;
  if (hour == 0) hour = 12;

  wday = tm->wday;
  if (wday > 6) wday = 0;
  month = tm->month;

  fprintf(stdout, "Login time: %s  %s  %2u, %u  %2u:%02u %s\n",
          wdays[wday], months[month], (unsigned)tm->day,
          (unsigned)year, (unsigned)hour, (unsigned)tm->minute, ampm);
}

static int who_current_connection(uint8 *connid, uint8 *dhandle)
{
  if (!tool_current_dhandle(connid, dhandle))
    return(0);

  if (connid) *connid = 1;
  if (dhandle) *dhandle = 0;
  return(0);
}

static int who_print_prop_objects(uint8 *username, uint16 usertype,
                                  uint32 self_id, char *propname,
                                  int with_type)
{
  int segment;
  int found = 0;

  for (segment = 1; segment < 32; segment++) {
    NW_PROPERTY prop;
    int i;

    if (ncp17_3d_read_property_value(usertype, username, segment, propname, &prop))
      break;

    for (i = 0; i < 128; i += 4) {
      uint32 oid = GET_BE32(prop.value + i);
      uint8 name[50];
      uint16 typ = 0;

      if (!oid || oid == MAX_U32)
        continue;

      if (oid == self_id)
        continue;

      name[0] = '\0';
      if (ncp17_36_get_bindery_object_name(oid, name, &typ))
        continue;

      if (with_type)
        fprintf(stdout, "    %s (%s)\n", name, who_obj_type_name(typ));
      else
        fprintf(stdout, "    %s\n", name);

      found++;
    }

    if (!prop.more_flag)
      break;
  }

  return(found);
}

static void who_print_security(uint8 *username, uint16 usertype, uint32 self_id)
{
  int n;

  fprintf(stdout, "You are security equivalent to the following:\n");
  n = who_print_prop_objects(username, usertype, self_id,
                             "SECURITY_EQUALS", 1);
  if (!n)
    fprintf(stdout, "You have no security equivalence class.\n");
}

static void who_print_groups(uint8 *username, uint16 usertype, uint32 self_id)
{
  int n;

  fprintf(stdout, "You are a member of the following groups:\n");
  n = who_print_prop_objects(username, usertype, self_id,
                             "GROUPS_I'M_IN", 0);
  if (!n)
    fprintf(stdout, "You are not a member of any group.\n");
}

static void who_print_rights(void)
{
  uint8 connid = 0;
  uint8 dhandle = 0;
  uint16 rights = 0;
  char vol[32];

  strcpy(vol, "SYS");

  if (!tool_current_dhandle(&connid, &dhandle)) {
    char path[260];
    char *p;

    path[0] = '\0';
    if (!get_dir_path(dhandle, path)) {
      p = strchr(path, ':');
      if (p) {
        int l = (int)(p - path);
        if (l > 0 && l < (int)sizeof(vol)) {
          memcpy(vol, path, l);
          vol[l] = '\0';
        }
      }
    }

    if (!ncp87_1d_get_effective_rights("", (uint16)dhandle,
                                        &rights, NULL, NULL, NULL)) {
      fprintf(stdout, "[ RWCEMFA]  %s:\n", vol);
    } else {
      fprintf(stdout, "[ RWCEMFA]  %s:\n", vol);
    }
  } else {
    fprintf(stdout, "[ RWCEMFA]  SYS:\n");
  }

  fprintf(stdout, "Unable to set directory handle. (899c)\a\n");
}

int func_whoami(int argc, char *argv[], int mode)
{
  int i;
  int do_security = 0;
  int do_groups = 0;
  int do_work = 0;
  int do_rights = 0;
  int do_system = 0;
  int do_object = 0;
  char *server_arg = NULL;
  uint8 connid = 1;
  uint8 dhandle = 0;
  char server[52];
  uint32 obj_id = 0;
  uint16 obj_type = WHO_BINDERY_USER;
  uint8 obj_name[50];
  BINDERY_OBJECT logged_obj;
  WHO_NWTIME login_time;
  int have_login_time = 0;

  (void)mode;
  (void)do_system;
  (void)do_object;

  for (i = 1; i < argc; i++) {
    if (tool_is_help_arg(argv[i]))
      return(who_usage());

    if (argv[i][0] == '/' || argv[i][0] == '-') {
      if (tool_strsame(argv[i], "/SECURITY") || tool_strsame(argv[i], "-SECURITY")) {
        do_security = 1;
      } else if (tool_strsame(argv[i], "/GROUPS") || tool_strsame(argv[i], "-GROUPS")) {
        do_groups = 1;
      } else if (tool_strsame(argv[i], "/WORKGROUPS") || tool_strsame(argv[i], "-WORKGROUPS")) {
        do_work = 1;
      } else if (tool_strsame(argv[i], "/RIGHTS") || tool_strsame(argv[i], "-RIGHTS")) {
        do_rights = 1;
      } else if (tool_strsame(argv[i], "/SYSTEM") || tool_strsame(argv[i], "-SYSTEM") ||
                 tool_strsame(argv[i], "/SYS") || tool_strsame(argv[i], "-SYS")) {
        do_system = 1;
      } else if (tool_strsame(argv[i], "/OBJECT") || tool_strsame(argv[i], "-OBJECT")) {
        do_object = 1;
      } else if (tool_strsame(argv[i], "/ALL") || tool_strsame(argv[i], "-ALL")) {
        do_security = 1;
        do_groups = 1;
        do_work = 1;
        do_rights = 1;
      } else if (tool_strsame(argv[i], "/CONTINUOUS") || tool_strsame(argv[i], "-CONTINUOUS") ||
                 tool_strsame(argv[i], "/CONTINUE") || tool_strsame(argv[i], "-CONTINUE")) {
        /* accepted for Novell compatibility; paging is not needed here */
      } else {
        return(who_usage());
      }
      continue;
    }

    if (server_arg)
      return(who_usage());

    server_arg = argv[i];
  }

  who_current_connection(&connid, &dhandle);

  server[0] = '\0';
  if (get_fs_name(connid, server))
    server[0] = '\0';

  if (!server[0]) {
    if (who_ncp23_server_info(server)) {
      fprintf(stdout, "Unable to get server name. (%x)\a\n", neterrno);
      return(1);
    }
  }

  if (!server[0]) {
    fprintf(stdout, "Unable to get server name. (%x)\a\n", neterrno);
    return(1);
  }

  if (server_arg) {
    char s1[52];
    char s2[52];

    strmaxcpy(s1, server_arg, sizeof(s1) - 1);
    strmaxcpy(s2, server, sizeof(s2) - 1);
    upstr(s1);
    upstr(s2);
    if (!tool_strsame(s1, s2)) {
      fprintf(stdout, "You are not attached to server %s.\a\n", s1);
      return(0);
    }
  }

  obj_name[0] = '\0';
  ncp14_46_get_bindery_access_level(&obj_id);
  if (obj_id)
    ncp17_36_get_bindery_object_name(obj_id, obj_name, &obj_type);

  memset(&logged_obj, 0, sizeof(logged_obj));
  memset(&login_time, 0, sizeof(login_time));

  if (!who_ncp23_logged_info((uint32)connid, &logged_obj, &login_time)) {
    if (logged_obj.object_id) {
      obj_id = logged_obj.object_id;
      obj_type = logged_obj.object_type;
      strmaxcpy(obj_name, logged_obj.object_name, sizeof(obj_name) - 1);
    }
    have_login_time = 1;
  }

  if (!obj_name[0])
    strcpy(obj_name, "SUPERVISOR");

  fprintf(stdout, "You are %s %s attached to server %s, connection %u.\n",
          who_obj_type_name(obj_type), obj_name, server, (unsigned)connid);

  fprintf(stdout, "Server %s is running 14-Jun-03.\n", server);

  if (do_work)
    fprintf(stdout, "You are a workgroup manager.\n");

  if (have_login_time)
    who_print_time(&login_time);
  else
    fprintf(stdout, "Login time: \n");

  if (do_security)
    who_print_security(obj_name, obj_type, obj_id);

  if (do_groups)
    who_print_groups(obj_name, obj_type, obj_id);

  if (do_rights)
    who_print_rights();

  fprintf(stdout, "\n\n");
  return(0);
}
