#include <sys/stat.h>
#include <unistd.h>

#include <stdio.h>  // rename

#include "buffer.h"
#include "byte.h"
#include "case.h"
#include "cdbmake.h"
#include "exit.h"
#include "getln.h"
#include "logmsg.h"
#include "open.h"
#include "stralloc.h"

#ifdef USE_CONFIG
  #include "fehsqm-config.h"
#else
  #include "auto_qmail.h"
#endif

#define WHO "qmail-newu"

static void die_chdir()
{
  logmsg(WHO, 110, ERROR, "unable to chdir");
}

static void die_nomem()
{
  logmsg(WHO, 111, FATAL, "fatal: out of memory");
}

static void die_opena()
{
  logmsg(WHO, 112, ERROR, "unable to open users/assign");
}

static void die_reada()
{
  logmsg(WHO, 110, ERROR, "unable to read users/assign");
}

static void die_format()
{
  logmsg(WHO, 112, ERROR, "bad format in users/assign");
}

static void die_opent()
{
  logmsg(WHO, 112, ERROR, "unable to open users/assign.cdb.tmp");
}

static void die_writet()
{
  logmsg(WHO, 112, ERROR, "unable to write users/assign.cdb.tmp");
}

static void die_rename()
{
  logmsg(WHO, 112, ERROR, "unable to move users/cdb.tmp to users/assign.cdb");
}

struct cdb_make cdb;
stralloc key = {0};
stralloc data = {0};

char inbuf[1024];
buffer bi;

int fd;
int fdtemp;

stralloc line = {0};
int match;

stralloc wildchars = {0};

int main()
{
  int i;
  int numcolons;

  umask(033);
  if (chdir(auto_qmail) == -1) die_chdir();

  fd = open_read("users/assign");
  if (fd == -1) die_opena();

  buffer_init(&bi, read, fd, inbuf, sizeof(inbuf));

  fdtemp = open_trunc("users/assign.cdb.tmp");
  if (fdtemp == -1) die_opent();

  if (cdb_make_start(&cdb, fdtemp) == -1) die_writet();

  if (!stralloc_copys(&wildchars, "")) die_nomem();

  for (;;) {
    if (getln(&bi, &line, &match, '\n') != 0) die_reada();
    if (line.len && (line.s[0] == '.')) break;
    if (!match) die_format();

    if (byte_chr(line.s, line.len, '\0') < line.len) die_format();
    i = byte_chr(line.s, line.len, ':');
    if (i == line.len) die_format();
    if (i == 0) die_format();
    if (!stralloc_copys(&key, "!")) die_nomem();
    if (line.s[0] == '+') {
      if (!stralloc_catb(&key, line.s + 1, i - 1)) die_nomem();
      case_lowerb(key.s, key.len);
      if (i >= 2)
        if (byte_chr(wildchars.s, wildchars.len, line.s[i - 1]) == wildchars.len)
          if (!stralloc_append(&wildchars, line.s + i - 1)) die_nomem();
    } else {
      if (!stralloc_catb(&key, line.s + 1, i - 1)) die_nomem();
      if (!stralloc_0(&key)) die_nomem();
      case_lowerb(key.s, key.len);
    }

    if (!stralloc_copyb(&data, line.s + i + 1, line.len - i - 1)) die_nomem();

    numcolons = 0;
    for (i = 0; i < data.len; ++i) {
      if (data.s[i] == ':') {
        data.s[i] = 0;
        if (++numcolons == 6) break;
      }
    }
    if (numcolons < 6) die_format();
    data.len = i;

    if (cdb_make_add(&cdb, key.s, key.len, data.s, data.len) == -1) die_writet();
  }

  if (cdb_make_add(&cdb, "", 0, wildchars.s, wildchars.len) == -1) die_writet();

  if (cdb_make_finish(&cdb) == -1) die_writet();
  if (fsync(fdtemp) == -1) die_writet();
  if (close(fdtemp) == -1) die_writet(); /* NFS stupidity */
  if (rename("users/assign.cdb.tmp", "users/assign.cdb") == -1) die_rename();

  _exit(0);
}