/*
**
** Copyright (C) 1994 Swedish University Network (SUNET)
**
**
** This program is developed by UDAC, Uppsala University by commission
** of the Swedish University Network (SUNET). 
**
** 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 2 of the License, 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 FITTNESS 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.
**
**
**                                        Martin.Wendel@udac.uu.se
**                                        Torbjorn.Wictorin@udac.uu.se
**
**                                        UDAC	
**                                        P.O. Box 174
**                                        S-751 04 Uppsala
**                                        Sweden
**
*/


#include "emil.h"
short det_sket_sig = FALSE;
long  alloc_total;
struct message *rootmess = NULL;

struct config_struct *source = NULL, 
                     *target = NULL;
char **mailer = NULL;
char	* recipient   = NULL,		/* Recipient	*/
	* sender      = NULL;		/* Sender 	*/
char * rmx     = NULL;		/* Recipient's mx */
int   want_unix_from = 0;
#ifdef SMTP
int smtp_client = 0;
#endif
#ifdef	sun
extern	char	* optarg;
#elif	defined(ultrix) || defined(_AUX_SOURCE)
extern	char	* optarg;
#endif
#ifdef DEBUG
int    edebug = FALSE;
#endif

void fix_config_struct(struct config_struct *);
int process;
char	ebuf[1024];

#ifndef	ultrix
int	syslog_facility	= LOG_FACILITY;
#endif

int	header_logging	= 0;
int	syslog_logging	= 0;
int	spawn_mailer	= 0;
int     pseudo_route    = 0;

int	in_fd		= 0;
FILE	* out_fd	= stdout;
FILE	* char_fd	= NULL;
FILE	* conf_fd	= NULL;
off_t   pz;
char    * hostname = NULL;

int	main(int argc, char *argv[])
{
  
  struct data *d;
  int	c;
  int   no_run = FALSE;


#ifdef	HAVE_GETPAGESIZE
	pz = (off_t) getpagesize();
#elif	defined(hpux) || defined(_AUX_SOURCE)
	pz = 4096;
#else
	pz = sysconf(_SC_PAGESIZE);
#endif
#ifdef	ultrix
  while((c=getopt(argc, argv, "s:r:x:i:m:o:e:c:A:B:H:F:C:S:T:l:h:fpgduvn")) >= 0) {
#else
  while((c=getopt(argc, argv, "s:r:x:l:i:m:o:e:c:A:B:H:F:C:S:T:h:f:pgduvn")) >= 0) {
#endif
	switch (c) {
	case	's':	/* Sender's name */
		sender	= optarg;
		break;
	case	'r':	/* Recipient's name */
		recipient	= optarg;
		break;
	case	'x':	/* Recipient's mail exchanger address */
		rmx	= optarg;
		break;
	case	'l':	/* syslog level */
		if (optarg == NULL) {
			/* Just increment */
			syslog_logging++;
			break;
		}
		else
		  switch(*optarg) {
		  case '1': syslog_logging = 1; break;
		  case '2': syslog_logging = 2; break;
		  case '3': syslog_logging = 3; break;
		  case '4': syslog_logging = 4; break;
		  case '5': syslog_logging = 5; break;
		  default:  syslog_logging++ ; break;
		  }
		break;
	      case 'f':
#ifndef	ultrix
		if (optarg == NULL) {
			/* Just increment */
			syslog_logging++;
			break;
		}
		*optarg = tolower(*optarg);
		if (optarg) switch (*optarg) {
		case	'm':	syslog_facility	= LOG_MAIL;	break;
		case	'd':	syslog_facility	= LOG_DAEMON;	break;
		case	'0':	syslog_facility	= LOG_LOCAL0;	break;
		case	'1':	syslog_facility	= LOG_LOCAL1;	break;
		case	'2':	syslog_facility	= LOG_LOCAL2;	break;
		case	'3':	syslog_facility	= LOG_LOCAL3;	break;
		case	'4':	syslog_facility	= LOG_LOCAL4;	break;
		case	'5':	syslog_facility	= LOG_LOCAL5;	break;
		case	'6':	syslog_facility	= LOG_LOCAL6;	break;
		case	'7':	syslog_facility	= LOG_LOCAL7;	break;
		default:	
			sprintf(ebuf,"Invalid parameter to -f: %s",optarg);
#ifdef DEBUG
		  if (edebug)
		    fprintf(stderr, ebuf);
#endif
			logger(LOG_WARNING,ebuf);
		}
#endif
		break;
	case	'h':	/* Log in header */
		if (optarg == NULL) {
			/* Just increment */
			header_logging++;
			break;
		}
		else
		  switch(*optarg) {
		  case '1': header_logging = 1; break;
		  case '2': header_logging = 2; break;
		  case '3': header_logging = 3; break;
		  case '4': header_logging = 4; break;
		  case '5': header_logging = 5; break;
		  default:  header_logging++ ; break;
		  }
		break;
	      case 'm':
		mailer = get_mailer(optarg);
		if (mailer == NULL) {
		        sprintf(ebuf, "Invalid mailer specification %s", optarg);
#ifdef DEBUG
			if (edebug)
			  fprintf(stderr, ebuf);
#endif
			logger(LOG_ERR, ebuf);
			fprintf(stderr, "Emil: %s\n", ebuf);
			exit(EX_USAGE);
		}
		spawn_mailer = 1;
		break;
#ifdef SMTP
        case    'n':    /* SMTP Client */
		smtp_client = 1;
		break;
#endif
        case    'p':    /* Add loop back pseudo route */
		pseudo_route = 1;
		break;
        case    'g':    /* Just return group */
		no_run = TRUE;
		break;
#ifdef DEBUG
        case    'd':    /* Switch on debugging on stderr */
		fprintf(stderr, "*** Emil version %s ***\n", EMIL_VERSION);
		fprintf(stderr, "Processing with debug switched on.\n");
		fprintf(stderr, "(This might produce voluminous output.)\n\n");
		edebug = TRUE;
		break;
#endif
	case	'i':	/* Input file */
		in_fd = open(optarg,O_RDONLY,0);
		if (in_fd < 0) {
			logger(LOG_ERR,"Cannot open input file");
			fprintf(stderr, "Emil: Cannot open input file\n");
			exit(EX_NOINPUT);
		}
		break;
	case	'o':	/* Output file */
		out_fd	= fopen(optarg,"w");
		if (out_fd == NULL) {
			logger(LOG_ERR,"Cannot open output file");
			fprintf(stderr, "Emil: Cannot open output file\n");
			exit(EX_CANTCREAT);
		}
		break;
	case	'e':	/* Main configuration file */
		conf_fd	= fopen(optarg,"r");
		if (conf_fd == NULL) {
			logger(LOG_ERR,"Cannot open emil.cf");
			fprintf(stderr, "Emil: Cannot open emil.cf\n");
			exit(EX_OSFILE);
		}
		break;
	case	'c':	/* charsets file */
		char_fd	= fopen(optarg,"r");
		if (char_fd == NULL) {
			logger(LOG_ERR,"Cannot open charsets file");
			fprintf(stderr, "Emil: Cannot open charsets file\n");
			exit(EX_OSFILE);
		}
		break;
	case	'A':	/* applefile encoding */
		if (target == NULL)
		  target = (struct config_struct *)Yalloc(sizeof(struct config_struct));
		target->appletype = NEWSTR(optarg);
		break;
	case	'B':	/* binary encoding */
		if (target == NULL)
		  target = (struct config_struct *)Yalloc(sizeof(struct config_struct));
		target->bin = NEWSTR(optarg);
		break;
	case	'H':	/* target header encoding */
		if (target == NULL)
		  target = (struct config_struct *)Yalloc(sizeof(struct config_struct));
		target->header = NEWSTR(optarg);
		break;
	case	'T':	/* target text encoding */
		if (target == NULL)
		  target = (struct config_struct *)Yalloc(sizeof(struct config_struct));
		target->text = NEWSTR(optarg);
		break;
	case	'F':	/* target format */
		if (target == NULL)
		  target = (struct config_struct *)Yalloc(sizeof(struct config_struct));
		target->format = NEWSTR(optarg);
		break;
	case	'C':	/* recipient charset */
		if (target == NULL)
		  target = (struct config_struct *)Yalloc(sizeof(struct config_struct));
		target->charset = NEWSTR(optarg);
		break;
	case	'S':	/* source charsets */
		if (source == NULL)
		  source = (struct config_struct *)Yalloc(sizeof(struct config_struct));
		source->charset = NEWSTR(optarg);
		break;
	case    'u':
		want_unix_from = 1;
		break;
	case    'v':
		printf("emil version %s at your service\n", EMIL_VERSION);
		fflush(stdout);
		exit(EX_OK);
		break;
	default:
		sprintf(ebuf,"Invalid flag: -%c",c);
#ifdef DEBUG
			if (edebug)
			  fprintf(stderr, ebuf);
#endif
		logger(LOG_WARNING,ebuf);
	}
   }
#ifdef SMTP  
  if (smtp_client)
    {
      if (sender == NULL)
	{
	  fprintf(stderr, "Emil: Need sender to use SMTP.\n");
	  exit(EX_USAGE);
	}
      if (recipient == NULL)
	{
	  fprintf(stderr, "Emil: Need recipient to use SMTP.\n");
	  exit(EX_USAGE);
	}
      if (rmx == NULL)
	{
	  fprintf(stderr, "Emil: Need relay to use SMTP.\n");
	  exit(EX_USAGE);
	}
      spawn_mailer = 0;
      smtp_open(rmx);
    }
#endif
  if (sender == NULL && source == NULL)
    { 
#ifdef DEBUG
      if (edebug)
	fprintf(stderr, "*** No sender, No source: Check arguments.\n");
#endif
      sender = NULL;
    }
  if (sender == NULL)
    {
#ifdef DEBUG
      if (edebug)
	fprintf(stderr, "Missing -s");
#endif
      logger(LOG_ERR,"Missing -s"); 
      sender   = "unknown@unknown"; 
    }
  if (recipient == NULL && target == NULL)
    { 
#ifdef DEBUG
      if (edebug)
	fprintf(stderr, "*** No recipient, No target: Check arguments.\n");
#endif
      recipient = NULL;
    }
  if (recipient == NULL)
    {
#ifdef DEBUG
      if (edebug)
	fprintf(stderr, "Missing -r");
#endif
      logger(LOG_ERR,"Missing -r"); 
      recipient   = "unknown@unknown"; 
    } 
  if (rmx == NULL) 
    { 
#ifdef DEBUG
      if (edebug)
	fprintf(stderr, "*** No recipient host: don't worry.\n");
#endif
      logger(LOG_DEBUG,"Missing -x"); 
      rmx = "unknown";         
    }
  sprintf(ebuf,"mail from %s to %s via %s",sender,recipient,rmx);
  logger(LOG_DEBUG,ebuf);
#ifdef DEBUG
      if (edebug)
	fprintf(stderr, "Processing message from %s to %s via %s.\n",
		sender, recipient, rmx);
#endif
  if (target == NULL)
    target = (struct config_struct *) getoption(recipient, sender, rmx);

  if (no_run == TRUE)
    {
#ifdef DEBUG
      if (edebug)
	fprintf(stderr, "\nResolving target group.\n");
#endif
      if (target == NULL || target->name == NULL)
	{
#ifdef DEBUG
	  if (edebug)
	    fprintf(stderr, "*** ERROR, unable to resolve target group\n");
#endif
	  target = NULL;
	}
      else
	fprintf(stdout, "%s\n", target->name);
      fflush(stderr);
      exit(EX_OK);
    }
  if (target == NULL)
    target = (struct config_struct *)Yalloc(sizeof(struct config_struct));
  fix_config_struct(target);
  if (source == NULL)
    source = (struct config_struct *) getoption(sender, recipient, rmx); /* rmx?? smx */

  sprintf(ebuf,"sender group = %s, recipient group = %s",
		source ? (source->name ? source->name : "unknown") : "Unknown",
		target ? (target->name ? target->name : "unknown") : "Unknown"
		);
#ifdef DEBUG
      if (edebug)
	fprintf(stderr, ebuf);
#endif
  logger(LOG_DEBUG,ebuf);
  if (source == NULL)
    source = (struct config_struct *)Yalloc(sizeof(struct config_struct));

  d = (struct data *)load_data();
#ifdef DEBUG
      if (edebug)
	fprintf(stderr, "Data loaded\n");
  fflush(stderr);
#endif
  logger(LOG_DEBUG,"Data loaded");
  rootmess = (struct message *)Yalloc(sizeof(struct message));
  rootmess->sd = d;
  rootmess->sdl = d;
  rootmess->td = d;
  process = 0;
#ifdef DEBUG
  if (edebug)
    fprintf(stderr, "\n* Parsing message.\n");
#endif
  if (target->iformat == TRANSPARENT || parse_message(rootmess) == NOK)
    {
#ifdef DEBUG
      if (edebug)
	{
	  if (target->iformat == TRANSPARENT)
	    fprintf(stderr, "*** TRANSPARENT Delivery ***.\n");
	  else
	    fprintf(stderr, "*** Parse message failed.\n");
	}
#endif
      det_sket_sig = TRUE;
      if (target->iformat != TRANSPARENT)
	logger(LOG_WARNING,"Parsing failed");
      d->offset = 0;
      if (spawn_mailer) do_fork();
      	print_log();
      out_message(rootmess);
      if (spawn_mailer) end_fork();
      closelog();
#ifdef SMTP
      if (smtp_client)
	smtp_close(rmx);
#endif
      exit(EX_OK);
    }
  process = 1;
  rootmess = fix_structure(rootmess);
#ifdef DEBUG
  if (edebug)
    fprintf(stderr, "\n* Processing message.\n");
#endif
  convert_applefile(rootmess);
  if (convert_data(rootmess) == NOK)
    {
#ifdef DEBUG
      if (edebug)
	fprintf(stderr, "*** Convert data failed.\n");
#endif
      det_sket_sig = TRUE;
      logger(LOG_WARNING, "Conversion failed");
      d->offset = 0;
      if (spawn_mailer) do_fork();
  	print_log();
      out_message(rootmess);
      if (spawn_mailer) end_fork();
      closelog();
#ifdef SMTP
      if (smtp_client)
	smtp_close(rmx);
#endif
      exit(EX_OK);
    }
  if (spawn_mailer) do_fork();
  print_log();
#ifdef DEBUG
  if (edebug)
    fprintf(stderr, "\n* Output message.\n");
#endif
  if (rootmess->sd->format == RFC822)
    multipart_kludge(rootmess);
  out_message(rootmess);
#ifdef SMTP
  if (smtp_client)
    smtp_close(rmx);
#endif
  if (spawn_mailer) end_fork();
  /* end_fork returns here if not spawning: */
  closelog();
#ifdef DEBUG
      if (edebug)
	fprintf(stderr, "Exit OK.\n");
#endif

  return EX_OK;
}


void
fix_config_struct(struct config_struct *c)
{
  if (c == NULL)
    return;
#ifdef DEBUG
      if (edebug)
	fprintf(stderr, "Fix target structure, format - ");
#endif
  if (c->format != NULL)
    {
      if (cmatch(c->format, "mime") == TRUE)
	{
	  c->iformat = MIME;
#ifdef DEBUG
	  if (edebug)
	    fprintf(stderr, "mime");
#endif
	}
      else
	if (cmatch(c->format, "mailtool") == TRUE)
	  {
	    c->iformat = MAILTOOL;
#ifdef DEBUG
	    if (edebug)
	      fprintf(stderr, "mailtool");
#endif
	  }
	else
	  if (cmatch(c->format, "transparent") == TRUE)
	    {
	      c->iformat = TRANSPARENT;
#ifdef DEBUG
	      if (edebug)
		fprintf(stderr, "TRANSPARENT");
#endif
	    }
	  else
	    {
	    c->iformat = RFC822;
#ifdef DEBUG
	    if (edebug)
	      fprintf(stderr, "other (rfc822)");
#endif
	  }
    }
  else
    {
      c->iformat = RFC822;
#ifdef DEBUG
      if (edebug)
	fprintf(stderr, "unspecified (rfc822)");
#endif
    }





#ifdef DEBUG
      if (edebug)
	fprintf(stderr, ", binary - ");
#endif

  if (c->bin != NULL)
    {
      if (strncasecmp(c->bin, "ba", 2) == 0)
	{
#ifdef DEBUG
	  if (edebug)
	    fprintf(stderr, "base64");
#endif
	  c->ibin = EBASE64;
	}
      else
	{
      if (strncasecmp(c->bin, "bi", 2) == 0)
	{
#ifdef DEBUG
	  if (edebug)
	    fprintf(stderr, "binhex");
#endif
	  c->ibin = EBINHEX;
	}
      else
	{
      if (strncasecmp(c->bin, "u", 1) == 0)
	{
#ifdef DEBUG
	  if (edebug)
	    fprintf(stderr, "uuencode");
#endif
	  c->ibin = EUUENCODE;
	}
      else
	{
      if (strncasecmp(c->bin, "q", 1) == 0)
	{
#ifdef DEBUG
	  if (edebug)
	    fprintf(stderr, "quoted-printable");
#endif
	  c->ibin = EQP;
	}
      else
	{
      if (strncasecmp(c->bin, "8", 1) == 0)
	{
#ifdef DEBUG
	  if (edebug)
	    fprintf(stderr, "8bit");
#endif
	  c->ibin = E8BIT;
	}
      else
	{
#ifdef DEBUG
	  if (edebug)
	    fprintf(stderr, "other (7bit)");
#endif
	  c->ibin = E7BIT;
	}}}}}
    }
  else
    {
#ifdef DEBUG
      if (edebug)
	fprintf(stderr, "unspecified (7bit)");
#endif
      c->ibin = E7BIT;
    }
      
#ifdef DEBUG
  if (edebug)
    fprintf(stderr, ", text - ");
#endif
  if (c->text != NULL)
    {
      if (strncasecmp(c->text, "ba", 2) == 0)
	{
#ifdef DEBUG
	  if (edebug)
	    fprintf(stderr, "base64");
#endif
	  c->itext = EBASE64;
	}
      else
	{
      if (strncasecmp(c->text, "bi", 2) == 0)
	{
#ifdef DEBUG
	  if (edebug)
	    fprintf(stderr, "binhex");
#endif
	  c->itext = EBINHEX;
	}
      else
	{
      if (strncasecmp(c->text, "u", 1) == 0)
	{
#ifdef DEBUG
	  if (edebug)
	    fprintf(stderr, "uuencode");
#endif
	  c->itext = EUUENCODE;
	}
      else
	{
      if (strncasecmp(c->text, "q", 1) == 0)
	{
#ifdef DEBUG
	  if (edebug)
	    fprintf(stderr, "quoted-printable");
#endif
	  c->itext = EQP;
	}
      else
	{
      if (strncasecmp(c->text, "8", 1) == 0)
	{
#ifdef DEBUG
	  if (edebug)
	    fprintf(stderr, "8bit");
#endif
	  c->itext = E8BIT;
	}
      else
	{
      if (strncasecmp(c->text, "s", 1) == 0)
	{
#ifdef DEBUG
	  if (edebug)
	    fprintf(stderr, "SE");
#endif
	  c->itext = ESE;
	}
      else
	{
#ifdef DEBUG
	  if (edebug)
	    fprintf(stderr, "other (7bit)");
#endif
	  c->itext = E7BIT;
	}}}}}}
    }
  else
    {
#ifdef DEBUG
      if (edebug)
	fprintf(stderr, "unspecified (7bit)");
#endif
      c->itext = E7BIT;
    }
#ifdef DEBUG
  if (edebug)
    fprintf(stderr, ", appletype - ");
#endif
  if (c->appletype != NULL)
    {
      if (strncasecmp(c->appletype, "s", 1) == 0)
	{
#ifdef DEBUG
	  if (edebug)
	    fprintf(stderr, "applesingle");
#endif
	  c->iapple = ASINGLE;
	}
      else
	{
      if (strncasecmp(c->appletype, "bi", 2) == 0)
	{
#ifdef DEBUG
	  if (edebug)
	    fprintf(stderr, "binhex");
#endif
	  c->iapple = ABINHEX;
	}
      else
	{
      if (strncasecmp(c->appletype, "d", 1) == 0)
	{
#ifdef DEBUG
	  if (edebug)
	    fprintf(stderr, "appledouble");
#endif
	  c->iapple = ADOUBLE;
	}
      else
	{
#ifdef DEBUG
	  if (edebug)
	    fprintf(stderr, "other (unknown)");
#endif
	}}}
    }
  else
    {
      if (c->ibin == EBINHEX)
	{
	  c->iapple = ABINHEX;
#ifdef DEBUG
      if (edebug)
	fprintf(stderr, "unspecified (using BinHex)");
#endif
	}
#ifdef DEBUG
      if (edebug)
	fprintf(stderr, "unspecified (unknown)");
#endif
    }
#ifdef DEBUG
	  if (edebug)
	    fprintf(stderr, ", header - ");
#endif

  if (c->header != NULL)
    {
      if (strncasecmp(c->header, "ba", 2) == 0)
	{
#ifdef DEBUG
	  if (edebug)
	    fprintf(stderr, "base64.\n");
#endif
	  c->htext = EBASE64;
	}
      else
	{
      if (strncasecmp(c->header, "q", 1) == 0)
	{
#ifdef DEBUG
	  if (edebug)
	    fprintf(stderr, "quoted-printable.\n");
#endif
	  c->htext = EQP;
	}
      else
	{
      if (strncasecmp(c->header, "8", 1) == 0)
	{
#ifdef DEBUG
	  if (edebug)
	    fprintf(stderr, "8bit.\n");
#endif
	  c->htext = E8BIT;
	}
      else
	{
      if (strncasecmp(c->header, "s", 1) == 0)
	{
#ifdef DEBUG
	  if (edebug)
	    fprintf(stderr, "SE.\n");
#endif
	  c->htext = ESE;
	}
      else
	{
#ifdef DEBUG
	  if (edebug)
	    fprintf(stderr, "other (7bit).\n");
#endif
	  c->htext = E7BIT;
	}}}}
    }
  else
    {
#ifdef DEBUG
      if (edebug)
	fprintf(stderr, "unspecified (7bit).\n");
#endif
      c->htext = E7BIT;
    }
}
