/*+++++++++++++++++
  refdb-client.c - functions common to all client applications
  markus@mhoenicka.de 2000-07-03

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.
   
   This program 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 General Public License for more details.
   
   You should have received a copy of the GNU General Public License
   along with this program; if not, see <http://www.gnu.org/licenses/>

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/

/* socket related includes */
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/un.h>
#include <netdb.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include "refdb.h"
#include "connect.h"
#include "page.h"
#include "readln.h"  /* for COMMAND definition */
#include "refdb-client.h"
#include "linklist.h" /* linked lists, needed by cgi.h */
#include "pref.h" /* for PREFS_BUF_LEN, depends on linklist.h */
#include "enigma.h" /* for password encryption */
#include "cgi.h" /* cgi-related stuff */
#include "strfncs.h" /* mstrcat */
#include "readris.h"

extern int n_verbose;
extern int n_done;
extern int n_broken_pipe;
extern int n_abort_connect;
extern int n_cgi;
extern char server_ip[];
extern char port_address[];
extern char the_pager[];
extern char username[];
extern char passwd[];
extern char no_encrypt[];
extern const char cs_term[];

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  connect_to_server(): establish a connection to the application server
  
  int connect_to_server 0 if successful, not zero if failure

  int *n_sockfd pointer to the variable which receives the socket
                file descriptor if successful

  char *server_ip ip address of server

  char *port_address port address of server

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int connect_to_server (int* n_sockfd, char* server_ip, char* port_address)
{
  int n_len; /* length of the address structure */
  struct sockaddr_in address; /* the server address */
  int n_result; /* connection result */
  FILE* errstream;

  errstream = (n_cgi) ? stdout : stderr;

  *n_sockfd = socket(AF_INET, SOCK_STREAM, 0);
  address.sin_family = AF_INET;
  address.sin_addr.s_addr = inet_addr(server_ip);
  address.sin_port = htons(atoi(port_address));
  n_len = sizeof(address);
  
  n_result = connect(*n_sockfd, (struct sockaddr *)&address, n_len);
  
  if (n_result != 0) {
    cgi_header(CGI_PLAIN);
    fprintf(errstream, "could not establish server connection\n");
    if (n_verbose) {
      fprintf(errstream, "(1) Check the settings for the server IP address and the port. You can set these either on the command line or in your init file\n(2)The refdb server may be stopped or crashed or otherwise unwilling to process the request.\n(3) The machine running the server may be down or overloaded.\n");
    }
  }

  return n_result;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  init_dialog(): starts the client-server dialog

  int init_dialog returns 0 if ok, 1 if an error occurred

  int n_sockfd file descriptor of the socket of our connection

  char* passwd ptr to a string with the unencrypted password

  char* inbuffer ptr to a string that will receive the server response
                 string must hold at least COMMAND_INBUF_LEN chars

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int init_dialog(int n_sockfd, char* passwd, char* inbuffer) {
  int numbyte;
  int cs_status;
  char outbuffer[16]; /* should be way enough for version numbers */
  char scrambled_passwd[PASSWD_LENGTH+1];
  FILE* errstream;

  errstream = (n_cgi) ? stdout : stderr;

  /* initialize string with zeros to terminate it*/
  memset((void*)scrambled_passwd, (int)'\0', (size_t)(PASSWD_LENGTH+1));

  /* send our protocol version to server */
  sprintf(outbuffer, "%d", REFDB_PROTOCOL_VERSION);

  numbyte = tiwrite(n_sockfd, outbuffer, TERM_YES);
  if (numbyte == -1) {
    cgi_header(CGI_PLAIN);
    fprintf(errstream, "could not write to refdbd\n");
    return 1;
  }

  /* read status */
  if ((cs_status = read_status(n_sockfd))) {
    cgi_header(CGI_PLAIN);
    fprintf(errstream, "%s\n", get_status_msg(cs_status));
    return 1;
  }
    
  /* read pseudo-random string for password encryption */
  numbyte = tread(n_sockfd, inbuffer, COMMAND_INBUF_LEN);
    
  if (numbyte == -1) {
    cgi_header(CGI_PLAIN);
    fprintf(errstream, "could not read from refdbd\n");
    return 1;
  }
  
  if (strlen(inbuffer) != 12) {
    cgi_header(CGI_PLAIN);
    fprintf(errstream, "server error: incorrect scramble string\n");
    return 1;
  }


  /* todo: check for no_encrypt setting and skip encryption if requested */
  if (*no_encrypt != 't') {
    if (enigma_encrypt(passwd, scrambled_passwd, inbuffer)) {
      cgi_header(CGI_PLAIN);
      fprintf(errstream, "password encryption error\n");
      return 1;
    }

    numberize_passwd(passwd, scrambled_passwd);
  }
/*    strcpy(passwd, scrambled_passwd); */

  return 0;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  listvalue(): lists various things
 
  int listvalue 0 if successful, 1 if error

  char *arg search string; if this is a cgi request, this ptr must be
            cast to struct liliform* to access the linked list with
	    form values

  char *command the command name

  char *help_string the string to display with the -h option

  char *err_string a string that identifies an error condition returned
                   by the application server

  int n_with_summary if 1, summary will be retrieved

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int listvalue (char* arg, char* command, char* help_string, char* err_string, int n_with_summary)
{
  struct simplelistvals slvals;
  struct liliform* ptr_current;
  char scrambled_passwd[PASSWD_LENGTH*3+1];
  char inbuffer[COMMAND_INBUF_LEN] = "";
  int retval;
  FILE* errstream;

  if (!command || !*command) {
    /* nothing to do */
    return 1;
  }

  errstream = (n_cgi) ? stdout : stderr;

  slvals.n_file_open = 0;
  slvals.n_file_append = 0;
  slvals.n_pipe = 0;
  slvals.outfile = NULL;
  slvals.outpipe = NULL;

  /* get us some buffer for output */
  slvals.outbuffer = malloc(strlen(arg) + strlen(command) + 128); 
  if (slvals.outbuffer == NULL) {
    return 1;
  }
  slvals.outbuffer[0] = '\0';

  if (!n_cgi) {
    if (strncmp(arg, "-h", 2) == 0) {
      cgi_header(CGI_PLAIN);
      fprintf(errstream, "%s", help_string);
      free(slvals.outbuffer);
      return 0;
    }
  }

  if (connect_to_server(&slvals.n_sockfd, server_ip, port_address) != 0) {
    cgi_header(CGI_PLAIN);
    fprintf(errstream, "could not connect to server\n");
    free(slvals.outbuffer);
    return 1;
  }

  strcpy(scrambled_passwd, passwd);

  if (init_dialog(slvals.n_sockfd, scrambled_passwd, inbuffer)) {
    cgi_header(CGI_PLAIN);
    fprintf(errstream, "password transfer failed\n");
    close(slvals.n_sockfd);
    free(slvals.outbuffer);
    return 1;
  }

  sprintf(slvals.outbuffer, "%s ", command);
  if (!n_cgi) {
    if (arg && *arg) {
      strcat(slvals.outbuffer, arg);
    }
  }
  else {
    ptr_current = get_liliform((struct liliform*)arg, "dbregexp");
    if (ptr_current) {
      strcat(slvals.outbuffer, ptr_current->value);
    }
  }
  strcat(slvals.outbuffer, " -u ");
  strcat(slvals.outbuffer, username);
  if (strlen(passwd) > 0) {
    strcat(slvals.outbuffer, " -w ");
    strcat(slvals.outbuffer, scrambled_passwd);
  }

  if (n_cgi) {
    strcat(slvals.outbuffer, " -t cgi");
  }

  retval = getsimplelist(&slvals, n_with_summary);

  if (!retval && err_string && *err_string
      && strncmp(slvals.inbuffer, err_string, strlen(err_string)) == 0) {
    retval = 1;
  }
  close(slvals.n_sockfd);
  free(slvals.outbuffer);

  return retval;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  getsimplelist(): convenience fn to retrieve data from the server
                   based on a simple command, followed by data from
		   the server, followed by an acknowledgement by the
		   client

  int getsimplelist 1 if failed, 0 if ok

  struct simplelistvals* slvals ptr to a structure containing all necessary
                   values to run the command and collect the data

  int n_with_summary if 1, the fn reads a summary from the server
                   after the data proper are transmitted; if 2, the fn
		   continues to read the result string even if a previous
		   error was signalled

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int getsimplelist(struct simplelistvals* slvals, int n_with_summary) {
  int numbyte;
  int n_read_done = 0;
  size_t byte_written = 0;
  FILE *pagerfp; /* ptr to file */
  FILE* errstream;
  int n_curr_trailing_z = 0;
  int n_last_trailing_z = 0;
  int cs_status;

  slvals->inbuffer[0] = '\0'; /* terminate just in case we fail and the
				caller checks the contents */
  errstream = (n_cgi) ? stdout : stderr;

  send_status(slvals->n_sockfd, 0, TERM_NO);

  numbyte = tiwrite(slvals->n_sockfd, slvals->outbuffer, TERM_YES);
  if (numbyte == -1) {
    cgi_header(CGI_PLAIN);
    fprintf(errstream, "%s\n", get_status_msg(110));
    return 1;
  }

  /* check server status */

  /* ToDo: must continue even if createdb returns error */
  cs_status = read_status(slvals->n_sockfd);

  if (cs_status != 0 /* success */
      && cs_status != 803) { /* partial success */
    cgi_header(CGI_PLAIN);
    fprintf(errstream, "%s\n", get_status_msg(cs_status));
    if (n_with_summary != 2) {
      return 1;
    }
  }
  
  /* openpager and open_outfile are guaranteed to return a valid file/pipe - 
     and be it stdout */
  if (slvals->n_file_open) {
    pagerfp = open_outfile(slvals->outfile, 0);
  }
  else if (slvals->n_file_append) {
    pagerfp = open_outfile(slvals->outfile, 1);
  }
  else if (slvals->n_pipe) {
    pagerfp = openpager(slvals->outpipe);
  }
  else {
    pagerfp = openpager(the_pager);
  }

  do {
    numbyte = tread(slvals->n_sockfd, slvals->inbuffer, OUTBUF_LEN);
    if (numbyte == -1) {
      fprintf(stderr, "could not read from refdbd. Stop\n");
      if (slvals->n_file_open || slvals->n_file_append) {
	close_outfile(pagerfp);
      }
      else {
	closepager(pagerfp);
      }
      n_broken_pipe = 0;
      return 1;
    }
    
    n_curr_trailing_z = get_trailz(slvals->inbuffer, numbyte);

    if (numbyte >= TERM_LEN) {
      if (n_curr_trailing_z >= TERM_LEN) {
	/* terminator is complete */
	n_read_done++;
	/* send back confirmation to the server */
	send_status(slvals->n_sockfd, 0, TERM_NO);
      }
    }
    else if (n_curr_trailing_z == numbyte
	     && n_curr_trailing_z + n_last_trailing_z >= TERM_LEN) {
      /* terminator is complete including the previous cycle */
      n_read_done++;
      /* send back confirmation to the server */
      send_status(slvals->n_sockfd, 0, TERM_NO);
    }
    else if (n_curr_trailing_z == numbyte) {
      /* terminator is still incomplete */
      n_last_trailing_z += n_curr_trailing_z;
      continue;
    }

    /* write numbyte chars to output, unless this is the last chunk: we do not
       want to write the terminating \0 */
    if (!n_broken_pipe) {
      if (n_last_trailing_z) {
	byte_written += fwrite(cs_term, sizeof(char), n_last_trailing_z, pagerfp);
      }
      byte_written += fwrite(slvals->inbuffer, sizeof(char), numbyte-n_curr_trailing_z, pagerfp);
    }
/*      printf("%s<<\n", slvals->inbuffer); */
    if (n_read_done && n_with_summary ==1) {
      send_status(slvals->n_sockfd, 0, TERM_NO);
      cs_status = read_status(slvals->n_sockfd);

      if (cs_status != 0 /* success */
	  && cs_status != 803) { /* partial success */
	cgi_header(CGI_PLAIN);
	fprintf(errstream, "%s", get_status_msg(cs_status));
	return 1;
      }

      numbyte = tread(slvals->n_sockfd, slvals->inbuffer, OUTBUF_LEN);
      if (numbyte == -1) {
	fprintf(stderr, "could not read from refdbd. Stop\n");
	n_broken_pipe = 0;
	return 1;
      }
      
      send_status(slvals->n_sockfd, 0, TERM_NO);
    }

    if (!n_read_done) {
      n_last_trailing_z = n_curr_trailing_z;
    }
  } while (!n_read_done);

  if (slvals->n_file_open || slvals->n_file_append) {
    close_outfile(pagerfp);
    fprintf(errstream, "%d byte written to %s\n", byte_written, slvals->outfile);
  }
  else {
    closepager(pagerfp);
  }

  if (n_with_summary == 1) {
    fprintf(errstream, "%s", slvals->inbuffer);
  }
  
  /* reset */
  n_broken_pipe = 0;

  return 0;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  build_batchcommand(): assembles a command string for commands run
                        in batch mode that contains only the relevant
			options and parameters. Must be called after
			getopt has dealt with all options relevant to
			the main program.

  char* build_batchcommand returns a ptr to a string containing the
                        command string or NULL in case of an error.
			The returned string is allocated with malloc()
			and must be freed by the calling function

  int argc number of arguments

  char** argv array of strings containing the arguments

  int optind index into argv

  char* s string containing the command

  char* the_command string containing all command options

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
char* build_batchcommand(int argc, char** argv, int optind, char* s, char* the_command) {
  char *batchstring, *new_batchstring;
  size_t stringsize;

  stringsize = strlen(s)+2;
  batchstring = malloc(stringsize);
  if (batchstring == NULL) {
    return NULL;
  }
  strcpy(batchstring, s);

  if (*the_command) {
    if ((new_batchstring = mstrcat(batchstring, the_command, &stringsize, 0)) == NULL) {
      free(batchstring);
      return NULL;
    }
    else {
      batchstring = new_batchstring;
    }
  }

  if (argv[optind] && argv[optind][0]) {
    int i = optind;
    while (argv[i] && argv[i][0]) {
      if ((new_batchstring = mstrcat(batchstring, " ", &stringsize, 0)) == NULL) {
	free(batchstring);
	return NULL;
      }
      else {
	batchstring = new_batchstring;
      }
      if ((new_batchstring = mstrcat(batchstring, argv[i], &stringsize, 0)) == NULL) {
	free(batchstring);
	return NULL;
      }
      else {
	batchstring = new_batchstring;
      }
      i++;
    }
  }
  return batchstring;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  send_xml_data(): reads xml data from an input stream and sends the
                   data chunk-wise to the server

  int send_xml_data returns 0 if ok, > 0 if there was an error
                   error codes: 1 = data read error
                                2 = write to server error
				3 = read from server error
				4 = server error
				5 = out of memory

  FILE *infp ptr to a FILE struct for the data input stream

  FILE *pagerfp ptr to a FILE struct for the output stream for server
                   messages

  FILE *errstream ptr to a FILE struct for error messages

  int n_sockfd file descriptor of client/server socket connection

  size_t* ptr_byte_written ptr to var that will be incremented by the number
                           of bytes written to a stream

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int send_xml_data(FILE *infp, FILE *pagerfp, FILE *errstream, int n_sockfd, size_t* ptr_byte_written) {
  int n_style_file_done = 0;
  int numbyte;
  int n_read_done = 0;
  int n_chunk_count = 0;
  int num_trailz;
  int cs_status;
  size_t n_result;
  size_t n_chunksize;
  char* buffer;
  char thebytes[11];
  char inbuffer[COMMAND_INBUF_LEN];


  n_chunksize = XMLPARSE_CHUNKSIZE;
  buffer = malloc(n_chunksize); 
  if (buffer == NULL) {
    return 5;
  }

  buffer[0] = '\0';

  do {
    /* ------------------------------------------------------------ */
    /* PHASE 1 */
    /* read next chunk and count the bytes */
    /* the data will be parsed by expat. This can handle XML data chunk-wise, so we don't have to worry about the document structure here */
    /*        printf("start readstyle"); */
    /*        fflush(stdout); */
    n_result = fread(buffer, 1, n_chunksize, infp);

/*        printf("end readstyle\n"); */
/*        fflush(stdout); */
    if (n_result < n_chunksize && !feof(infp)) {
      /* no more data available */
      fprintf(errstream, "%s\n", get_status_msg(401));
      send_status(n_sockfd, 401, TERM_NO);
      free(buffer);
      return 1;
    }
    else if (n_result < n_chunksize) {
      n_style_file_done++;
    }

    if (n_result > 0) { /* if chunk is not empty */
      /* send length information to database server */
      send_status(n_sockfd, 0, TERM_NO);

      sprintf(thebytes, "%d", n_result);
      numbyte = tiwrite(n_sockfd, thebytes, TERM_YES); /* is 10 on the safe side? */
/*       printf("%s\n", thebytes); */
/*       printf("%s\n", buffer); */

      if (numbyte == -1) {
	fprintf(errstream, "could not write to refdbd. Stop\n");
	free(buffer);
	return 2;
      }

      /* ------------------------------------------------------------ */
      /* PHASE 2 */
      /* wait for acknowledgement of database server */
      if ((cs_status = read_status(n_sockfd))) {
	fprintf(errstream, "%s\n", get_status_msg(cs_status));
	return 1;
      }

      /* ------------------------------------------------------------ */
      /* PHASE 3 */
      /* send a dataset */
      numbyte = iwrite(n_sockfd, buffer, n_result);
/*       printf("%s\n", buffer); */
      if (numbyte == -1) {
	fprintf(errstream, "could not write to refdbd. Stop\n");
	free(buffer);
	return 2;
      }
  
      /* ------------------------------------------------------------ */
      /* PHASE 4 */
      /* read messages from application server */
      n_read_done = 0;

      cs_status = read_status(n_sockfd);
      if (cs_status == 400) {
	/* retrieve server-generated error message */
	do {
	  numbyte = tread(n_sockfd, inbuffer, OUTBUF_LEN);
	  /*   	printf("phase4 server reply:%s<<\n", inbuffer); */
	  if (numbyte == -1) {
	    /* timeout while reading */
	    fprintf(errstream, "%s", get_status_msg(109));
	    return 3;
	  }

	  /* we rely on the fact that the server reply is no more than
	     OUTBUF_LEN in length */
	  if ((num_trailz = get_trailz(inbuffer, numbyte)) >= TERM_LEN) { /* if transmission ends */
	    n_read_done++;
	  }
	  /* write numbyte chars to output, unless this is the last chunk:
	     we do not want to write the terminating \0 */
	  if (!n_broken_pipe) {
	    *ptr_byte_written += fwrite(inbuffer, sizeof(char), numbyte-num_trailz, pagerfp);
	  }
	  /* 	printf("%s", inbuffer); */
	} while (!n_read_done);

	return 4;
      }
      /* else: status is 403, chunk added successfully */


      n_chunk_count++;
    }
/*        else { */
/*        } */
  } while (!n_style_file_done);

  free(buffer);
  return 0;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  send_ris_data(): reads ris data from an input stream and sends the
                   data set-wise to the server

  int send_ris_data returns 0 if ok, > 0 if there was an error
                   error codes: 1 = data read error
                                2 = write to server error
				3 = read from server error
				4 = server error
				5 = out of memory

  FILE *infp ptr to a FILE struct for the data input stream

  FILE *pagerfp ptr to a FILE struct for the output stream for server
                   messages

  FILE *errstream ptr to a FILE struct for error messages

  int n_sockfd file descriptor of client/server socket connection

  char** ptr_ris_set_buffer ptr to an allocated buffer, may be realloc'd

  size_t* ptr_n_setlength ptr to length of ris_set_buffer

  int* ptr_n_setcount ptr to set counter

  char* default_ris ptr to default ris filename

  int n_cgi if 1, assume CGI data

  size_t* ptr_byte_written ptr to var that will be incremented by the number
                           of bytes written to a stream

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int send_ris_data(FILE *infp, FILE *pagerfp, FILE *errstream, int n_sockfd, char** ptr_ris_set_buffer, size_t* ptr_n_setlength, int* ptr_n_setcount, char* default_ris, int n_cgi, size_t* ptr_byte_written) {
  int n_result;
  int n_read_done = 0;
  int n_ris_file_done = 0;
  int numbyte;
  int num_trailz;
  int cs_status;
  char thebytes[11] = "";
  char inbuffer[COMMAND_INBUF_LEN];


  do {
    /* ------------------------------------------------------------ */
    /* PHASE 1 */
    /* read next dataset and count the bytes */
    if (!n_cgi) {
      (*ptr_ris_set_buffer)[0] = '\0';
      *ptr_n_setlength = TERM_LEN; /* for the terminating \0's */
      n_result = read_ris_set(infp, default_ris, ptr_ris_set_buffer, ptr_n_setlength, 0);

      if (n_result == 0) {
	cgi_header(CGI_PLAIN);
	fprintf(errstream, "%s\n", get_status_msg(401));
	send_status(n_sockfd, 401, TERM_NO);
	return 1;
      }
      else if (n_result == 2) {
	n_ris_file_done++;
      }
    }
    else { /* if cgi, there is only one set to deal with */
      n_ris_file_done++;
    }

    if (*ptr_n_setlength != TERM_LEN) { /* if dataset is not empty */
      /* send length information to database server */
      send_status(n_sockfd, 0, TERM_NO);

      sprintf(thebytes, "%d", *ptr_n_setlength);
      numbyte = tiwrite(n_sockfd, thebytes, TERM_YES); /* is 10 on the safe side? */
      /*        printf("%s\n", thebytes); */

      if (numbyte == -1) {
	cgi_header(CGI_PLAIN);
	fprintf(errstream, "could not write to refdbd. Stop\n");
	return 2;
      }

      /* ------------------------------------------------------------ */
      /* PHASE 2 */
      /* wait for acknowledgement of database server */
      if ((cs_status = read_status(n_sockfd))) {
	fprintf(errstream, "%s\n", get_status_msg(cs_status));
	return 1;
      }

      /* ------------------------------------------------------------ */
      /* PHASE 3 */
      /* send a dataset */
      numbyte = iwrite(n_sockfd, *ptr_ris_set_buffer, *ptr_n_setlength);
      if (numbyte == -1) {
	cgi_header(CGI_PLAIN);
	fprintf(errstream, "could not write to refdbd. Stop\n");
	return 2;
      }
  
      /* ------------------------------------------------------------ */
      /* PHASE 4 */
      /* read messages from application server */
      n_read_done = 0;

      cs_status = read_status(n_sockfd);
      if (cs_status == 400
	  || cs_status == 407
	  || cs_status == 408
	  || cs_status == 413
	  || cs_status == 414
	  || cs_status == 702) {
	/* retrieve server-generated error/status message */
	do {
	  numbyte = tread(n_sockfd, inbuffer, OUTBUF_LEN);
	  if (numbyte == -1) {
	    cgi_header(CGI_PLAIN);
	    fprintf(errstream, "%s", get_status_msg(109));
	    return 3;
	  }
	  
	  /* we rely on the fact that the server reply is no more than
	     OUTBUF_LEN in length */
	  if ((num_trailz = get_trailz(inbuffer, numbyte)) >= TERM_LEN) { /* if transmission ends */
	    n_read_done++;
	  }
	  /* write numbyte chars to output, unless this is the last chunk:
	     we do not want to write the terminating \0 */
	  if (!n_broken_pipe) {
	    if (!n_cgi) {
	      *ptr_byte_written += fwrite(inbuffer, sizeof(char), numbyte-num_trailz, pagerfp);
	    }
	  }
/* 	   printf("%s", inbuffer); */
	} while (!n_read_done);

	if (cs_status == 400
	    || cs_status == 702) {
	  return 4;
	}
	/* else: status is 408, dataset added successfully */
      }

      (*ptr_n_setcount)++;
    }
    else {
      /* we're done sending datasets */
/*       send_status(n_sockfd, 402, TERM_NO); */
    }
  } while (!n_ris_file_done);

  return 0;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  read_terminated_string(): reads a terminated string, usually a dataset

  size_t read_terminated_string returns the number of bytes written to
                            pagerfp. In case of an error, *ptr_error
			    is set to 1

  struct simplelistvals* ptr_slvals ptr to struct with data for dialog

  FILE *pagerfp ptr to a FILE struct for the output stream for server
                   messages

  int* ptr_error ptr to error variable

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
size_t read_terminated_string(struct simplelistvals* ptr_slvals, FILE* pagerfp, int* ptr_error) {
    int n_read_done = 0;
    int n_curr_trailing_z = 0;
    int n_last_trailing_z = 0;
    size_t byte_written = 0;
    int numbyte = 0;

    *ptr_error = 0;

    do { /* loop until a terminated string is complete */
/*       printf("looking for a terminated string...\n"); */
      if (n_curr_trailing_z) {
	numbyte += tread(ptr_slvals->n_sockfd, ptr_slvals->inbuffer+numbyte, TERM_LEN-n_curr_trailing_z);
      }
      else {
	numbyte = tread(ptr_slvals->n_sockfd, ptr_slvals->inbuffer, OUTBUF_LEN);
      }

      if (numbyte == -1) {
	fprintf(stderr, "%s\n", get_status_msg(109));
	*ptr_error = 1;
	return 0;
      }
      
      n_curr_trailing_z = get_trailz(ptr_slvals->inbuffer, numbyte);
/*       printf("n_curr_trailing_z went to %d<<n_last_trailing_z went to %d<<numbyte went to %d\n", n_curr_trailing_z, n_last_trailing_z, numbyte); */
      if (numbyte >= TERM_LEN) {
	if (n_curr_trailing_z >= TERM_LEN) {
	  /* terminator is complete */
/* 	  printf("found complete terminator\n"); */
	  n_last_trailing_z = 0;
	  n_read_done++;
	  /* send back confirmation to the server */
	  send_status(ptr_slvals->n_sockfd, 0, TERM_NO);
	}
	else {
	  /* terminator is still incomplete */
/* 	  printf("incomplete terminator, trying again\n"); */
	}
      }
      else if (n_curr_trailing_z == numbyte
	       && n_curr_trailing_z + n_last_trailing_z >= TERM_LEN) {
/* 	printf("completed terminator\n"); */
	n_last_trailing_z = 0;
	/* terminator is complete including the previous cycle */
	n_read_done++;
	/* send back confirmation to the server */
	send_status(ptr_slvals->n_sockfd, 0, TERM_NO);
      }
      else if (n_curr_trailing_z == numbyte) {
	/* terminator is still incomplete */
/* 	printf("incomplete terminator, trying again\n"); */
	n_last_trailing_z += n_curr_trailing_z;
	continue;
      }

      /* write numbyte chars to output, unless this is the last chunk: we do not
	 want to write the terminating \0 */
      if (!n_broken_pipe) {
	if (n_last_trailing_z) {
/* 	  printf("writing n_last_trailing_z\n"); */
	  byte_written += fwrite(cs_term, sizeof(char), n_last_trailing_z, pagerfp);
	}
/* 	printf("writing numbytes\n"); */
	byte_written += fwrite(ptr_slvals->inbuffer, sizeof(char), numbyte-n_curr_trailing_z, pagerfp);
      }
/*       printf("n_read_done is %d<<n_done is %d<<\n", n_read_done, n_done); */
/*       printf("inbuffer went to:%s<<\nn_read_done is %d<<\n", ptr_slvals->inbuffer, n_read_done); */
      fflush(stdout);
      if (!n_read_done) {
	n_last_trailing_z = n_curr_trailing_z;
      }
    } while (!n_read_done);

    return byte_written;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  pipehandler(): handler for the SIGPIPE signal. Sets the global
                 variable n_broken_pipe to non-zero. This condition
                 can be used by a function writing to a pipe to abort
                 further write attempts. Any function that uses
                 n_broken_pipe should reset it to zero when it is done
                 dealing with the broken pipe.

  void pipehandler

  int sig the received signal

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
void pipehandler(int sig) {
  n_broken_pipe = 1;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  inthandler(): handler for the SIGINT signal. Sets the global
                 variable n_abort_connect to non-zero. This function
		 can be used to make writing to and reading from
		 the application server interruptible. Any function
		 that uses n_abort_connect should reset it to zero
		 when it is done.

  void inthandler

  int sig the received signal

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
void inthandler(int sig) {
  n_abort_connect = 1;
  fprintf(stderr, "aborting...\n");
}

