/* TN5250 - An implementation of the 5250 telnet protocol.
 * Copyright (C) 1997 Michael Madore
 * 
 * This file is part of TN5250.
 *
 * TN5250 is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1, or (at your option)
 * any later version.
 * 
 * TN5250 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 Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this software; see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
 * Boston, MA 02111-1307 USA
 * 
 */
#include "tn5250-private.h"

/* External declarations of initializers for each type of stream. */
extern int tn5250_telnet_stream_init (Tn5250Stream *This);
extern int tn3270_telnet_stream_init (Tn5250Stream *This);
#ifdef HAVE_LIBSSL
extern int tn5250_ssl_stream_init (Tn5250Stream *This);
#endif
#ifndef NDEBUG
extern int tn5250_debug_stream_init (Tn5250Stream *This);
#endif

/* This structure and the stream_types[] array defines what types of
 * streams we can create. */
struct _Tn5250StreamType {
   const char *prefix;
   int (* init) (Tn5250Stream *This);
};

typedef struct _Tn5250StreamType Tn5250StreamType;

static const Tn5250StreamType stream_types[] = {
#ifndef NDEBUG
   { "debug:", tn5250_debug_stream_init },
#endif
   { "telnet:", tn5250_telnet_stream_init },
   { "tn5250:", tn5250_telnet_stream_init },
#ifdef HAVE_LIBSSL
   { "ssl:", tn5250_ssl_stream_init },
   { "telnet-ssl:", tn5250_ssl_stream_init },
   { "telnets:", tn5250_ssl_stream_init },
#endif
   { NULL, NULL }
};

static void streamInit(Tn5250Stream *This, long timeout)
{
  This->options = 0;
  This->status = 0;
  This->config = NULL;
  This->connect = NULL;
  This->disconnect = NULL;
  This->handle_receive = NULL;
  This->send_packet = NULL;
  This->destroy = NULL;
  This->record_count = 0;
  This->records = This->current_record = NULL;
  This->sockfd = (SOCKET_TYPE) - 1;
  This->msec_wait = timeout;
  This->streamtype = TN5250_STREAM;
  This->rcvbufpos = 0;
  This->rcvbuflen = -1;
  tn5250_buffer_init(&(This->sb_buf));
}

/****f* lib5250/tn5250_stream_open
 * NAME
 *    tn5250_stream_open
 * SYNOPSIS
 *    ret = tn5250_stream_open (to);
 * INPUTS
 *    const char *         to         - `URL' of host to connect to.
 *    Tn5250Config     *   config     - config to associate w/stream
 * DESCRIPTION
 *    Opens a 5250 stream to the specified host.  URL is of the format 
 *    [protocol]:host:[port], where protocol is currently one of the following:
 *
 *       telnet - connect using tn5250 protocol
 *       tn5250 - connect using tn5250 protocol
 *       debug  - read recorded session from debug file
 *
 *    This is maintained by a protocol -> function mapping.  Each protocol has
 *    an associated function which is responsible for initializing the stream.
 *    Stream initialization sets up protocol specific functions to handle
 *    communication with the host system.
 *
 *    The next protocol to add is SNA.  This will allow the emulator to use 
 *    APPC (LU 6.2) to establish a session with the AS/400.
 *****/
Tn5250Stream *tn5250_stream_open (const char *to, Tn5250Config *config)
{
   Tn5250Stream *This = tn5250_new(Tn5250Stream, 1);
   const Tn5250StreamType *iter;
   const char *postfix;
   int ret;

   if (This != NULL) {

      streamInit(This, 0);

      if (config != NULL) 
           tn5250_stream_config(This, config);

      /* Figure out the stream type. */
      iter = stream_types;
      while (iter->prefix != NULL) {
	 if (strlen (to) >= strlen(iter->prefix)
	       && !memcmp (iter->prefix, to, strlen (iter->prefix))) {
	    /* Found the stream type, initialize it. */
	    ret = (* (iter->init)) (This);
	    if (ret != 0) {
	       tn5250_stream_destroy (This);
	       return NULL;
	    }
	    break;
	 }
	 iter++;
      }

      /* If we haven't found a type, assume telnet. */
      if (iter->prefix == NULL) { 
	 ret = tn5250_telnet_stream_init (This);
	 if (ret != 0) {
	    tn5250_stream_destroy (This);
	    return NULL;
	 }
	 postfix = to;
      } else 
	 postfix = to + strlen (iter->prefix);

      /* Connect */
      ret = (* (This->connect)) (This, postfix);
      if (ret == 0)
	 return This;

      tn5250_stream_destroy (This);
   }
   return NULL;
}

/****f* lib5250/tn5250_stream_host
 * NAME
 *    tn5250_stream_host
 * SYNOPSIS
 *    ret = tn5250_stream_host (masterSock);
 * INPUTS
 *    SOCKET_TYPE	masterSock	-	Master socket
 * DESCRIPTION
 *    DOCUMENT ME!!!
 *****/
Tn5250Stream *tn5250_stream_host (SOCKET_TYPE masterfd, long timeout,
				  int streamtype)
{
   Tn5250Stream *This = tn5250_new(Tn5250Stream, 1);
   int ret;

   if (This != NULL) {
      streamInit(This, timeout);
      if(streamtype == TN5250_STREAM) 
	{
	  /* Assume telnet stream type. */
	  ret = tn5250_telnet_stream_init (This);
	}
      else
	{
	  ret = tn3270_telnet_stream_init (This);
	}
      if (ret != 0) {
         tn5250_stream_destroy (This);
         return NULL;
      }
      /* Accept */
      printf("masterfd = %d\n", masterfd);
      ret = (* (This->accept)) (This, masterfd);
      if (ret == 0)
	 return This;

      tn5250_stream_destroy (This);
   }
   return NULL;
}


/****f* lib5250/tn5250_stream_config
 * NAME
 *    tn5250_stream_config
 * SYNOPSIS
 *    tn5250_stream_config (This, config);
 * INPUTS
 *    Tn5250Stream *       This       - Stream object.
 *    Tn5250Config *       config     - Configuration object.
 * DESCRIPTION
 *    Associates a stream with a configuration object.  The stream uses the
 *    configuration object at run time to determine how to operate.
 *****/
int tn5250_stream_config (Tn5250Stream *This, Tn5250Config *config)
{
   /* Always reference before unreferencing, in case it's the same
    * object. */
   tn5250_config_ref (config);
   if (This->config != NULL)
      tn5250_config_unref (This->config);
   This->config = config;
   return 0;
}

/****f* lib5250/tn5250_stream_destroy
 * NAME
 *    tn5250_stream_destroy
 * SYNOPSIS
 *    tn5250_stream_destroy (This);
 * INPUTS
 *    Tn5250Stream *       This       - 
 * DESCRIPTION
 *    DOCUMENT ME!!!
 *****/
void tn5250_stream_destroy(Tn5250Stream * This)
{
   /* Call particular stream type's destroy handler. */
   if (This->destroy)
      (* (This->destroy)) (This);

   /* Free the environment. */
   if (This->config != NULL)
      tn5250_config_unref (This->config);
   tn5250_buffer_free(&(This->sb_buf));
   tn5250_record_list_destroy(This->records);
   free(This);
}

/****f* lib5250/tn5250_stream_get_record
 * NAME
 *    tn5250_stream_get_record
 * SYNOPSIS
 *    ret = tn5250_stream_get_record (This);
 * INPUTS
 *    Tn5250Stream *       This       - 
 * DESCRIPTION
 *    DOCUMENT ME!!!
 *****/
Tn5250Record *tn5250_stream_get_record(Tn5250Stream * This)
{
   Tn5250Record *record;
   int offset;

   record = This->records;
   TN5250_ASSERT(This->record_count >= 1);
   TN5250_ASSERT(record != NULL);

   This->records = tn5250_record_list_remove(This->records, record);
   This->record_count--;

   if(This->streamtype == TN5250_STREAM)
     {
       TN5250_ASSERT(tn5250_record_length(record)>= 10);
       offset = 6 + tn5250_record_data(record)[6];
     }
   else
     {
       offset = 0;
     }

   TN5250_LOG(("tn5250_stream_get_record: offset = %d\n", offset));
   tn5250_record_set_cur_pos(record, offset);

   return record;
}

/****f* lib5250/tn5250_stream_setenv
 * NAME
 *    tn5250_stream_setenv
 * SYNOPSIS
 *    tn5250_stream_setenv (This, name, value);
 * INPUTS
 *    Tn5250Stream *       This       - 
 *    const char *         name       -
 *    const char *         value      -
 * DESCRIPTION
 *    Set an 'environment' string.  This is made to look like setenv().
 *****/
void tn5250_stream_setenv(Tn5250Stream * This, const char *name, const char *value)
{
   char *name_buf;
   if (This->config == NULL) {
      This->config = tn5250_config_new ();
      TN5250_ASSERT (This->config != NULL);
   }
   name_buf = (char*)malloc (strlen (name) + 10);
   strcpy (name_buf, "env.");
   strcat (name_buf, name);
   tn5250_config_set (This->config, name_buf, value);
   free (name_buf);
}

/****f* lib5250/tn5250_stream_getenv
 * NAME
 *    tn5250_stream_getenv
 * SYNOPSIS
 *    ret = tn5250_stream_getenv (This, name);
 * INPUTS
 *    Tn5250Stream *       This       - 
 *    const char *         name       - 
 * DESCRIPTION
 *    Retrieve the value of a 5250 environment string.
 *****/
const char *tn5250_stream_getenv(Tn5250Stream * This, const char *name)
{
   char *name_buf;
   const char *val;

   if (This->config == NULL)
      return NULL;

   name_buf = (char*)malloc (strlen (name) + 10);
   strcpy (name_buf, "env.");
   strcat (name_buf, name);
   val = tn5250_config_get (This->config, name_buf);
   free (name_buf);
   return val;
}

/****f* lib5250/tn5250_stream_unsetenv
 * NAME
 *    tn5250_stream_unsetenv
 * SYNOPSIS
 *    tn5250_stream_unsetenv (This, name);
 * INPUTS
 *    Tn5250Stream *       This       - 
 *    const char *         name       - 
 * DESCRIPTION
 *    Unset a 5250 environment string.
 *****/
void tn5250_stream_unsetenv(Tn5250Stream * This, const char *name)
{
   char *name_buf;
   if (This->config == NULL)
      return; /* Nothing to unset. */

   name_buf = (char*)malloc (strlen (name) + 10);
   strcpy (name_buf, "env.");
   strcat (name_buf, name);
   tn5250_config_unset (This->config, name_buf);
   free (name_buf);
}

int tn5250_stream_socket_handle (Tn5250Stream *This)
{
   return (int) This->sockfd;
}

/* vi:set sts=3 sw=3: */


syntax highlighted by Code2HTML, v. 0.9.1