/* babel - News transport agent for STiK
 *
 * main.c - Main code
 *
 * (c)1996 Mark Baker. Distributable under the terms of the GNU
 *                     general public licence
 *
 * $Id: main.c,v 1.15 1996/09/23 21:10:46 mnb20 Exp $
 */

#include <stdio.h> 
#include <string.h>
#include <time.h>
#include <ctype.h>

#include "babel.h"

/* String lengths */
#define MAXLINELENGTH 255
#define MAXTIMELENGTH 18
#define STATUSLENGTH 80

/* File handles imported from files.c */
extern FILE *act_in ;
extern FILE *act_out ;
extern FILE *descriptions ;
extern FILE *groups ;
extern FILE *newgroups ;

/* Flag to record whether we are connected to a server */
int open_flag ;

/* Time zone */
#define TZ_UNKNOWN 100
#define SECONDS_IN_AN_HOUR 3600

int tz = TZ_UNKNOWN ;

/* Options */
char opt_mode ;
int opt_logging ;
char opt_directory[MAXLINELENGTH] ;

/*
 * main() initialises everything, then runs process_file() to do all
 *    the work.
 */

int main( int argc, char **argv )
{
  parse_command_line( argc, argv ) ;

  initialise_gem() ;
  initialise_stik() ;

  status_line( strings.filesopen ) ;
  open_files() ;

  process_file() ;

  status_line( strings.filesclose ) ;
  close_files() ;

  shutdown_gem() ;

  return 0 ;
}

/*
 * parse_command_line()'s use is obvious
 */

void parse_command_line( int argc, char **argv )
{
  int showhelp = 0 ;
  int pos ;
  char *arg ;

  /* Defaults */
  opt_mode = 'g' ;
  opt_logging = 0 ;
  opt_directory[0] = '\0' ;

  /* Skip program name */
  argv++ ;

  /* One, not zero, as we're skipping program name */
  while( argc > 1 )
    {
      /* If argument doesn't start with - */
      if( **argv != '-' )
	/* We don't currently have any non-option arguments */
	showhelp = 1 ;

      /* If next character is a -, it's a long-name option */
      if( (*argv)[1] == '-' )
	pos = 2 ;
      else
	pos = 1 ;

      switch( (*argv)[pos] )
	{
	case 'h': /* -h or --help */
	  showhelp = 1 ;
	  break ;
	case 'v': /* -v or --version */
	  printf( strings.version ) ;
	  exit(0) ;
	  break ;
	case 'g' : /* -g or --gem */
	  opt_mode = 'g' ;
	  break ;
	case 't' : /* -t or --text */
	  opt_mode = 't' ;
	  break ;
	case 'q' : /* -q or --quiet */
	  opt_mode = 'q' ;
	  break ;
	case 'l' : /* -l or --log */
	  opt_logging = 1 ;
	  break ;
	case 'd' : /* -d or --directory */
	  arg = *argv ;

	  /* Search for an = */
	  while ( *arg != '\0' && *arg != '=' )
	    arg++ ;

	  /* If we found one */
	  if( *arg == '=' )
	    /* Character after it is argument */
	    arg++ ;
	  else if( argc-- != 1 )
	    /* Argument is next entry in argv */
	    arg = *++argv ;

	  /* Copy it */
	  strcpy( opt_directory, arg ) ;
	}

      argc-- ;
      argv++ ;
    }

  if( showhelp )
    {
      /* Show help text and quit */
      printf( "%s\n", strings.version ) ;
      printf( "%s", strings.helptext ) ;
      exit(0) ;
    }
}

/*
 * process_file() parses the active file and controls everything. Maybe
 *    it ought to be split up. Basically it's a big while loop to go through
 *    the active file, acting on each command as it gets to it.
 *
 *    17 Sept 96; It _really_ needs to be split up. Some day I'll get round
 *    to it.
 */

void process_file( void )
{
  char line_buffer[MAXLINELENGTH] ;
  char optline[MAXLINELENGTH] ;
  char *token, *token2, *token3 ;
  char server_scan_time[MAXTIMELENGTH] ;
  char group_scan_time[MAXTIMELENGTH] ;
  char timenow[MAXTIMELENGTH] ;
  time_t currtime ;
  char statusstr[MAXLINELENGTH] ;
  char basename[MAXLINELENGTH] ;
  int group_headers_only, server_headers_only ;
  int group_read_only, server_read_only ;
  char *fgets_return ;

  /* Default time */
  currtime = time( NULL ) ;
  if( tz == TZ_UNKNOWN )
    {
      /* No timezone known, use local time */
      strftime( server_scan_time, MAXTIMELENGTH, "%y%m%d %H%M%S", 
		localtime( &currtime ) ) ;
    }
  else
    {
      /* Adjust for timezone, and use the GMT time */
      currtime -= tz * SECONDS_IN_AN_HOUR ;
      strftime( server_scan_time, MAXTIMELENGTH, "%y%m%d %H%M%S GMT", 
		localtime( &currtime ) ) ;
    }

  /* Get first line */
  fgets_return = fgets( line_buffer, MAXLINELENGTH, act_in ) ;

  /* While there's lines left in the file */
  while( fgets_return != NULL )
    {
      /* Get first token */
      token = strtok( line_buffer, " \t\n" ) ;

      /* Skip blank lines */
      if( token == NULL ) 
	{
	   fgets_return = fgets( line_buffer, MAXLINELENGTH, act_in ) ;
	  continue ;
	}

      /* Time zone */
      if( !strcmp( token, "time" ) )
	{
	  /* Read time zone */
	  token = strtok( NULL, " \t\n" ) ;
	  if( token != NULL )
	    {
	      tz = atoi( token ) ;
	      fprintf( act_out, "time %+d\n", tz ) ;
	    }

	  /* Read next line */
	   fgets_return = fgets( line_buffer, MAXLINELENGTH, act_in ) ;
	}
 
      /* Existing server */
      else if( !strcmp( token, "server" ) )
	{
	  /* Read server name */
	  token2 = strtok( NULL, " \t\n" ) ;

	  /* Read file basename */
	  token = strtok( NULL, " \t\n" ) ;
	  strcpy( basename, token ) ;

	  /* Write to updated file (unchanged) */
	  fprintf( act_out, "server %s %s\n", token2, token ) ;

	  /* Close an open server */
	  if( open_flag )
	    close_server() ;

	  /* Open server */
	  sprintf( statusstr, strings.serveropen, token2 ) ;
	  status_line( statusstr ) ;

	  open_server( token2 ) ;
	  open_flag = 1 ;

	  /* Clear flags */
	  server_headers_only = 0 ;
	  server_read_only = 0 ;

	  while( 1 )
	    {
	      /* Get what might be an option */
	      fgets_return = fgets( optline, MAXLINELENGTH, act_in ) ;

	      /* Stop if end of file reached */
	      if( fgets_return == NULL )
		break ;

	      token3 = strtok( optline, " \t\n" ) ;

	      /* Default article life */
	      if( !strcmp( token3, "days" ) )
		{
		  /* Dump line */
		  fprintf( act_out, "\tdays %s\n", 
			   strtok( NULL, "\n" ) ) ;
		}
	      /* Maximum article life */
	      else if( !strcmp( token3, "max-days" ) )
		{
		  /* Dump line */
		  fprintf( act_out, "\tmax-days %s\n",
			   strtok( NULL, "\n" ) ) ;
		}
	      /* Read headers only */
	      else if( !strcmp( token3, "headers-only" ) )
		{
		  /* Dump line */
		  fprintf( act_out, "\theaders-only\n" ) ;

		  server_headers_only = 1 ;
		}
	      /* Group is read only */
	      else if( !strcmp( token3, "read-only" ) )
		{
		  /* Dump line */
		  fprintf( act_out, "\tread-only\n" ) ;

		  server_read_only = 1 ;
		}
	      else if( !strcmp( token3, "date" ) )
		{
		  /* Find last date */
		  token3 = strtok( NULL, "\n" ) ;
		  strncpy( server_scan_time, token3, MAXTIMELENGTH ) ;

		  /* Write to active file */
		  currtime = time( NULL ) ;
		  if( tz == TZ_UNKNOWN )
		    {
		      /* No timezone known, use local time */
		      strftime( timenow, MAXTIMELENGTH, "%y%m%d %H%M%S", 
				localtime( &currtime ) ) ;
		    }
		  else
		    {
		      /* Adjust for timezone, and use the GMT time */
		      currtime -= tz * SECONDS_IN_AN_HOUR ;
		      strftime( timenow, MAXTIMELENGTH, "%y%m%d %H%M%S GMT", 
				localtime( &currtime ) ) ;
		    }
		  fprintf( act_out, "\tdate %s\n", timenow ) ;
		}		  

	      /* It's not an option. We just ignore it, so the
		 other code (it'll be a server or a group) can
		 process it correctly */
	      else
		{
		  /* Undo the effect of strtok */
		  if( token3 != NULL )
		    {
		      optline[ strlen( token3 ) + 1 ] = ' ' ;
		    }
		  break ;
		}
	    }

	  /* This is necessary so line after options is parsed */
	  strcpy( line_buffer, optline ) ;

	  /* Find new groups on server */
	  new_groups( basename, server_scan_time ) ;

	  /* This is necessary so line after options is parsed */
	  strcpy( line_buffer, optline ) ;
	}

      /* New server */
      else if( !strcmp( token, "new-server" ) )
	{
	  /* Read server name */
	  token = strtok( NULL, " \t\n" ) ;
	  if( token == NULL ) 
	    {
	      /* Read next line */
	      fgets_return = fgets( line_buffer, MAXLINELENGTH, act_in ) ;
	      continue ;
	    }

	  /* Close server if one is open */
	  if( open_flag )
	    close_server() ;
	  
	  /* Get group list */
	  new_server( token, basename ) ;

	  /* Write updated active file */
	  fprintf( act_out, "server %s %s\n", token, basename ) ;

	  /* Read next line */
	   fgets_return = fgets( line_buffer, MAXLINELENGTH, act_in ) ;
	}

      /* A newsgroup */
      else if( !strcmp( token, "group" ) )
	{

	  /* Read group name */
	  token = strtok( NULL, " \t\n" ) ;

	  /* Read group file name */
	  token2 = strtok( NULL, " \t\n" ) ;

	  /* Write to updated file (unchanged) */
	  fprintf( act_out, "\tgroup %s %s\n", token, token2 ) ;

	  /* Clear flags */
	  group_headers_only = server_headers_only ;
	  group_read_only = server_read_only ;
	  strcpy( group_scan_time, server_scan_time ) ;

	  while( 1 )
	    {
	      /* Get what might be an option */
	      fgets_return = fgets( optline, MAXLINELENGTH, act_in ) ;

	      /* Stop if end of file reached */
	      if( fgets_return == NULL )
		break ;

	      token3 = strtok( optline, " \t\n" ) ;

	      /* Description */
	      if( !strcmp( token3, "desc" ) )
		{
		  /* Dump line */
		  fprintf( act_out, "\t\tdesc %s\n",
			   strtok( NULL, "\n" ) ) ;
		}
	      /* Current article pointer. Used by newsreader */
	      else if( !strcmp( token3, "current" ) )
		{
		  /* Dump line */
		  fprintf( act_out, "\t\tcurrent %s\n",
			   strtok( NULL, "\n" ) ) ;
		}
	      /* Default article life */
	      else if( !strcmp( token3, "days" ) )
		{
		  /* Dump line */
		  fprintf( act_out, "\t\tdays %s\n", 
			   strtok( NULL, "\n" ) ) ;
		}
	      /* Maximum article life */
	      else if( !strcmp( token3, "max-days" ) )
		{
		  /* Dump line */
		  fprintf( act_out, "\t\tmax-days %s\n",
			   strtok( NULL, "\n" ) ) ;
		}
	      /* Read headers only */
	      else if( !strcmp( token3, "headers-only" ) )
		{
		  /* Dump line */
		  fprintf( act_out, "\t\theaders-only\n" ) ;

		  group_headers_only = 1 ;
		}
	      /* Group is read only */
	      else if( !strcmp( token3, "read-only" ) )
		{
		  /* Dump line */
		  fprintf( act_out, "\t\tread-only\n" ) ;

		  group_read_only = 1 ;
		}
	      /* New messages since date */
	      else if( !strcmp( token3, "date" ) )
		{
		  /* Find last date */
		  token3 = strtok( NULL, "\n" ) ;
		  strncpy( group_scan_time, token3, MAXTIMELENGTH ) ;

		  /* Note, it's not written to the active file,  as if it
		   * was it would be the same as the server default anyway
		   */
		}

	      /* It's not an option. We just ignore it, so the
		 other code (it'll be a server or a group) can
		 process it correctly */
	      else
		{
		  /* Undo the effect of strtok */
		  if( token3 != NULL )
		    {
		      optline[ strlen( token3 ) + 1 ] = ' ' ;
		    }
		  break ;
		}
	    }

	  /* Now call routine that actually does the work */
	  do_group( token, token2, group_headers_only, group_scan_time ) ;

	  /* This is necessary so line after options is parsed */
	  strcpy( line_buffer, optline ) ;
	}

      /* Else it's an error! */
      else
	{
	  sprintf( statusstr, strings.activeerror, token ) ;
	  alert( statusstr ) ;
	  byebye(1) ;
	}
    }

  /* Close an open server */
  if( open_flag )
    close_server() ;
}

/*
 * new_server() connects to a server, and reads a full group list,
 *    and a complete descriptions list.
 *
 * server_name is, obviously, the hostname of the server, groupfile
 * is the base filename associated with the server 
 */

void new_server( char *server_name, char *groupfile )
{
  char statusstr[STATUSLENGTH] ;
  char groupline[MAXLINELENGTH] ;
  int has_xgtitle = 1 ;
  char *gptr ;

  /* Create file with unique name */
  sprintf( statusstr, strings.groupfile, server_name ) ;
  status_line( statusstr ) ;
  group_list_file( server_name, groupfile ) ;

  /* Open server */
  sprintf( statusstr, strings.serveropen, server_name ) ;
  status_line( statusstr ) ;
  open_server( server_name ) ;

  /* Send command to get group list */
  status_line( strings.fullgroups ) ;
  write_string( "list\n" ) ;

  /* Get 215 reply */
  read_line( groupline, MAXLINELENGTH ) ;
  groupline[3] = '\0' ;
  if( strcmp( groupline, "215" ) )
    {
      alert( strings.unexpected ) ;
      byebye(1) ;
    }

  /* Read group list */
  while( 1 )
    {
      /* Read a line */
      read_line( groupline, MAXLINELENGTH ) ;

      /* Check for last line */
      if( groupline[0] == '.' )
	break ;

      /* Find end of group name and null-terminate it */
      for( gptr = groupline; !isspace( *gptr ); gptr++ ) ;
      *gptr = '\0' ;

      /* Print group name to group file */
      fprintf( groups, "%s\n", groupline ) ;
    }

  /* Send command to get descriptions */
  status_line( strings.descriptions ) ;
  write_string( "xgtitle *\n" ) ;

  /* Get 282 reply */
  read_line( groupline, MAXLINELENGTH ) ;
  groupline[3] = '\0' ;
  if( !strcmp( groupline, "500" ) )
    has_xgtitle = 0 ;
  else if( strcmp( groupline, "282" ) )
    {
      alert( strings.unexpected ) ;
      byebye(1) ;
    }

  if( has_xgtitle )
    {
      while( 1 )
	{
	  /* Read a line */
	  read_line( groupline, MAXLINELENGTH ) ;

	  /* Have we got to the end? */
	  if( groupline[0] == '.' )
	    break ;

	  /* Find end of group name and null terminate it */
	  for( gptr = groupline; !isspace( *gptr ); gptr++ ) ;
	  *gptr = '\0' ;

	  /* Skip whitespace */
	  while( isspace( *++gptr ) ) ;

	  /* Only store description if it isn't a ? */
	  if( strcmp( gptr, "?" ) )
	    {
	      /* Print group name */
	      fprintf( descriptions, "%s ", groupline ) ;
	      
	      /* Print description */
	      fprintf( descriptions, "%s\n", gptr ) ;
	    }
	}
    }

  /* Close files */
  fclose( groups ) ;
  fclose( descriptions ) ;
}

/*
 * new_groups() checks a server for any new groups since last
 *    time we logged on. Then for each one in turn, it
 *    looks for a description and appends it to the .dsc file
 * 
 * basename is the filename associated with the server (without 
 * extension), date is a string containing the date the server
 * was last contacted.
 */

void new_groups( char *basename, char *date )
{
  FILE *new_groups ;
  char groupline[MAXLINELENGTH] ;
  char command[MAXLINELENGTH] ;
  char *gptr ;

  /* Open file */
  new_groups_file( basename ) ;

  /* Send command to get group list */
  status_line( strings.newgroups ) ;
  sprintf( groupline, "newgroups %s\n", date ) ;
  write_string( groupline ) ;

  /* Get 231 reply */
  read_line( groupline, MAXLINELENGTH ) ;
  groupline[3] = '\0' ;
  if( strcmp( groupline, "231" ) )
    {
      alert( strings.unexpected ) ;
      byebye(1) ;
    }

  /* Read back new groups */
  while( 1 )
    {
      /* Read a line */
      read_line( groupline, MAXLINELENGTH ) ;

      /* Check for last line */
      if( groupline[0] == '.' )
	break ;

      /* Find end of group name and null-terminate it */
      for( gptr = groupline; !isspace( *gptr ); gptr++ ) ;
      *gptr = '\0' ;

      /* Print group name to new groups file and group list */
      fprintf( newgroups, "%s\n", groupline ) ;
      fprintf( groups, "%s\n", groupline ) ;
    }

  /* Rewind file so we can read back from it to get descriptions */
  rewind( newgroups ) ;

  /* Get descriptions */
  status_line( strings.descriptions ) ;
  while( fgets( groupline, MAXLINELENGTH, newgroups ) )
    {
      /* Send xgtitle command */
      sprintf( command, "xgtitle %s", groupline ) ;
      write_string( command ) ;

      /* Get 215 reply */
      read_line( command, MAXLINELENGTH ) ;
      command[3] = '\0' ;
      if( !strcmp( command, "500" ) )
	/* No xgtitle command - oh well, never mind */
	break ;
      else if( strcmp( command, "282" ) )
	{
	  alert( strings.unexpected ) ;
	  byebye(1) ;
	}

      /* Read what ought to be the description */
      read_line( command, MAXLINELENGTH ) ;

      /* Is there a description */
      if( command[0] != '.' )
	{
	  /* Find beginning of end of group name and null-terminate it */
	  for( gptr = command; !isspace( *gptr ); gptr++ ) ;
	  *gptr = '\0' ;
	      
	  /* Print group name */
	  fprintf( descriptions, "%s ", command ) ;

	  /* Skip whitespace */
	  while( isspace( *++gptr ) ) ;
	  
	  /* Print description */
	  fprintf( descriptions, "%s\n", gptr ) ;
	  
	  /* Ignore the dot and any lines up to it */
	  do
	    {
	      read_line( command, MAXLINELENGTH ) ;
	    }
	  while( command[0] != '.' ) ;
	}
    }

  /* Close files */
  fclose( groups ) ;
  fclose( newgroups ) ; 
  fclose( descriptions ) ;
}

/*
 * byebye() tidies up and quits, and is called when we get an error
 *
 * retstat is the program status to return to the caller
 */

void byebye( retstat )
{
  if( open_flag )
    close_server() ;
  
  shutdown_gem() ;
  
  exit( retstat ) ;
}
