/*-----------------------------------------------------------------*/
/*! 
  \file scscpfileserver.c
  \brief SCSCP server
  \author  M. Gastineau 
           Astronomie et Systemes Dynamiques, IMCCE, CNRS, Observatoire de Paris. 

   Copyright, 2008, 2009, 2010, 2011, 2012, 2013,   M. Gastineau, IMCCE-CNRS
   email of the author : gastineau@imcce.fr

   \bug M. GASTINEAU 20/04/09 : update for SCSCP 1.3
   \bug M. GASTINEAU 29/03/12 : correct message if type of action is missing 
    (SCSCP_option_return_???)  and protocol is 1.2 or previous (ticket #8275)
*/
/*-----------------------------------------------------------------*/

/*-----------------------------------------------------------------*/
/* License  of this file :
 This file is "dual-licensed", you have to choose one  of the two licenses 
 below to apply on this file.
 
    CeCILL-C
    	The CeCILL-C license is close to the GNU LGPL.
    	( http://www.cecill.info/licences/Licence_CeCILL-C_V1-en.html )
 
 or CeCILL v2.0
      The CeCILL license is compatible with the GNU GPL.
      ( http://www.cecill.info/licences/Licence_CeCILL_V2-en.html )
 

This library is governed by the CeCILL-C or the CeCILL license under 
French law and abiding by the rules of distribution of free software.  
You can  use, modify and/ or redistribute the software under the terms 
of the CeCILL-C or CeCILL license as circulated by CEA, CNRS and INRIA  
at the following URL "http://www.cecill.info". 

As a counterpart to the access to the source code and  rights to copy,
modify and redistribute granted by the license, users are provided only
with a limited warranty  and the software's author,  the holder of the
economic rights,  and the successive licensors  have only  limited
liability. 

In this respect, the user's attention is drawn to the risks associated
with loading,  using,  modifying and/or developing or reproducing the
software by the user in light of its specific status of free software,
that may mean  that it is complicated to manipulate,  and  that  also
therefore means  that it is reserved for developers  and  experienced
professionals having in-depth computer knowledge. Users are therefore
encouraged to load and test the software's suitability as regards their
requirements in conditions enabling the security of their systems and/or
data to be ensured and,  more generally, to use and operate it in the 
same conditions as regards security. 

The fact that you are presently reading this means that you have had
knowledge of the CeCILL-C or CeCILL license and that you accept its terms.
*/
/*-----------------------------------------------------------------*/

#if HAVE_CONFIG_H
#include "scscpconfig.h"
#endif /*HAVE_CONFIG_H*/
#if HAVE_WINDOWS_H
#include <winsock2.h>
#include <ws2tcpip.h>
#include <windows.h>
#endif /*HAVE_WINDOWS_H*/
#include <sys/types.h>
#if HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#if HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#include <errno.h>
#if HAVE_UNISTD_H
#include <unistd.h>
#endif
#if HAVE_STRING_H
#include <string.h>
#endif
#include <libxml/parser.h>
#include <libxml/tree.h>
#if HAVE_STRING_H
#include <string.h>
#endif
#define __SCSCP_WITHIN_SCSCP
#include "scscp.h"
#include "scscpinternal.h"
#include "scscpfileserver.h"
#include "scscpfileclient.h" 
#include "scscpdebug.h"
#include "scscpprocedurecall.h"
#include "scscpprocedurecompleted.h"
#include "scscpprocedureterminated.h"


static void SCSCP_ss_closesocketclient(int socketid);
   /*! read the Connection information Message from the client */
static int SCSCP_ss_readCIM(SCSCP_Fileserver* server,int socketid, const char **supportedversion, SCSCP_status *status);
   /*! write the Connection information Message to the client */
static int SCSCP_ss_writeCIM(SCSCP_Fileserver* server,int socketid);
   /*! write the CIM version to the client */
static int SCSCP_ss_writeCIMVersion(int socketid, const char *supportedversion);

/*-----------------------------------------------------------------*/
/*! listen for the clients on the specified port
    return 0 on failure
    on error, geterror() will give the error message
    @param port (in) port on the server of the SCSCP server
    @param status (inout) error code
*/
/*-----------------------------------------------------------------*/
static int SCSCP_ss_listen_an(SCSCP_Fileserver* server, int port, SCSCP_status *status)
{
  int bres = 1;
  static int binitsocket=1;
  SCSCP_debugprint("SCSCP_ss_listen_an(%d) - entree\n", port);

  if (binitsocket && !SCSCP_initsocket())
  {
   SCSCP_debugprint("SCSCP_ss_listen_an() - socket failed\n");
   SCSCP_status_seterror(status, SCSCP_STATUS_ERRNO);
   bres = 0;
  }
  /* creer une socket pour se connecter au serveur */
  else if ((server->m_fd = socket(AF_INET, SOCK_STREAM, 0))==INVALID_SOCKET)
  {
   SCSCP_debugprint("SCSCP_ss_listen_an() - socket failed\n");
   SCSCP_status_seterror(status, SCSCP_STATUS_ERRNO);
   bres = 0;
  }
  else
  {
   int optval = 1;
   struct sockaddr_in adresseServeur;
   memset(&adresseServeur, 0, sizeof(adresseServeur));
   adresseServeur.sin_family = AF_INET;
   adresseServeur.sin_addr.s_addr = htonl(INADDR_ANY);
   adresseServeur.sin_port = htons(port);/* host to network short */

   SCSCP_debugprint("SCSCP_ss_listen_an() - socket sucess m_fd=%d\n",server->m_fd);

   /* avoid the error of type: "Address already in use" */
#if !HAVE_WINDOWS_H
#if defined(SOL_SOCKET) && defined(SO_REUSEADDR)
   setsockopt(server->m_fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
#endif
#endif
      
   /* se connecter au serveur */
   if (bind(server->m_fd,  (struct sockaddr *) &adresseServeur, sizeof(adresseServeur)) < 0)
   {
    SCSCP_debugprint("SCSCP_ss_listen_an() - bind failed\n");
    SCSCP_status_seterror(status, SCSCP_STATUS_ERRNO);
    bres = 0;
   }
   else if (listen(server->m_fd,  5) < 0)
   {
    SCSCP_debugprint("SCSCP_ss_listen_an() - listen failed\n");
    SCSCP_status_seterror(status, SCSCP_STATUS_ERRNO);
    bres = 0;
   }
  }
  
  binitsocket = 0;
  SCSCP_debugprint("SCSCP_ss_listen_an() - returns %d\n", bres);
  return bres;
}

/*-----------------------------------------------------------------*/
/*! accept an incoming connection
 @param newsocket (out) socket to perform the communication
 @param negotiatedversion (out) negotiated version of the protocol 
 @param status (inout) error code
*/
/*-----------------------------------------------------------------*/
static int SCSCP_ss_accept(SCSCP_Fileserver* server, int* newsocket, 
                           const char** negotiatedversion, SCSCP_status *status)
{
    int bres = 1;
    struct sockaddr that;
    const char *supportedversion = NULL;
    socklen_t lg = sizeof(that);
    int retry=1;
    
    SCSCP_debugprint("SCSCP_ss_accept() - enter\n");
    
    do
    {
		retry = 0;
		*newsocket = accept(server->m_fd, &that, &lg);
		if (*newsocket==INVALID_SOCKET)
		{
		 SCSCP_debugprint("SCSCP_Fileserver::Accept() - accept failed\n");
		 SCSCP_status_seterror(status, SCSCP_STATUS_ERRNO);
		 bres = 0;
		}
		else
		{
		 bres = SCSCP_ss_writeCIM(server,*newsocket);
		 if (bres) bres = SCSCP_ss_readCIM(server, *newsocket,&supportedversion, status);
		 if (bres) bres = SCSCP_ss_writeCIMVersion(*newsocket, supportedversion);
		 if (!bres) 
		 {
			SCSCP_ss_closesocketclient(*newsocket);
			retry = 1;
		 }
		}
    } while(retry);
    
    *negotiatedversion = supportedversion;
    
    SCSCP_debugprint("SCSCP_ss_accept(%d) - returns %d\n", *newsocket, bres);
    return bres;
}


/*-----------------------------------------------------------------*/
/*! close the connexion to the client
 @param socketid (in) socket descriptor
*/
/*-----------------------------------------------------------------*/
static void SCSCP_ss_closesocketclient(int socketid)
{
 if (socketid!=-1) 
 { 
  const char *msg="<?scscp quit ?>";
  send(socketid, msg, strlen(msg)*sizeof(char), 0);
  closesocket(socketid);  
 };
}


/*-----------------------------------------------------------------*/
/*! read the Connection information Message from the client
  choose the negotiated version
  and return 1 on success
 @param socketid (in) socket descriptor
 @param supportedversion (out) negotiated version.
         =NULL if no common version
 @param status (inout) error code        
 @bug M. Gastineau 09/09/08 : add bres in the the while test in the case 
      that recv is failed. 
*/
/*-----------------------------------------------------------------*/
int SCSCP_ss_readCIM(SCSCP_Fileserver* server, int socketid, const char** supportedversion, SCSCP_status *status)
{
#define CIMLEN 4094
    int bres = 1;
    char c[CIMLEN];
    char *versionstr;
    int j=-1;
    
    SCSCP_debugprint("SCSCP_ss_readCIM(,%d,) - enter\n", socketid);
    do
    {
     j++;
     bres=(recv(socketid, c+j, sizeof(char), 0)!=SOCKET_ERROR);
    } while (bres && c[j]!='\n' && j<CIMLEN);
    if (j==CIMLEN) bres = 0;
    
    if (!bres)
    {
     SCSCP_status_seterror(status, SCSCP_STATUS_ERRNO);
    }
    else
    {
     c[j] = '\0';
     SCSCP_debugprint("SCSCP_Fileserver::ReadCIM - read '%s'\n", c);
     versionstr = SCSCP_findPIstring(c,"version");
     *supportedversion = NULL;
     if (versionstr)
     {
      size_t k;
      for ( k=0; k<server->m_services.m_allowedversionscount && *supportedversion==NULL; k++)
      {
       if (strcmp(versionstr,server->m_services.m_allowedversions[k])==0) *supportedversion = server->m_services.m_allowedversions[k];
      }
     }
    }
    
    SCSCP_debugprint("SCSCP_ss_readCIM(,'%s') -returns %d\n", *supportedversion, bres);
    return bres;
}


/*-----------------------------------------------------------------*/
/*! write the Connection information Message to the client
  and return 1 on success
 @param server (inout) SCSCP server
 @param socketid (in) socket descriptor
*/
/*-----------------------------------------------------------------*/
int SCSCP_ss_writeCIM(SCSCP_Fileserver* server, int socketid)
{
    int bres;
    size_t j;
    const char *msg1="<?scscp service_name=\"";
     const char *msg2="\" service_version=\"";
     const char *msg3="\" service_id=\"";
     const char *msg4="\" scscp_versions=\"";
     const char *msg5="\" ?>\n";
     SCSCP_debugprint("SCSCP_Fileserver::WriteCIM(%d) - entree\n", socketid);
 
    bres = (send(socketid, msg1, strlen(msg1)*sizeof(char), 0)!=SOCKET_ERROR);
    if (bres) bres = (send(socketid, server->m_services.m_servicename, strlen(server->m_services.m_servicename)*sizeof(char), 0)!=SOCKET_ERROR);
    
    if (bres) bres = (send(socketid, msg2, strlen(msg2)*sizeof(char), 0)!=SOCKET_ERROR);
    if (bres) bres = (send(socketid, server->m_services.m_serviceversion, strlen(server->m_services.m_serviceversion)*sizeof(char), 0)!=SOCKET_ERROR);
    
    if (bres) bres = (send(socketid, msg3, strlen(msg3)*sizeof(char), 0)!=SOCKET_ERROR);
    if (bres) bres = (send(socketid, server->m_services.m_serviceid, strlen(server->m_services.m_serviceid)*sizeof(char), 0)!=SOCKET_ERROR);
    
    if (bres) bres = (send(socketid, msg4, strlen(msg4)*sizeof(char), 0)!=SOCKET_ERROR);
    for (j=0; j<server->m_services.m_allowedversionscount && bres; j++)
    {
      bres = (send(socketid, server->m_services.m_allowedversions[j], strlen(server->m_services.m_allowedversions[j])*sizeof(char), 0)!=SOCKET_ERROR);
      if (bres) bres = (send(socketid, " ", strlen(" ")*sizeof(char), 0)!=SOCKET_ERROR);
    }
    
    if (bres) bres = (send(socketid, msg5, strlen(msg5)*sizeof(char), 0)!=SOCKET_ERROR);
    
    SCSCP_debugprint("SCSCP_Fileserver::WriteCIM - returns %d\n", bres);
    return bres;
}

/*-----------------------------------------------------------------*/
/*! write the SCSCP version of the server the client
  and return 1 on success
  if (supportedversion==NULL) => close the session with "scscp quit" 
  and return 0
 @param socketid (in) socket descriptor
 @param supportedversion (in) negotiated version.
         =NULL if no common version => close connection
*/
/*-----------------------------------------------------------------*/
static int SCSCP_ss_writeCIMVersion(int socketid, const char *supportedversion)
{
    int bres;
    const char *msg1="<?scscp version=\"";
     const char *msg2="\" ?>\n";
    const char *msg3="<?scscp quit reason=\"not supported version\" ?>\n";
    
    SCSCP_debugprint("SCSCP_Fileserver::WriteCIMVersion(%d, %s) - enter\n", socketid, supportedversion);
 
    if (supportedversion!=NULL)
    {
     bres = (send(socketid, msg1, strlen(msg1)*sizeof(char), 0)!=SOCKET_ERROR);
     if (bres) bres = (send(socketid, supportedversion, strlen(supportedversion)*sizeof(char), 0)!=SOCKET_ERROR);
     if (bres) bres = (send(socketid, msg2, strlen(msg2)*sizeof(char), 0)!=SOCKET_ERROR);
    }
    else
    {
     bres = (send(socketid, msg3, strlen(msg3)*sizeof(char), 0)!=SOCKET_ERROR);    
     bres = 0; /* => force close connection */
    }
    SCSCP_debugprint("SCSCP_Fileserver::WriteCIMVersion - returns %d\n", bres);
    return bres;
}

/*-----------------------------------------------------------------*/
/*! checks if SCSCP server is null and returns 0 on error
  @param server (in) object to be checked
  @param status (inout) status error
*/
/*-----------------------------------------------------------------*/
static int SCSCP_ss_checkNULL(SCSCP_socketserver* server, SCSCP_status* status)
{
   int res = 1;
   
   if (server==NULL || *server==NULL)
   {
     SCSCP_status_seterror(status,  SCSCP_STATUS_SERVEROBJECTNULL);
     res = 0;
   }
   return res;
}


/*-----------------------------------------------------------------*/
/*! initialize the object server and set status if an error occurs 
 @param server (inout) object to be initialized
 @param servicename (in) name of service
 @param serviceversion (in) service version
 @param serviceid (in) service version
 @param status (inout) status error
 @param ...    (in) allowed version to be negotiated
*/
/*-----------------------------------------------------------------*/
int SCSCP_ss_init(SCSCP_socketserver* server, SCSCP_status* status, 
                  const char *servicename, const char *serviceversion, const char *serviceid, ...)
{
   va_list versions;
   int res = 1;
   
   if (server==NULL)
   {
     SCSCP_status_seterror(status,  SCSCP_STATUS_SERVEROBJECTNULL);
     res = 0;
   }
   else
   {
    SCSCP_socketserver pserver;
    *server = pserver = (SCSCP_Fileserver*)SCSCP_malloc(sizeof(SCSCP_Fileserver),status);
    if (pserver==NULL) 
    {
     res = 0;
    }
    else
    {
     /* count elements */
     size_t  counter = 0;
     va_list listcount;
     va_start(listcount, serviceid);
     while (va_arg(listcount, const char*)!=NULL) counter++;
     va_end(listcount);

     pserver->m_fd = -1; 
     pserver->m_port = -1; 
     SCSCP_ss_initcmn(&pserver->m_services, status);

     /* duplicate the elements of versions */
     pserver->m_services.m_allowedversions = (SCSCP_string*)SCSCP_malloc(sizeof(SCSCP_string)*counter, status);
     res = (pserver->m_services.m_allowedversions!=NULL);
     if (res)
     {
      size_t cpt;
      va_start(versions, serviceid);
      for( cpt=0; cpt<counter; cpt++)
      {
       pserver->m_services.m_allowedversions[cpt] = SCSCP_strdup(va_arg(versions, const char*), status);
       res = (pserver->m_services.m_allowedversions[cpt]!=NULL);
     }
      pserver->m_services.m_allowedversionscount = counter;
      va_end(versions);
     }
     /* duplicate information about the services */
     if (res)
     {
       SCSCP_ss_setservice(&pserver->m_services,servicename, serviceversion, serviceid, status);
     }
    }
   }
   return res;
}

/*-----------------------------------------------------------------*/
/*! clear the object server previously initialized by SCSCP_ss_init  
 @param server (inout) object to be deleted
 @param status (inout) status error
*/
/*-----------------------------------------------------------------*/
int SCSCP_ss_clear(SCSCP_socketserver* server, SCSCP_status* status)
{
   SCSCP_socketserver pserver;
   int res = SCSCP_ss_checkNULL(server, status);
   if (!res) return res;

   pserver = *server;
   res = SCSCP_ss_clearcmn(&pserver->m_services, status);
   free(pserver);

   return res;
}

/*-----------------------------------------------------------------*/
/*! the object server listen on the specified port  
 @param server (inout) server 
 @param port (in) port number.
   if port=0 then a random port is chosen
 @param firstavailable (in) = 0 => if the port isn't available, return an error
                            = 1 => retry until a port is available (such that >port)
 @param status (inout) status error
*/
/*-----------------------------------------------------------------*/
int SCSCP_ss_listen(SCSCP_socketserver* server, int port, int firstavailable, SCSCP_status* status)
{
   SCSCP_socketserver pserver;
   int res = SCSCP_ss_checkNULL(server, status);
   int curport = port;
   if (res) 
   {
      pserver = *server;
      do
      { 
       res = SCSCP_ss_listen_an(pserver,curport, status);
       if (res==0 && firstavailable==1) curport++;
      } while (res==0 && firstavailable==1 && curport>port);
   }
   if (res) 
   {
     	/* a random port was used */
    	if (port==0)
    	{
     		socklen_t lenadresseServeur=sizeof(struct sockaddr_in);
     		struct sockaddr_in adresseServeur;
		    memset(&adresseServeur, 0, sizeof(adresseServeur));
      		if (getsockname(pserver->m_fd, (struct sockaddr *)&adresseServeur, &lenadresseServeur)!=0) 
      		{
       			SCSCP_ss_close(server, status);
       			SCSCP_status_seterror(status, SCSCP_STATUS_ERRNO);
       			res = 0;
      		}
      		else
      		{
      			pserver->m_port = ntohs(adresseServeur.sin_port);    
     		}			
    	}
		else
		{
			pserver->m_port = curport;
		}	
   } 
   return res;
}

/*-----------------------------------------------------------------*/
/*! return the port from the server is listening.  
 it returns -1 if the server is not listening
 @param server (in) server 
*/
/*-----------------------------------------------------------------*/
int SCSCP_ss_getport(SCSCP_socketserver* server)
{
   SCSCP_socketserver pserver;
   int res = -1;
   if (server!=NULL)
   {
      pserver = *server;
      res = pserver->m_port;
   }
   return res;
}


/*-----------------------------------------------------------------*/
/*! the object server terminates to listen  
 @param server (inout) server
 @param status (inout) status error
*/
/*-----------------------------------------------------------------*/
int SCSCP_ss_close(SCSCP_socketserver* server, SCSCP_status* status)
{
   SCSCP_socketserver pserver;
   int res = SCSCP_ss_checkNULL(server, status);
   if (res) 
   {
      pserver = *server;
      if (pserver->m_fd!=-1) 
      { 
      	closesocket(pserver->m_fd);  
      	pserver->m_fd = -1;
      	pserver->m_port = -1; 
      };
   }
   return res;
}

/*-----------------------------------------------------------------*/
/*! the object waits for an incoming connection 
   and accepts a incoming client connection  
 @param server (inout) server
 @param incomingclient (out) new client
 @param status (inout) status error
*/
/*-----------------------------------------------------------------*/
int SCSCP_ss_acceptclient(SCSCP_socketserver* server, SCSCP_incomingclient* incomingclient, SCSCP_status* status)
{
   SCSCP_socketserver pserver;
   const char *supportedversion;
   
   int res = SCSCP_ss_checkNULL(server, status);
   if (res) 
   {
      int newsocket;
      pserver = *server;
      res = SCSCP_ss_accept(pserver, &newsocket, &supportedversion, status);
      if (res)
      {
       SCSCP_sc_init(incomingclient, status, supportedversion, NULL);
       if (*incomingclient!=NULL)
       {
        SCSCP_sc_setsocket(*incomingclient, newsocket);
       }
       else
       {
        SCSCP_status_seterror(status, SCSCP_STATUS_NOMEM);
        res = 0;
       }
      }
   }
   return res;
}


/*-----------------------------------------------------------------*/
/*! receive the "procedure call" message from the client :
  read the attribute of the incoming procedure call and the type of the incoming message 
  store the arguments in the openmathbuffer 
 @param client (inout) scscp client
 @param options (inout) options of the procedure return
 @param msgtype (out) type of returned message
 @param openmathbuffer (out) openmath buffer. 
                 This buffer must be freed with 'free'
 @param status (inout) status error
*/
/*-----------------------------------------------------------------*/
int SCSCP_ss_callrecvstr(SCSCP_incomingclient* client, SCSCP_calloptions* options,
                         SCSCP_msgtype *msgtype, char **openmathbuffer, SCSCP_status* status)
{
 int res = SCSCP_ss_callrecvheader(client, options, msgtype, status);
 if (res)
 {
  *openmathbuffer = SCSCP_ss_getxmlnoderawstring(client, SCSCP_ss_getxmlnode(client, status), status);
 }
 return res;
}

/*-----------------------------------------------------------------*/
/*!  receive the "procedure call" message from the client : 
    read the attribute of the incoming procedure call and the type of the incoming message 
  @param client (inout) scscp client
  @param options (inout) options of the procedure return. can't be NULL.
  @param msgtype (out) type of returned message.
   could be SCSCP_msgtype_ProcedureCall, SCSCP_msgtype_Interrupt, in the current specification
  @param status (inout) status error
*/
/*-----------------------------------------------------------------*/
int SCSCP_ss_callrecvheader(SCSCP_incomingclient* client, SCSCP_calloptions* options,
                            SCSCP_msgtype *msgtype, SCSCP_status* status)
{
   SCSCP_incomingclient pclient;
   int res;
   
   SCSCP_debugprint("SCSCP_ss_callrecvheader() - start\n");
   res = SCSCP_sc_checkNULL(client, status);
   
   if (res) 
   {
    pclient = *client;
    do
    {
     char bufferPI[SCSCP_PI_MAXLENBUFFER];
     char *callid;
     
     SCSCP_status mystatus = SCSCP_status_getinitializer();
     /* lecture du nouveau PI */
     res = SCSCP_io_ReadPI(&pclient, msgtype, bufferPI, &mystatus);
     SCSCP_status_copy(status, &mystatus);
     if (SCSCP_status_is(&mystatus)== SCSCP_STATUS_RECVQUIT) 
     {
       SCSCP_sc_setrecvquit(pclient);  
     }
     /* read the header of the procedure call */
     if (res)
     {
       switch (*msgtype)
       {
        case SCSCP_msgtype_ProcedureCall:
           res = SCSCP_procedurecall_read(client, *options, &pclient->m_iter, status);
           break;

        case SCSCP_msgtype_Interrupt:
           callid = SCSCP_findPIstring(bufferPI,"call_id");
           if (callid==NULL)
           {
            SCSCP_debugprint("SCSCP_ss_callrecvheader() - unknown call_id : PI='%s'\n",bufferPI);
            res = 0;
            SCSCP_status_seterror(status,SCSCP_STATUS_RECVCANCEL);
           }
           else
           {
            /* set the call id to options */
            char *calliddup = SCSCP_strdup(callid,status);
            if (calliddup) 
            {
             SCSCP_co_set_writablecallid(options, calliddup, status);
            }
            else
            {
             res =0;
            }
           }
           break;
         
         default: break;    
        }   
     }
    } while (res==0 && SCSCP_status_is(status)==SCSCP_STATUS_RECVCANCEL);  
   }
   
   SCSCP_debugprint("SCSCP_ss_callrecvheader(,%d,%d) - returns %d\n",(int)(*msgtype), SCSCP_status_is(status), res);
   return res;
}

/*-----------------------------------------------------------------*/
/*! write the openmath buffer to the scscp client
 @param client (inout) scscp incoming client
 @param param (in) openmath expression (must be a const char *)
 @param status (inout) status error
*/
/*-----------------------------------------------------------------*/
static int SCSCP_ss_callsendstr_callback(SCSCP_incomingclient* client, void *param, SCSCP_status* status)
{
  const char *openmathbuffer = (const char*)param;
  return SCSCP_io_write(client, openmathbuffer, status);
}

/*-----------------------------------------------------------------*/
/*!the server send the "procedure terminated" message
    with an error type and a message to the client 
 @param client (inout) scscp client
 @param options (in) options of the call
 @param cdname (in) name of the cd for the error
 @param symbolname (in) name of the symbole for the error
 @param openmathbuffer (in) openmath expression
 @param status (inout) status error
*/
/*-----------------------------------------------------------------*/
int SCSCP_ss_sendterminatedstr(SCSCP_incomingclient* client, SCSCP_returnoptions* options,  
                               const char *cdname, const char *symbolname,
                                const char *messagebuffer, SCSCP_status* status)
{
   int res = SCSCP_sc_checkNULL(client, status);
   SCSCP_debugprint("SCSCP_ss_sendterminatedstr(%p,%p,%s,%s,%s, %p) - enter\n", client, options, cdname, symbolname, messagebuffer, status);
   
   if (res) 
   {
    SCSCP_procedureterminated aprocterm;
    res &= SCSCP_procedureterminated_init2(&aprocterm, *options, cdname, symbolname, messagebuffer, status );
    res &= SCSCP_procedureterminated_write(client, &aprocterm, status);
   }
   
   SCSCP_debugprint("SCSCP_ss_sendterminatedstr(%d) - returns %d\n", SCSCP_status_is(status), res);
   return res;
}

/*-----------------------------------------------------------------*/
/*! the server send the "procedure completed" message  
    with a openmath expression as arguments  to the client
 @param client (inout) scscp client
 @param options (in) options of the call
 @param openmathbuffer (in) openmath expression
 @param status (inout) status error
*/
/*-----------------------------------------------------------------*/
int SCSCP_ss_sendcompletedstr(SCSCP_incomingclient* client, SCSCP_returnoptions* options, 
                              const char *openmathbuffer, SCSCP_status* status)
{
   return SCSCP_ss_sendcompletedhook(client, options, SCSCP_ss_callsendstr_callback, (void*)openmathbuffer, status);
}


/*-----------------------------------------------------------------*/
/*! the server send the "procedure completed" message to the client. 
    The arguments  are written by callbackwriteargs
 @param client (inout) scscp client
 @param options (in) options of the call
 @param callbackwriteargs (in) callback function 
             to write the parameters of the procedure call
 @param param (inout) opaque data given to callbackwriteargs
 @param status (inout) status error
*/
/*-----------------------------------------------------------------*/
int SCSCP_ss_sendcompletedhook(SCSCP_incomingclient* client, SCSCP_returnoptions* options, 
                          int (*callbackwriteargs)(SCSCP_io* stream, void *param, SCSCP_status* status),
                          void *param, SCSCP_status* status)
{
   int res = SCSCP_sc_checkNULL(client, status);
   if (!res) return res;
   
   res = SCSCP_procedurecompleted_write(client, *options, callbackwriteargs, param, status);
   
   return res;
}

/*-----------------------------------------------------------------*/
/*! the server sends the "information" message to the client. 
 @param client (inout) scscp client
 @param messagebuffer (in) information 
 @param status (inout) status error
*/
/*-----------------------------------------------------------------*/
int SCSCP_ss_infomessagesend (SCSCP_incomingclient* client, const char * messagebuffer, SCSCP_status* status)
{
   int res = SCSCP_sc_checkNULL(client, status);
   SCSCP_debugprint("SCSCP_ss_infomessagesend(%p,'%s',%p) - enter\n",client, messagebuffer, status);
   if (res) 
   {
     res = SCSCP_io_writef(client, status, "<?scscp info=\"", messagebuffer,"\" ?>\n", NULL);
   }
   SCSCP_debugprint("SCSCP_ss_infomessagesend(,%d) - returns %d\n",SCSCP_status_is(status), res);
   return res;
}


/*-----------------------------------------------------------------*/
/*! close an incoming connection 
 @param client (inout) scscp client
 @param status (inout) status error
*/
/*-----------------------------------------------------------------*/
int SCSCP_ss_closeincoming(SCSCP_incomingclient* client, SCSCP_status* status)
{
 int res = SCSCP_sc_checkNULL(client, status);
 if (res)
 {
   SCSCP_sc_close(client, status);
   res = SCSCP_sc_clear(client, status);
 }
 return res;
}

/*-----------------------------------------------------------------*/
/*! return the first node in the current location of the openmath message
 @param client (inout) incoming client
 @param status (inout) status error
*/
/*-----------------------------------------------------------------*/
SCSCP_xmlnodeptr SCSCP_ss_getxmlnode(SCSCP_incomingclient *client, SCSCP_status* status)
{
 int res = SCSCP_sc_checkNULL(client, status);
 return res?(*client)->m_iter:NULL;
}

/*----------------------------------------------------------------------------------------------*/
/*!  return node and its children as a raw string 
  return NULL  on error
  @param client (in) client. mustn't be NULL 
  @param node (in) current valid node. mustn't be NULL 
  @param status (in) error code
*/
/*----------------------------------------------------------------------------------------------*/
char* SCSCP_ss_getxmlnoderawstring(SCSCP_incomingclient* client, SCSCP_xmlnodeptr node, SCSCP_status *status)
{
 return SCSCP_sc_getxmlnoderawstring(client, node, status);
}

/*-----------------------------------------------------------------*/
/*! get the current encoding for the OpenMath objects 
 @param client (inout) scscp client.  mustn't be NULL
 @param encodingtype (out) current encoding. mustn't be NULL
 @param status (inout) status error
*/
/*-----------------------------------------------------------------*/
int SCSCP_ss_get_encodingtype(SCSCP_incomingclient* client, SCSCP_encodingtype* encodingtype, SCSCP_status* status)
{
   return SCSCP_sc_get_encodingtype(client, encodingtype, status);
}

/*-----------------------------------------------------------------*/
/*! set the current encoding for the OpenMath objects 
 @param client (inout) scscp client. mustn't be NULL
 @param encodingtype (in) new encoding
 @param status (inout) status error
*/
/*-----------------------------------------------------------------*/
int SCSCP_ss_set_encodingtype(SCSCP_socketclient* client, SCSCP_encodingtype encodingtype, SCSCP_status* status)
{
   return SCSCP_sc_set_encodingtype(client, encodingtype, status);
}


