/*
 ===========================================================================
 =                                                                         =
 =  (C) Copyright 1991 The Trustees of Indiana University                  =
 =                                                                         =
 =  Permission to use, copy, modify, and distribute this program for       =
 =  non-commercial use and without fee is hereby granted, provided that    =
 =  this copyright and permission notice appear on all copies and          =
 =  supporting documentation, the name of Indiana University not be used   =
 =  in advertising or publicity pertaining to distribution of the program  =
 =  without specific prior permission, and notice be given in supporting   =
 =  documentation that copying and distribution is by permission of        =
 =  Indiana University.                                                    =
 =                                                                         =
 =  Indiana University makes no representations about the suitability of   =
 =  this software for any purpose. It is provided "as is" without express  =
 =  or implied warranty.                                                   =
 =                                                                         =
 ===========================================================================
 =                                                                         =
 = File:                                                                   =
 =   POPSTAT.C - Version 1.0                                               =
 =                                                                         =
 = Date:                                                                   =
 =   April, 1992                                                           =
 =                                                                         =
 = Synopsis:                                                               =
 =   Command line utility for UNIX and VMS, to gather IUPOP3 statistics.   =
 =                                                                         =
 = Authors:                                                                =
 =   Larry Hughes                                                          =
 =   Indiana University                                                    =
 =   University Computing Services, Network Applications                   =
 =                                                                         =
 ===========================================================================
*/

/* ======================================================================== */
/* Includes */
/* ======================================================================== */
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <netdb.h>
#include <errno.h>
#include <ctype.h>

/* ======================================================================== */
/* Defines */
/* ======================================================================== */
#define TRUE  1
#define FALSE 0

#define POP_PORT 110

#define BUFFERSIZE 256 

#define POP_ACK  "+OK"
#define POP_NAK  "-ERR"

#define CRLF "\r\n"

#define ExitStatus(x) if (exit_status == EXIT_NORMAL) exit_status = x;

enum exits 
{
  EXIT_NORMAL,     /* normal exit */
  EXIT_USAGE,      /* printed usage then exited */
  EXIT_HOST,       /* invalid server host name */
  EXIT_SOCKET,     /* socket creation failed */
  EXIT_CONNECT,    /* connect failed */
  EXIT_AUTH,       /* Kerberos authentication failed */
  EXIT_NETREAD,    /* network read failed */
  EXIT_NETWRITE,   /* network write failed */
  EXIT_FOPEN,      /* fopen on mail file failed */
  EXIT_NAK,        /* server NAK */
};

#ifdef UCX
#define netread  read
#define netwrite write
#define netclose close
#endif UCX

#ifdef MULTINET
#define netread  socket_read
#define netwrite socket_write
#define netclose socket_close
#endif MULTINET

#ifndef VMS
#define netread  read
#define netwrite write
#define netclose close
#endif VMS

/* ======================================================================== */
/* Global Variables */
/* ======================================================================== */
int pop_socket;
int exit_status = EXIT_NORMAL;

int debug = FALSE;

char host[65]     = "\0";
char username[32] = "\0";
char password[32] = "\0";

char *transport   = "tcp";

char *service     = "pop3";

int  num_tokens;
char *tokens[64];

/* ======================================================================== */
/* Prototypes */
/* ======================================================================== */
int  connect_to_server();
int  main();
void make_argv();
int  net_readln();
int  net_writeln();
int  pop_ack();
int  pop_command();
void pop_converse();
int  pop_xtnd_stats();

/* ======================================================================== */
/* Connect to Server */
/* ======================================================================== */
int connect_to_server(port)
int port;
{
  int count = 0;
  int connected = FALSE;
  struct sockaddr_in raddr;
  struct hostent *hp;

  hp = gethostbyname(host);
  if (hp == NULL)
  {
    printf("%s: host unknown\n", host);
    ExitStatus(EXIT_HOST);
  }
  else
  {
    if ((pop_socket = socket(hp->h_addrtype, SOCK_STREAM, 0)) < 0)
    {
      perror("socket");
      ExitStatus(EXIT_SOCKET);
    }
    else
    {
      raddr.sin_family = hp->h_addrtype;
      if (port == 0)
        raddr.sin_port = htons(POP_PORT);
      else
        raddr.sin_port = htons(port);
 
      while (!connected && hp->h_addr_list[count])
      {
        memcpy(&raddr.sin_addr, hp->h_addr_list[count], hp->h_length);
        if (debug) printf("Trying %s\n", inet_ntoa(&raddr.sin_addr));
        if (connect(pop_socket,(struct sockaddr *)&raddr,sizeof(raddr)) == 0) 
          connected = TRUE;
        else
          count++;
      }
      if (!connected)
      {
        perror("connect");
        ExitStatus(EXIT_CONNECT);
      }
    }
  }
  return(connected);
}

/* ======================================================================= */
/* Make Argv */
/* ======================================================================= */
void make_argv(string, argc, argv, delimiters)
char *string;
int *argc;
char *argv[];
char *delimiters;
{
#define isdelimiter(a,b) (strchr(b,a) != NULL)
  register char *cpointer;
  register char **argpointer = argv;

  *argc = 0;
  cpointer = string;
  while (*cpointer)
  {
    while (*cpointer && isdelimiter(*cpointer, delimiters)) cpointer++;
    if (*cpointer == '\0') break;
    if (*cpointer == '\"')
    {
      cpointer++;
      *argpointer++ = cpointer;
      (*argc)++;
      while (*cpointer && (*cpointer != '\"')) cpointer++;
    }
    else
    {
      *argpointer++ = cpointer;
      (*argc)++;
      while (*cpointer && !isdelimiter(*cpointer, delimiters)) cpointer++;
    }
    if (*cpointer == '\0') break;
    *(cpointer++) = '\0';
  }
  *(argpointer++) = 0;
}

/* ======================================================================== */
/* Net Read Line */
/* ======================================================================== */
int net_readln(socket, buffer)
int socket;
char *buffer;
{
  int rval    = 0;
  int count   = 0;
  int reading = TRUE;
  int status  = FALSE;
  int term_size;
  char *terminators = CRLF;

  term_size = strlen(terminators);
  while (reading)
  {
    rval = netread(socket, (buffer+count), 1);
    if (rval <= 0)
    {
      printf("netread failed\n");
      ExitStatus(EXIT_NETREAD);
      reading = FALSE;
    }
    else
    {
      *(buffer + (++count)) = '\0';
      if (count >= term_size)
      {
        if (strcmp(buffer+count-term_size, terminators) == 0)
        {
          *(buffer+count-term_size) = '\0';
          reading = FALSE;
          status  = TRUE;
        }
      }
    }
  }
  if (debug) printf("%s\n",buffer);
  return(status);
}

/* ======================================================================== */
/* Net Write Line */
/* ======================================================================== */
int  net_writeln(socket, buffer)
int socket;
char *buffer;
{
  int size;
  int status = FALSE;

  if (debug) printf("%s",buffer);
  size = strlen(buffer);
  if (netwrite(socket, buffer, size) == size) 
    status = TRUE;
  else
  {
    printf("netwrite failed\n");
    ExitStatus(EXIT_NETWRITE);
  }
  return(status);
}

/* ======================================================================== */
/* POP Acknowledge */
/* ======================================================================== */
int pop_ack(buffer)
char *buffer;
{
  int  retval = FALSE;
  char copy[256];

  num_tokens = 0;
  if (net_readln(pop_socket, buffer))
  {
    strcpy(copy, buffer);
    make_argv(copy, &num_tokens, tokens, " ");
    if (num_tokens > 0)
    {
      if (strcmp(tokens[0], POP_ACK) == 0) retval = TRUE;
    }
  }
  return(retval);
}

/* ======================================================================== */
/* POP Command */
/* ======================================================================== */
int pop_command(command)
char *command;
{
  int  ok = TRUE;
  int  retval = FALSE;
  char buffer[BUFFERSIZE];

  if (*command) ok = net_writeln(pop_socket, command);
  if (ok)
  {
    memset(buffer, '\0', BUFFERSIZE);
    if (pop_ack(buffer))
      retval = TRUE;
    else
    {
      printf("%s\n", buffer);
      ExitStatus(EXIT_NAK);
    }
  }
  return(retval);
}

/* ======================================================================== */
/* POP Converse */
/* ======================================================================== */
void pop_converse()
{
  enum pop_states 
    { POP_USER, POP_PASS, POP_XTND_STATS, POP_QUIT };

  int  state = POP_USER;
  int  status;
  char buffer[BUFFERSIZE];

  status = pop_command("\0");
  while ((state <= POP_QUIT) && status)
  {
    switch (state++)
    {
      case POP_USER:
        sprintf(buffer, "USER %s\r\n", username);
        status = pop_command(buffer);
        break;

      case POP_PASS:
        sprintf(buffer, "PASS %s\r\n", password);
        status = pop_command(buffer);
        break;

      case POP_XTND_STATS:
        if (status = pop_command("XTND STATS\r\n"))
          pop_retrieve_stats();
        break;

      case POP_QUIT: 
      default:
        status = pop_command("QUIT\r\n");
        break;
    }
  }
}

/* ======================================================================== */
/* POP Retrieve Stats */
/* ======================================================================== */
int pop_retrieve_stats()
{
  int  retval     = TRUE;
  int  retrieving = TRUE;
  char buffer[BUFFERSIZE];

  while (retrieving)
  {
    memset(buffer, '\0', BUFFERSIZE);
    if (!net_readln(pop_socket, buffer))
    {
      retrieving = FALSE;
      retval = FALSE;
    }
    else
    {
      if (strcmp(buffer, ".") == 0) 
        retrieving = FALSE;
      else 
        printf("%s\n", buffer);
    }
  }
  return(retval);
}

/* ======================================================================== */
/* Main */
/* ======================================================================== */
int main(argc, argv)
int argc;
char *argv[];
{
  int bad_option = FALSE;
  int port = 0;

  while (argc > 1)
  {
    argc--; argv++;
    if (argv[0][0] != '-')
      bad_option = TRUE;
    else
    {
      switch (argv[0][1])
      {
        case 'D': case 'd':
          debug = TRUE;
          break;

        case 'H': case 'h':
          argc--; argv++;
          if (argc > 0) strcpy(host, argv[0]);
          break;

        case 'U': case 'u':
          argc--; argv++;
          if (argc > 0) strcpy(username, argv[0]);
          break;

        case 'P': case 'p':
          argc--; argv++;
          if (argc > 0) strcpy(password, argv[0]);
          break;

        case 'X': case 'x':
          argc--; argv++;
          if (argc > 0) port = atoi(argv[0]);
          break;

        default:
          bad_option = TRUE;
          break;
      }
    }
  }

  if ((!*host) || (!*username) || (!*password) || bad_option)
  {
    printf("Usage: popstat -h host -u user -p password [-x port] [-d]\n");
    ExitStatus(EXIT_USAGE);
  }
  else
  {
    if (connect_to_server(port))
    {
      pop_converse();
      close(pop_socket);
    }
  }

  if (debug) 
    printf("Exiting with exit_status == %d, errno == %d\n", exit_status, errno);

  exit(exit_status);
}
