// This may look like C code, but it is really -*- C++ -*-
/*
 ************************************************************************
 *				TCP transactor
 *
 * This code is to perform a single transaction -- a request/reply
 * exchange -- with a "server" on the other end of a TCP pipe.
 * This code establishes a connection to a server, sends a simple request,
 * listens to the reply and prints it on its standard output. This
 * code can be used then to talk to any TCP server (HTTP daemon,
 * or an RPC-like service). This code is particularly useful as a scripting
 * tool (in sh or other scripts) to talk to TCP daemons.
 *
 * Synopsis:
 *	tcp-trans host:port req-string1 req-string2 ...
 *
 * This code opens a TCP pipe to host:port and sends req-string1,
 * req-string2 etc. to it. The code sends a CRLF after each req-string.
 * A req-string could be specified as "", in which case a single CRLF
 * is sent. Having sent all the req-strings the code reads from the pipe
 * and dumps everything it reads into the stdout. The server is supposed
 * to close the connection when it finished replying to the request.
 * This tcp-trans exits with a return code of 0 when the exchange
 * has concluded without any error. Otherwise, the return code is set
 * appropriately. A more detailed description of the error would also
 * be printed on stderr.
 *
 * Example:
 * 	tcp-trans localhost:79 user-name
 * will emulate finger
 * 
 *	tcp-trans localhost:80 "GET / HTTP/1.0" ""
 * will fetch the root web page off the site.
 * 
 * 	tcp-trans some.host:25 "expn <postmaster>" "quit"
 * reveals the real person behind the postmaster
 * 
 *	tcp-trans some.host:13
 * prints the current timestamp off the some.host
 * 
 * $Id: tcp-trans.cc,v 1.4 2003/09/18 19:27:28 oleg Exp oleg $
 *
 ************************************************************************
 */

#include "TCPstream.h"

#include "myenv.h"
#include "Logger.h"
#include "std.h"
using std::cout;

                                // Print help as how to use the program
static void help(const char * error_message)
{
  message("\n%s\n",error_message);
  const char * messages [] =
  { "\n\t\tTCP Transactor\n",
    "This code is to perform a single transaction -- a request/reply",
    "exchange -- with a 'server' on the other end of a TCP pipe.",
    "This code establishes a connection to a server, sends a simple request,",
    "listens to the reply and prints it on its standard output.\n",
    "Usage",
    "\ttcp-trans host:port req-string1 req-string2 ...",
    "\n",
    "This code opens a TCP pipe to host:port and sends req-string1,",
    "req-string2 etc. to it. The code sends a CRLF after each req-string.",
    "A req-string could be specified as \"\"\n",
    "\nExamples:",
    "\ttcp-trans localhost:80 'GET / HTTP/1.0' ''\n"
  };
  for(register size_t i=0; i<sizeof(messages)/sizeof(messages[0]); i++)
    message("\n%s",messages[i]);
  message("\n\n");
  exit(4);
}

			// Parse the "host:port" string and return
			// the complete socket address
			// Errors are fatal
static SocketAddr parse_host_port(const char host_port_str [])
{
  const char * const colon_pos = strchr(host_port_str,':');
  if( colon_pos == 0 )
    Logger() << "The first argument '" << host_port_str
	     << "' should be in a host:port format" << endl,
    exit(4);
   
  char buffer[64];
  if( colon_pos - host_port_str > (signed)sizeof(buffer) - 1 )
    Logger() << "The first argument '" << host_port_str
	     << "' is too long" << endl,
    exit(4);
  xstrncpy(buffer,host_port_str,colon_pos - host_port_str);
  char * p;
  const int port_no = strtol(colon_pos+1,&p,10);
  if( *p != '\0' || port_no == 0 )
    Logger() << "The port number in '" << host_port_str
	     << "' is null or ill-specified" << endl,
    exit(4);
  return SocketAddr(buffer,port_no);
}

                                // Routing module
int main(const int argc, const char * argv[])
{
  if( argc < 1 + 1 )                // First 1 stands for argv[0]
      help("at least one argument, host:port is needed");

  Logger clog;
  TCPstream tcp_stream;
  
  const SocketAddr target_addr = parse_host_port(argv[1]);
  //clog << "Attempting to connect to " << target_addr << endl;
  
  tcp_stream.connect(target_addr);
  if( !tcp_stream.good() )
    clog << "Connection failed!" << endl, exit(4);
   
  for(register int i=2; i < argc; i++)
    tcp_stream << argv[i] << "\r\n";
  tcp_stream.flush();
  
  assert( tcp_stream.good() );
  
  cout << tcp_stream.rdbuf();
  
  return 0;
}
