/*
 * mars-nwe-dosutils - NetWare/DOS utility tools.
 *
 * Copyright (C) 2026 Mario Fetka
 * Copyright (C) 1993,1996 Martin Stover, Marburg, Germany
 *
 * 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: LOGIN, LOGOUT, PASSWD and PROFILE command implementation for NetWare servers.
 * Depends on: net.h, nwcrypt.h/nwcrypt.c password encryption helpers, netcall.c requester helpers, tools.c shared utility routines.
 */

#include "net.h"
#include "nwcrypt.h"
#include <time.h>

#ifndef BLACK
#define BLACK 0
#endif
#ifndef BLUE
#define BLUE 1
#endif
#ifndef LIGHTGRAY
#define LIGHTGRAY 7
#endif
#ifndef WHITE
#define WHITE 15
#endif

static uint8 script_login_name[64];
static uint8 script_file_server[52];

static char **build_argv(char *buf, int bufsize, char *str);
extern int read_command_file(char *fstr);
extern int get_fs_name(int connid, char *name);

static int login_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) {
    if (server) server[0] = '\0';
    return(-1);
  }

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

  return(0);
}

static uint8 login_video_attr = 0x07;

static void login_gotoxy(int x, int y)
{
  REGS regs;

  regs.h.ah = 0x02;
  regs.h.bh = 0x00;
  regs.h.dh = (uint8)(y - 1);
  regs.h.dl = (uint8)(x - 1);
  int86(0x10, &regs, &regs);
}

static void login_cls_attr(uint8 attr)
{
  REGS regs;

  regs.h.ah = 0x06;
  regs.h.al = 0x00;
  regs.h.bh = attr;
  regs.h.ch = 0;
  regs.h.cl = 0;
  regs.h.dh = 24;
  regs.h.dl = 79;
  int86(0x10, &regs, &regs);
  login_gotoxy(1, 1);
}

static void login_write_attr(int x, int y, const char *s, uint8 attr)
{
  REGS regs;
  int col = x;

  while (*s) {
    login_gotoxy(col++, y);
    regs.h.ah = 0x09;
    regs.h.al = (uint8)*s++;
    regs.h.bh = 0;
    regs.h.bl = attr;
    regs.x.cx = 1;
    int86(0x10, &regs, &regs);
  }
  login_gotoxy(col, y);
}

static void login_fill_line(int y, uint8 attr)
{
  REGS regs;

  login_gotoxy(1, y);
  regs.h.ah = 0x09;
  regs.h.al = ' ';
  regs.h.bh = 0;
  regs.h.bl = attr;
  regs.x.cx = 80;
  int86(0x10, &regs, &regs);
}

static void login_screen_normal(void)
{
  login_video_attr = 0x07;
}


static int login_help(void)
{
  fprintf(stdout, "\n");
  fprintf(stdout, "  LOGIN                              General Help                 Mars NWE\n");
  fprintf(stdout, "  ------------------------------------------------------------------------\n");
  fprintf(stdout, "  Purpose: To gain access to the network.\n");
  fprintf(stdout, "  Syntax:  LOGIN [/VER] [[Server | Tree/][Username] [/options]\n");
  fprintf(stdout, "\n");
  fprintf(stdout, "  To:                                      Use:\n");
  fprintf(stdout, "    Login without running login scripts     /NS\n");
  fprintf(stdout, "    Clear the screen before executing        /CLS\n");
  fprintf(stdout, "    Specify a script file                    /S filename\n");
  fprintf(stdout, "    Display version information              /VER\n");
  fprintf(stdout, "\n");
  fprintf(stdout, "  Examples:\n");
  fprintf(stdout, "    LOGIN SUPERVISOR\n");
  fprintf(stdout, "    LOGIN SERVER/SUPERVISOR\n");
  fprintf(stdout, "\n");
  return(0);
}

static void login_banner(void)
{
  login_cls_attr(0x07);            /* normal black background */

  /*
   * NetWare-like header, but blue for Mars NWE:
   *   blue separator
   *   blue title line
   *   blue separator
   * then normal black prompt area.
   */
  login_fill_line(1, 0x1f);        /* white on blue */
  login_fill_line(2, 0x1f);        /* white on blue */
  login_write_attr(36, 2, "Mars NWE", 0x1f);
  login_fill_line(3, 0x1f);        /* white on blue */

  login_screen_normal();
  login_gotoxy(1, 4);
}

static char *skip_spaces(char *p)
{
  while (*p == 32 || *p == '\t') p++;
  return(p);
}

static void strip_quotes(char *s)
{
  char *d = s;
  char quote = 0;

  while (*s) {
    if (!quote && (*s == '"' || *s == '\'')) {
      quote = *s++;
      continue;
    }
    if (quote && *s == quote) {
      quote = 0;
      s++;
      continue;
    }
    *d++ = *s++;
  }
  *d = '\0';
}


static void script_get_timevar(char *name, char *out, int outlen)
{
  time_t now;
  struct tm *tmv;
  int hour;
  static char *months[] = {
    "JANUARY", "FEBRUARY", "MARCH", "APRIL", "MAY", "JUNE",
    "JULY", "AUGUST", "SEPTEMBER", "OCTOBER", "NOVEMBER", "DECEMBER"
  };
  static char *days[] = {
    "SUNDAY", "MONDAY", "TUESDAY", "WEDNESDAY",
    "THURSDAY", "FRIDAY", "SATURDAY"
  };

  *out = '\0';

  time(&now);
  tmv = localtime(&now);
  if (!tmv) return;

  upstr(name);

  if (!strcmp(name, "GREETING_TIME")) {
    if (tmv->tm_hour < 12) strcpy(out, "MORNING");
    else if (tmv->tm_hour < 18) strcpy(out, "AFTERNOON");
    else strcpy(out, "EVENING");
  } else if (!strcmp(name, "MONTH_NAME")) {
    strmaxcpy(out, months[tmv->tm_mon], outlen - 1);
  } else if (!strcmp(name, "MONTH")) {
    sprintf(out, "%02d", tmv->tm_mon + 1);
  } else if (!strcmp(name, "DAY")) {
    sprintf(out, "%02d", tmv->tm_mday);
  } else if (!strcmp(name, "YEAR")) {
    sprintf(out, "%04d", tmv->tm_year + 1900);
  } else if (!strcmp(name, "DAY_OF_WEEK")) {
    strmaxcpy(out, days[tmv->tm_wday], outlen - 1);
  } else if (!strcmp(name, "HOUR")) {
    hour = tmv->tm_hour % 12;
    if (!hour) hour = 12;
    sprintf(out, "%d", hour);
  } else if (!strcmp(name, "MINUTE")) {
    sprintf(out, "%02d", tmv->tm_min);
  } else if (!strcmp(name, "SECOND")) {
    sprintf(out, "%02d", tmv->tm_sec);
  } else if (!strcmp(name, "AM_PM")) {
    strcpy(out, tmv->tm_hour >= 12 ? "PM" : "AM");
  }
}

static void script_expand_var(char *name, char *out, int outlen)
{
  upstr(name);
  *out = '\0';

  if (!strcmp(name, "LOGIN_NAME")) {
    strmaxcpy(out, script_login_name, outlen - 1);
  } else if (!strcmp(name, "FILE_SERVER")) {
    strmaxcpy(out, script_file_server, outlen - 1);
  } else if (!strcmp(name, "P_STATION") || !strcmp(name, "STATION")) {
    strcpy(out, "000000000000");
  } else {
    script_get_timevar(name, out, outlen);
    if (!*out) {
      strcpy(out, "%");
      strncat(out, name, outlen - strlen(out) - 1);
    }
  }
}

static void script_put_expanded(char *s)
{
  while (s && *s) {
    if (*s == '%') {
      char name[64];
      char value[128];
      int i = 0;

      s++;
      while ((*s == '_' ||
              (*s >= 'A' && *s <= 'Z') ||
              (*s >= 'a' && *s <= 'z') ||
              (*s >= '0' && *s <= '9')) &&
             i < (int)sizeof(name) - 1) {
        name[i++] = *s++;
      }
      name[i] = '\0';

      if (i) {
        script_expand_var(name, value, sizeof(value));
        fprintf(stdout, "%s", value);
      } else {
        fputc('%', stdout);
      }
    } else {
      fputc(*s++, stdout);
    }
  }
}

static void script_write(char *p)
{
  p = skip_spaces(p);
  strip_quotes(p);
  script_put_expanded(p);
  fputc('\n', stdout);
}

static void script_join_args(char **argv, int argc)
{
  if (argc > 2) {
    char *p = argv[argc-1];
    while (p-- > argv[1]) {
      if (*p == '\0') *p = 32;
    }
  }
}

static void script_call_line(char *line)
{
  char *buf;
  char **argv;
  int argc = 0;
  char **pp;

  buf = xmalloc(512);
  argv = build_argv(buf, 512, line);
  if (argv != NULL) {
    pp = argv;
    while (*pp) {
      argc++;
      pp++;
    }
    if (argc > 0) {
      upstr(argv[0]);
      call_func_entry(argc, argv);
    }
  }
  xfree(buf);
}

static int script_eval_if(char *line)
{
  char tmp[512];
  char *p;
  char *q;
  int neg = 0;

  strmaxcpy(tmp, line, sizeof(tmp) - 1);
  upstr(tmp);

  if (strstr(tmp, "MEMBER OF") != NULL) {
    if (strstr(tmp, "EVERYONE") != NULL) return(1);
    return(0);
  }

  p = strstr(tmp, "LOGIN_NAME");
  if (p != NULL) {
    q = strstr(p, "<>");
    if (q != NULL) neg = 1;
    else q = strchr(p, '=');

    if (q != NULL) {
      char want[64];
      char have[64];
      int i = 0;

      q += neg ? 2 : 1;
      q = skip_spaces(q);
      if (*q == '"' || *q == '\'') q++;

      while (*q && *q != '"' && *q != '\'' && *q != 32 && *q != '\t' && i < 63)
        want[i++] = *q++;
      want[i] = '\0';

      strmaxcpy(have, script_login_name, sizeof(have) - 1);
      upstr(have);

      if (neg) return(strcmp(have, want) != 0);
      return(strcmp(have, want) == 0);
    }
  }

  p = strstr(tmp, "DAY_OF_WEEK");
  if (p != NULL) {
    q = strchr(p, '=');
    if (q != NULL) {
      char want[64];
      char have[64];
      int i = 0;

      q++;
      q = skip_spaces(q);
      if (*q == '"' || *q == '\'') q++;

      while (*q && *q != '"' && *q != '\'' && *q != 32 && *q != '\t' && i < 63)
        want[i++] = *q++;
      want[i] = '\0';

      strcpy(have, "DAY_OF_WEEK");
      script_get_timevar(have, have, sizeof(have));
      upstr(have);

      return(strcmp(have, want) == 0);
    }
  }

  return(0);
}

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

static int script_execute_line(char *line)
{
  char work[512];
  char cmd[32];
  char *p;
  char *arg;
  int i;

  strmaxcpy(work, line, sizeof(work) - 1);
  p = skip_spaces(work);

  if (!*p || *p == ';') return(0);

  i = 0;
  while (p[i] && p[i] != 32 && p[i] != '\t' && i < 31) {
    cmd[i] = p[i];
    i++;
  }
  cmd[i] = '\0';
  upstr(cmd);

  arg = skip_spaces(p + i);

  if (!strcmp(cmd, "WRITE") || !strcmp(cmd, "ECHO")) {
    script_write(arg);
    return(0);
  }

  if (!strcmp(cmd, "PAUSE")) {
    fprintf(stdout, "Strike any key when ready . . .");
    fflush(stdout);
    getch();
    fprintf(stdout, "\n");
    return(0);
  }

  if (!strcmp(cmd, "CLS")) {
    login_cls_attr(0x07);
    return(0);
  }

  if (!strcmp(cmd, "BREAK")) {
    return(0);
  }

  if (!strcmp(cmd, "SET")) {
    if (*arg) putglobenv(arg);
    return(0);
  }

  if (!strcmp(cmd, "DRIVE")) {
    arg = skip_spaces(arg);
    if ((*arg >= 'A' && *arg <= 'Z') || (*arg >= 'a' && *arg <= 'z')) {
      if (*arg >= 'a') setdisk(*arg - 'a');
      else setdisk(*arg - 'A');
    }
    return(0);
  }

  if (!strcmp(cmd, "MAP")) {
    char up[512];

    strmaxcpy(up, arg, sizeof(up) - 1);
    upstr(up);

    if (!strncmp(up, "DISPLAY", 7)) {
      return(0);
    }

    if (!strncmp(up, "ROOT ", 5)) {
      char callbuf[512];
      sprintf(callbuf, "MAP %s", skip_spaces(arg + 5));
      script_call_line(callbuf);
      return(0);
    }

    if (!strncmp(up, "INS ", 4) || !strncmp(up, "INSERT ", 7)) {
      char callbuf[512];
      char *a = arg;
      if (!login_strnicmp(up, "INS ", 4)) a += 4;
      else a += 7;
      sprintf(callbuf, "PATHINS %s", skip_spaces(a));
      script_call_line(callbuf);
      return(0);
    }

    if (!strncmp(up, "DEL ", 4) || !strncmp(up, "DELETE ", 7)) {
      char callbuf[512];
      char *a = arg;
      if (!login_strnicmp(up, "DEL ", 4)) a += 4;
      else a += 7;
      sprintf(callbuf, "PATHDEL %s", skip_spaces(a));
      script_call_line(callbuf);
      return(0);
    }
  }

  if (!strcmp(cmd, "ATTACH")) {
    return(0);
  }

  if (!strcmp(cmd, "EXIT")) {
    return(1);
  }

  script_call_line(p);
  return(0);
}

static int try_login_script_file(char *name)
{
  return(read_command_file(name));
}

static int run_login_script(void)
{
  char profile[200];
  char drive;

  /*
   * Novell LOGIN looks for the system login script using server based paths,
   * for example \\SERVER\SYS\PUBLIC\NET$LOG.DAT.  Try that first.
   */
  if (*script_file_server) {
    sprintf(profile, "\\\\%s\\SYS\\PUBLIC\\NET$LOG.DAT", script_file_server);
    if (try_login_script_file(profile) != -2) return(0);

    sprintf(profile, "\\\\%s\\SYS\\PUBLIC\\net$log.dat", script_file_server);
    if (try_login_script_file(profile) != -2) return(0);

    sprintf(profile, "\\\\%s\\SYS\\LOGIN\\LOGIN", script_file_server);
    if (try_login_script_file(profile) != -2) return(0);

    sprintf(profile, "\\\\%s\\SYS\\LOGIN\\NET$LOG.DAT", script_file_server);
    if (try_login_script_file(profile) != -2) return(0);
  }

  /*
   * Then try current directory and the executable path.  LOGIN.EXE is often
   * executed from PUBLIC, so this covers SYS:PUBLIC\NET$LOG.DAT without
   * relying on an absolute drive path.
   */
  if (try_login_script_file("NET$LOG.DAT") != -2) return(0);
  if (try_login_script_file("net$log.dat") != -2) return(0);
  if (try_login_script_file("LOGIN") != -2) return(0);
  if (try_login_script_file("login") != -2) return(0);

  if (*prgpath) {
    sprintf(profile, "%sNET$LOG.DAT", prgpath);
    if (try_login_script_file(profile) != -2) return(0);

    sprintf(profile, "%snet$log.dat", prgpath);
    if (try_login_script_file(profile) != -2) return(0);

    sprintf(profile, "%sLOGIN", prgpath);
    if (try_login_script_file(profile) != -2) return(0);

    sprintf(profile, "%slogin", prgpath);
    if (try_login_script_file(profile) != -2) return(0);
  }

  /*
   * Fallbacks for requesters/runtimes that cannot open UNC from C fopen().
   */
  if (try_login_script_file("\\PUBLIC\\NET$LOG.DAT") != -2) return(0);
  if (try_login_script_file("\\public\\net$log.dat") != -2) return(0);
  if (try_login_script_file("\\PUBLIC\\LOGIN") != -2) return(0);
  if (try_login_script_file("\\public\\login") != -2) return(0);

  if (try_login_script_file("\\LOGIN\\LOGIN") != -2) return(0);
  if (try_login_script_file("\\login\\login") != -2) return(0);
  if (try_login_script_file("\\LOGIN\\NET$LOG.DAT") != -2) return(0);
  if (try_login_script_file("\\login\\net$log.dat") != -2) return(0);

  for (drive = 'C'; drive <= 'Z'; drive++) {
    sprintf(profile, "%c:\\PUBLIC\\NET$LOG.DAT", drive);
    if (try_login_script_file(profile) != -2) return(0);

    sprintf(profile, "%c:\\public\\net$log.dat", drive);
    if (try_login_script_file(profile) != -2) return(0);

    sprintf(profile, "%c:\\PUBLIC\\LOGIN", drive);
    if (try_login_script_file(profile) != -2) return(0);

    sprintf(profile, "%c:\\LOGIN\\LOGIN", drive);
    if (try_login_script_file(profile) != -2) return(0);

    sprintf(profile, "%c:\\LOGIN\\NET$LOG.DAT", drive);
    if (try_login_script_file(profile) != -2) return(0);
  }

  return(-2);
}


static int do_change_object_passwd(char *name,
                                   uint16 objtyp,
                                   char *oldpassword,
                                   char *newpassword)

{
  uint8 key[8];

  if (!ncp17_17_get_encryption_key(key)) {
    uint32 objid = ncp17_35_get_bindery_object_id(name, objtyp);
    if (objid) {
      uint8 oldpwd[16];       /* old passwd as stored by server */
      uint8 newpwd[16];       /* new passwd as stored by server */
      uint8 cryptkey[8];
      uint8 tmpid[4];
      uint8 passwdx;
      int   newlen;

      memcpy(cryptkey, key, 8);
      U32_TO_BE32(objid, tmpid);

      shuffle(tmpid, oldpassword, strlen(oldpassword), oldpwd);
      shuffle(tmpid, newpassword, strlen(newpassword), newpwd);

      nw_encrypt(cryptkey, oldpwd, cryptkey);

      /*
       * Same keyed change password transformation as ncpfs
       * ncp_change_login_passwd(): encrypt both 8-byte halves of the
       * stored new password using the stored old password as key material.
       * newpassencrypt() intentionally mutates oldpwd; the passwd length
       * byte must be calculated afterwards, just like ncpfs does it.
       */
      newpassencrypt(oldpwd,     newpwd);
      newpassencrypt(oldpwd + 8, newpwd + 8);

      newlen = strlen(newpassword);
      if (newlen > 63) newlen = 63;
      passwdx = (uint8)(((newlen ^ oldpwd[0] ^ oldpwd[1]) & 0x7f) | 0x40);

      if (!ncp17_4b_keyed_change_password(cryptkey, name, objtyp, passwdx, newpwd)) {
        ;;
        return(0);
      }
    }
  }

  /*
   * Fallback for old servers/requesters where Get Encryption Key is not
   * available.  Keep the original unencrypted behavior as fallback only.
   */
  if (!ncp17_40_change_password_unencrypted(name, objtyp, oldpassword, newpassword)) {
    ;;
    return(0);
  }

  return(-1);
}

static int do_object_login(char *name, uint16 objtyp, char *password, int option)
{
  uint8 key[8];
  if (!(option & 1) && !ncp17_17_get_encryption_key(key)) {
    uint32 objid = ncp17_35_get_bindery_object_id(name, objtyp);
    if (objid) {
      uint8 buff[128];
      uint8 encrypted[8];
      uint8 tmpid[4];
      U32_TO_BE32(objid, tmpid);
      shuffle(tmpid, password, strlen(password), buff);
      nw_encrypt(key, buff, encrypted);
      if (!ncp17_18_keyed_object_login(encrypted, name, objtyp)) {
        ;;
        return(0);
      }
    }
  } else { /* now we use old unencrypted algorithmus */
    if (!ncp17_14_login_object_unencrypted(name, objtyp, password)) {
      return(0);
    }
  }
  return(-1);
}

static void beep(void)
{
  fprintf(stdout, "\007");
}

static int get_raw_str(uint8 *s, int maxlen, int doecho)
/* returns len of readed str */
{
  int len = 0;
  while (len < maxlen){
    int key = getch();
    if (key == '\r' || key == '\n') break;
    switch (key) {
      case    8 : if (len) {
                    --len;
                    --s;
                    if (doecho) {
                      fprintf(stdout, "\010 \010");
                      fflush(stdout);
                    }
                  } else beep();
                  continue;

      case  '\t': beep();
                  continue;

      default   : *s++=(uint8)key;
                  len++;
      		  break;
    } /* switch */
    if (doecho) {
      fprintf(stdout, "%c", (uint8)key);
      fflush(stdout);
    }
  }
  *s='\0';
  return(len);
}

static void getstr(char *what, char *str, int rsize, int doecho)
{
  fprintf(stdout, "%s: ", what);
  fflush(stdout);
  get_raw_str(str, rsize, doecho);
  fprintf(stdout, "\n");
}

static int login_usage(void)
{
  return(login_help());
}


#ifdef MAINTAINER_BUILD
static int login_hidden_password_arg(char *arg, uint8 *passwd, int passwd_size)
{
  char *value = NULL;

  if (!strnicmp(arg, "/PASSWORD:", 10) || !strnicmp(arg, "-PASSWORD:", 10))
    value = arg + 10;
  else if (!strnicmp(arg, "/PWD:", 5) || !strnicmp(arg, "-PWD:", 5))
    value = arg + 5;

  if (value != NULL) {
    strmaxcpy(passwd, value, passwd_size - 1);
    memset(arg, 0, strlen(arg));
    return(1);
  }

  return(0);
}

static int login_extract_hidden_password(int *argc, char *argv[],
                                         uint8 *passwd, int passwd_size)
{
  int i;

  passwd[0] = '\0';

  for (i = 1; i < *argc; i++) {
    if (login_hidden_password_arg(argv[i], passwd, passwd_size)) {
      int j;

      for (j = i; j < (*argc - 1); j++)
        argv[j] = argv[j + 1];

      argv[*argc - 1] = NULL;
      (*argc)--;
      return(1);
    }
  }

  return(0);
}
#endif

int func_login(int argc, char *argv[], int mode)
{
  int result=-1;
  int option=0;
  uint8 uname[200];
  uint8 upasswd[200];
#ifdef MAINTAINER_BUILD
  uint8 hidden_passwd[200];
  int have_hidden_passwd = 0;
#endif
  SEARCH_VECTOR  save_drives;
  int interactive_login = 0;
  int no_script = 0;

  if (argc > 1) {
    if (!strcmp(argv[1], "/?") || !strcmp(argv[1], "-?") || !strcmp(argv[1], "?"))
      return(login_help());
    if (!strcmp(argv[1], "/VER") || !strcmp(argv[1], "-VER")) {
      fprintf(stdout, "Mars NWE LOGIN 0.99\n");
      return(0);
    }
  }

#ifdef MAINTAINER_BUILD
  have_hidden_passwd = login_extract_hidden_password(&argc, argv,
                                                     hidden_passwd,
                                                     sizeof(hidden_passwd));
#endif

  if (argc > 1) {
    if (argv[1][0] == '-' || argv[1][0] == '/') {
      if (argv[1][1] == 'u' || argv[1][1] == 'U') option |= 1;
      else if (!strcmp(argv[1], "/NS") || !strcmp(argv[1], "-NS")) no_script = 1;
      else if (!strcmp(argv[1], "/CLS") || !strcmp(argv[1], "-CLS")) login_cls_attr(0x07);
      else return(login_usage());
      argc--;
      argv++;
    }
  }
  get_search_drive_vektor(save_drives);
  remove_nwpathes();
  if (argc > 1) strmaxcpy(uname, argv[1], sizeof(uname) -1);
  else uname[0]='\0';
#ifdef MAINTAINER_BUILD
  if (have_hidden_passwd) strmaxcpy(upasswd, hidden_passwd, sizeof(upasswd) -1);
  else
#endif
  if (argc > 2) strmaxcpy(upasswd, argv[2], sizeof(upasswd) -1);
  else upasswd[0]='\0';

  if (!uname[0]) {
    interactive_login = 1;
    login_banner();
  }

  while (result) {
    if (!uname[0]) getstr("Enter your login name", uname, sizeof(uname)-1, 1);
    if (uname[0]) {
      upstr(uname);
      upstr(upasswd);
      if ((result = do_object_login(uname, 0x1,  upasswd, option)) < 0 && !*upasswd) {
        getstr("Enter your password", upasswd, sizeof(upasswd)-1, 0);
        upstr(upasswd);
        result = do_object_login(uname, 0x1, upasswd, option);
      }
      if (result < 0) {
        fprintf(stdout, "Login incorrect\n\n");
        uname[0]   = '\0';
        upasswd[0] = '\0';
      }
    } else break;
  }
  if (result > -1) {
    strmaxcpy(script_login_name, uname, sizeof(script_login_name) - 1);

    script_file_server[0] = '\0';
    if (get_fs_name(1, script_file_server) || !script_file_server[0])
      login_ncp23_server_info(script_file_server);

    if (!script_file_server[0]) {
      fprintf(stdout, "No known file server.\n");
#ifdef MAINTAINER_BUILD
      memset(hidden_passwd, 0, sizeof(hidden_passwd));
#endif
      memset(upasswd, 0, sizeof(upasswd));
      return(1);
    }

    if (interactive_login)
      fprintf(stdout, "You are attached to server %s.\n", script_file_server);

    remove_nwpathes();

    if (!no_script)
      run_login_script();
  } else {
    (void)set_search_drive_vektor(save_drives);
  }
#ifdef MAINTAINER_BUILD
  memset(hidden_passwd, 0, sizeof(hidden_passwd));
#endif
  memset(upasswd, 0, sizeof(upasswd));
  return(result);
}

int func_logout(int argc, char *argv[], int mode)
{
  remove_nwpathes();
  if (logout()) {
    fprintf(stderr, "logout=%d\n", neterrno);
    return(1);
  }
  return(0);
}


int func_passwd(int argc, char *argv[], int mode)
{
  int result=0;
  uint8 uname[100];
  uint8 upasswd[130];
  uint32 my_obj_id;

  if (ncp14_46_get_bindery_access_level(&my_obj_id) < 0 || my_obj_id == MAX_U32 || !my_obj_id) {
    fprintf(stderr, "Cannot get actual user id\n");
    result = -1;
  }

  if (!result && argc > 1) {
    uint32 obj_id;
    strmaxcpy(uname, argv[1], sizeof(uname) -1);
    upstr(uname);
    obj_id = ncp17_35_get_bindery_object_id(uname,  1);
    if (!obj_id) {
      fprintf(stderr, "Unkwown user: %s\n", uname);
      return(-1);
    }
  } else if (!result) {
    uint16 obj_typ;
    if (ncp17_36_get_bindery_object_name(my_obj_id, uname, &obj_typ) || obj_typ != 1) {
      fprintf(stderr, "Cannot get actual username\n");
      result=-1;
    }
  }
  if (!result && *uname) {
    uint8 newpasswd[130];
    uint8 newpasswd2[130];
    if (my_obj_id == 1L) *upasswd='\0';
    else {
      getstr("Old password", upasswd, sizeof(upasswd)-1, 0);
      upstr(upasswd);
    }
    getstr("New password", newpasswd, sizeof(newpasswd)-1, 0);
    getstr("New password again", newpasswd2, sizeof(newpasswd2)-1, 0);
    if (!strcmp(newpasswd, newpasswd2)) {
      upstr(uname);
      upstr(newpasswd);
      if (do_change_object_passwd(uname, 1, upasswd, newpasswd) < 0)
         result = -1;
    } else {
      result = -1;
      fprintf(stderr, "Password misspelled\n");
    }
  }
  if (result < 0) fprintf(stderr, "Password not changed");
  return(result);
}

static int get_line(FILE *f, char *buff, int bufsize, uint8 *str, int strsize)
/* returns command line or -1 if ends */
{
  if ((FILE*) NULL != f) {
    while (fgets(buff, bufsize, f) != NULL){
      char *p       = buff;
      char *beg     = NULL;
      char c;
      int  len=0;
      while (0 != (c = *p++) && c != '\n' && c != '\r' && c != '#') {
        if (!beg){
          if (c != '\t' && c != 32) {
            beg = p - 1;
            len = 1;
          }
        } else ++len;
      }
      if (len) {
        strmaxcpy((uint8*)str, (uint8*)beg, min(len, strsize-1));
        return(0);
      }
    }
  }
  return(-1);
}



static char **build_argv(char *buf, int bufsize, char *command)
/* routine returns **argv for use with execv routines */
/* buf will contain the path component 	     	      */
{
  int len        = strlen(command);
  int offset     = ((len+4) / 4) * 4; /* aligned offset for **argv */
  int components = (bufsize - offset) / 4;
  if (components > 1) {  /* minimal argv[0] + NULL */
    char **argv  = (char **)(buf+offset);
    char **pp    = argv;
    char  *p     = buf;
    char  c;
    int   i=0;
    --components;
    memcpy(buf, command, len);
    memset(buf+len, 0, bufsize - len);
    *pp    = p;
    while ((0 != (c = *p++)) && i < components) {
      if (c == 32 || c == '\t') {
        *(p-1) = '\0';
        if (*p != 32 && *p != '\t') {
          *(++pp)=p;
          i++;
        }
      } else if (!i && c == '/') {  /* here i must get argv[0] */
        *pp=p;
      }
    }
    return(argv);
  }
  return(NULL);
}

int read_command_file(char *fstr)
{
  FILE *f=fopen(fstr, "r");
  int  result=-1;

  if (f != NULL) {
    char *linebuf = xmalloc(512);
    char *buf     = xmalloc(512);
    int stack[16];
    int level = 0;
    int active = 1;

    while (get_line(f, buf, 512, linebuf, 512) > -1) {
      char tmp[512];
      char *p;

      strmaxcpy(tmp, linebuf, sizeof(tmp) - 1);
      p = skip_spaces(tmp);
      upstr(p);

      if (!strncmp(p, "IF ", 3)) {
        if (level < 16) {
          stack[level++] = active;
          active = active && script_eval_if(linebuf);
        }
        result = 0;
        continue;
      }

      if (!strcmp(p, "END") || !strncmp(p, "END ", 4)) {
        if (level > 0) active = stack[--level];
        result = 0;
        continue;
      }

      if (!active) {
        result = 0;
        continue;
      }

      if (script_execute_line(linebuf))
        break;

      result = 0;
    }

    fclose(f);
    xfree(linebuf);
    xfree(buf);
  } else result=-2;

  return(result);
}

int func_profile(int argc, char *argv[], int mode)
{
  if (argc < 2) {
    fprintf(stderr, "usage:\t%s fn\n", funcname);
    return(-1);
  }
  if (read_command_file(argv[1]) == -2) {
    fprintf(stderr, "command file %s not found\n", argv[1]);
  }
  return(0);
}

int func_cwd(int argc, char *argv[], int mode)
{
  char pathname[65];
  int  len;
  if (argc < 2) {
    fprintf(stderr, "usage:\t%s path\n", funcname);
    return(-1);
  }
  strmaxcpy(pathname, argv[1], sizeof(pathname) -1);
  korrpath(pathname);
  if (0 != (len = strlen(pathname))) {
    char *p=pathname+len-1;
    if (*p == '/' || *p == ':') {
      *(++p) = '.';
      *(++p) = '\0';
      len++;
    }
    if (!chdir(pathname)) {
      if (len > 2 && *(pathname+1) == ':')   /* device changed */
        setdisk(*pathname - 'a' );
    } else {
      fprintf(stderr, "cannot chdir to %s\n", pathname);
      return(1);
    }
    return(0);
  } else return(-1);
}

int func_echo(int argc, char *argv[], int mode)
{
  if (argc > 1)
    fprintf(stdout, "%s\n", argv[1]);
  return(0);
}

int func_exec(int argc, char *argv[], int mode)
{
  if (argc > 1)  {
    char *buf   = xmalloc(512);
    char *buff  = xmalloc(512);
    char *p     = buff;
    int   k     = 0;
    char **nargv;
    while (++k < argc) {
      strcpy(p, argv[k]);
      p    += strlen(argv[k]);
      *p++  = 32;
      *p    = '\0';
    }
    nargv=build_argv(buf, 512, buff);
    xfree(buff);
    if (nargv != NULL) {
      if (!mode)
        spawnvp(P_WAIT, buf, (const char *const *)nargv);
      else
        execvp(buf, (const char *const *)nargv);
    }
    xfree(buf);
  }
  return(0);
}

