/*
*       Novell NetWare container file -> RFC822 message format converter
*
*       (C) 1999 Przemyslaw Frasunek <venglin@lagoon.freebsd.org.pl>
*
* $Log: nwmail.c,v $
* Revision 1.1.1.1  2001/05/21 15:38:58  venglin
* initial import into CVS
*
* Revision 1.5  1999/07/05 07:51:47  venglin
* Deleting non-container files.
*
* Revision 1.4  1999/06/03 12:51:34  venglin
* Little bug ;)
*
* Revision 1.3  1999/06/03 12:49:32  venglin
* CRLF->LF converter built-in.
*
* Revision 1.2  1999/05/25 18:05:11  venglin
* setgid() is now executed before setuid() -- problems with FreeBSD-3.1.
*
* Revision 1.1  1999/05/23 11:57:25  venglin
* Initial revision
*
*/

#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <err.h>
#include <string.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fts.h>
#include <syslog.h>
#include <varargs.h>
#include <sys/wait.h>

#define MAUPA 64
#define INTERVAL 10
#define BUFSIZE 132
#define MAILCOMMAND "/usr/sbin/sendmail -t -oem"
#define CR 13

static const char rcsid[] =
   "$Id: nwmail.c,v 1.1.1.1 2001/05/21 15:38:58 venglin Exp $";

char *av0;
FILE *phile;
uid_t realuid;
gid_t realgid;
pid_t pid;
int f;
char hostname[256];
FILE *mailc;
FTS *fts;
FTSENT *p; 

static inline void usage(void)
{
  (void)fprintf(stderr, "usage: %s <spooldir>\n", av0);
  exit(1);
}

static inline void eatthis(name, uid, gid)
char *name;
uid_t uid;
gid_t gid;
{
  static char *months[12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }, buf[BUFSIZE];
  struct tm *date;
  time_t t;
  char b;

  if (setgid(gid))
  {
    syslog(LOG_ERR, "fatal: cannot change gid from %d to %d [egid %d] (%m)", realgid, gid, getegid());
    exit(1);
  }

  if (setuid(uid))
  {
    syslog(LOG_ERR, "fatal: cannot change uid from %d to %d [euid %d] (%m)", realuid, uid, geteuid());
    exit(1);
  }

  if (seteuid(uid)) 
  {
    syslog(LOG_ERR, "fatal: cannot change euid from %d to %d [euid %d] (%m)", realuid, uid, geteuid());
    exit(1);
  }

  if (setegid(gid))
  {
    syslog(LOG_ERR, "fatal: cannot change egid from %d to %d [egid %d] (%m)", realgid, gid, getegid());
    exit(1);
  }

  if ((phile=fopen(name, "r")) == NULL)
  {
    syslog(LOG_WARNING, "warning: cannot open container file %s (%m)", name);
    exit(1);
  }
 
  fgets(buf, sizeof(buf), phile);
       
  if (strncmp(buf, "$$", 2))
  {
    syslog(LOG_WARNING, "warning: file %s isn't a container -- deleting", name);
    unlink(name);
    exit(1);
  }

  do
    fgets(buf, sizeof(buf), phile);
  while(strchr(buf, MAUPA) != NULL);

  if((mailc = popen(MAILCOMMAND, "w")) == NULL)
  {
    syslog(LOG_ERR, "fatal: cannot open pipe to %s (%m)", MAILCOMMAND);
    exit(1);
  }

  t = time(NULL);  
  date = gmtime(&t);

  fprintf(mailc, "Received: from SpoolDir by %s (%s %d invoked by uid %d); %.2d %s %.4d %.2d:%.2d:%.2d -0000\n", hostname, av0, pid, realuid, date->tm_mday, months[date->tm_mon], 1900+(date->tm_year), date->tm_hour, date->tm_min, date->tm_sec); 
  fprintf(mailc, "Message-ID: <%d.%d.%s@%s>\n", (int)t, pid, av0, hostname);

  b=getc(phile);

  while (b != EOF)
  { 
    if (b != CR)
    putc(b, mailc);
    b = getc(phile);
  }

  if(pclose(mailc))
    syslog(LOG_WARNING, "warning: cannot close pipe to %s (%m)", MAILCOMMAND);

  if ((fclose(phile)) < 0)
    syslog(LOG_WARNING, "warning: cannot close container file %s (%m)", name);
 
  if ((unlink(name)) < 0)
    syslog(LOG_WARNING, "warning: cannot remove container file %s (%m)", name);

  if (fts_close(fts))
    syslog(LOG_WARNING, "warning: cannot close fts (%m)");

  exit(0);
}

int main(argc, argv)
int argc;
char **argv;
{
  if (strchr(argv[0], '/'))
    av0 = strrchr(argv[0], '/') + 1;
  else
    av0 = argv[0];

  if (argc < 2) (void)usage();

  openlog(av0, 0, LOG_MAIL);

  realuid = getuid();
  realgid = getgid();
  pid = getpid();
  gethostname(hostname, sizeof(hostname));

  syslog(LOG_INFO, "status: starting (pid %d uid %d)", pid, realuid);

  f = fork();
  if (f < 0)
    syslog(LOG_WARNING, "warning: cannot fork (%m), staying foreground");

  if (f != 0)
  {
    syslog(LOG_INFO, "status: fork ok (pid %d)", f);
    exit(0);
  } 

  for(;;) 
  {
  if (!(fts = fts_open(&argv[1], FTS_PHYSICAL|FTS_NOCHDIR|FTS_XDEV, (int (*)())NULL))) 
  {
    syslog(LOG_ERR, "fatal: cannot open spooldir %s (%m)", &argv[1]);
    errx(1, "%s: %s: %s.\n", av0, &argv[1], strerror(errno));
  }

  while ((p = fts_read(fts)) != NULL) {
    switch (p->fts_info) {
      case FTS_DNR:
        if (p->fts_errno != ENOENT)
          syslog(LOG_WARNING, "warning: cannot chdir to directory %s (%s)", p->fts_path, strerror(p->fts_errno));
        continue;

      case FTS_ERR:
        syslog(LOG_ERR, "fatal: directory structure %s traversing error %s", p->fts_path, strerror(p->fts_errno));
        errx(1, "%s: %s: %s.\n", av0, p->fts_path, strerror(p->fts_errno));

      case FTS_F:

      f = fork();
      if (f < 0)
      {
        syslog(LOG_ERR, "fatal: cannot fork processing child (%m)");
        errx(1, "%s: cannot fork processing child: %s", av0, strerror(errno));
      }
      
      if (f != 0)
      {     
        syslog(LOG_INFO, "status: fork ok (processing child, pid %d)", f);
        (void)wait(&f);
        break;
      }   

        (void)eatthis(p->fts_accpath, p->fts_statp->st_uid, p->fts_statp->st_gid);
    }
  }
    fts_close(fts);
    sleep(INTERVAL);
  }
}

