/*
 * client.c,v 1.4 1994/01/31 02:04:10 franktor Exp
 */

#include "client.h"
#include <sr-address.h>
#include <malloc.h>
#include <string.h>


/*
 * handleOpenRequest(int fd, int ref, HC_OpenRequest)
 *
 * First argument is the file-descriptor in connection to the client.
 * Second argument is the ref which has been chosen by the client.
 * Third argument is a pointer to the open-request packet from the client.
 *
 * Opens a connection to a remote SR-server, sends SRInitialiseRequest.
 */

void
handleOpenRequest(int fd, int ref, HC_OpenRequest *req)
{
  SRInitialiseRequest *query;
  Client *client;
  ServerConnection *conn;
  int remoteRef;
  char *msg;
  SR_Address *where;

  client = fd2client(fd);
  if (!client)
  {
    LOG(facHigh, llevExceptions, "handleOpenRequest: No such client.");
    return;
  }
  if ( !str2sr_address ( req->address, &where, &msg ) )
  {
    HC_Packet *p;
    char buf[MAX_BUF];
    p = (HC_Packet *) malloc(sizeof(HC_Packet));
    p->type = hcOpenResponse;
    p->ref = ref;
    p->u.open_response = (HC_OpenResponse *) malloc(sizeof(HC_OpenResponse));
    sprintf(buf, "Failed to resolve address %s: %s", req->address, msg);
    LOG ( facHigh, llevExceptions, buf);
    strcat(buf, "\n");
    p->u.open_response->openAccepted = False;
    p->u.open_response->message = strdup(buf);
    p->u.open_response->preferredMessageSize = 0;
    p->u.open_response->maximumMessageSize = 0;
    p->u.open_response->initialisationStatus = False;
    p->u.open_response->implementationId = (char *) NULL;
    p->u.open_response->implementationName = (char *) NULL;
    p->u.open_response->implementationVersion = (char *) NULL;
    p->u.open_response->userInformationField = (char *) NULL;
    
    send_struct(fd, (void *) p, &client->selectOnWrite);
    return;
  }
  if (!SR_OpenAssociation(where, (presContext **) NULL, &remoteRef))
  {
    HC_Packet *p = (HC_Packet *) malloc(sizeof(HC_Packet));
    p->type = hcOpenResponse;
    p->ref = 0; /* No new ref, since open not accepted */
    p->u.open_response = (HC_OpenResponse *) malloc(sizeof(HC_OpenResponse));
    LOG(facHigh, llevExceptions, "Failed to open connection to remote server");
    p->u.open_response->openAccepted = False;
    p->u.open_response->message = strdup("Failed to open connection to remote server.");
    p->u.open_response->preferredMessageSize = 0;
    p->u.open_response->maximumMessageSize = 0;
    p->u.open_response->initialisationStatus = False;
    p->u.open_response->implementationId = (char *) NULL;
    p->u.open_response->implementationName = (char *) NULL;
    p->u.open_response->implementationVersion = (char *) NULL;
    p->u.open_response->userInformationField = (char *) NULL;
    send_struct(fd, (void *) p, &client->selectOnWrite);
    return;
  }
  LOG(facHigh, llevDebug, "Accepted association %d with SR server.", remoteRef);
  conn = (ServerConnection *) malloc(sizeof(ServerConnection));
  conn->client_ref = ref;
  conn->aborted = False;
  conn->server_ref = remoteRef;	/* What reference the server calls us by */
  conn->last_packet = hcOpenRequest;
  if (client->servers)
  {
    conn->next = client->servers;
    client->servers = conn;
  }
  else
  {
    conn->next = (ServerConnection *) NULL;
    client->servers = conn;
  }

  query = (SRInitialiseRequest *) malloc(sizeof(SRInitialiseRequest));

/*
 * From where do I get the following variables?
 * Hard coded, or should I make a configuration file?
 */
  query->protocolVersion = PROTO_V2;
  query->options = OPT_SEARCH + OPT_PRESENT + OPT_DELSET;

  query->preferredMessageSize = req->preferredMessageSize;
  query->maximumMessageSize = req->maximumMessageSize;
/*
 * Not supported in high-level protocol as now now:
 */
  query->authentication = (Authentication *) NULL;

  query->implementationId = (char *) strdup("SR High-level Client server");
  query->implementationName = (char *) strdup("HlvlClient");
  query->implementationVersion = (char *) strdup("SRHlvlClient_1.0");

  if (req->userInformationField)
  {
    query->userInformationField = (EXTERN *) malloc(sizeof(EXTERN));
    query->userInformationField->externEncoding = extEncOctetString;
    query->userInformationField->octStr = (octetString *) malloc(sizeof(octetString));
    query->userInformationField->octStr->value = (char *) strdup(req->userInformationField);
    query->userInformationField->octStr->len = strlen(req->userInformationField);
  } else
    query->userInformationField = (EXTERN *) NULL;

  if (SR_SendInitialiseRequest(conn->server_ref, query) == False)
  {
    LOG(facHigh, llevExceptions, "Failed to send SRInitialiseRequest.");
    abortServerConnection(client, conn);
  } else
    LOG(facHigh, llevTrace, "Sent SRInitialiseRequest.");
}


/*
 * handlePresentRequest(int fd, int ref, HC_PresentRequest *req)
 *
 * First argument is the file-descriptor in connection to the client.
 * Second argument is the ref which has been chosen by the client.
 * Third argument is a pointer to the present-request packet from the client.
 * 
 * Sends the present-request along to the correct SR-server.
 */

void
handlePresentRequest(int fd, int ref, HC_PresentRequest *req)
{
  SRPresentRequest *sr_req;
  Client *client;
  ServerConnection *conn;

  client = fd2client(fd);
  if (!client)
  {
    LOG(facHigh, llevExceptions, "handlePresentRequest: No such client.");
    return;
  }
  conn = find_connection(client, ref);
  if (!conn)
  {
    LOG(facHigh, llevExceptions, "handlePresentRequest: unknown ref (%d).", ref);
    return;
  }
  if (conn->last_packet != hcNone)
    LOG(facHigh, llevExceptions, "Internal: PresentRequets out of order.");
  if (conn->aborted == True)
  {
    abort_connection(client, conn);
    return;
  }
  conn->last_packet = hcPresentRequest;
  sr_req = (SRPresentRequest *) malloc(sizeof(SRPresentRequest));
  if (req->resultSetId == (char *) NULL)
    sr_req->resultSetId = (char *) NULL;
  else
    sr_req->resultSetId = strdup(req->resultSetId);
  sr_req->resultSetStartPoint = req->resultSetStartPoint;
  sr_req->numberOfRecordsRequested = req->numberOfRecordsRequested;
  sr_req->recordComposition = copyRecordComposition(req->recordComposition);
  sr_req->preferredRecordSyntax = OID_str2Oid(req->preferredRecordSyntax);

  if (SR_SendPresentRequest(conn->server_ref, sr_req) == False)
  {
    LOG(facHigh, llevExceptions, "Failed to send SRPresentRequest.");
    abortServerConnection(client,conn);
  } else
    LOG(facHigh, llevTrace, "Sent SRPresentRequest.");
}


/*
 * void handleSearchRequest(int fd, int ref, HC_SearchRequest *req)
 *
 * First argument is the file-descriptor in connection to the client.
 * Second argument is the ref which has been chosen by the client.
 * Third argument is a pointer to the search-request packet from the client.
 * 
 * Sends the request along to the correct SR-server.
 */

void handleSearchRequest(int fd, int ref, HC_SearchRequest *req)
{
  Client *client;
  SRSearchRequest *sr_req;
  int i,nrofdbs;
  DatabaseName *prev = (DatabaseName *) NULL;
  ServerConnection *conn;

  client = fd2client(fd);
  if (!client)
  {
    LOG(facHigh, llevExceptions, "handleSearchRequest: No such client.");
    return;
  }
  conn = find_connection(client, ref);
  if (!conn)
  {
    LOG(facHigh, llevExceptions, "handleSearchRequest: unknown ref (%d).", ref);
    return;
  }
  if (conn->last_packet != hcNone)
    LOG(facHigh, llevExceptions, "Internal: SearchRequets out of order.");
  if (conn->aborted == True)
  {
    abort_connection(client, conn);
    return;
  }
  conn->last_packet = hcSearchRequest;
  sr_req = (SRSearchRequest *) malloc(sizeof(SRSearchRequest));
  sr_req->smallSetUpperBound = req->smallSetUpperBound;
  sr_req->largeSetLowerBound = req->largeSetLowerBound;
  sr_req->mediumSetPresentNumber = req->mediumSetPresentNumber;
  sr_req->replaceIndicator = req->replaceIndicator;
  if (req->proposedResultSetId)
    sr_req->proposedResultSetId = strdup(req->proposedResultSetId);
  else
    sr_req->proposedResultSetId = (char *) NULL;

  for (nrofdbs = 0, prev = req->databaseNames;prev;prev = prev->next,nrofdbs++);
  if (nrofdbs)
  {
    DatabaseName *dbn;
    sr_req->databaseId = (char **) malloc(sizeof(char *) * (nrofdbs + 1));
    for (dbn = req->databaseNames,i = 0; dbn; i++,dbn = dbn->next)
    {
      if (dbn->database)
        sr_req->databaseId[i] = strdup(dbn->database);
      else
      {
        LOG(facHigh, llevExceptions, "Warning: Null-pointer in databases link.");
        sr_req->databaseId[i] = (char *) NULL;
      }
    }
    sr_req->noDatabaseIds = nrofdbs;
  }
  else
  {
    sr_req->noDatabaseIds = 0;
    sr_req->databaseId = (char **) NULL;
  }

  sr_req->smallSetRecordComposition  = copyRecordComposition(req->smallSetRecordComposition);
  sr_req->mediumSetRecordComposition = copyRecordComposition(req->mediumSetRecordComposition);
  sr_req->preferredRecordSyntax = OID_str2Oid(req->preferredRecordSyntax);
  sr_req->query = copyQuery(req->query);

  if (SR_SendSearchRequest(conn->server_ref, sr_req) == False)
  {
    LOG(facHigh, llevExceptions, "Failed to send SRSearchRequest.");
    abortServerConnection(client,conn);
  } else
    LOG(facHigh, llevTrace, "Sent SRSearchRequest.");
}

/*
 * void handleDeleteResultSetRequest(int fd, int ref, HC_DeleteResultSetRequest *req)
 *
 * First argument is the file-descriptor in connection to the client.
 * Second argument is the ref which has been chosen by the client.
 * Third argument is a pointer to the DeleteResultSetRequest packet from the client.
 * 
 * Sends the request along to the correct SR-server.
 */

void handleDeleteResultSetRequest(int fd, int ref, HC_DeleteResultSetRequest *req)
{
  SRDeleteResultSetRequest *sr_req;
  Client *client;
  ServerConnection *conn;

  client = fd2client(fd);
  if (!client)
  {
    LOG(facHigh, llevExceptions, "handleDeleteResultSetRequest: No such client.");
    return;
  }
  conn = find_connection(client, ref);
  if (!conn)
  {
    LOG(facHigh, llevExceptions, "handleDeleteResultSetRequest: unknown ref.");
    return;
  }

  if (conn->last_packet != hcNone)
    LOG(facHigh, llevExceptions, "Internal: DeleteResultSetRequets out of order.");
  if (conn->aborted == True)
  {
    abort_connection(client, conn);
    return;
  }
  conn->last_packet = hcDeleteResultSetRequest;
  sr_req = (SRDeleteResultSetRequest *) malloc(sizeof(SRDeleteResultSetRequest));
  if (req->all == True)
  {
    sr_req->deleteSetFunction = deleteSetFunction_all;
    sr_req->resultSetList = (ResultSetList *) NULL;
  }
  else
  {
    /*
     * In the future these requests can be batched to improve speed.
     */
    sr_req->deleteSetFunction = deleteSetFunction_list;
    sr_req->resultSetList = (ResultSetList *) malloc(sizeof(ResultSetList));
    if (req->resultSetId)
      sr_req->resultSetList->resultSetId = strdup(req->resultSetId);
    else
    {
      LOG(facHigh, llevExceptions, "resultSetId empty.");
      sr_req->resultSetList->resultSetId = (char *) NULL;
    }
    sr_req->resultSetList->next = (ResultSetList *) NULL;
  }

  if (SR_SendDeleteResultSetRequest(conn->server_ref, sr_req) == False) {
    LOG(facHigh, llevExceptions, "Failed to send SRDeleteResultSetRequest.");
    abortServerConnection(client,conn);
  } else
    LOG(facHigh, llevTrace, "Sent SRDeleteResultSetRequest.");
}

/*
 * void handleCloseRequest(int fd, int ref, HC_CloseRequest *req)
 *
 * First argument is the file-descriptor in connection to the client.
 * Second argument is the ref which has been chosen by the client.
 * Third argument is a pointer to the close-request packet from the client.
 * 
 * Sends the request along to the correct SR-server.
 */

void handleCloseRequest(int fd, int ref, HC_CloseRequest *req)
{
  Client *client;
  ServerConnection *conn;
  SRCloseRequest *sr_req;

  client = fd2client(fd);
  if (!client)
  {
    LOG(facHigh, llevExceptions, "handleCloseRequest: No such client.");
    return;
  }
  conn = find_connection(client, ref);
  if (!conn)
  {
    LOG(facHigh, llevExceptions, "handleCloseRequest: unknown ref.");
    return;
  }

  if (conn->last_packet != hcNone)
    LOG(facHigh, llevExceptions, "Internal: CloseRequets out of order.");
  if (conn->aborted == True)
  {
    abort_connection(client, conn);
    return;
  }
  conn->last_packet = hcCloseRequest;
  sr_req = (SRCloseRequest *) malloc(sizeof(SRCloseRequest));
  
  if (SR_SendCloseRequest(conn->server_ref, sr_req) == False)
  {
    LOG(facHigh, llevExceptions, "Failed to send SCloseRequest.");
    abortServerConnection(client,conn);
  } else
    LOG(facHigh, llevTrace, "Sent SRCloseRequest.");
}
