/* userdel.c - delete a user
 *
 * Copyright 2014 Ashwini Kumar <ak.ashwini1981@gmail.com>
 *
 * See http://refspecs.linuxfoundation.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/userdel.html

USE_USERDEL(NEWTOY(userdel, "<1>1r", TOYFLAG_NEEDROOT|TOYFLAG_SBIN))
USE_USERDEL(OLDTOY(deluser, userdel, TOYFLAG_NEEDROOT|TOYFLAG_SBIN))

config USERDEL
  bool "userdel"
  default n
  help
    usage: userdel [-r] USER
    usage: deluser [-r] USER
  
    Options:
    -r remove home directory
    Delete USER from the SYSTEM
*/

#define FOR_userdel
#include "toys.h"

static void update_groupfiles(char *filename, char* username)
{
  char *filenamesfx = NULL, *sfx = NULL, *line = NULL;
  FILE *exfp, *newfp;
  int ulen = strlen(username);
  struct flock lock;

  filenamesfx = xmprintf("%s+", filename);
  sfx = strchr(filenamesfx, '+');
  exfp = xfopen(filename, "r+");

  *sfx = '-';
  unlink(filenamesfx);
  if (link(filename, filenamesfx)) error_msg("Can't create backup file");

  *sfx = '+';
  lock.l_type = F_WRLCK;
  lock.l_whence = SEEK_SET;
  lock.l_start = lock.l_len = 0;

  if (fcntl(fileno(exfp), F_SETLK, &lock) < 0)
    perror_msg("Couldn't lock file %s",filename);

  lock.l_type = F_UNLCK; //unlocking at a later stage

  newfp = xfopen(filenamesfx, "w+");

  while ((line = get_line(fileno(exfp))) != NULL){
    sprintf(toybuf, "%s:",username);
    if (!strncmp(line, toybuf, ulen+1)) goto LOOP;
    else {
      char *n, *p = strrchr(line, ':');

      if (p && *++p && (n = strstr(p, username))) {
        do {
          if (n[ulen] == ',') {
            *n = '\0';
            n += ulen + 1;
            fprintf(newfp, "%s%s\n", line, n);
            break;
          } else if (!n[ulen]) {
            if (n[-1] == ',') n[-1] = *n = '\0';
            if (n[-1] == ':') *n = '\0';
            fprintf(newfp, "%s%s\n", line, n);
            break;
          } else n += ulen;
        } while (*n && (n=strstr(n, username)));
        if (!n) fprintf(newfp, "%s\n", line);
      } else fprintf(newfp, "%s\n", line);
    }
LOOP:
    free(line);
  }
  fcntl(fileno(exfp), F_SETLK, &lock);
  fclose(exfp);
  errno = 0;
  fflush(newfp);
  fsync(fileno(newfp));
  fclose(newfp);
  rename(filenamesfx, filename);
  if (errno){
    perror_msg("File Writing/Saving failed: ");
    unlink(filenamesfx);
  }
  free(filenamesfx);
}

void userdel_main(void)
{
  struct passwd *pwd = NULL;

  pwd = xgetpwnam(*toys.optargs);
  update_password("/etc/passwd", pwd->pw_name, NULL);
  update_password("/etc/shadow", pwd->pw_name, NULL);

  // delete the group named USER, and remove user from group.
  // could update_password() be used for this? 
  // not a good idea, as update_passwd() updates one entry at a time
  // in this case it will be modifying the files as many times the 
  // USER appears in group database files. So the customized version
  // of update_passwd() is here.
  update_groupfiles("/etc/group", *toys.optargs);
  update_groupfiles("/etc/gshadow", *toys.optargs);

  if (toys.optflags & FLAG_r) {
    char *arg[] = {"rm", "-fr", pwd->pw_dir, NULL, NULL};

    sprintf(toybuf, "/var/spool/mail/%s",pwd->pw_name);
    arg[3] = toybuf;
    xexec(arg);
  }
}
