/*-------------------------------------------------------------------------
 *  Listserv - Unix Mailing List manager (sub-set of FRECP's
 *             Bitnet Listserv tool.
 *
 *  Copyright (C) 1991,1992  Kimmo Suominen, Christophe Wolfhugel
 *
 *  Please read the files COPYRIGHT and AUTHORS for the extended
 *  copyrights refering to this file.
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 1, or (at your option)
 *  any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *----------------------------------------------------------------------*/

static char rcsid[] = "@(#)$Id: l.c,v 1.34 93/03/02 22:13:17 wolf Exp $";

#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <time.h>
#include <unistd.h>
#include <ctype.h>
#include <sys/ioctl.h>
#include <sys/param.h>
#include <sys/times.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "conf.h"
#include "ext.h"
#include "lp.h"
#include "str.h"
#include "popen.h"
#include "ad.h"
#include "lc.h"
#include "messages.h"
#ifdef FAKESYSLOG
# include "fakesyslog.h"
#else
# include <syslog.h>
#endif

int     Debug = 0;

short	subj;

int	msgHere = 0, done = 0;
char	listName[64];
char	From[MAXLINE], To[MAXLINE], ReplyTo[MAXLINE];
char	buf[MAXLINE], rcpt[MAXLINE];
int	d, h, m, s;
long	startTime, endTime, msgSize;
struct 	tms t;

static	int ctr;

void lTodhms(t)
long t;
{
   d = t / ( 24 * 3600L );
   h = ( t % ( 24 * 3600L )) / 3600;
   m = ( t % 3600 ) /60;
   s = t % 60;
}

char *instring(s1, s2)
char *s1, *s2;
{
   if ((int)strlen(s2) > (int)strlen(s1)) return(NULL);
   for (; *(s1 + strlen(s2) - 1); s1++)
      if (strncasecmp(s1, s2, strlen(s2)) == 0) return(s1);
   return(NULL);
}

char *strlwr(s)
char *s;
{
   char *p = s;

   if (s==NULL) return(NULL);
   for (; *s; s++) *s = tolower(*s);
   return(p);
}

char *strupr(s)
char *s;
{
   char *p = s;

   if (s == NULL) return(NULL);
   for (; *s; s++) *s = toupper(*s);
   return(p);
}

void mailMsg(to, subj)
char *to, *subj;
{
   FILE *f, *g;
   char buf[1024];

   sprintf(buf, "%s %s", SENDMAIL, to);
   if ((g = l_popen(buf, "w")) == NULL) {
      syslog(LOG_ERR, "mailMsg - popen failed: %m");
      exit(2);
   } /* endif */
   if ((f = fopen("msg","r")) == NULL) {
      syslog(LOG_ERR, "mailMsg - fopen failed: %m");
      exit(2);
   } /* endif */
   fprintf(g, "Subject: %s\n\n", subj);
   while (fgets(buf, sizeof(buf), f) != NULL)
      fputs(buf, g);
   l_pclose(g); fclose(f);
}

void sendMsg()
{
   char cmd[MAXLINE + 256], *p;
   int c;
   FILE *f, *h;

#ifdef ADD_REQUEST
   sprintf(cmd, "%s -f%s-request %s", SENDMAIL, listName, rcpt);
#else
   sprintf(cmd, "%s %s", SENDMAIL, rcpt);
#endif
   if ((h = l_popen(cmd,"w")) == NULL) {
      syslog(LOG_ERR, "sendMsg - popen: %m");
      exit(2);
   } /* endif */
   if ((f = fopen("msg", "r")) == NULL) {
      syslog(LOG_ERR, "sendMsg - popen: %m");
      exit(2);
   } /* endif */
   while (fgets(buf, sizeof(buf), f) != NULL && buf[0] != '\n') {
      /* if the line is too long, throw the rest away */
      if (index(buf, '\n') == NULL) {
         while ((c = getc(f)) != '\n' && c != EOF)
            continue;
      } /* endif */
      while ((c = getc(f)) == ' ' || c == '\t') {
	 p = &buf[strlen(buf)];
	 *p++ = ' ';
	 if (fgets(p, sizeof(buf) - (p - buf), f) == NULL)
            break;
      } /* endwhile */
      if (!feof(f) && !ferror(f))
	 ungetc(c, f);

      if (strncasecmp(buf, "Status: ", 8) == 0) continue;
      if (strncasecmp(buf, "X-Envelope-To: ", 15) == 0) continue;
      if (strncasecmp(buf, "X-Listserv-To: ", 15) == 0) continue;
      if (strncasecmp(buf, ">From ", 6) == 0) continue;
      if (strncasecmp(buf, "Return-receipt-to: ", 19) == 0) continue;
#ifdef STRIP_RECEIVED
      if (strncasecmp(buf, "Received: ", 10) == 0) continue;
#endif
      if (ReplyTo[0] != 0 && strncasecmp(buf, "Reply-To: ", 10) == 0)
	 continue;
      if (*GetErrorsTo() != '\0' && strncasecmp(buf, "Errors-To: ", 11) == 0)
	 continue;
#ifdef ADD_SENDER
      if (strncasecmp(buf, "Sender: ", 8) == 0) continue;
#endif
      fputs(buf, h);
         /* strcpy(cmd, listName); strupr(cmd);
	 fprintf(h, "To: %s Distribution List <%s@%s>\n", cmd, listName, HOST);
	 */
   } /* endwhile */
#ifdef ADD_SENDER
   fputs(versSender, h);
#endif
   if (*GetErrorsTo() != '\0') fprintf(h, "Errors-To: %s\n", GetErrorsTo());
   if (ReplyTo[0] != 0) fprintf(h, "Reply-To: %s\n", ReplyTo);
   if (subj == 0) fprintf(h, "Subject: <none>\n");
   if (ctr != 0) fprintf(h, "X-Sequence: %d\n", ctr);
   do {
      fputs(buf, h);
   } while (fgets(buf, sizeof(buf), f) != NULL);
   fclose(f); l_pclose(h);
}

void saveMsg()
{
  FILE *f, *g;
  long ti;
  struct tm *t;
  int dir = 0, oldfile = 0;
  char cmd[64];
  struct stat st;

  time(&ti); t = localtime(&ti); f = fopen("msg","r");
  if (chdir(listName) == -1)
    return;
  else {
    dir = 1;
    sprintf(cmd, "log%02d%02d", t->tm_year, t->tm_mon + 1);
    oldfile = stat(cmd, &st) + 1;
    g = fopen(cmd, "a");
  } /* endif */
  if (!oldfile) {
    fprintf(g, "This digest for list %s has been created on %s\n\n",
	    listName, asctime(t));
    fputs("------- THIS IS A RFC934 COMPLIANT DIGEST, YOU CAN BURST IT -------\n\n", g);
  }
  while (fgets(buf, sizeof(buf), f) != NULL && buf[0] != '\n') {
    if (strncmp(buf, "Subject:", 8) == 0 || strncmp(buf, "Date:", 5) == 0)
      fputs(buf, g);
    if (strncmp(buf, "From:", 5) == 0) {
      fprintf(g, "From: %s\n", From);
      if (subj == 0) fprintf(g, "Subject: <none>\n");
    } /* endif */
    strlwr(buf);
  } /* endwhile */
  if (ctr != 0) fprintf(g, "X-Sequence: %d\n", ctr);
  do {
    if (buf[0] == '-') { fprintf(g, "- "); }
    fputs(buf, g);
  } while (fgets(buf, 512, f) != NULL);
  fputs("------- CUT --- CUT --- CUT --- CUT --- CUT --- CUT --- CUT -------\n\n", g);
  fclose(f); fclose(g);
  if (dir == 1) chdir(TULPDIR);
}

void fwdMsg()
{
   FILE *f, *g, *h;
   int i;
   char *p;
   char cmd[128];

   strcpy(listName, To);
   if (ReadUserList(listName) == -1) { 
      syslog(LOG_INFO, "Sending error (list not found)");
      mailMsg("list-errors", "List-Errors");
      unlink("msg"); return; 
   } /* endif */
   strcpy(buf, GetReplyTo());
   if (ReplyTo[0] == 0 || instring(buf, ",respect") == NULL) {
      if (strncasecmp(buf, "list", 4) == 0) {
         strcpy(cmd, listName);
         strupr(cmd);
         sprintf(ReplyTo, "%s Distribution List <%s@%s>", cmd, listName, HOST);
      } else if (strncasecmp(buf, "sender", 6) != 0)
         sprintf(ReplyTo, "%s", strtok(buf, ","));
   } /* endif */
   RewindUserList();
   if (strcasecmp(GetSend(), "private") == 0 && !IsUser(From)) {
      syslog(LOG_INFO,
	     "Mail to list %s from %s - Refused, this user is not allowed",
	     listName, From);
      sprintf(buf, "%s %s", SENDMAIL, strtok(From, " ")); /* NAK to sender */
      if ((g = l_popen(buf, "w")) == NULL) {
	 syslog(LOG_ERR, "fwdMsg - popen failed: %m");
	 unlink("msg"); 
         CloseUserList();
         return;
      } /* endif */
      if ((f = fopen("msg", "r")) == NULL) {
         syslog(LOG_ERR, "fwdMsg - fopen failed: %m");
         unlink("msg"); 
         CloseUserList();
         return;
      } /* endif */
      fprintf(g, TULP_HDRMAILNAK(From, listName));
      fprintf(g, TULP_MAILPRIVATE);
      while (fgets(buf, sizeof(buf), f) != NULL)   /* unsent message */
         fputs(buf, g);
      l_pclose(g); fclose(f);
      mailMsg("list-errors", "attempt to send mail by a non subscriber");
                                            /* to inform list-errors */
   } else if (strcasecmp(GetSend(), "editor") == 0 && !IsEditor(From)) {
      syslog(LOG_INFO,"Mail to list %s from %s - Article to moderate",
                      listName, From);
      RewindEditorList();
      if (GetEditor(buf) != NULL) {
	 mailMsg(strtok(buf, " "), "Article to moderate");
      } /* endif */
   } else {			/* Ok to send */
      strcpy(buf, listName);	/* X-Sequence - update listName.n */
      strcat(buf, ".n");
      ctr = 0;
      h = fopen(buf, "r+"); 
      if (h != NULL) {
         fscanf(h, "%d", &ctr);
         rewind(h); fprintf(h, "%d\n", ++ctr); fclose(h);
      } /* endif */
      syslog(LOG_INFO,"List %s - Msg from %s accepted", listName, From);
      saveMsg();		/* save Msg in log file */
      i = 0; p = rcpt;
      while (GetUser(buf) != NULL) {
	 strtok(buf, "\t\r\n ");
	 if (((p - rcpt) + strlen(buf)+2) < sizeof(rcpt) && i++ < BATCHSIZE) {
	    sprintf(p, "%s ", buf);
	    p += strlen(p);
	 } else {
            i = 0; p = rcpt;
	    sendMsg();
	 } /* endif */
      } /* endwhile */
      if (i != 0) sendMsg();
   } /* endif */
   unlink("msg");
   CloseUserList();
}

int  copyMail()
{
   FILE *h;
   char *p;
   int c;
   struct stat st;

   if (access("msg", 0) == -1) {
      winInc(); 
      if ((h = popen(MAILX, "w")) == NULL) return(-1);
      fprintf(h, "s 1 msg\n");
      fprintf(h, "q\n");
      pclose(h);
   } /* endif */
   if ((h = fopen("msg", "r")) == NULL) return(-1); 
   To[0] = 0;
   fstat(fileno(h), &st); msgSize = st.st_size;
   subj = 0; To[0] = ReplyTo[0] = 0;
   while (fgets(buf, sizeof(buf), h) != NULL && buf[0] != '\n') {
      /* if the line is too long, throw the rest away */
      if (index(buf, '\n') == NULL) {
	 while ((c = getc(h)) != '\n' && c != EOF)
	    continue;
      } /* endif */
      while ((c = getc(h)) == ' ' || c == '\t') {
	 p = &buf[strlen(buf) - 1];
	 *p++ = ' ';
	 if (fgets(p, sizeof(buf) - (p - buf), h) == NULL)
	    break;
      } /* endif */
      if (!feof(h) && !ferror(h))
	 ungetc(c, h);
      strtok(buf, "\n");
      if (Debug) {
	 fprintf(stderr, "Collected line: %s\n", buf);
      } /* endif */
      if (strlen(buf) > MAXLINE) {
	 syslog(LOG_ERR, "Header line too long: %s", strtok(buf, ":"));
	 fclose(h);
	 return(-2);
      } /* endif */
      if (strncmp(buf, "Reply-To: ", 10) == 0) {
         strcpy(ReplyTo, buf + 10);
      } /* endif */
      if (strncmp(buf, "Subject: ", 9) == 0) { 
         /* We could save subject here, but we don't need it */
         subj = 1; 
         continue; 
      } /* endif */
      if (strncmp(buf, "From: ", 6) == 0) {
         /* Usually just one author */
         strcpy(From, buf + 6);
         continue;
      } /* endif */
      if (strncmp(buf, "X-Listserv-To: ", 15) == 0 ) {
         strcpy(To, buf + 15);
         continue;
      } /* endif */
   } /* endwhile */
   adChange(From);
   strlwr(To);
   fclose(h);
   if (To[0] == 0) {
      syslog(LOG_ERR, "Empry X-Listserv-To or not found");
      return(-2);
   } /* endif */
}

void sTe()
{
   done = 1;
}

#ifndef NO_SIGUSR1
void sPau()
{
   msgHere = 1;
}
#endif

void main(int argn, char **argv)
{
   struct stat st;
   struct sigaction sa;
   FILE *f;
   int i;
   extern void reapchild();

   while ((i = getopt(argn, argv, "d")) != EOF) {
      switch (i) {
	 case 'd':
	    Debug = 1;
	    break;
	 default:
	    fprintf(stderr,"usage: %s [-d]\n", *argv);
	    exit(1);
      } /* endsw */
   } /* endif */
   versInit();
   if (chdir(TULPDIR) == -1) {
      perror("Can't chdir' to TULPDIR. Aborting.");
      exit(2);
   } /* endif */
   setbuf(stdout, NULL); printf("%s: daemon started for %s\n\r", vers, HOST);
   printf("Copyright (C) 1991,1992  Kimmo Suominen, Christophe Wolfhugel\n");
   printf("Listserv comes with ABSOLUTELY NO WARRANTY; for details see\n");
   printf("the GNU General Public License that is furnished with the\n");
   printf("sources of this package.\n");
   if (Debug == 0)
      if (fork()) exit (0);   /* Dissociate us from tty */
   times(&t);
   time(&startTime);
   nice(NICENESS);
   if (Debug == 0) {
      freopen("/dev/null", "w", stdout);
      freopen("/dev/null", "w", stderr);
      close(fileno(stdin));
      setpgrp();
   } /* endif */
   sigemptyset(&sa.sa_mask);
#ifdef SA_RESTART
   sa.sa_flags = SA_RESTART;	/* Restart system calls?  SA_RESTART */
				/* Children?  SA_NOCLDWAIT, SA_NOCLDSTOP */
#else
   sa.sa_flags = 0;
#endif
   sa.sa_handler = sTe;
   sigaction(SIGTERM, &sa, NULL);
#ifdef SIGURGENT
   sigaction(SIGURGENT, &sa, NULL);
#endif
   sa.sa_handler = SIG_IGN;
   /* sa.sa_flags = SA_RESETHAND;	/* Reset to SIG_DFL, not blocked */
#ifndef NO_SIGUSR1
   sigaction(SIGUSR1, &sa, NULL);
#endif
   sa.sa_handler = reapchild;
   sigaction(SIGCHLD, &sa, NULL);
   umask(022);
   openlog("listserv", LOG_PID, LOG_AUTH);
   syslog(LOG_INFO, "Listserv Started");
   f = fopen(PIDFILE, "w");
   if (f == NULL) {
      syslog(LOG_ERR, "Can't open PIDFILE for writing. Aborting.");
      exit(2);
   } /* endif */
   fprintf(f, "%d", getpid());
   fclose(f);
   umask(077);
   while (!done) {
      if (access("msg", 0) == -1 && (stat(MAILFILE, &st) == -1 
	  || st.st_size == 0)) {
#ifdef NO_SIGUSR1
         sleep(NO_SIGUSR1);
#else
         if (msgHere == 0) {
	   sa.sa_handler = sPau;
	   sigaction(SIGUSR1,&sa,NULL);
	   pause();
	   sa.sa_handler=SIG_IGN;
	   sigaction(SIGUSR1,&sa,NULL);
	 }
#endif
         continue;
      } /* endif */
      msgHere = 0;
      if ((i = copyMail()) == -1) continue;
      if (i == -2) {
	 mailMsg("list-errors", "Unknown error in saveMsg(), see the syslog");
	 unlink("msg");
	 continue;
      } /* endif */
      if (instring(From, "mailer") != NULL || 
	  instring(From, "postmaster") != NULL ||
	  instring(From, "listserv") != NULL ||
	  instring(From, "root") != NULL ||
	  instring(From, "system") != NULL ||
	  instring(From, "-request") != NULL ||
	  instring(From, "owner-") != NULL) {
         syslog(LOG_INFO,"Message from Mailer-Daemon");
	 mailMsg("list-errors", "Trouble");
         unlink("msg");
         continue;
      } /* endif */
      if (strcmp(To, "listserv") == 0)
         { listservCmd(); } else { fwdMsg(); }
   } /* endwhile */
   syslog(LOG_INFO,"Listserv has been terminated");
   time(&endTime); lTodhms(endTime-startTime);
   times(&t);
   t.tms_utime+=t.tms_cutime;
   t.tms_stime+=t.tms_cstime;
   syslog(LOG_INFO,"Total elapsed time: +%dd %02d:%02d:%02d\n",d,h,m,s);
   lTodhms(t.tms_utime/HZ);
   syslog(LOG_INFO,"User CPU time:      +%dd %02d:%02d:%02d\n",d,h,m,s);
   lTodhms(t.tms_stime/HZ);
   syslog(LOG_INFO,"System CPU time:    +%dd %02d:%02d:%02d\n",d,h,m,s);
   lTodhms((t.tms_utime+=t.tms_stime)/HZ);
   syslog(LOG_INFO,"Total CPU time:     +%dd %02d:%02d:%02d\n",d,h,m,s);
   syslog(LOG_INFO,"%% CPU used:  %02.02f%%\n",
             (float)(t.tms_utime/HZ*100)/(float)(endTime-startTime+1));
   closelog();
   unlink(PIDFILE);
   exit(0);
}
