#include <unistd.h>
#include "fd.h"
#include "wait.h"
#include "prot.h"
#include "buffer.h"
#include "stralloc.h"
#include "scan.h"
#include "exit.h"
#include "cdbread.h"
#include "case.h"
#include "readclose.h"
#include "auto_qmail.h"
#include "auto_uids.h"
#include "qlx.h"
#include "error.h"
#include "open.h"
#include "byte.h"

char *aliasempty;

void initialize(int argc,char **argv)
{
  aliasempty = argv[1];
  if (!aliasempty) _exit(100);
}

int truncreport = 3000;

void report(buffer *log,int wstat,char *s,int len)
{
  int i;
  if (wait_crashed(wstat)) { buffer_putsflush(log,"Zqmail-lspawn: qmail-local crashed.\n"); return; }

  switch (wait_exitcode(wstat)) {
    case QLX_CDB:
      buffer_putsflush(log,"Zqmail-lspawn: Trouble reading users/assign.cdb.\n"); return;
    case QLX_NOMEM:
      buffer_putsflush(log,"Zqmail-lspawn: Out of memory.\n"); return;
    case QLX_SYS:
      buffer_putsflush(log,"Zqmail-lspawn: Temporary failure.\n"); return;
    case QLX_NOALIAS:
      buffer_putsflush(log,"Zqmail-lspawn: Unable to find alias user!\n"); return;
    case QLX_ROOT:
      buffer_putsflush(log,"Zqmail-spawn: Not allowed to perform deliveries as root.\n"); return;
    case QLX_USAGE:
      buffer_putsflush(log,"Zqmail-spawn: Internal bug.\n"); return;
    case QLX_NFS:
      buffer_putsflush(log,"Zqmail-spawn: NFS failure in qmail-local.\n"); return;
    case QLX_EXECHARD:
      buffer_putsflush(log,"Dqmail-spawn: Unable to run qmail-local.\n"); return;
    case QLX_EXECSOFT:
      buffer_putsflush(log,"Zqmail-spawn: Unable to run qmail-local.\n"); return;
    case QLX_EXECPW:
      buffer_putsflush(log,"Zqmail-spawn: Unable to run qmail-getpw.\n"); return;
    case 111: case 71: case 74: case 75:
      buffer_put(log,"Z",1); break;
    case 0:
      buffer_put(log,"K",1); break;
    case 100:
    default:
      buffer_put(log,"D",1); break;
  }

  for (i = 0; i < len; ++i) 
    if (!s[i]) break;

  buffer_put(log,s,i);
}

stralloc lower = {0};
stralloc nughde = {0};
stralloc wildchars = {0};

static struct cdb c;

void nughde_get(char *local)
{
  char *(args[3]);
  int pi[2];
  int gpwpid;
  int gpwstat;
  int r;
  int fd;
  int flagwild;

  if (!stralloc_copys(&lower,"!")) _exit(QLX_NOMEM);
  if (!stralloc_cats(&lower,local)) _exit(QLX_NOMEM);
  if (!stralloc_0(&lower)) _exit(QLX_NOMEM);
  case_lowerb(lower.s,lower.len);

  if (!stralloc_copys(&nughde,"")) _exit(QLX_NOMEM);

  fd = open_read("users/assign.cdb");
  if (fd == -1)
    if (errno != ENOENT)
      _exit(QLX_CDB);

  if (fd != -1) {
    unsigned int i;

    cdb_init(&c,fd);
    r = cdb_find(&c,"",0);
    if (r != 1) _exit(QLX_CDB);
    if (!stralloc_ready(&wildchars,cdb_datalen(&c))) _exit(QLX_NOMEM);
    wildchars.len = cdb_datalen(&c);
    if (cdb_read(&c,wildchars.s,wildchars.len,cdb_datapos(&c)) == -1) _exit(QLX_CDB);

    i = lower.len;
    flagwild = 0;

    do {
      /* i > 0 */
      if (!flagwild || (i == 1) || (byte_chr(wildchars.s,wildchars.len,lower.s[i - 1]) < wildchars.len)) {
        r = cdb_find(&c,lower.s,i);
        if (r == -1) _exit(QLX_CDB);
        if (r == 1) {
          if (!stralloc_ready(&nughde,cdb_datalen(&c))) _exit(QLX_NOMEM);
          nughde.len = cdb_datalen(&c);
          if (cdb_read(&c,nughde.s,nughde.len,cdb_datapos(&c)) == -1) _exit(QLX_CDB);
          if (flagwild)
            if (!stralloc_cats(&nughde,local + i - 1)) _exit(QLX_NOMEM);
          if (!stralloc_0(&nughde)) _exit(QLX_NOMEM);
          close(fd);
          return;
        }
      }
      --i;
      flagwild = 1;
    } while (i);

    close(fd);
  }

  if (pipe(pi) == -1) _exit(QLX_SYS);

  args[0] = "bin/qmail-getpw";
  args[1] = local;
  args[2] = 0;
  switch (gpwpid = fork()) {
    case -1:
      _exit(QLX_SYS);
    case 0:
      if (prot_gid(auto_gidn) == -1) _exit(QLX_USAGE);
      if (prot_uid(auto_uidp) == -1) _exit(QLX_USAGE);
      close(pi[0]);
      if (fd_move(1,pi[1]) == -1) _exit(QLX_SYS);
      execv(*args,args);
      _exit(QLX_EXECPW);
  }
  close(pi[1]);

  if (readclose_append(pi[0],&nughde,128) == -1) _exit(QLX_SYS);

  if (wait_pid(&gpwstat,gpwpid) != -1) {
    if (wait_crashed(gpwstat)) _exit(QLX_SYS);
    if (wait_exitcode(gpwstat) != 0) _exit(wait_exitcode(gpwstat));
  }
}

int spawn(int fdmess,int fdout,const char *s,char *r,const int at) 
{
  int f;

  if (!(f = fork())) {
    char *(args[11]);
    unsigned long u;
    int n;
    int uid;
    int gid;
    char *x;
    unsigned int xlen;
   
    r[at] = 0;
    if (!r[0]) _exit(0); /* <> */

    if (chdir(auto_qmail) == -1) _exit(QLX_USAGE);

    nughde_get(r);

    x = nughde.s;
    xlen = nughde.len;

    args[0] = "bin/qmail-local";
    args[1] = "--";
    args[2] = x;
    n = byte_chr(x,xlen,0); 
    if (n++ == xlen) _exit(QLX_USAGE); 
    x += n; 
    xlen -= n;

    scan_ulong(x,&u);
    uid = u;
    n = byte_chr(x,xlen,0); 
    if (n++ == xlen) _exit(QLX_USAGE); 
    x += n; 
    xlen -= n;

    scan_ulong(x,&u);
    gid = u;
    n = byte_chr(x,xlen,0); 
    if (n++ == xlen) _exit(QLX_USAGE); 
    x += n; 
    xlen -= n;

    args[3] = x;
    n = byte_chr(x,xlen,0); 
    if (n++ == xlen) _exit(QLX_USAGE); 
    x += n; 
    xlen -= n;

    args[4] = r;
    args[5] = x;
    n = byte_chr(x,xlen,0); 
    if (n++ == xlen) _exit(QLX_USAGE); 
    x += n; 
    xlen -= n;

    args[6] = x;
    n = byte_chr(x,xlen,0);
    if (n++ == xlen) _exit(QLX_USAGE); 
    x += n; 
    xlen -= n;

    args[7] = r + at + 1;
    args[8] = s;
    args[9] = aliasempty;
    args[10] = 0;

    if (fd_move(0,fdmess) == -1) _exit(QLX_SYS);
    if (fd_move(1,fdout) == -1) _exit(QLX_SYS);
    if (fd_copy(2,1) == -1) _exit(QLX_SYS);
    if (prot_gid(gid) == -1) _exit(QLX_USAGE);
    if (prot_uid(uid) == -1) _exit(QLX_USAGE);
    if (!getuid()) _exit(QLX_ROOT);

    execv(*args,args);
    if (errno) _exit(QLX_EXECSOFT);
    _exit(QLX_EXECHARD);
  }
  return f;
}