#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <time.h>
#include <pwd.h>
#include <grp.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#if defined(__linux__)
#define DB_DBM_HSEARCH 1
#include <db.h>
#include <crypt.h>
#else
#include <ndbm.h>
#endif

char *pw_file = "@pw_file@";
char *user = "@user@";
char *group = "@group@";

static void errexit(char *s)
{
    fprintf(stderr, "wgpasswd: %s\n", s);
    exit(1);
}

static void usage(void)
{
    fprintf(stderr,
	"usage: wgpasswd username password\n"
	"       wgpasswd -d username\n");
    exit(1);
}

int main(int argc, char **argv)
{
    char tmp[256];
    char *username, *password = NULL;
    char *upd_mode;
    struct stat st_buf;
    int new_file, new_user = 0;
    DBM *db;
    int open_flags, open_mode;
    datum key, val, dtmp;
    int c, r, delete = 0;

    while ((c = getopt(argc, argv, ":d")) != -1) {
	switch (c) {
	    case 'd':
		delete++;
		break;
	    case '?':
	    case ':':
	    default:
		usage();	
	}
    }

    if ((argc - optind) < (delete ? 1 : 2)) 
	usage();

    username = (char *)malloc(strlen(argv[optind]) + 1);
    strcpy(username, argv[optind]);
    if (! delete) {
	password = (char *)malloc(strlen(argv[optind + 1]) + 1);
	strcpy(password, argv[optind + 1]);
    }

    new_file = stat(pw_file, &st_buf);

    open_flags = delete ? O_RDWR : O_CREAT|O_RDWR;
    open_mode = 0644;
    if (! (db = dbm_open(pw_file, open_flags, open_mode)))
	errexit("Can't initialize database");

    key.dptr = username;
    key.dsize = strlen(key.dptr);

    if (delete) {
	r = dbm_delete(db, key);
	if (r > 0) {
	    sprintf(tmp, "User %s not in database", username);
	    errexit(tmp);
	} else if (r < 0) {
	    sprintf(tmp, "Can't delete user %s", username);
	    errexit(tmp);
	}
	upd_mode = "deleted";
    } else {
	unsigned long seed[2];
	char salt[] = "$1$........";
	const char *const seedchars =
         "./0123456789ABCDEFGHIJKLMNOPQRST"
         "UVWXYZabcdefghijklmnopqrstuvwxyz";
	int i;
 
	/* Generate a (not very) random seed.
          You should do it better than this... */
	seed[0] = time(NULL);
	seed[1] = getpid() ^ (seed[0] >> 14 & 0x30000);
     
	/* Turn it into printable characters from `seedchars'. */
	for (i = 0; i < 8; i++)
	    salt[3+i] = seedchars[(seed[i/5] >> (i%5)*6) & 0x3f];
 
       /* Get the encrypted password. */
	val.dptr = crypt(password, salt);
	val.dsize = strlen(val.dptr);

	dtmp = dbm_fetch(db, key);
	if (! dtmp.dptr)
	    new_user++;
	r = dbm_store(db, key, val, DBM_REPLACE);
	if (r < 0) {
	    sprintf(tmp, "Can't update user %s: %s",
	    	username, strerror(dbm_error(db)));
	    errexit(tmp);
	}
	upd_mode = new_user ? "added" : "updated";
    }

    dbm_close(db);

    if (new_file) {
	struct passwd *pwd;
	struct group *grp;
	char pw_fullname[128];

	if (! (pwd = getpwnam(user))) {
	    sprintf(tmp, "User %s not in system passwd file", user);
	    errexit(tmp);
	}
	if (! (grp = getgrnam(group))) {
	    sprintf(tmp, "Group %s not in system group file", group);
	    errexit(tmp);
	}

	sprintf(pw_fullname, "%s.db", pw_file);
	if (chown(pw_fullname, pwd->pw_uid, grp->gr_gid) < 0)
	    errexit("Can't chown() password file");
	if (chmod(pw_fullname, 0600) < 0)
	    errexit("Can't chmod() password file");
    }

    fprintf(stderr, "User %s %s successfully\n", username, upd_mode);
    exit(0);
}
.
