RCS_ID_C="$Id: passwdunit.c,v 3.2 1994/05/19 04:29:29 ppessi Exp $";
/*
 * passwdunit.c --- unit for passwd entries
 *
 * Author: ppessi <Pekka.Pessi@hut.fi>
 *
 * This file is part of the AmiTCP/IP User Library.
 *
 * Copyright © 1993 AmiTCP/IP Group, <AmiTCP-Group@hut.fi>
 *                  Helsinki University of Technology, Finland.
 *
 * Created      : Wed Sep 15 01:25:26 1993 ppessi
 * Last modified: Thu May 19 04:01:50 1994 ppessi
 */

#include "entries.h"
#include <string.h>
#include <assert.h>

static void *CopyEntToPasswd(struct NetInfoReq *req, struct Ent *e);
static struct Ent *CopyPasswdToEnt(BASE, struct NetInfoReq *req);
static struct Ent *ParsePasswd(BASE, register UBYTE *p);
static void GetMembers(BASE, struct NetInfoReq *req, struct NetInfoMap *nim);
static int PrintPasswd(BASE, BPTR file, struct Ent *e);

const static struct MapMethods passwd_methods[1] = 
{
  {
    ParsePasswd,
    PrintPasswd,
    CopyEntToPasswd,
    CopyPasswdToEnt,
    EntCleanup,
    NULL,
    EntHandleNotify,
  },
};

/*
 * Allocate and initialize passwd map
 */
struct NetInfoMap *InitPasswdMap(BASE)
{
  struct NetInfoMap *nim = AllocVec(sizeof(*nim), MEMF_CLEAR|MEMF_PUBLIC);

  if (nim) {
    nim->nim_Methods = passwd_methods;
    nim->nim_Name = "passwd";
    nim->nim_Filename = _PATH_PASSWD;
  }

  return nim;
}

/*
 * Copy an PasswdEnt structure to passwd structure 
 */
static void *CopyEntToPasswd(struct NetInfoReq *req, struct Ent *e)
{
  struct PasswdEnt *pe = (struct PasswdEnt *)e;
  struct NetInfoPasswd *pw = req->io_Data;
  UBYTE  *to = (UBYTE *)(pw + 1);

  ULONG size = req->io_Length;
  ULONG actual = sizeof(*pw) + pe->pe_tlen;

  req->io_Actual = actual;
  
  if (size < actual) {
    return NULL;
  }

  /* copy strings */
  to = stpcopy(pw->pw_name   = to, pe->pe_passwd->pw_name);
  to = stpcopy(pw->pw_passwd = to, pe->pe_passwd->pw_passwd);
  to = stpcopy(pw->pw_gecos  = to, pe->pe_passwd->pw_gecos);
  to = stpcopy(pw->pw_dir    = to, pe->pe_passwd->pw_dir);
  to = stpcopy(pw->pw_shell  = to, pe->pe_passwd->pw_shell);

  assert(to == (UBYTE *)req->io_Data + actual);

  /* .. and ids */
  pw->pw_uid = pe->pe_passwd->pw_uid;
  pw->pw_gid = pe->pe_passwd->pw_gid;

  return pw;
}

/*
 * Copy an passwd structure into a internal entry
 */
static struct Ent *CopyPasswdToEnt(BASE, struct NetInfoReq *req)
{
  struct NetInfoPasswd *pw = (struct NetInfoPasswd*)req->io_Data;
  struct PasswdEnt *pe;
  UBYTE *to;
  ULONG txtlen;

  /*
   * These cause EFAULT
   */
  if (pw == NULL || (1 & (LONG)pw) != 0 ||
      pw->pw_name == NULL || 
      pw->pw_passwd == NULL ||
      pw->pw_gecos == NULL ||
      pw->pw_dir == NULL ||
      pw->pw_shell == NULL) {
    req->io_Error = IOERR_BADADDRESS;
    return NULL;
  }

  txtlen = strlen(pw->pw_name) + strlen(pw->pw_passwd) + strlen(pw->pw_gecos) +
    strlen(pw->pw_dir) + strlen(pw->pw_shell) + 5;

  pe = AllocVec(sizeof(*pe) + txtlen, MEMF_CLEAR);
  if (pe == NULL) {
    req->io_Error = NIERR_NOMEM;
    return NULL;
  }

  pe->pe_node.ln_Type = ENT_PASSWD;
  pe->pe_node.ln_Pri  = ENT_CHANGED;
  pe->pe_tlen = txtlen;
  pe->pe_passwd->pw_uid = pw->pw_uid;
  pe->pe_passwd->pw_gid = pw->pw_gid;

  to = (UBYTE *)(pe + 1);
  to = stpcopy(pe->pe_passwd->pw_name   = to, pw->pw_name);
  to = stpcopy(pe->pe_passwd->pw_passwd = to, pw->pw_passwd);
  to = stpcopy(pe->pe_passwd->pw_gecos  = to, pw->pw_gecos);
  to = stpcopy(pe->pe_passwd->pw_dir    = to, pw->pw_dir);
  to = stpcopy(pe->pe_passwd->pw_shell  = to, pw->pw_shell);

  assert(to == pe->pe_passwd->pw_name + txtlen);

  return (struct Ent *)pe;
}

/****** netinfo.device/passwd **********************************************

   NAME
        passwd - format of the password file

   DESCRIPTION

        The passwd files are files consisting of newline separated records,
        one per user, containing seven bar (`|') separated fields.  These
        fields are as follows:

            name      User's login name.

            password  User's encrypted password.

            uid       User's id.

            gid       User's login group id.

            gecos     General information about the user.

            home_dir  User's home directory.

            shell     User's login shell.

        The name field is the login used to access the computer account, and
        the uid field is the number associated with it.  They should both be
        unique across the system (and often across a group of systems) since
        they control file access.

        While it is possible to have multiple entries with identical login
        names and/or identical user id's, it is usually a mistake to do so.
        Routines that manipulate these files will often return only one of
        the multiple entries, and that one by random selection.

        The login name must never begin with a hyphen (`-'); also, it is
        strongly suggested that neither upper-case characters or dots (`.')
        be part of the name, as this tends to confuse mailers. No field may
        contain a bar (`|') as this has been used to separate the fields in
        the user database.

        The password field is the encrypted form of the password. The actual
        format is not restricted by netinfo.device, but usergroup.library
        uses same format as Version 7 UNIX (POSIX) or 4.4BSD.  The format
        used by MultiUser 1.5 or AS225r2ß is also possible, but its use is
        strongly discouraged.

        If the password field is empty, no password will be required to gain
        access to the machine.  This is almost invariably a mistake. Because
        these files contain the encrypted user passwords, they should not be
        readable by anyone without appropriate privileges.

        The group field is the group that the user will be placed in upon
        login.

        The gecos field normally contains comma (`,') separated subfields as
        follows:

            name           user's full name
            office         user's office number
            wphone         user's work phone number
            hphone         user's home phone number

        This information is used by the finger program.

        The user's home directory is the full DOS path name where the user
        will be placed on login.

        The shell field is the command interpreter the user prefers. If
        there is nothing in the shell field, the CLI is assumed.

   FILES
        AmiTCP:db/passwd

   SEE ALSO
        group

   COPYRIGHT
        Copyright 1980, 1991 The Regents of the University of California.
        Copyright 1993, 1994 AmiTCP/IP-Group, <AmiTCP-Group@hut.fi>, 
        Helsinki University of Technology, Helsinki

   HISTORY
        A passwd file format appeared in Version 6 AT&T UNIX.

        The used format is basically the same as in the Multiuser Library
        1.3 and AS225r2 use, except the password field.

****************************************************************************
*/

/* 
 * Parse a passwd database entry
 */
static struct Ent *ParsePasswd(BASE, register UBYTE *p)
{
  int i;
  UBYTE *to, *np = p, *field[PASSWDFIELDS];
  ULONG txtlen;
  LONG uid, gid;
  struct PasswdEnt *pe;

  for (i = 0;
       i < PASSWDFIELDS && (field[i++] = strsep(&np, "|\n")) && np && *np;)
    ;

  assert(np != NULL);
  if (i < PASSWDFIELDS || !np) {
    return NULL;
  }

  if ((i = StrToLong(field[2], &uid)) <= 0 || field[2][i] != '\0')
    return NULL;
  if ((i = StrToLong(field[3], &gid)) <= 0 || field[3][i] != '\0')
    return NULL;

  txtlen = np - field[0] - (field[4] - field[2]);
  pe = AllocVec(sizeof(*pe) + txtlen, MEMF_CLEAR);
  if (!pe)
    return NULL;

  pe->pe_node.ln_Type = ENT_PASSWD;
  pe->pe_tlen = txtlen;
  pe->pe_passwd->pw_uid = uid;
  pe->pe_passwd->pw_gid = gid;

  to = (UBYTE *)(pe + 1);
  to = stpcopy(pe->pe_passwd->pw_name   = to, field[0]);
  to = stpcopy(pe->pe_passwd->pw_passwd = to, field[1]);
  to = stpcopy(pe->pe_passwd->pw_gecos  = to, field[4]);
  to = stpcopy(pe->pe_passwd->pw_dir    = to, field[5]);
  to = stpcopy(pe->pe_passwd->pw_shell  = to, field[6]);

  assert(to == pe->pe_passwd->pw_name + txtlen);

  return (struct Ent *)pe;
}

/*
 * Print out an passwd entry
 */
static int PrintPasswd(BASE, BPTR file, struct Ent *e)
{
  struct PasswdEnt *pe = (struct PasswdEnt *)e;

  if (VFPrintf(file, "%s|%s|%ld|%ld|%s|%s|%s\n", (LONG*)pe->pe_passwd) == -1)
    return NIERR_ACCESS;
  else
    return 0;
}

/*
 * Find all UIDs in the named group
 */
static void GetMembers(BASE, struct NetInfoReq *req, struct NetInfoMap *nim)
{
  req->io_Error = NIERR_NOTFOUND;
  TermIO(req);
}
