#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <strings.h>
#include <stdio.h>
#include <ctype.h>
#include "converse.h"

#define F_NOTIFICATION 1

struct recipient {
  char *r_name;
  struct hostent *r_host;
} *parse_address();

char hostname[BUFSIZ];

main (argc, argv)
     int argc;
     char **argv;
{
  int i, j, flags = 0;
  struct recipient **recpt;
  char **msg, **get_message(), *cp;
  struct hostent *hp;

  /* argv handling belongs here.  command line may include: -n for notification,
     -m for all following is message, and addresses.  Currently only user@host
     addresses are accepted.  If no args, act as reply. */


  if (argc < 2)
    usage();

  /* Look up hostname just once */
  if (gethostname(hostname, BUFSIZ) == -1) {
    perror("gethostbyname");
    exit(1);
  }
  if ((hp = gethostbyname(hostname)) != NULL)
    strcpy(hostname, hp->h_name);

#ifdef notdef
  i = gethostid();
  if ((hp = gethostbyaddr(&i, sizeof(i), AF_INET)) == NULL)
    cp = inet_ntoa(i);
  else
    cp = hp->h_name;
  strcpy(hostname, cp);
#endif notdef

  /* Collect recipients */
  recpt = (struct recipient **) calloc(argc-1, sizeof(struct recipient *));
  for (i = 1, j = 0; i < argc; i++)
    if ((recpt[j] = parse_address(argv[i])) != NULL)
      j++;

  /* Get a message */
  msg = get_message();

  /* Send the message */
  for (i = 0; i < j; i++) {
    printf("[%s@%s", recpt[i]->r_name, recpt[i]->r_host->h_name);
    fflush(stdout);
    if (deliver_message(recpt[i], msg, flags))
      printf("  OK]\n");
  }

  exit();
}

usage ()
{

  fprintf(stderr, "usage: converse [-n] [-m message] user@host [user1@host1] ...\n");
  exit (-1);
}

/* This could be extended to understand user%host@relay
   and @relay:user@host syntax */
struct recipient *parse_address (addr)
     char *addr;
{
  int i;
  char *cp;
  struct hostent *hp;
  struct recipient *recpt;

  if ((cp = index(addr, '@')) == NULL) {
    fprintf(stderr, "%s: No '@'\n", addr);
    return (NULL);
  }

  /* Look up the host name first, before we tear apart the address */
  if ((hp = gethostbyname(cp+1)) == NULL) {
#ifdef INET_ADDRESS_PARSING
	if (hp == NULL) {
		static struct hostent def;
		static struct in_addr defaddr;
		static char *alist[1];
		static char namebuf[128];
		int inet_addr();

		defaddr.s_addr = inet_addr(host);
		if (defaddr.s_addr == -1) {
			printf("unknown host: %s\n", host);
			return (1);
		}
		strcpy(namebuf, host);
		def.h_name = namebuf;
		def.h_addr_list = alist, def.h_addr = (char *)&defaddr;
		def.h_length = sizeof (struct in_addr);
		def.h_addrtype = AF_INET;
		def.h_aliases = 0;
		hp = &def;
	}

#endif INET_ADDRESS_PARSING
    fprintf(stderr, "%s: %s: No such host\n", addr, cp+1);
    return (NULL);
  }

  /* Allocate memory for the returned structure, and start filling it in */
  recpt = (struct recipient *) calloc(1, sizeof(struct recipient));
  *cp = '\0';
  recpt->r_name = addr;

  /* Copy hostent structure, since it will get womped by the next lookup */
  recpt->r_host = (struct hostent *) calloc (1, sizeof(struct hostent));
  recpt->r_host->h_name = (char *) calloc(strlen(hp->h_name), sizeof(char));
  strcpy(recpt->r_host->h_name, hp->h_name);
  recpt->r_host->h_aliases = NULL;
  recpt->r_host->h_addrtype = hp->h_addrtype;
  recpt->r_host->h_length = hp->h_length;
  for (i = 0; hp->h_addr_list[i] != NULL; i++);
  recpt->r_host->h_addr_list = (char **) calloc(i, sizeof(char *));
  for (i = 0; hp->h_addr_list[i] != NULL; i++) {
    recpt->r_host->h_addr_list[i] = (char *) calloc(hp->h_length, sizeof(char));
    bcopy(hp->h_addr_list[i], recpt->r_host->h_addr_list[i], hp->h_length);
  }
    
  return(recpt);
}

char **get_message()
{
  char **msg = NULL, buf[BUFSIZ];
  int size = 0, n = 0, len;

  puts("Msg:");

  while (fgets(buf, BUFSIZ, stdin) != NULL) {
    
    /* n > size-1 only on initialization */
    if (n >= size-1) {
      /* allocate enough for 10 more lines */
      size += 10;
      if (msg == NULL)
	msg = (char **) calloc(size, sizeof(char *));
      else
	msg = (char **) realloc(msg, size * sizeof(char *));
    }

    /* Each line is big enough for the text and a null.
     The terminating character (usually a newline) gets replaced with CHNL */
    len = strlen(buf);
    msg[n] = (char *) calloc(len+1, sizeof(char));
    strcpy(msg[n], buf);
    msg[n][len-1] = '\215';
    msg[n][len] = '\0';

    n++;
  }

  return(msg);
}

deliver_message(recpt, msg, flags)
     struct recipient *recpt;
     char **msg;
     int flags;
{
  /* Should try CHAOS SEND for CHAOS hosts
     No point in falling back on TCP SMTP, since only LispMs
     seem to implement TCP SMTP SEND anyway, and they're Conversers */
  deliver_via_converse(recpt, msg, flags);
}

deliver_via_converse(recpt, msg, flags)
     struct recipient *recpt;
     char **msg;
     int flags;
{
  struct servent *serv;
  struct sockaddr_in sin;
  char buf[BUFSIZ];
  int s, hostlen=BUFSIZ, retval = 0, i;
  FILE *ofp;

  if ((serv = getservbyname("converse", "tcp")) == NULL) {
    fprintf(stderr, "\nconverse/tcp: No such service\n");
#ifndef CONVERSE_PORT
    return(0);
#endif
  }

  if ((s = socket(recpt->r_host->h_addrtype, SOCK_STREAM, 0)) < 0) {
    perror("\nsocket");
    return(0);
  }

  sin.sin_family = recpt->r_host->h_addrtype;
  bcopy(recpt->r_host->h_addr, (char *) &sin.sin_addr, recpt->r_host->h_length);
#ifdef CONVERSE_PORT
  sin.sin_port = htons(CONVERSE_PORT);
#else
  sin.sin_port = serv->s_port;
#endif

  if (connect(s, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
    perror("\nconnect");
    close(s);
    return(0);
  }

  /* Get a FILE pointer */
  if ((ofp = fdopen(s, "w")) == NULL) {
    perror("fdopen ofp");
    return(0);
  }

  /* Let the user know we made a connection */
  fputs("...", stdout);
  fflush(stdout);

  /* Send the header information.  Order and case are unimportant.
     We should also supply the "also" field. */
  fprintf(ofp, "%s %s@%s\215",
	  converse_kwds[CONVERSE_FROM], getlogin(), hostname);
  fprintf(ofp, "%s %s\215",
	  converse_kwds[CONVERSE_TO], recpt->r_name);
  { /* date handling */
    char *cp, *ctime();
    int now;

    now = time(0);
    cp = ctime(&now);
    cp[strlen(cp)-1] = '\0';
    fprintf(ofp, "%s %s\215",
	    converse_kwds[CONVERSE_DATE], cp);
  }
  if (flags & F_NOTIFICATION)
    fprintf(ofp, "%s Yes",
	    converse_kwds[CONVERSE_NOTIFICATION]);
  fprintf(ofp, "\215");
  fflush(ofp);

  /* Read the reply.  The first character is either '+' - go ahead,
     '%' - message will be misdelivered but you may still send it,
     or '-' - go away.  Rest of line is a message */
  if (read(s, buf, BUFSIZ) < 1) {
    perror("\nread");
    close(s);
    fclose(ofp);
    return(0);
  }

  for (i = strlen(buf)-1; isspace(buf[i]); i--);
  buf[i+1] = '\0';

  switch (buf[0]) {
  case '-':
    printf(" - %s - Message not sent]\n", buf+1);
    retval = 0;
    break;

  case '%':
    /* Should ask, but heck */
    printf(" - %s - Sending anyway...", buf+1);
    /* Fall through to send */

  case '+':
    for (i = 0; msg[i] != NULL; i++)
      fputs(msg[i], ofp);
    fflush(ofp);
    retval = 1;
    break;

  default:
    printf(" - Unexpected %c in %s - Message not sent]\n", buf[0], buf);
    retval = 0;
    break;

  }
  close(s);
  fclose(ofp);
  return(retval);
}
