/*
 * *******************************************************************
 * *	                                                             *
 * *       IRIS User Product Insert receiver example program         *
 * *                                                                 *
 * *******************************************************************
 * File: utils/examples/upi_rcv.C
 *
 *              COPYRIGHT (c) 1996, 1997, 2002, 2003  BY
 *         SIGMET INCORPORATED, WESTFORD MASSACHUSETTS, U.S.A.
 * 
 * THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY BE USED AND  COPIED
 * ONLY  IN  ACCORDANCE WITH  THE  TERMS  OF  SUCH  LICENSE  AND WITH THE
 * INCLUSION OF THE ABOVE COPYRIGHT NOTICE.  THIS SOFTWARE  OR  ANY OTHER
 * COPIES  THEREOF MAY NOT BE PROVIDED OR OTHERWISE MADE AVAILABLE TO ANY
 * OTHER PERSON.  NO TITLE TO AND OWNERSHIP OF  THE  SOFTWARE  IS  HEREBY
 * TRANSFERED.
 */

#include <ctype.h>
#include <errno.h>
#include <locale.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#ifndef LINUX
#include <string.h>
#endif  /* #ifndef LINUX */

#include <unistd.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/un.h>

#include "sigtypes.h"
#include "signetwork.h"

#define MAX_PACKET_SIZE (90)
#define ADDRESS_FAMILY_INET (0)
#define ADDRESS_FAMILY_UNIX (1)

struct command_line
{
  char sDirectory[DIRECTORY_SIZE];
  char sHost[MAXHOSTNAMELEN];
  unsigned int iPort;
  UINT1 lDeleteFile;
  UINT1 iAddress;
};

/* Function prototypes in the network receiver. */

void upi_process_file
(const char *sfile_a,
 const struct command_line *pCommand_a
 );

static void run_tcpip
( const struct command_line *pCommand_a
  );

static void upi_handle_one
( int socket_a,
  const struct command_line *pCommand_a );

static void process_one_packet
(char *sone_packet_a,
 const struct command_line *pCommand_a );

static void ProcessCommandLine
( struct command_line *pCommand_a,
  int iArgc_a,
  char **sArgv_a );

/* Shared variables */

static struct command_line Command_c;

/* =====================================================
 * This is an example program showing how to receive files from IRIS network
 * output.  This program prints out a message every time a file is received, and
 * deletes the file. 
 *
 */
int main( int argc_a, char** argv_a )
{

  /* ------------------------------------------------------------------- */

  setlocale( LC_ALL, "" );

  /* Process command line arguments */
  ProcessCommandLine( &Command_c, argc_a, argv_a );

  run_tcpip( &Command_c );

  exit(EXIT_SUCCESS) ;
}

/* ===========================================
 * Run with specified addressing and port number.  This code is somewhat complicated
 * in order to support both AF_INET and AF_UNIX addressing.
 */
static void run_tcpip
( const struct command_line *pCommand_a
   )
{
  int ifamily, sockfd, newsockfd, iserver_addr_size, ierror;
#ifdef LINUX
  socklen_t iclient_addr_size;
#else
  int iclient_addr_size;
#endif
  struct sockaddr_un client_addr_un;
  struct sockaddr_un server_addr_un;
  struct sockaddr_in client_addr_in;
  struct sockaddr_in server_addr_in;
  struct sockaddr *pserver_addr;
  struct sockaddr *pclient_addr;

  /* ------------------------------------------ */

  if (pCommand_a->iAddress == ADDRESS_FAMILY_UNIX)
    {
      char *sptr;
      int ilength;
      sptr = getenv( "IRIS_KEYS" );
      if (!sptr)
	{
	  printf( "Environment variable 'IRIS_KEYS' not defined.\n" );
	  return;
	}
      ifamily = AF_UNIX;
      memset( &server_addr_un, 0, sizeof(server_addr_un) );
      server_addr_un.sun_family = ifamily;
      sprintf( server_addr_un.sun_path, "%siris_port%d", sptr, pCommand_a->iPort );

      /* In case file left over from last time */
      ierror = remove( server_addr_un.sun_path );
      if (ierror)
	{
	 if (errno != ENOENT) perror( "remove" );
	}

      ilength = strlen( server_addr_un.sun_path );
      pserver_addr = (struct sockaddr *)(void *)&server_addr_un;

      iserver_addr_size = sizeof(server_addr_un) - sizeof(server_addr_un.sun_path) + ilength;
#ifdef AIX
      server_addr_un.sun_len = iserver_addr_size;
#endif

      pclient_addr = (struct sockaddr *)(void *)&client_addr_un;
      iclient_addr_size = sizeof(client_addr_un);
    }
  else
    {
      ifamily = AF_INET;
      memset( &server_addr_in, 0, sizeof(server_addr_in));
      server_addr_in.sin_family = ifamily;
      server_addr_in.sin_addr.s_addr = htonl(INADDR_ANY);
      server_addr_in.sin_port = htons(pCommand_a->iPort);
      pserver_addr = (struct sockaddr *)(void*)&server_addr_in;
      iserver_addr_size = sizeof(server_addr_in);

      pclient_addr = (struct sockaddr *)(void*)&client_addr_in;
      iclient_addr_size = sizeof(client_addr_in);
    }
  sockfd = socket(ifamily,SOCK_STREAM,0);
  if (sockfd == -1)
    {
      perror( "socket" );
      return;
    }

  /* We now have a valid opened socket, it must be closed if we exit */

  /* This example only handles a single connection at a time.  Multiple
   * connections are possible, but it requires forks.
   */
  ierror = bind(sockfd, pserver_addr, iserver_addr_size );
  if (ierror == 0)
    {
      ierror = listen(sockfd, 5 );
      if (ierror != -1)
	{
	  printf( "Listening on port %d\n", pCommand_a->iPort );
	  while( TRUE )
	    {
	      newsockfd = accept(sockfd, pclient_addr, &iclient_addr_size);
	      if (newsockfd != -1)
		{
		  printf( "Connection established.\n" );

		  upi_handle_one( newsockfd, pCommand_a );
		  
		  ierror = close( newsockfd );
		  if (ierror == -1)
		    {
		      perror( "close newsockfd" );
		    }
		  printf( "Connection closed.\n" );
		}
	      else
		{
		  perror( "accept" );
		}
	    }  /* end while */

	}
      else
	{
	  perror( "listen" );
	}
    }
  else
    {
      ierror = errno;
      if (ierror == EADDRINUSE)
	printf( "Address is still in use, try waiting 1 minute.\n" );
      else
	perror( "bind" );
    }

  /* Close the socket before returning */
  ierror = close( sockfd );
  if (ierror == -1)
    {
      perror( "close" );
    }

  return;

}

/* ==============================================================
 * Handle the communication from one other system.  This routine will
 * return when the communication is disconnected.
 */
static void upi_handle_one
( int socket_a,
  const struct command_line *pCommand_a
  )
{
  int irecv_size;
  char sbuffer[MAX_PACKET_SIZE], sone_packet[MAX_PACKET_SIZE+1], *ptr;
  int ibuffer_offset, imax_read_size, isize;


  ibuffer_offset = 0;
  while( TRUE )
    {
      imax_read_size = MAX_PACKET_SIZE - ibuffer_offset;
      irecv_size = recv( socket_a, &sbuffer[ibuffer_offset], imax_read_size, 0);
      if( irecv_size > 0 )
	{
	  /* This means that some data was received.  Process all packets in
	   * the buffer.  Each packet ends in a zero, and there are no zeros
	   * within packets.
	   */
	  ibuffer_offset += irecv_size;

	  /* Loop through, processing all the individual packet contained 
	   * within the buffer
	   */
	  ptr = (char *)memchr( sbuffer, 0, ibuffer_offset);
	  while( ptr )
	    {
	      ptr++;
	      isize = (ptr-sbuffer);
	      memmove( sone_packet, sbuffer, isize );
	      process_one_packet( sone_packet, pCommand_a );

	      /* Flush packet from buffer */
	      ibuffer_offset -= isize;
	      if (ibuffer_offset > 0)
		{
		  memmove( sbuffer, ptr, ibuffer_offset );
		  ptr = (char *)memchr( sbuffer, 0, ibuffer_offset);
		}
	      else
		{
		  ptr = NULL;
		}
	    }

	  /* If the buffer is full at this point, then we have a buffer overflow.
	   * Signal it, then flush the contents.
	   */
	  if (ibuffer_offset >= MAX_PACKET_SIZE)
	    {
	      /* Copy the packet and add trailing zero */
	      isize = MAX_PACKET_SIZE;
	      memmove( sone_packet, sbuffer, isize );
	      sone_packet[isize] = 0;
	      printf( "Packet overflow, flushing the data;\n" );
	      ibuffer_offset = 0;
	    }
	}
      else if (irecv_size < 0)
	/* This means that there was an error.  Signal and exit for all errors
	 */
	{
	  perror( "recv" );
	  return;
	}
      else
	/* This case happens if size == 0.  That means that the socket is 
	 * closed.  We should return right away.
	 */
	{
	  return;
	}

    }

  return;
}

/* ================================================
 * Process a single packet of data.
 */
static void process_one_packet
(char *sone_packet_a,
 const struct command_line *pCommand_a )
{
  char *scommand, *sfile;
  /* Now parse received string.  Break it into command and file name. */
  scommand = strtok( sone_packet_a, " " );
  sfile    = strtok( NULL, " " );

  if (0 == strcmp( scommand, "FILE" ))
    {
      if (sfile != NULL)
	/* This command indicates that a file has been transmitted */
	upi_process_file( sfile, pCommand_a );
      else
	printf( "Filename missing from 'FILE' command.\n" );
    }

  else if (0 == strcmp( scommand, "NOP" ))
    {
      /* This command is a nop, used just to verify that the link is up */
    }

  else
    {
      printf( "Invalid command: '%s'\n", scommand );
    }
  return;
}

/* ======================================================
 * Process the file notification.  We will simply print a message,
 * then delete the file.
 */
void upi_process_file
(const char *sfile_a,
 const struct command_line *pCommand_a )
{
  char spath_name[160];
  int ierror, ilength, ispace_available;
  
  /* Concatinate the filename onto the directory */
  strcpy( spath_name, pCommand_a->sDirectory );
  ilength = strlen( spath_name );
  ispace_available = 159-ilength;
  strncat( spath_name, sfile_a, ispace_available );
  spath_name[159] = 0;


  printf( "Received file: '%s'\n", spath_name );

  if (pCommand_a->lDeleteFile)
    {
      ierror = remove( spath_name );
      if (ierror) perror( "remove" );
    }

  return;

}

/* ==================================
 * Process the command line.
 */
static void ProcessCommandLine
( struct command_line *pCommand_a,
  int iArgc_a,
  char **sArgv_a )
{
  int iArg, ierror;
  char *sptr;

  /* Initialize to default values */
  memset( pCommand_a, 0, sizeof(*pCommand_a) );

  pCommand_a->iPort  = PORT_UPI_RECEIVER;
  ierror = gethostname( pCommand_a->sHost, MAXHOSTNAMELEN );
  if (ierror < 0)
    {
      perror( "gethostname" );
      exit(EXIT_FAILURE);
    }
  strcpy( pCommand_a->sDirectory, "/usr/iris_data/product/" );
  pCommand_a->lDeleteFile = TRUE;
  pCommand_a->iAddress = ADDRESS_FAMILY_INET;


  for (iArg=1; iArg<iArgc_a; iArg++)
    {
      char *sArg=sArgv_a[iArg];
      if ( 0 == strncmp( sArg, "-h", 2 ) )
	{
	  printf( "Command line options:\n" );
	  printf( "  -a af   : specify address format (TCPIP or UNIX)\n" );
	  printf( "  -d dddd : specify the directory to write to\n" );
	  printf( "  -keep   : Do not delete the files\n" );
	  printf( "  -h      : help\n" );
	  printf( "  -n hhhh : specify host name to send to (defaults to localhost)\n" );
	  printf( "  -p pppp : specify port number (default %d)\n", PORT_UPI_RECEIVER );
	  exit(EXIT_SUCCESS);
	}
      else if ( 0 == strcmp( sArg, "-a" ) )
	{
	  iArg++;
	  if (iArg<iArgc_a)
	    {
	      char sAF[16];
	      strncpy( sAF, sArgv_a[iArg], 15 );
	      sAF[15]=0;
	      STR_UPPER( sAF, 15 );
	      /* Process the address scheme */
	      if (0 == strcmp( sAF, "TCPIP" ))
		{
		  pCommand_a->iAddress = ADDRESS_FAMILY_INET;
		}
	      else if (0 == strcmp( sAF, "UNIX" ))
		{
		  pCommand_a->iAddress = ADDRESS_FAMILY_UNIX;
		}
	      else
		{
		  printf( "Address format '%s' invalid.\n", sAF );
		  exit(EXIT_FAILURE);
		}
	    }
	  else
	    {
	      printf( "Missing address format '-a'\n" );
	      exit(EXIT_FAILURE);
	    }
	}
      else if ( 0 == strcmp( sArg, "-d" ) )
	{
	  iArg++;
	  if (iArg<iArgc_a)
	    {
	      int iLength;
	      /* Process the directory name */
	      strncpy( pCommand_a->sDirectory, sArgv_a[iArg], DIRECTORY_SIZE-2 );
	      pCommand_a->sDirectory[DIRECTORY_SIZE-2] = 0;

	      /* Make sure search directory has a trailing "/" */
	      iLength = strlen(pCommand_a->sDirectory);
	      if (pCommand_a->sDirectory[iLength-1]!= '/')
		{
		  pCommand_a->sDirectory[iLength] = '/';
		  pCommand_a->sDirectory[iLength+1] = 0;
		}
	    }
	  else
	    {
	      printf( "Missing directory name after '-d'\n" );
	      exit(EXIT_FAILURE);
	    }
	}
      else if (0 == strcmp( sArg, "-keep" ))
	{
	  pCommand_a->lDeleteFile = FALSE;
	}  
      else if ( 0 == strcmp( sArg, "-n" ) )
	{
	  iArg++;
	  if (iArg<iArgc_a)
	    {
	      /* Process the host name */
	      strncpy( pCommand_a->sHost, sArgv_a[iArg], MAXHOSTNAMELEN-1 );
	      pCommand_a->sHost[MAXHOSTNAMELEN-1] = 0;
	    }
	  else
	    {
	      printf( "Missing host name after '-n'\n" );
	      exit(EXIT_FAILURE);
	    }
	}
      else if ( 0 == strcmp( sArg, "-p" ) )
	{
	  iArg++;
	  if (iArg<iArgc_a)
	    {
	      /* Process the port number */
	      pCommand_a->iPort = strtol( sArgv_a[iArg], &sptr, 10 );
	      if ((pCommand_a->iPort<MIN_USER_PORT) || (pCommand_a->iPort>65535))
		{
		  printf( "Port number %d invalid.\n", pCommand_a->iPort );
		  exit(EXIT_FAILURE);
		}
	    }
	  else
	    {
	      printf( "Missing port number after '-p'\n" );
	      exit(EXIT_FAILURE);
	    }
	}
      else
	{
	  printf( "Unrecognized command line argument '%s'\n", sArgv_a[iArg] );
	  exit(EXIT_FAILURE);
	}
    }
}
