/*-------------------------------------------------------------------------
 *  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: lc.c,v 1.22 93/03/02 22:40:31 wolf Exp $";

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <limits.h>
#include <sys/param.h>
#include <sys/types.h>
#include <signal.h>
#include <sys/stat.h>
#include <dirent.h>
#include <sys/times.h>
#include "conf.h"
#include "ext.h"
#include "l.h"
#include "lp.h"
#include "str.h"
#include "popen.h"
#include "messages.h"
#ifdef FAKESYSLOG
# include "fakesyslog.h"
#else
# include <syslog.h>
#endif

FILE *l;
char adrs[MAXLINE];
static char cmd[MAXLINE + 1];

struct kword {
  char k[4];
  void (*job)();
};

void help(), list(), review(), subscribe(), signoff(), listindex(),
     getFile(), addUser(), delUser();

struct kword kw[] = { "hel", help,
                      "lis", list,
                      "rev", review,
                      "sub", subscribe,
                      "sig", signoff,
                      "uns", signoff,
                      "ind", listindex,
                      "get", getFile,
                      "add", addUser,
                      "del", delUser,
                      "",  NULL };

/*
 * Spawn return message MTA
 */

FILE *getMTA(char *recipient)
{
  FILE *mta;

#ifdef ADD_REQUEST
  sprintf(cmd,"%s -flistserv-request %s",SENDMAIL,recipient);
#else
  sprintf(cmd,"%s %s",SENDMAIL,recipient);
#endif
  mta = l_popen(cmd,"w");
  fprintf(mta, versFrom);
#ifdef ADD_SENDER
  fprintf(mta, versSender);
#endif
  return(mta);
}

/*
 *  HELP
 */

void help()
{
  FILE *f,*h;

  strlwr(buf);
  syslog(LOG_INFO,"HELP to %s",From);
  h=getMTA(adrs);
  fprintf(h,TULP_HDRHELP(From));
  f=fopen("helpfile","r");
  while (fgets(buf, sizeof(buf), f) != NULL)
    fputs(buf,h);
  fclose(f);
  l_pclose(h);
}


/*
 *  LIST
 */

void list()
{
  FILE *f,*h;

  syslog(LOG_INFO,"LIST to %s",From);
  h=getMTA(adrs);
  fprintf(h,TULP_HDRLIST(From));
  f=fopen("lists","r");
  while (fgets(buf, sizeof(buf) ,f) != NULL) {
    if (buf[0] == '#') continue;
    strtok(buf,",");
    fprintf(h,"%-15s  * %s\n",buf,strtok(NULL,"\n"));
  } /* endwhile */
  fclose(f);
  l_pclose(h);
}


/*
 *  REVIEW list
 */

void review()
{
  FILE *h;
  char *s;

  s=strtok(NULL," \t\n"); strlwr(s);
  syslog(LOG_INFO,"REV '%s' to %s",s,From);
  if (s == NULL)
    fprintf(l,TULP_SYNTAX);
  else
    if (ReadUserList(s) == -1)
      fprintf(l,TULP_NOSUCHLIST);
    else {
      h=getMTA(adrs);
      fprintf(h,TULP_HDRREV(From,s));
      RewindCommentList();
      RewindUserList();
      while (GetComment(buf) != NULL)
	fprintf(h,"%s\n",buf);
      if (!IsOwner(From) && (strcasecmp(GetReview(), "owner") == 0
       || (strcasecmp(GetReview(),"private") == 0 && !IsUser(From))))
	fprintf(l,TULP_REVPRIVATE);
      else
	while (GetUser(buf) != NULL) {
	  strtok(buf,"(");
	  buf[strlen(buf)-1]=0;
	  fprintf(h,"%-40s %s\n",buf,strtok(NULL,")\n"));
	} /* endwhile */
      l_pclose(h);
    } /* endif */
  CloseUserList();
}


/*
 *  SUBSCRIBE list first last
 */

void subscribe()
{
  FILE *f,*h;
  char *s1,*s2;

  s1=strtok(NULL," \n");
  strlwr(s1);
  s2=strtok(NULL,"\n");
  syslog(LOG_INFO,"SUB '%s' from %s",s1,From);
  if (s1 == NULL || s2 == NULL)
    fprintf(l,TULP_SYNTAX);
  else {
    if (ReadUserList(s1) == -1)
      fprintf(l,TULP_NOSUCHLIST);
    else {
      RewindUserList();
      RewindOwnerList();
      if (strcasecmp(GetSubscription(),"closed") == 0)
	fprintf(l,TULP_SUBCLOSED);
      else
	if (strcasecmp(GetSubscription(),"owner") == 0) {
	  fprintf(l,TULP_SUBFORWARDED);
	  rcpt[0]=0;
	  while (GetOwner(cmd) != NULL) {
	    strcat(rcpt,strtok(cmd," \t\r\n"));
	    strcat(rcpt," ");
	  } /* endwhile */
	  sprintf(cmd,"%s %s",SENDMAIL,rcpt);
	  f=l_popen(cmd,"w");
	  fprintf(f,TULP_SUBREQUEST(adrs,s1,s2));
	  l_pclose(f);
	} else {
	  sprintf(cmd,"%s (%s)",adrs,s2);
	  switch (AddUser(cmd)) {
	  case 0:
	    h=getMTA(adrs);
	    fprintf(h,TULP_HDRWELCOME(From,s1));
	    strcat(s1,".w");
	    f=fopen(s1,"r");
	    while (fgets(buf, sizeof(buf), f) != NULL)
	      fputs(buf,h);
	    fclose(f);
	    l_pclose(h);
	    break;
	  case 1:
	    fprintf(l,TULP_SUBALREADY);
	    break;
	  } /* endsw */
	  WriteUserList();
	} /* endif */
    } /* endif */
    CloseUserList();
  } /* endif */
}


/*
 *  SIGNOFF list
 */

void signoff()
{
  FILE *f;
  char *s;

  s=strtok(NULL," \t\n");
  strlwr(s);
  syslog(LOG_INFO,"SIG '%s' from %s",s,From);
  if (s == NULL)
    fprintf(l,TULP_SYNTAX);
  else
    if (ReadUserList(s) == -1)
      fprintf(l,TULP_NOSUCHLIST);
    else {
      RewindOwnerList();
      rcpt[0]=0;
      while (GetOwner(cmd) != NULL) {
	strcat(rcpt,strtok(cmd," "));
	strcat(rcpt," ");
      } /* endwhile */
      sprintf(cmd,"%s %s",SENDMAIL,rcpt);
      f=l_popen(cmd,"w");
      if (DelUser(adrs) == -1) {
	fprintf(f,TULP_SIGFAILED(adrs,s));
	fprintf(l,TULP_NOTONLIST);
      } else {
	fprintf(f,TULP_SIGSUCCESS(adrs,s));
	fprintf(l,TULP_SIGDONE);
      }
      l_pclose(f);
      WriteUserList();
      CloseUserList();
    } /* endif */
}

/*
 *  INDEX list
 */

void listindex()
{
#define MAX_FILES  640
  static char files[MAX_FILES][64+1], swp[64+1];
  FILE *h;
  char *s;
  int i,j,noFiles;
  DIR *dirp;
  struct dirent *de;
  struct stat st;

  s=strtok(NULL,"\n");
  strlwr(s);
  syslog(LOG_INFO,"IND %s from %s",s,From);
  if (s == NULL || s[0] == '/' || s[0] == '~' || strstr(s,"..") != NULL)
    fprintf(l,TULP_SYNTAX);
  else {
    if (ReadUserList(s) != -1 && (strcasecmp(GetSend(),"public") != 0
     || strcasecmp(GetReview(), "public") != 0)
    && !IsUser(From) && !IsOwner(From))
      fprintf(l,TULP_NOTONLIST);
    else {
      if (chdir(s) == -1) fprintf(l,TULP_NONESTORED);
      else {
	h=getMTA(adrs);
	fprintf(h,TULP_HDRINDEX(From,s));
	dirp=opendir(".");
	noFiles=0;
	while (noFiles < MAX_FILES && (de=readdir(dirp)) != NULL)
	  if (de->d_name[0] != '.')
	    strcpy(files[noFiles++],de->d_name);
	closedir(dirp);
	for (i=0; i < noFiles-1; i++) {
	  for (j=i+1; j < noFiles; j++) {
	    if (strcmp(files[j],files[i]) < 0) {
	      strcpy(swp,files[i]);
	      strcpy(files[i],files[j]);
	      strcpy(files[j],swp);
	    } /* endif */
	  } /* endfor */
	} /* endfor */
	for (i=0; i < noFiles; i++) {
	  stat(files[i],&st);
	  if (st.st_mode & S_IFDIR) strcat(files[i],"/");
	  fprintf(h,"%-40s %10ld  %s",files[i],st.st_size,ctime(&st.st_mtime));
	} /* endif */
	chdir(TULPDIR);
	if (noFiles == 0) fprintf(l,TULP_INDNOFILES);
	l_pclose(h);
      } /* endif */
    } /* endif */
    CloseUserList();
  } /* endif */
}

/*
 *  GET list file
 */

void getFile()
{
  FILE *f,*h;
  char *s,*t;

  s=strtok(NULL," ");
  t=strtok(NULL," \n");
  strlwr(s);
  strlwr(t);
  syslog(LOG_INFO,"GET %s %s from %s",
	 (s == NULL) ? " " : s, (t == NULL) ? " " : t, From);
  if (ReadUserList(s) != -1 && (strcasecmp(GetSend(),"public") != 0
   || strcasecmp(GetReview(), "public") != 0)
   && !IsUser(From) && !IsOwner(From))
    fprintf(l,TULP_NOTONLIST);
  else {
    if (chdir(s) == -1) fprintf(l,TULP_NONESTORED);
    else {
      if (strstr(t,"..") != NULL || *t == '/' || *t == '~') *t=0;
      f=fopen(t,"r");
      if (f == NULL) fprintf(l,TULP_GETNOSUCHFILE);
      else {
	h=getMTA(adrs);
	fprintf(h,TULP_HDRGET(From,s,t));
	while (fgets(buf, sizeof(buf), f) != NULL)
	  fputs(buf,h);
	fclose(f);
	l_pclose(h);
      } /* endif */
      chdir(TULPDIR);
    } /* endif */
  } /* endif */
  CloseUserList();
}

/*
 *  ADD list user@host.foo.bar First Last
 */

void addUser()
{
  FILE *f,*h;
  char *s,*t,*u;

  s=strtok(NULL," \n");
  t=strtok(NULL," \n");
  u=strtok(NULL,"\n"); 
  strlwr(s);
  syslog(LOG_INFO,"ADD '%s' %s from %s",s,t,From);
  if (s == NULL || t == NULL || u == NULL)
    fprintf(l,TULP_SYNTAX);
  else
    if (ReadUserList(s) == -1)
      fprintf(l,TULP_NOSUCHLIST);
    else {
      RewindUserList();
      RewindOwnerList();
      if (!IsOwner(From))
	fprintf(l,TULP_NOTOWNER);
      else {
	h=getMTA(t);
	sprintf(cmd,"%s (%s)",t,u);
	switch (AddUser(cmd)) {
	case 0:
	  fprintf(h,TULP_HDRWELCOME(cmd,s));
	  fprintf(h,TULP_ADDBY(cmd,s,From));
	  strcat(s,".w");
	  f=fopen(s,"r");
	  while (fgets(buf, sizeof(buf),f) != NULL)
	    fputs(buf,h);
	  fclose(f);
	  fprintf(l,TULP_ADDSUCCESS);
	  break;
	case 1:
	  fprintf(h,TULP_SUBUPDATED(cmd,s,From));
	  fprintf(l,TULP_ADDALREADY);
	  break;
	} /* endsw */
	l_pclose(h);
      } /* endif */
      WriteUserList();
      CloseUserList();
    }
}

/*
 *  DELETE list user@host.foo.bar
 */

void delUser()
{
  FILE *h;
  char *s1,*s2;

  s1=strtok(NULL," \n");
  s2=strtok(NULL," \n");
  strlwr(s1);
  syslog(LOG_INFO,"DEL '%s' %s from %s",s1,s2,From);
  if (s1 == NULL || s2 == NULL)
    fprintf(l,TULP_SYNTAX);
  else
    if (ReadUserList(s1) == -1)
      fprintf(l,TULP_NOSUCHLIST);
    else {
      RewindUserList();
      RewindOwnerList();
      if (!IsOwner(From))
	fprintf(l,TULP_NOTOWNER);
      else
	if (!IsUser(s2))
	  fprintf(l,TULP_DELNOTONLIST);
	else {
	  sprintf(cmd,"%s",s2);
	  switch (DelUser(cmd)) {
	  case 0:
	    h=getMTA(s2);
	    fprintf(h,TULP_DELREMOVED(s2,s1,From));
	    l_pclose(h);
	    fprintf(l,TULP_DELSUCCESS);
	    break;
	  case -1:
	    fprintf(l,TULP_DELINCONSISTENT);
	    break;
	  } /* endsw */
	} /* endif */
      WriteUserList();
      CloseUserList();
    } /* endif */
}


int doCmd()
{
  int i;
  char s[4];

  if (buf[0] == '\n') return(0);
  strncpy(s,strtok(buf," "),3);
  s[3]='\0';
  strlwr(s);
  for (i=0; kw[i].k[0] != 0; i++)
    if (strncmp(kw[i].k,s,3) == 0) {
      kw[i].job();
      return(0);
    }
  return(-1);
}

void listservCmd()
{
   int done, noCmd;
   char cmd[256];
   FILE *f;
#ifdef CPUTYPE
   struct tms t;
   long tt;

   times(&t); tt=t.tms_stime+t.tms_utime+t.tms_cstime+t.tms_cutime;
#endif

   noCmd = 0;
   done  = 0;
   strcpy(adrs, From); strtok(adrs, " ");
   l = getMTA(adrs);
   fprintf(l, TULP_HDROUTPUT(From));
   f = fopen("msg", "r");
   buf[0] = 0;
   while (fgets(buf, sizeof(buf), f) != NULL && buf[0] != '\n')
    buf[0]=0;
   if (buf[0] == 0)
      fprintf(l, TULP_NOBODY);
   else {
      while (!done && fgets(buf, sizeof(buf) - 3, f) != NULL) {
         if (strncasecmp(buf, "quit", 4) == 0
	     || strncasecmp(buf, "end", 3) == 0
	     || strncasecmp(buf, "stop", 4) == 0
	     || buf[0] == '-') {
	    fprintf(l, "> %s",buf);
	    fprintf(l, TULP_EXITING);
            break;
         } /* endif */
#ifdef SHUTDOWN
         if (strcmp(SHUTDOWN, buf) == 0) {
	    syslog(LOG_INFO, "Shutdown by %s", From);
	    done = 1;  /* this is clean */
	    noCmd++;
	    continue;
         } /* endif */
#endif
         if (buf[0] != '\n') {
 	    fprintf(l,"> %s",buf);
	    if (doCmd() == -1)
	       fprintf(l,TULP_SAYWHAT);
	    else {
	       noCmd++;
	       fprintf(l,TULP_OKAY);
	    } /* endif */
         } /* endif */
      } /* endwhile */
   } /* endif */
   if (noCmd == 0) {
      fprintf(l,TULP_NOCOMMANDS);
      syslog(LOG_INFO,"no commands from",From);
      mailMsg("listman","No commands");
   } /* endif */
   fclose(f);
   unlink("msg");
#ifdef CPUTYPE
   times(&t);
   tt=(t.tms_stime+t.tms_utime+t.tms_cstime+t.tms_cutime)-tt;
   fprintf(l,TULP_TOTAL((float)tt/HZ,CPUTYPE));
#endif
   l_pclose(l);
}
