/*
 *
 *	Copyright (C) 1988, 1989 by the Massachusetts Institute of Technology
 *    	Developed by the MIT Student Information Processing Board (SIPB).
 *    	For copying information, see the file mit-copyright.h in this release.
 *
 */
/*
 *
 * dsmail.c - Modifies/parses mail to discuss
 *
 *        $Source: /mit/discuss/source/mclient/RCS/dsmail.c,v $
 *
 *        $Log:	dsmail.c,v $
 * Revision 1.16  91/07/27  02:27:28  srz
 * Added check in case EX_CONFIG wasn't defined.
 * 
 * Revision 1.15  91/07/05  20:48:09  bjaspan
 * ported to svr4
 * 
 * Revision 1.14  91/07/05  19:16:33  bjaspan
 * actually, I meant ucbinclude
 * 
 * Revision 1.13  91/07/05  19:12:47  bjaspan
 * use /usr/ucb/sysexits.h on svr4
 * 
 * Revision 1.12  91/05/29  16:54:27  raeburn
 * Discarded some unused code; use exit codes that sendmail will recognize, and
 * use SMTP codes in error messages printed.  Renamed PipeToMeeting to
 * SendToMeeting.  Some other minor changes dealing with initialization of
 * variables.  Deleted useless `p' option, added h to indicate that hostname
 * should be extracted from meeting name (not currently used, though).
 * 
 * Revision 1.11  90/02/24  18:46:12  srz
 * Added deriving signature from From: field.
 * 
 * Revision 1.10  89/06/03  00:30:54  srz
 * Added standard copyright notice.
 * 
 * Revision 1.9  89/04/17  17:09:21  srz
 * Added error table initialization.
 * 
 * Revision 1.8  89/01/05  07:13:11  raeburn
 * replaced included header files with <discuss/discuss.h>; also added
 * newline after message header
 * 
 * Revision 1.7  88/01/15  22:42:39  srz
 * set_module now returns fatal flag, again.
 * 
 * Revision 1.6  88/01/05  03:09:30  srz
 * Put location of DSPIPE into config.h
 * 
 * Revision 1.5  87/10/24  04:26:44  wesommer
 * Rewritten for speed and efficiency.  "popen" is a crock.
 * 
 * Revision 1.4  87/07/17  03:34:57  tytso
 * Bugfixes made, added -p option, removed zippy stuff
 * 
 * Revision 1.3  87/05/02  02:12:58  tytso
 * Replaced the regexp and options parsing code with the UNIX
 * library routines.  Cleaned up code.  Added -A (all headers)
 * option.
 * 
 */

#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/file.h>
#ifdef SVR4
#include "/usr/ucbinclude/sysexits.h"
#define EX_CONFIG EX_SOFTWARE
#else
#include <sysexits.h>
#endif

#include <discuss/discuss.h>
#include <rpc.h>
#include "config.h"

#define DEFAULT_SUBJECT "No subject found in mail header"
#define BUFFLEN 400
#define LISTLEN 40

/* Ultrix sux */
#ifndef EX_CONFIG
#define EX_CONFIG EX_SOFTWARE
#endif

#ifndef	lint
static char rcsid[] =
    "$Header: /mit/discuss/source/mclient/RCS/dsmail.c,v 1.16 91/07/27 02:27:28 srz Exp $";
#endif

char *malloc(), *realloc ();
char *mktemp();
FILE *popen();
FILE *fopen();
FILE *fdopen();
int getopt();

extern char *optarg;                /* External variables for getopt */
extern int optind;

char *deflist[] = {
	"^to$","^from$","^cc$",".*-to$",".*-from$","^date$",NULL
};
		
char *subjlist[] = {
	"subject",NULL
};

char *inreplyto[] = {
	"in-reply-to",NULL
};

char *fromlist[] = {
	"^from$",NULL
};     

char *progname;
char *save[LISTLEN],*reject[LISTLEN];
char *usr_mtg = "";
int dodefs, allfields, debug, have_host;
char *optarg;
int optind;
extern tfile unix_tfile();
int reply_to;

void PRS();
void MakeLowerCase();
void SendToMeeting();

main(argc,argv)
	int argc;
	char *argv[];
{
	FILE *f;
	char line[BUFFLEN+1],*keyword=0,*key;
	char *subject=NULL;
	int i,ok_prev=0,iscont = 0, have_signature = 0;
	char filename[60], signature[35];

	init_dsc_err_tbl();
	PRS(argc,argv);
	line[BUFFLEN]='\0';
	if (debug) 
		f=stdout;
	else {
		(void) mktemp(strcpy(filename, "/tmp/DSXXXXXX"));
		if ((f = fopen(filename,"w+")) == NULL)
			perror(filename);
		if (fchmod(fileno(f),0600))
			perror("chmod temp file");
	}
	if (debug) printf("start of text->");
	reply_to = 0;
	
	if (fgets(line,BUFFLEN,stdin)==NULL)
		exit(EX_NOINPUT);
	while (*line != '\n') {
		if (!iscont) {
			char *cp;
			for (i=0;line[i] && line[i]!=':';i++) ;
			keyword = realloc (keyword, 2*(i+1));
			key = keyword + i + 1;
			(void) strncpy(keyword,line,i);
			keyword[i]='\0';
			MakeLowerCase(strcpy(key,keyword));
			if (list_compare(key,subjlist)) {
				for (i++;line[i]==' ';i++) ;
				/* if luser tries two subject lines, we */
				/* ignore the second subject line */
				if (subject==NULL) {
					subject=malloc(strlen(line)-i+1);
					(void) strcpy(subject,line+i);
				}	
			} else if (list_compare(key, inreplyto)) {
				if ((cp = index(line,'[')) != 0
				    && index(++cp, ']')
				    && isdigit (*cp))
				    reply_to = atoi(cp);
			}
			else if (list_compare(key,fromlist)) {
				cp = index(line, ':');
				if (cp != NULL) {
					cp++;
					extract_full_name(cp, signature, sizeof(signature));
					have_signature = TRUE;
					if (debug)
					    printf ("got sig: %s\n",
						    signature);
				}
			}
		}
		if ((ok_prev && iscont) || (!iscont && CheckField(key))) {
			ok_prev=1;
			fprintf(f,"%s",line);
		} else
			ok_prev=0;
		if (!iscont) {
			free(key);
			free(keyword);
		}
		iscont = line[strlen(line)-1] != '\n';
		if (fgets(line,BUFFLEN,stdin)==NULL)
			goto bye;
	}
	fprintf (f, "\n");
	while (fgets(line,BUFFLEN,stdin)!=NULL) {
		fprintf(f,"%s",line);
	}
	if (fflush(f) == EOF) {
		fprintf (stderr, "250-Can't write to ");
		fflush (stderr);
		perror (debug ? "standard output" : filename);
		exit (EX_TEMPFAIL);
	}
	
	SendToMeeting(f,(subject==NULL) ? DEFAULT_SUBJECT : subject,
		      (have_signature) ? signature : NULL, 
		      reply_to);
 bye:
	if (!debug)
		(void) unlink(filename); 
	exit (EX_OK);
	
}

void SendToMeeting(f,subject, signature, reply_to)
	char *subject;
	char *signature;     
	FILE *f;
	int reply_to;
{
	int len;
	static char module[100] = "discuss@";
	int fatal_err, result, smtp_code, exit_code;
	tfile transaction;
	int trn_no;
	
	gethostname(&module[8], sizeof(module)-8);
	init_rpc();
	set_module(module, &fatal_err, &result);
	switch (result) {
	case 0:
	    break;
	default:
	    smtp_code = 554;
	    exit_code = EX_CONFIG;
	    break;
	}
	if (result) {
		fprintf(stderr, "%03d-Can't connect to discuss server: %s\n",
			smtp_code, error_message(result));
		exit (exit_code);
	}

	rewind(f); lseek(fileno(f), 0, L_SET);
	transaction = unix_tfile(fileno(f));

	len=strlen(subject);
	if (len && (subject[--len] == '\n'))
		subject[len]='\0';
	if (debug) {
		printf("subject is ``%s''\nreplying to %d in %s\n",
		       subject, reply_to, usr_mtg);
	} 
	if (signature == NULL || *signature == '\0') {
		add_trn(usr_mtg, transaction, subject, reply_to, &trn_no, &result);
	} else {
		if (get_server_version() < SERVER_2)
			add_trn(usr_mtg, transaction, subject, reply_to, &trn_no, &result);
		else
		     	add_trn2(usr_mtg, transaction, subject, signature, reply_to, &trn_no, &result);
	}

	switch (result) {
	case 0:
	    break;
	default:
	    smtp_code = 550;
	    exit_code = EX_CANTCREAT;
	}
	if (result) {
	    fprintf (stderr, "%03d-Can't enter transaction", smtp_code);
	    if (reply_to)
		fprintf (stderr, " as reply to %d", reply_to);
	    fprintf (stderr, "\n%03d- into meeting %s: %s\n",
		     smtp_code, usr_mtg, error_message (result));
	    exit(exit_code);
	}
	(void) fclose(f);
}

void MakeLowerCase(s)
	char *s;
{
	int i;
	for (i=0;s[i];i++)
		s[i]=isupper(s[i]) ? tolower(s[i]) : s[i];
}

int CheckField(key)
	char *key;
{
	int keepfield;

	keepfield=allfields;
	if (!keepfield && dodefs && list_compare(key,deflist))
		keepfield=1;
	if (!keepfield && list_compare(key,save))
		keepfield=1;
	if (keepfield && list_compare(key,reject))
		keepfield=0;
	return(keepfield);
}

/* Parse command line arguments */
void PRS(argc,argv)
	int argc;
	char **argv;
{
	int c,rp,sp;
	
	progname=argv[0];
	sp=rp=0;
	optind=1;		/* Initialize for getopt */
	while ((c = getopt(argc,argv,"AZDda:r:h")) != EOF)
		switch(c) {
		case 'd':
			dodefs=!dodefs;
			break;
		case 'D':
			debug=!debug;
			break;
		case 'A':
			allfields=!allfields;
			break;
		case 'a':
			MakeLowerCase(optarg);
			save[sp++]=optarg;
			if (sp>=LISTLEN) {
				fprintf(stderr,"500-Too many accept fields\n");
				exit (EX_USAGE);
			}
			break;
		case 'h':
			have_host = 1;
			break;
		case 'r':
			MakeLowerCase(optarg);
			reject[rp++]=optarg;
			if (sp>=LISTLEN) {
				fprintf(stderr,"500-Too many reject fields\n");
				exit (EX_USAGE);
			}
		}    
	if (optind>=argc) 
		goto lusage;
	usr_mtg=argv[optind];
	if (have_host) {
		usr_mtg = index (usr_mtg, ':');
		if (!usr_mtg)
			goto lusage;
		usr_mtg++;
	}
	save[sp]=NULL;		/* Insert terminators */
	reject[rp]=NULL;
	return;
 lusage:
	printf("500-Usage: %s [-dADZ] [-a field] [-r field] meeting-path-name\n",
	       progname);
	exit (EX_USAGE);
}

char *re_comp();

int list_compare(s,list)
	char *s,**list;
{
	char *err;

	while (*list!=NULL) {
		err=re_comp(*list++);
		if (err) {
			fprintf(stderr,"554-%s - %s\n",
				progname, err, list[-1]);
			exit(EX_CONFIG);
			}
		if (re_exec(s))
			return(1);
	}
	return(0);
}

strip_addr(addr, dest, dest_size)
char *addr,*dest;
int dest_size;
{
     char *dest_end,*dp,*sp,*quote_start;
     int paren_level,found_angle;

     dest_end = &dest[dest_size-1];
     dp = dest;
     sp = addr;
     paren_level = 0;

eat_white:
     while (isspace(*sp) && *sp != '\n')
	  sp++;

     if (*sp == '(')
	  goto eat_comment;

     if (*sp == '"')
	  goto eat_string;

     if (*sp == '<') {
	  dp = dest;
	  sp++;
	  found_angle = TRUE;
	  goto eat_white;
     }

     if (*sp == '>' && found_angle) {
	  *sp++;
	  goto eat_white;
     }

     if (*sp == '\0' || *sp == '\n') {
	  *dp++ = '\0';
	  goto post_proc;
     }

     *dp++ = *sp++;
     if (dp == dest_end) {
	  *dp++ = '\0';
	  goto post_proc;
     }

     goto eat_white;

eat_comment:
     paren_level++;
     sp++;

cont_comment:
     while (*sp != ')' && *sp != '(' && *sp) {
	  sp++;
     }

     if (*sp == '\0') {
	  *dp = '\0';
	  goto post_proc;
     }

     if (*sp == '(')
	  goto eat_comment;

     sp++;		/* ) */
     paren_level--;
     if (paren_level <= 0)
	  goto eat_white;

     goto cont_comment;

eat_string:
     quote_start = sp;
     sp++;

     while(*sp != '"' && *sp) 
	  sp++;

     if (!*sp) {
	  *dp = '\0';
	  goto post_proc;
     }

     if (*++sp == '@') {		/* "foo"@bar */
	  sp = quote_start;
	  *dp++ = *sp++;
	  while (dp < dest_end && *sp != '"')
	       *dp++ = *sp++;

	  if (dp == dest_end) {
	       *dp = '\0';
	       goto post_proc;
	  }
	  *dp++ = *sp++;
	  if (dp == dest_end) {
	       *dp++ = '\0';
	       goto post_proc;
	  }
     }
     goto eat_white;

     /* No post processing */
post_proc:
     return;
}

/*
 *
 *   Routine to extract a full name from an address.  If no full name
 *   can be found, then we simply return the stripped address.
 *
 */

extract_full_name(addr, dest, dest_size)
char *addr,*dest;
int dest_size;
{
     char *dest_end,*dp,*sp,*bracket,*close_paren;
     int paren_level,non_white;

     dest_end = &dest[dest_size-1];
     dp = dest;
     sp = addr;

     /* Find angle bracket (if possible) */
     while (*sp && *sp != '<' && *sp != '\n')
	  sp++;

     bracket = NULL;
     if (*sp == '<')
	  bracket = sp;

     non_white = 0;
     if (bracket != NULL) {
	  for (sp = addr; sp < bracket; sp++) {
	       if (!isspace(*sp) && *sp != '"')
		    non_white++;
	  }
     }

     if (non_white > 1) {		/* We have a name */
	  sp = addr;
	  while (isspace(*sp) || *sp == '"')	/* Skip leading spaces */
	       sp++;

	  while (isspace(*(bracket-1)) || *(bracket-1) == '"')  /* Skip trailing spaces */
	       bracket--;

	  /* Copy it over */
	  while (sp < bracket && dp < dest_end)
	       *dp++ = *sp++;

	  *dp++ = '\0';
	  return;
     }

     /* Now, let's see if we have name in a comment (look back from the
	end for a parenthesis. */
     for (sp = addr; *sp && *sp != '\n'; sp++)
	  ;

     sp--;
     while (sp > addr && isspace(*sp))
	  sp--;

     if (*sp == ')') {			/* Name in comment */
	  close_paren = sp;
	  paren_level = 1;
	  sp--;

	  for (;sp > addr; sp--) {
	       if (*sp == ')')
		    paren_level++;
	       else if (*sp == '(') {
		    paren_level--;
		    if (paren_level == 0)
			 break;
	       }
	  }

	  if (*sp == '(') {	      	/* Copy it over */
	       sp++;

	       while(isspace(*sp))
		    sp++;

	       while (sp < close_paren && dp < dest_end)
		    *dp++ = *sp++;

	       *dp = '\0';
	       return;
	  }
     }

     strip_addr(addr, dest, dest_size);
     return;
}	  
