/* nwtests.c 20-May-96 */

/****************************************************************
 * (C)opyright (C) 1993,1996  Martin Stover, Marburg, Germany   *
 ****************************************************************/

#include "net.h"


static int tests_same_arg(char *a, char *b)
{
  while (*a || *b) {
    int ca = *a++;
    int cb = *b++;
    if (ca >= 'a' && ca <= 'z') ca -= 32;
    if (cb >= 'a' && cb <= 'z') cb -= 32;
    if (ca != cb) return(0);
  }
  return(1);
}

static void tests_usage(void)
{
  fprintf(stdout, "Usage: TESTS [OLD|NETCALL|E300|NCP87 [file]|NCP87F2 [file]]\n");
}

static int tests_netcall(void)
{
  unsigned char req[4];
  unsigned char repl[4];
  int asm_rc;
  int c_rc;

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

  fprintf(stdout, "TEST Net_Call ASM vs C\n");
  fprintf(stdout, "Call: INT 21h AH=19h get current drive\n");
  fprintf(stdout, "Expected: AL=0 for A:, 1 for B:, 2 for C:, ...\n\n");

  asm_rc = Net_Call(0x1900, req, repl);
  fprintf(stdout, "ASM Net_Call(1900h) rc=%04X drive=%c:\n",
          asm_rc, 'A' + (asm_rc & 0xff));

  c_rc = Net_Call_C(0x1900, req, repl);
  fprintf(stdout, "C   Net_Call_C(1900h) rc=%04X drive=%c:\n",
          c_rc, 'A' + (c_rc & 0xff));

  Net_Call_C_Dump();

  if ((asm_rc & 0xff) == (c_rc & 0xff))
    fprintf(stdout, "\nNETCALL C basic register test OK\n");
  else
    fprintf(stdout, "\nNETCALL C basic register test FAILED\n");

  return(0);
}

static int tests_old(int argc, char *argv[], int mode)
{

  int level     = ncp_17_02(NWCONN, 6);
  int dirhandle = alloc_temp_dir_handle(0, "SYS:", 'd', NULL);
  int result    = -1;
  uint8  *path  = (argc < 2) ? "SYS:\\TMP" : argv[1];
  if (dirhandle > -1) {
    result = ncp_16_02(dirhandle, "SYSTEM/", NULL, NULL, NULL, NULL);
    result = ncp_16_02(dirhandle, "SYSTEM", NULL, NULL, NULL, NULL);
  }
  fprintf(stdout, "dirhandle=%d, result=%d\n", dirhandle, result);
  result = redir_device_drive(0x4, "u:", path);
  fprintf(stdout, "redir path=%s, result=%d\n", path, result);

  path="Q1";
  result = redir_device_drive(0x3, "LPT1", path);
  fprintf(stdout, "redir path=%s, result=%d\n", path, result);

  {
    int k =-1;
    uint8 devname[20];
    uint8 remotename[130];
    int  devicetyp;
    while ((result = list_redir(++k, &devicetyp, devname, remotename)) > -1){
       fprintf(stdout, "index=%d, dev=%s(%d), %s result=%d\n",
            k, devname, devicetyp, remotename, result);
    }
  }
  if (level > -1) (void) ncp_17_02(NWCONN, level);
  return(0);
}

static void tests_dump_bytes(char *title, unsigned char *p, int len)
{
  int i;
  fprintf(stdout, "%s", title);
  for (i = 0; i < len; i++)
    fprintf(stdout, " %02X", p[i]);
  fprintf(stdout, "\n");
}

static int tests_netcall_e300(void)
{
  struct {
    uint16 len;
    uint8  func;
  } req_asm, req_c;

  struct {
    uint16 len;
    uint8  data[16];
  } repl_asm, repl_c;

  int asm_rc;
  int c_rc;

  memset(&req_asm, 0, sizeof(req_asm));
  memset(&req_c, 0, sizeof(req_c));
  memset(&repl_asm, 0, sizeof(repl_asm));
  memset(&repl_c, 0, sizeof(repl_c));

  /*
   * NCP 17/17 Get Login Encryption Key.
   * This is a known safe bindery/login helper call used by LOGIN/PASSWD code.
   * Request via old DOS NetWare API AX=E300.
   */
  req_asm.len = 1;
  req_asm.func = 0x17;
  repl_asm.len = 8;

  req_c.len = 1;
  req_c.func = 0x17;
  repl_c.len = 8;

  fprintf(stdout, "TEST Net_Call ASM vs C E300/NCP17/17\n");
  fprintf(stdout, "Call: Get Login Encryption Key\n\n");

  asm_rc = Net_Call(0xE300, &req_asm, &repl_asm);
  fprintf(stdout, "ASM Net_Call(E300h) rc=%04X\n", asm_rc);
  tests_dump_bytes("ASM key:", repl_asm.data, 8);

  c_rc = Net_Call_C(0xE300, &req_c, &repl_c);
  fprintf(stdout, "C   Net_Call_C(E300h) rc=%04X\n", c_rc);
  tests_dump_bytes("C   key:", repl_c.data, 8);

  Net_Call_C_Dump();

  if ((asm_rc & 0xff) == (c_rc & 0xff))
    fprintf(stdout, "\nNETCALL C E300 return test OK\n");
  else
    fprintf(stdout, "\nNETCALL C E300 return test FAILED\n");

  /*
   * Keys can differ per request, so do not require byte-identical keys.
   * We only compare return status and check that both calls returned 8 bytes.
   */
  return(0);
}


static void tests_put_word_lh(uint8 *p, uint16 v)
{
  p[0] = (uint8)(v & 0xff);
  p[1] = (uint8)((v >> 8) & 0xff);
}

static void tests_put_dword_lh(uint8 *p, uint32 v)
{
  p[0] = (uint8)(v & 0xff);
  p[1] = (uint8)((v >> 8) & 0xff);
  p[2] = (uint8)((v >> 16) & 0xff);
  p[3] = (uint8)((v >> 24) & 0xff);
}

static uint32 tests_get_dword_lh(uint8 *p)
{
  return((uint32)p[0] |
         ((uint32)p[1] << 8) |
         ((uint32)p[2] << 16) |
         ((uint32)p[3] << 24));
}

static int tests_get_current_drive(void)
{
  REGS regs;

  regs.h.ah = 0x19;
  int86(0x21, &regs, &regs);
  return((int)regs.h.al);
}

static int tests_current_dhandle(uint8 *dhandle)
{
  uint8 connid = 0;
  uint8 flags = 0;
  int drive;

  drive = tests_get_current_drive();

  if (get_drive_info((uint8)drive, &connid, dhandle, &flags)) {
    fprintf(stdout, "NCP87DBG get_drive_info failed drive=%c:\n", 'A' + drive);
    return(-1);
  }

  fprintf(stdout, "NCP87DBG drive=%c: connid=%u dhandle=%u flags=%02X\n",
          'A' + drive, connid, *dhandle, flags);

  if (!connid || (flags & 0x80)) {
    fprintf(stdout, "NCP87DBG current drive is not a network drive\n");
    return(-1);
  }

  return(0);
}

static int tests_add_handle_path(uint8 *p, uint8 dhandle, char *name)
{
  int nlen;

  nlen = strlen(name);
  if (nlen > 255) nlen = 255;

  /*
   * ncpfs ncp_add_handle_path2() form for dirstyle 0:
   *   vol/handle byte
   *   dirbase dword
   *   dirstyle byte
   *   NW path: component-count, len, bytes
   */
  *p++ = dhandle;
  tests_put_dword_lh(p, 0L); p += 4;
  *p++ = 0;                 /* NCP_DIRSTYLE_HANDLE */
  *p++ = 1;                 /* one component */
  *p++ = (uint8)nlen;
  memcpy(p, name, nlen);
  p += nlen;

  return(1 + 4 + 1 + 1 + 1 + nlen);
}

static int tests_ncp87_read_one(char *name, unsigned int ax)
{
  struct {
    uint16 len;
    uint8  data[320];
  } req;
  struct {
    uint16 len;
    uint8  data[256];
  } repl;

  uint8 *p;
  uint8 dhandle = 0;
  int hlen;
  int rc;
  uint32 attr;

  if (tests_current_dhandle(&dhandle))
    return(1);

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

  p = req.data;
  *p++ = 6;                 /* NCP87 subfunction 6: obtain file/subdir info */
  *p++ = 0;                 /* source namespace DOS */
  *p++ = 0;                 /* target namespace DOS */
  tests_put_word_lh(p, 0x0006); p += 2;       /* SA_ALL: hidden+system */
  tests_put_dword_lh(p, 0x00000004UL); p += 4;/* RIM_ATTRIBUTES */
  hlen = tests_add_handle_path(p, dhandle, name);
  p += hlen;

  req.len = (uint16)(p - req.data);
  repl.len = sizeof(repl.data);

  fprintf(stdout, "NCP87DBG TRY AX=%04X name=%s req.len=%u repl.len=%u\n",
          ax, name, req.len, repl.len);
  tests_dump_bytes("NCP87DBG req:", req.data, req.len > 48 ? 48 : req.len);

  rc = Net_Call_C(ax, &req, &repl);

  fprintf(stdout, "NCP87DBG rc=%04X repl.len=%u\n", rc, repl.len);
  tests_dump_bytes("NCP87DBG repl:", repl.data, repl.len > 64 ? 64 : repl.len);
  Net_Call_C_Dump();

  attr = tests_get_dword_lh(repl.data);
  fprintf(stdout, "NCP87DBG attr[0]=%08lX\n", attr);

  return(0);
}

static int tests_ncp87(int argc, char *argv[])
{
  char *name = "LOGIN.EXE";

  if (argc > 2)
    name = argv[2];

  fprintf(stdout, "TEST NCP87 obtain RIM_ATTRIBUTES for %s\n", name);
  fprintf(stdout, "This is read-only.  It does not modify the file.\n\n");

  /*
   * Try only the old-shell-style AX first.  0xF257 previously hung when used
   * inside FLAG, so do not try it here automatically.
   */
  return tests_ncp87_read_one(name, 0xE200);
}

static int tests_ncp87f2(int argc, char *argv[])
{
  char *name = "LOGIN.EXE";

  if (argc > 2)
    name = argv[2];

  fprintf(stdout, "TEST NCP87 F257 obtain RIM_ATTRIBUTES for %s\n", name);
  fprintf(stdout, "WARNING: this variant may hang on some clients.\n\n");

  return tests_ncp87_read_one(name, 0xF257);
}


int func_tests(int argc, char *argv[], int mode)
{
  if (argc < 2)
    return tests_old(argc, argv, mode);

  if (tests_same_arg(argv[1], "NETCALL"))
    return tests_netcall();

  if (tests_same_arg(argv[1], "E300") || tests_same_arg(argv[1], "NETCALLE300"))
    return tests_netcall_e300();

  if (tests_same_arg(argv[1], "NCP87"))
    return tests_ncp87(argc, argv);

  if (tests_same_arg(argv[1], "NCP87F2"))
    return tests_ncp87f2(argc, argv);

  if (tests_same_arg(argv[1], "OLD"))
    return tests_old(argc - 1, argv + 1, mode);

  if (tests_same_arg(argv[1], "/?") || tests_same_arg(argv[1], "-?") ||
      tests_same_arg(argv[1], "?")) {
    tests_usage();
    return(0);
  }

  return tests_old(argc, argv, mode);
}
