#include <sys/types.h>
#include <sys/stat.h>
#include "readwrite.h"
#include "direntry.h"
#include "prioq.h"
#include "env.h"
#include "now.h"
#include "stralloc.h"
#include "subfd.h"
#include "substdio.h"
#include "getline.h"
#include "error.h"
#include "open.h"
#include "lock.h"
#include "gfrom.h"
#include "str.h"
#include "exit.h"
#include "myctime.h"

void warn(s) char *s; { substdio_putsflush(subfderr,s); }
void die(s) char *s; { warn(s); _exit(1); }
void die_nomem() { die("maildir2mbox: fatal: out of memory\n"); }

char *maildir;
char *mbox;
char *mboxtmp;

DIR *dir;
direntry *d;

stralloc filenames = {0};
prioq pq = {0};
prioq pq2 = {0};

stralloc line = {0};

stralloc ufline = {0};

char inbuf[SUBSTDIO_INSIZE];
char outbuf[SUBSTDIO_OUTSIZE];

char *subdir[2] = { "cur", "new" };

void main()
{
 substdio ssin;
 substdio ssout;
 datetime_sec time;
 unsigned long pos;
 struct prioq_elt pe;
 int loop;
 struct stat st;
 int fdoldmbox;
 int fdnewmbox;
 int fd;
 int match;

 time = now();

 maildir = env_get("MAILDIR");
 if (!maildir) die("maildir2mbox: fatal: MAILDIR not set\n");
 mbox = env_get("MAIL");
 if (!mbox) die("maildir2mbox: fatal: MAIL not set\n");
 mboxtmp = env_get("MAILTMP");
 if (!mboxtmp) die("maildir2mbox: fatal: MAILTMP not set\n");

 if (chdir(maildir) == -1)
   die("maildir2mbox: fatal: unable to chdir to MAILDIR\n");

 if (dir = opendir("tmp"))
  {
   while (d = readdir(dir))
    {
     if (!str_diff(d->d_name,".")) continue;
     if (!str_diff(d->d_name,"..")) continue;
     if (!stralloc_copys(&filenames,"tmp/")) die_nomem();
     if (!stralloc_cats(&filenames,d->d_name)) die_nomem();
     if (!stralloc_0(&filenames)) die_nomem();
     if (stat(filenames.s,&st) == 0)
       if (time > st.st_atime + 129600)
	 unlink(filenames.s);
    }
   closedir(dir);
  }

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

 for (loop = 0;loop < 2;++loop)
  {
   dir = opendir(subdir[loop]);
   if (!dir) die("maildir2mbox: fatal: unable to read subdirectory\n");
   while (d = readdir(dir))
    {
     if (!str_diff(d->d_name,".")) continue;
     if (!str_diff(d->d_name,"..")) continue;
     pos = filenames.len;
     if (!stralloc_cats(&filenames,subdir[loop])) die_nomem();
     if (!stralloc_cats(&filenames,"/")) die_nomem();
     if (!stralloc_cats(&filenames,d->d_name)) die_nomem();
     if (!stralloc_0(&filenames)) die_nomem();
     if (stat(filenames.s + pos,&st) == 0)
       if (st.st_mtime < time) /* don't want to mix up the order */
        {
         pe.dt = st.st_mtime;
         pe.id = pos;
         if (!prioq_insert(&pq,&pe)) die_nomem();
         if (!prioq_insert(&pq2,&pe)) die_nomem();
        }
    }
   closedir(dir);
  }

 if (!prioq_min(&pq,&pe)) _exit(0); /* nothing new */

 fdoldmbox = open_read(mbox);
 if (fdoldmbox == -1) die("maildir2mbox: fatal: unable to open MAIL\n");
 if (lock_ex(fdoldmbox) == -1)
   die("maildir2mbox: fatal: unable to lock MAIL\n");

 fdnewmbox = open_trunc(mboxtmp);
 if (fdnewmbox == -1)
   die("maildir2mbox: fatal: unable to create MAILTMP\n");

 substdio_fdbuf(&ssin,read,fdoldmbox,inbuf,sizeof(inbuf));
 substdio_fdbuf(&ssout,write,fdnewmbox,outbuf,sizeof(outbuf));

 switch(substdio_copy(&ssout,&ssin))
  {
   case -2: die("maildir2mbox: fatal: trouble reading MAIL\n");
   case -3: die("maildir2mbox: fatal: trouble writing to MAILTMP\n");
  }

 while (prioq_min(&pq,&pe))
  {
   prioq_delmin(&pq);

   fd = open_read(filenames.s + pe.id);
   if (fd == -1)
     die("maildir2mbox: fatal: unable to open new message\n");
   substdio_fdbuf(&ssin,read,fd,inbuf,sizeof(inbuf));

   if (getline2(&ssin,&line,&match,'\n') != 0)
     die("maildir2mbox: fatal: trouble reading new message\n");
   
   if (!stralloc_copys(&ufline,"From XXX ")) die_nomem();
   if (match && (line.len >= 14))
     if (!str_diffn(line.s,"Return-Path: <",14))
      {
       if (line.s[14] == '>')
	{
         if (!stralloc_copys(&ufline,"From MAILER-DAEMON ")) die_nomem();
	}
       else
	{
	 int i;
         if (!stralloc_ready(&ufline,line.len)) die_nomem();
         if (!stralloc_copys(&ufline,"From ")) die_nomem();
	 for (i = 14;i < line.len - 2;++i)
	   if ((line.s[i] == ' ') || (line.s[i] == '\t'))
	     ufline.s[ufline.len++] = '-';
	   else
	     ufline.s[ufline.len++] = line.s[i];
         if (!stralloc_cats(&ufline," ")) die_nomem();
	}
      }
   if (!stralloc_cats(&ufline,myctime(pe.dt))) die_nomem();
   if (substdio_put(&ssout,ufline.s,ufline.len) == -1)
     die("maildir2mbox: fatal: trouble writing to MAILTMP\n");

   while (match && line.len)
    {
     if (gfrom(line.s,line.len))
       if (substdio_puts(&ssout,">") == -1)
         die("maildir2mbox: fatal: trouble writing to MAILTMP\n");
     if (substdio_put(&ssout,line.s,line.len) == -1)
       die("maildir2mbox: fatal: trouble writing to MAILTMP\n");
     if (!match)
      {
       if (substdio_puts(&ssout,"\n") == -1)
         die("maildir2mbox: fatal: trouble writing to MAILTMP\n");
       break;
      }
     if (getline2(&ssin,&line,&match,'\n') != 0)
       die("maildir2mbox: fatal: trouble reading new message\n");
    }
   if (substdio_puts(&ssout,"\n"))
     die("maildir2mbox: fatal: trouble writing to MAILTMP\n");

   close(fd);
  }

 if (substdio_flush(&ssout) == -1)
   die("maildir2mbox: fatal: write error\n");
 if (fsync(fdnewmbox) == -1)
   die("maildir2mbox: fatal: write error\n");
 if (close(fdnewmbox) == -1) /* NFS dorks */
   die("maildir2mbox: fatal: write error\n");
 if (rename(mboxtmp,mbox) == -1)
   die("maildir2mbox: fatal: unable to move MAILTMP to MAIL\n");
 
 while (prioq_min(&pq2,&pe))
  {
   prioq_delmin(&pq2);
   if (unlink(filenames.s + pe.id) == -1)
     warn("maildir2mbox: warning: unlink trouble, message will be delivered twice\n");
  }

 _exit(0);
}
