/* Copyright (C) 1991, 1992, 1993 Free Software Foundation, Inc.

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.

This library 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
Library General Public License for more details.

You should have received a copy of the GNU Library General Public
License along with this library; see the file COPYING.LIB.  If
not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite
330, Boston, MA  02111-1307, USA.  */

/* This code was borrowed from proftpd for muddleftpd :-P */

#include "../ftpd.h"

#define NPWDFIELDS 	7
#define NGRPFIELDS 	4

#ifndef BUFSIZ
#define BUFSIZ		1024
#endif

/* provides fgetpwent()/fgetgrent() functions.  Note that the
 * format of the files is probably NOT platform dependant, so
 * use of these functions will require a strict format
 * "username:password:uid:gid:gecos:home:default_shell"
 */

#ifndef HAVE_FGETPWENT

static char pwdbuf[BUFSIZ];
static char *pwdfields[NPWDFIELDS];
static struct passwd pwent;

static struct passwd *_pgetpwent(const char *buf)
{
  register	int	i;
  register	char	*cp;
  char			*ep;
  char			**fields;
  char			*buffer;
  struct	passwd	*pwd;

  fields = pwdfields;
  buffer = pwdbuf;
  pwd = &pwent;

  strncpy(buffer,buf,BUFSIZ-1);
  buffer[BUFSIZ-1] = '\0';

  for(cp = buffer, i = 0; i < NPWDFIELDS && cp; i++) {
    fields[i] = cp;
    while(*cp && *cp != ':')
      ++cp;
    if(*cp)
      *cp++ = '\0';
    else
      cp = 0;
  }

  if(i != NPWDFIELDS || *fields[2] == '\0' || *fields[3] == '\0')
    return 0;

  pwd->pw_name = fields[0];
  pwd->pw_passwd = fields[1];
  if(fields[2][0] == '\0' ||
     ((pwd->pw_uid = strtol(fields[2], &ep, 10)) == 0 && *ep))
       return 0;

  if(fields[3][0] == '\0' ||
     ((pwd->pw_gid = strtol(fields[3], &ep, 10)) == 0 && *ep))
       return 0;

  pwd->pw_gecos = fields[4];
  pwd->pw_dir = fields[5];
  pwd->pw_shell = fields[6];

  return pwd;  
}

struct passwd *fgetpwent(FILE *fp)
{
  char buf[BUFSIZ];

  if (fgets(buf,sizeof(buf),fp) != (char*)0)
  {
    buf[strlen(buf)-1] = '\0';
    return _pgetpwent(buf);
  }

  return NULL;
}
#endif /* HAVE_FGETPWENT */

#ifndef HAVE_FGETGRENT


#define MAXMEMBERS 4096

static char *grpbuf = NULL;
static struct group grent;
static char *grpfields[NGRPFIELDS];
static char *members[MAXMEMBERS+1];

static char *fgetbufline(char **buf, int *size, FILE *fp)
{
  char *rbuf = NULL,*cp;

  if(!*size || !*buf) {
    *size = BUFSIZ;
    *buf = rbuf = malloc(*size);
    if(!rbuf)
      return 0;
  }

  cp = rbuf;

  while(fgets(cp,(*size) - (cp - rbuf), fp) != (char *)0) {
    if(strchr(cp,'\n'))
      return rbuf;

    *size += *size;
    *buf = realloc(rbuf,*size);

    if(!*buf)
      break;

    cp = *buf + (cp - rbuf);
    rbuf = *buf;
    cp = strchr(cp,'\0');
  }

  free(rbuf);
  *buf = NULL; *size = 0;
  return 0;
}

static char **_grlist(char *s)
{
  int nmembers = 0;

  while(s && *s && nmembers < MAXMEMBERS) {
    members[nmembers++] = s;
    while(*s && *s != ',')
      s++;
    if(*s)
      *s++ = '\0';
  }

  members[nmembers] = (char*)0;
  return members;
}

static struct group *
_pgetgrent(const char *buf)
{
  int i;
  char *cp;

  i = strlen(buf);

  if(!grpbuf)
    grpbuf = malloc(i);
  else
    grpbuf = realloc(grpbuf,i);

  if(!grpbuf)
    return NULL;

  strncpy(grpbuf,buf,i);
  grpbuf[i-1] = '\0';
  if((cp = strrchr(grpbuf,'\n')))
    *cp = '\0';

  for(cp = grpbuf, i = 0; i < NGRPFIELDS && cp; i++) {
    grpfields[i] = cp;
    if((cp = strchr(cp,':')))
      *cp++ = 0;
  }

  if(i < (NGRPFIELDS-1) || *grpfields[2] == '\0')
    return 0;

  grent.gr_name = grpfields[0];
  grent.gr_passwd = grpfields[1];
  grent.gr_gid = atoi(grpfields[2]);
  grent.gr_mem = _grlist(grpfields[3]);

  return &grent;
}

struct group *fgetgrent(FILE *fp)
{
  char *buf = NULL;
  int size = 0;
  char *cp;
  struct group *g;

  if(fgetbufline(&buf,&size,fp) != (char*)0) {
    if((cp = strchr(buf,'\n')) != (char*)0)
      *cp = '\0';

    g = _pgetgrent(buf);
    free(buf);
    return g;
  }

  return 0;
}

#endif /* HAVE_FGETGRENT */
