/* mail.c -- SMTP interface for GNU finger */

/* Copyright (C) 1992 Free Software Foundation, Inc.

   This file is part of GNU Finger.

   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, 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.  */

#include <config.h>
#include <stdio.h>
#include <sys/types.h>

#include <netdb.h>
#include <netinet/in.h>

#if !defined (hpux)
#include <arpa/inet.h>
#endif

#include <tcp.h>

#include <general.h>
#include <fingerpaths.h>
#include <util.h>


#define FORWMSG "Mail forwarded to "
#define DISPWIDTH 50


extern char *progname;

/* Return a new string which is the name of the SMTP server host.  If
   this cannot be determined, return a NULL pointer instead.  Single
   argument ERROR_STREAM is a stream to print errors to; a NULL value
   for this argument means print no errors. */

static char *
getsmtphost (error_stream)
  FILE *error_stream;
{
  char *smtphost;
  FILE *stream;
  char *buffer = NULL;
  int buffer_size = 0;
  int nchars;


  stream = fopen (SMTPHOSTFILE, "r");

  if (!stream)
    {
      if (error_stream)
	fprintf (error_stream, "%s: missing server file `%s'\n",
		 progname, SMTPHOSTFILE);

      return ((char *)NULL);
    }

  /* Read lines from this file until we find the server host's name. */
  while ((nchars = getline (&buffer, &buffer_size, stream)) >= 0)
    {
      register char *t, *t1;

      t = buffer;

      while (*t && whitespace (*t))
	t++;

      if (*t == '#')
	continue;

      t1 = t;

      while (*t1 && !whitespace (*t1))
	t1++;

      *t1 = '\0';

      smtphost = savestring (t);
      break;
    }

  fclose (stream);

  if (nchars < 0)
    {
      if (error_stream)
	fprintf (error_stream, "%s: invalid contents of server file `%s'\n",
		 progname, SMTPHOSTFILE);

      if (buffer)
	free (buffer);

      return ((char *)NULL);
    }

  if (buffer)
    free (buffer);

  return (smtphost);
}


/* Like FGETS, except reads until CR-LF. Doesn't return anything,
   either. Returns on LF, removes all occurences of CR (the LF is also
   removed). */

static void
xfgets (buf, bufsize, stream)
  char *buf;
  int bufsize;
  FILE *stream;
{
  register char *cp;

  for (cp = buf; cp < buf + bufsize - 1; cp++)
    switch (*cp = fgetc (stream))
      {
      case '\n':

	*cp = 0;
	return;

      case '\r':
	cp--;
	break;

#if 0
      default:

	fprintf (stdout, "%c", *cp); fflush (stdout);
#endif
      }

  buf[bufsize-1] = 0;
}


/* Print forwarding address of USER on STREAM. Zero is returned and
   nothing printed if no forwarding has been set up. */

int
mail_expand (login_name, stream)
  char *login_name;
  FILE *stream;
{
  FILE *smtpin, *smtpout;
  int sockfd, i, is_first, success = 0, column;
  long addr;
  char rbuf[1024], *smtphost;
  struct hostent *host;


  /* Get SMTP host name */
  if (!(smtphost = getsmtphost (NULL)))
    return 0;

  /* Connect to SMTP */
  if (digit (*smtphost))
    {
      addr = (long)inet_addr (smtphost);
      host = gethostbyaddr (&addr, 4, AF_INET);
    }
  else
    host = gethostbyname (smtphost);

  if (!host)
    return 0;
      
  bcopy (host->h_addr, &addr, 4);

  if ((sockfd = tcp_to_service ("smtp", (char *)&addr)) < 0)
    return 0;

  smtpin = fdopen (sockfd, "r");
  smtpout = fdopen (dup (sockfd), "w");

  xfgets (rbuf, sizeof rbuf, smtpin);

  /* Send EXPN packet and print response to STREAM */
  fprintf (smtpout, "EXPN %s\n", login_name);
  fflush (smtpout);

  xfgets (rbuf, sizeof rbuf, smtpin);

  /* The inet addr seems to always be of the form "250.+<...>"  */
  if (rbuf[0] == '2' && rbuf[1] == '5' && rbuf[2] == '0' && rbuf[3])
    for (is_first = 1, column = 0;;)
      {
	if (rbuf[strlen(rbuf)-1] == '>')
	  {
	    char *mailaddr;
	    int tailpos = strlen (rbuf) - 1;
	    
	    rbuf[tailpos] = 0;
	    
	    for (mailaddr = rbuf + tailpos;
		 mailaddr > rbuf+4 && *mailaddr != '<'; mailaddr--);
	    
	    if (strcmp (++mailaddr, login_name))
	      {
		/* Current position plus: address plus */
		int need_nl = column + strlen (mailaddr) +

		  /* if first entry, then title, plus */
		  (is_first ? strlen (FORWMSG) : 0) +

		    /* if contination line, then comma, otherwise dot */
		    (rbuf[3] == '-' ? 2 : 1) > DISPWIDTH;

		column += fprintf (stream, "%s%s%s",
				   is_first ? FORWMSG : "", mailaddr,
				   rbuf[3] == '-' ? (need_nl ? ",\r\n    " : ", ") : ".\r\n");

		/* Need to fix the column counter for NL'ed continuation lines */
		if (rbuf[3] == '-' && need_nl)
		  column = 4;
 
		success = 1;
		is_first = 0;
	      }
	  }
	else
	  {
	    /* Don't know how record is formatted - just print it in its
	       entirety. */

	    /* Current position plus: address plus */
	    int need_nl = column + strlen (rbuf+4) +
	      
	      /* if first entry, then title, plus */
	      (is_first ? strlen (FORWMSG) : 0) +
		
		/* if contination line, then comma, otherwise dot */
		(rbuf[3] == '-' ? 2 : 1) > DISPWIDTH;
	    
	    column += fprintf (stream, "%s%s%s",
			       is_first ? FORWMSG : "", rbuf+4,
			       rbuf[3] == '-' ? (need_nl ? ",\r\n    " : ", ") : ".\r\n");
	    
	    /* Need to fix the column counter for NL'ed continuation lines */
	    if (rbuf[3] == '-' && need_nl)
		  column = 4;
	    
	    success = 1;
	    is_first = 0;
	  }
	
	if (rbuf[3] == '-')
	  xfgets (rbuf, sizeof rbuf, smtpin);
	else
	  break;
      }

  fclose (smtpin);
  fclose (smtpout);
  return success;
}


/* List expansion of ALIAS on STREAM. Zero is returned and nothing
   printed if no matching alias exists. Otherwise the expansion is
   printed and a non-zero result returned. */

int
mail_list (alias, stream)
  char *alias;
  FILE *stream;
{
  FILE *smtpin, *smtpout;
  int sockfd, i;
  long addr;
  char rbuf[1024], *smtphost;
  struct hostent *host;


  /* Get SMTP host name */
  if (!(smtphost = getsmtphost (NULL)))
    return 0;

  /* Connect to SMTP */
  if (digit (*smtphost))
    {
      addr = (long)inet_addr (smtphost);
      host = gethostbyaddr (&addr, 4, AF_INET);
    }
  else
    host = gethostbyname (smtphost);

  if (!host)
    return 0;
      
  bcopy (host->h_addr, &addr, 4);

  if ((sockfd = tcp_to_service ("smtp", (char *)&addr)) < 0)
    return 0;

  smtpin = fdopen (sockfd, "r");
  smtpout = fdopen (dup (sockfd), "w");

  /* Ditch SMTP greeting */
  xfgets (rbuf, sizeof rbuf, smtpin);

  /* Send EXPN packet and print response to STREAM */
  fprintf (smtpout, "EXPN %s\n", alias);
  fflush (smtpout);

  xfgets (rbuf, sizeof rbuf, smtpin);

  /* Loop responses */
  if (rbuf[0] == '2' && rbuf[1] == '5' && rbuf[2] == '0' && rbuf[3])
    {
      if (rbuf[3] == ' ')
	fprintf (stream, "The alias %s expands to %s.\r\n", alias, rbuf+4);
      else
	{
	  fprintf (stream, "%s is an alias for the following:\r\n", alias);

	  for (;;)
	    {
	      fprintf (stream, "    %s%s\r\n", rbuf+4, rbuf[3] == '-' ? "," : "\r\n");

	      if (rbuf[3] == '-')
		xfgets (rbuf, sizeof rbuf, smtpin);
	      else
		break;
	    }
	}
    }
  else
    {
      /* No such alias */
      fclose (smtpin);
      fclose (smtpout);
      return 0;
    }

  fclose (smtpin);
  fclose (smtpout);
  return 1;
}
