static const char* version_id =
  "$Id: msg_server.cc,v 3.10 1999/10/29 20:32:32 v2kea318 Exp $";

// $Log: msg_server.cc,v $
// Revision 3.10  1999/10/29  20:32:32  v2kea318
// Redesign info_writer to be derived from the binary_function template.
//
// Revision 3.9  1999/10/28  17:15:50  v2kea318
// Use a functor object to do the connection testing in check_daemons_thread.
// Ensure that we trap all exception in that thread and continue running.
//
// Revision 3.8  1999/10/26  16:01:04  v2kea318
// Got rid of the exit_core_dump stuff.  This never worked anyway.  Updated
// some of the documentation for the program.
//
// Revision 3.7  1999/10/22  20:50:46  v2kea318
// Use the OO Thread interface for thread things.
//
// Revision 3.6  1999/10/14  16:41:47  v2kea318
// Use the pseudo machine independant thread.h header to define all
// thread related things.
//
// Revision 3.5  1999/09/21  20:03:03  v2kea318
// Tell the ServerSocket::accept method to return a Pointer to a Socket that
// is thread-safe.
//
// Revision 3.4  1999/08/16  21:03:36  v2kea318
// *** empty log message ***
//
// Revision 3.3  1999/08/12  19:25:17  v2kea318
// Do not embed C++ (//) style comments inside C (/* */) style comments.
//
// Revision 3.2  1999/08/10  21:14:31  v2kea318
// Start a thread that runs periodically, iterating over the registration
// table, checking that each registered daemon is still running.  If it fails,
// deregister the entry and clean up the table.
//
// Revision 3.1  1999/08/06  17:48:11  v2kea318
// Redesigned (and simplified) the server.  It will now store information
// about users, but not sockets to their daemon processes.  Instead, the
// msg program will directly contact the recipient daemon process, using
// information obtained from the server.
//
// Revision 2.12  1999/07/30  21:38:38  v2kea318
// Redesigned the mutex handling in deliverer_struct::operator() to use
// the new Mutex class.  Managing this mutex is now easier since the
// class destructor will take care of the release for us.
//
// Revision 2.11  1999/07/30  20:40:50  v2kea318
// Use Pthread.h instead of pthread.h to define threads.  This is a
// temporary kludge because of some extraneous spaces in the standard
// version.
//
// Revision 2.10  1999/07/30  20:24:09  v2kea318
// Finished converting to G++ compliant code.
//
// Revision 2.9  1999/07/30  17:55:48  v2kea318
// Reformat the header file names for Standard C++ compliance.
//
// Revision 2.8  1999/07/09  14:50:57  v2kea318
// Reset the read_string() function usage to get rid of the optional boolean
// flag.  The routine now blocks until all bytes are read, so there is no need
// for such a flag.
//
// Revision 2.7  1999/07/06  19:47:50  v2kea318
// Added a routine to force a core dump on exit.
//
// Revision 2.6  1999/05/19  20:38:29  v2kea318
// Fixed the use the bool flag in distribute_message and in the
// deliverer_struct object to be a reference to a bool.  The for_each
// algorithm works on a copy of the struct function, not a reference,
// which means changing this flag in the () operator will not be propogated
// back to the caller unless we use a reference.
//
// Revision 2.5  1999/05/19  20:24:38  v2kea318
// Update the output of the MsgUserTable::all_users() method.
//
// Revision 2.4  1999/05/19  18:58:12  v2kea318
// *** empty log message ***
//
// Revision 2.3  1999/05/19  18:55:05  v2kea318
// Modified the delivery code to handle the case that the socket to the
// daemon process has been closed without the information node being
// deleted.  If a ClosedSocket exception is thrown, deregister the node.
// If the message is not sent to any daemon in the user list, throw
// a user_not_registered exception, since the user no longer is.
//
// Revision 2.2  1999/05/11  20:51:00  v2kea318
// Redesigned the list management in query_user_list to eliminate unnecessary
// copies.  We now obtain a pointer to the list.  The iteration over this
// list is now implemented via a for_each algorithm with a struct funct.
//
// Revision 2.1  1999/05/11  17:17:45  v2kea318
// Redesigned the server to be handle each connection to the server in
// a separate thread.  This makes for a more efficient server, since
// routing messages can take some time and handling many messages
// at the same time in a sequential way could be quite slow for users.
// Mutex objects have been added to the user information table and to
// each node in the table.  Before writing to the socket connected to
// the user's daemon program, the server obtains a lock on the mutex
// for that information node, thereby blocking other threads from writing
// to the socket at the same time.
//
// Revision 1.19  1999/05/07  21:06:08  v2kea318
// As the daemon program has been modified to deregister itself when
// killed, much of the overhead in the router is unecessary.  This
// routine has been significantly cleaned up.  The lists of recipient
// and sender information is no longer merged.  A routine to handle
// the full distribution (by iterating over a list) is added and implemented
// with a for_each algorithm from STL.  Now, when a user sends a message
// to him- or her- self, s/he will receive one copy of type ROUTE and one
// copy of type COPY.
//
// Revision 1.18  1999/05/07  20:25:25  v2kea318
// When delivering a message, get the right user id.  For the COPY, it
// should be the recipient.
//
// Revision 1.17  1999/05/07  20:20:27  v2kea318
// Nearly everything works.  The only remaining design bug is that, if a
// user sends him- or her- self a msg, only COPY types are sent.
//
// Revision 1.16  1999/05/07  18:47:59  v2kea318
// *** empty log message ***
//
// Revision 1.15  1999/05/07  16:49:00  v2kea318
// Changed the "wildcard" user id for query_user_list.
//
// Revision 1.14  1999/05/07  16:20:19  v2kea318
// Send message text as a string.
//
// Revision 1.13  1999/05/07  16:18:01  v2kea318
// Use routines from the string-transfer.h module to transfer strings.
//
// Revision 1.12  1999/05/06  19:49:33  v2kea318
// *** empty log message ***
//
// Revision 1.11  1999/05/06  19:20:15  v2kea318
// Merge two lists using the merge method for STL lists.
//
// Revision 1.10  1999/05/05  18:36:09  v2kea318
// Moved the get_action() function to a separate header file.
//
// Revision 1.9  1999/05/05  18:01:22  v2kea318
// There is no need to send a success code back to the client, simply to
// acknowledge a connection.
//
// Revision 1.8  1999/05/05  15:19:29  v2kea318
// Restructured the exception hierarchy a bit.
//
// Revision 1.7  1999/05/05  15:10:56  v2kea318
// *** empty log message ***
//
// Revision 1.6  1999/05/05  15:07:47  v2kea318
// Make ServerException derived from the standard runtime_error class.
// Moved the eval_response() routine and action_failed class to a separate
// module.
//
// Revision 1.5  1999/05/05  14:07:07  v2kea318
// Got rid of the signal handling stuff.  There's no need to worry about
// that.  If the server dies, all sockets will be closed and this will
// trigger an exception in all daemons, which will tell them that the
// server is down.
//
// Revision 1.4  1999/05/04  22:06:13  v2kea318
// Cut back on the number of responses on data transfer.  Only take (or send)
// a response from (or to) the remote system when it really needs to be
// tested, e.g., when sending an action code that the remote system may not
// understand.  When sending size_t type values, make sure we call the
// appropriate conversion routine (ntohl, htonl) for transfer across the
// network.
//
// Revision 1.3  1999/05/04  20:47:15  v2kea318
// When any termination signal is raised, handle it by first sending a
// code to all daemons currently connected that the server is about to
// die.  It's up to the daemon what to do.
//
// Revision 1.2  1999/05/04  20:26:52  v2kea318
// When routing the message, be sure to send a copy back to the sender.
//
// Revision 1.1  1999/05/03  20:10:47  v2kea318
// Initial revision
//
/***************************************************************************

The main routine for the msg registration server.  Each user daemon will
register itself with this server.  The msg routine, which routes messages
to the desired user, will query this server for information about the
user.

When the daemon program starts, it is supposed to establish a connection
to the server and register itself (with the action REGISTER_USER).  The
server will manage a table, storing information about all active users.
This table is used for routing messages.  When a CLOSE_SESSION action is
received by the server from a client, the user will be de-registered.

Transaction information for each action.  The server will send (or
receive) a response code for each transaction marked with a *.

REGISTER_USER*:
  client sends own user id as string
  client sends port number on which the daemon is running.

QUERY_REG_LIST*:
  client sends user id being queried ("-all" for all users)*
  server sends the number of elements being returned (type size_t)
  server sends the user id and machine name for each element as strings
  server sends the port number for the user's daemon.
  server sends response code

CLOSE_SESSION*:
  client sends own user id as string*

Values of type size_t are transferred over the web in the network
standard byte order, so the recipient must translate the value back
to the host standard byte order.  Only 4 bytes will be used in
transferring size_t values, regardless of the size of the size_t
type.

Values of type string are transferred according to the routines in
string-transfer.h.

***************************************************************************/

#include "Thread.h"
#include "ServerSocket.h"
#include "Socket.h"
#include <iostream>
#include <cstdlib>
#include "msg_codes.h"
#include "msg_constants.h"
#include "msg_user_table.h"
#include <stdexcept>
#include "action_evaluation.h"
#include "action_transfer.h"
#include "string-transfer.h"
#include <algorithm>
#include <functional>

// type definitions.
typedef Pointer<Socket> SocketPtr;
typedef MsgUserTable::UserInfoList UserInfoList;
typedef Pointer<MsgUserTable::UserInfoList> UserInfoListPtr;

// exception classes.
class ServerException : public action_failed {
public:
  ServerException(const string& d = "failed action",
		  msg_action_code c = general_failure) 
    : action_failed(c), desc(d) {}
  const string& describe() const { return desc; }
private:
  string desc;
};

class bad_action : public ServerException {  
  // a client requested an unrecognized action.
public:
  bad_action() 
    : ServerException("unrecognized action code requested", 
		      unrecognized_action) {}
};

class user_not_registered : public ServerException {
  // the requested user is not registered.
public:
  user_not_registered(const string& u) 
    : ServerException("user not registered", bad_user_id), uid(u)
    {}
  const string& user() const { return uid; }
private:
  string uid;
};

/* main routine for handling socket connections.  This is the initial
   routine for each thread.  Parameters will be passed via a dynamically
   allocated ThreadParameters object; the thread is responsible for
   freeing the memory for this object. */
struct ThreadParameters {
  SocketPtr sock;
  MsgUserTable& info_tab;
  ThreadParameters(SocketPtr s, MsgUserTable& t) 
    : sock(s), info_tab(t)
    {}
};
void* handle_connection(void* pars);

// subroutines, for handling the requested actions to the server
void register_user(SocketPtr& client, MsgUserTable& info);
void query_user_list(SocketPtr& client, MsgUserTable& info);
void close_session(SocketPtr& client, MsgUserTable& info);

// define a table to map action codes to their implementation routines.
typedef void (*ActionImplementation)(SocketPtr&, MsgUserTable&);
typedef struct {
  msg_action_code code;
  ActionImplementation imp;
} ActionTranslator;
static ActionTranslator actions[] = {
  { REGISTER_USER  , register_user },
  { QUERY_REG_LIST , query_user_list },
  { CLOSE_SESSION  , close_session },
  { 0              , NULL }
};

// do a table lookup for an action code.  return NULL on failure.
static ActionImplementation get_action_impl(const msg_action_code code)
{
  for (ActionTranslator* ptr = actions; ptr->imp != NULL; ptr++)
    if (code == ptr->code) return ptr->imp;

  // mustn't have found it.  return NULL
  return NULL;
}

// send server response codes.
inline void send_response(const SocketPtr& sock, 
			  const msg_action_code code = success)
{
  send_action(sock, code, false);
}

//***************************************************************************

struct CheckDaemonPars 
{
    MsgUserTable& table;
    CheckDaemonPars(MsgUserTable& t) : table(t) {}
};
static void* check_daemons_thread(void* p);

//***************************************************************************

int main(int argc, char* argv[])
{
  // build a user information table
  MsgUserTable user_info;

  try {
    /* spawn the thread that will check the registered daemons to make
       sure they are still alive. */
    Thread::create()->start(check_daemons_thread, 
			    new CheckDaemonPars(user_info));

    // build the server socket.
    ServerSocket server(msg_port);

    // start accepting connections.
    while (true) {
      // accept connections, building the client socket thread-safe.
      SocketPtr client = server.accept(true);
      Thread::create()->start(handle_connection,
			      new ThreadParameters(client, user_info));
    }

    // handle fatal exceptions.
  } catch (const SocketBase::SocketException& se) {
    cerr << "fatal socket exception:  " << se.what() << endl;
  } catch (const exception& e) {
    cerr << e.what() << endl;
  } catch (...) {
    cerr << "unhandled exception" << endl;
  }

  // we died.  we never get here.
  return EXIT_FAILURE;
}

//***************************************************************************

static inline string read_userid(SocketPtr& client)
{
  string user;
  read_string(*client, user);
  return user;
}

//***************************************************************************

void register_user(SocketPtr& client, MsgUserTable& info)
{
  // get the user id.
  string user = read_userid(client);

  // get the port number.
  size_t port;
  read_int(*client, port);

  // register.
  info.register_user(user, client, port);

  // success.
  send_response(client, success);
}

//***************************************************************************

struct info_writer : 
  public binary_function<UserInfoList::value_type, SocketPtr, void> {
  void operator () (const UserInfoList::value_type& u_info,
		    const SocketPtr& sock) const
    {
      write_string(*sock, u_info.userid);
      write_string(*sock, u_info.machine);
      write_int(*sock, u_info.port);
    }
};

void query_user_list(SocketPtr& client, MsgUserTable& info)
{
  // get the desired user id.
  string user = read_userid(client);
  
  // get the data for the desired user.
  Pointer<UserInfoList> data = (user == "-all") ? info.all_users() 
                                                : info.query_user(user);

  // is there data?
  size_t nels;
  if ((nels = data->size()) == 0) {
    send_response(client, bad_user_id);
    return;
  }
  send_response(client, success);

  // send the number of elements.
  write_int(*client, nels);

  // iterate over the data and send the information to the client.
  for_each(data->begin(), data->end(), bind2nd(info_writer(), client));

  // send success
  send_response(client, success);
}

//***************************************************************************

void close_session(SocketPtr& client, MsgUserTable& info)
{
  // get the user id.
  string user = read_userid(client);

  // remove user from the table.
  info.remove(user, client->gethostname());

  // success.
  send_response(client, success);
}

//***************************************************************************

void* handle_connection(void* pars)
{
  // get parameters.
  ThreadParameters* t_pars = static_cast<ThreadParameters *>(pars);
  MsgUserTable& user_info  = t_pars->info_tab;
  SocketPtr client         = t_pars->sock;

  // free the memory.
  delete t_pars;

  // start handling the connection
  try {
    // get the action.
    msg_action_code action = get_action(client);
    
    // implement it.
    ActionImplementation impl = get_action_impl(action);
    if (impl == NULL) throw bad_action();
    send_response(client, success); // valid action.
    (*impl)(client, user_info);
    
    // handle errors
  } catch (const action_failed& af) {
    send_response(client, af.failure_code());
  } catch (const SocketBase::ClosedSocket& cs) {
    /* if the socket is closed in mid-transaction, just eat the
       exception */
  } catch (const SocketBase::SocketIOBadNBytesException& nbn) {
    send_response(client, insufficient_data);
  } catch (const SocketBase::SocketException& se) {
    send_response(client, error_transfer_data);
  } catch (const exception& e) {
    cerr << "general exception: " << e.what() << endl;
    send_response(client, general_failure);
  } catch (...) {
    cerr << "unhandled exception" << endl;
    send_response(client, general_failure);
  }

  // that's it.
  return NULL;
}

//***************************************************************************

#include "test_connection.h"
#include <functional>

struct TestConnection 
  : public unary_function<MsgUserTable::UserInformation, void> {
  result_type operator() (const argument_type&) const;
  TestConnection(MsgUserTable& t) : table(t) {}
  MsgUserTable& table;
};

TestConnection::result_type
TestConnection::operator() (const TestConnection::argument_type& info) const
{
  try {
    test_connection(info.machine, info.port);
  } catch (const SocketBase::SocketException&) {
    // took some socket exception, so deregister the daemon
    table.remove(info.userid, info.machine);
  }
}

//***************************************************************************

void* check_daemons_thread(void* p)
{
    // get parameters.
    CheckDaemonPars* pars = static_cast<CheckDaemonPars *>(p);
    MsgUserTable& table = pars->table;
    delete pars;

    // infinite loop.
    while (true) {
      try {
	// iterate over the table, testing each daemon.
	Pointer<UserInfoList> users = table.all_users();
	for_each(users->begin(), users->end(), TestConnection(table));
      } catch (const exception& e) {
	cerr << "check_daemons_thread exception:  " << e.what() << endl;
      } catch (...) {
	cerr << "check_daemons_thread: unknown exception" << endl;
      }

      // sleep for 3 minutes.
      sleep(180);
    }

    return NULL;    
}

