/*----------------------------------------------------------------------------*/
/*                                                                            */
/*  Copyright (c) 1996 - 2001 by                                              */
/*  blaxxun interactive Inc, San Francisco, California, USA                   */
/*  All rights reserved                                                       */
/*                                                                            */
/*  This software is furnished under a license  and may be  used and  copied  */
/*  only in  accordance of the  terms of such license and with the inclusion  */
/*  of the above copyright notice.  This software or any other copies therof  */
/*  may not be provided or otherwise made available to any other person.  No  */
/*  title to and ownership of the software is hereby transferred.             */
/*                                                                            */
/*  The information in this software is subject to change without notice and  */
/*  should not be construed as a commitment by blaxxun interactive Inc.       */
/*                                                                            */
/*----------------------------------------------------------------------------*/

/*
------------------------------------------------------------------------------

  MODULE NAME:  $RCSfile: apevent.c,v $
  REVISION:     $Revision: 1.22 $
  DATE:         $Date: 2001/02/15 17:51:27 $
  AUTHOR:       $Author: peter $
  STATE:        $State: Exp $

  CREATION DATE:       Oct 29, 1996

  =DocStart= Module apevent.c
  File apevent.c

  PROJECT:            VWP         - blaxxun virtual worlds platform
  SYSTEM:             server      - server processes
  COMPONENT:          apserver    - Community Server API server

  DESCRIPTION:
      Event functions called. The functions given in this file are
      examples for the Event Functions of the Interface.

      Customize them to your own needs.

  =DocEnd=

------------------------------------------------------------------------------
*/

/*----------------------------------------------------------------------------*/
/*             LOCAL INCLUDE FILES                                            */
/*----------------------------------------------------------------------------*/
#include "apextern.h"

/*----------------------------------------------------------------------------*/
/*             MODULE VARIABLE DECLARATIONS                                   */
/*----------------------------------------------------------------------------*/

#if !defined (lint)
static char rcsid[] = "@(#) $Id: apevent.c,v 1.22 2001/02/15 17:51:27 peter Exp $";

/*
 * the following function is needed to suppress compiler warnings, don't call it
 */
static void rcsid_fkt()
{
    if( ! *rcsid )
        *rcsid = 0;
    if( 0 )
        rcsid_fkt();
}
#endif

/*----------------------------------------------------------------------------*/
/*             FUNCTION PROTOTYPES                                            */
/*----------------------------------------------------------------------------*/

/*----------------------------------------------------------------------------*/
/*             FUNCTIONS                                                      */
/*----------------------------------------------------------------------------*/

/*  =DocStart= Function chEventStartup
------------------------------------------------------------------------------
  FUNCTION NAME:        chEventStartup

  DESCRIPTION:
    This function is called when the API server is started.

    The code given as an example here creates a session to each Community Server
    given on the command line as:

        -i hostname:port

    If the option 

        -i all

    is given the example code allows sessions from any Community Server
    sending a request subcribe.

    Add your own code as you like, but make sure to create at least one
    session or to allow sessions from any hosts.

  NOTE: In order to make the process run correctly on NT this function must
        not perform any exits, use a return code of 110 or higher as the
        example does.

  RETURN VALUES:
    int rc == 0: ok process started successfully
        rc != 0: the startup failed, the process will go down with
                 the exitcode returned

------------------------------------------------------------------------------
*/
int chEventStartup(
int        argc,			/* number of parameters             */
char      *argv[]			/* comamnd line parameters          */
)
/*  =DocEnd= */
{
    int        rc;
    int        i;
    char     * ptr;
    char     * hostname = NULL;
    CH_port_t  port = 0;
    char     * addr;
    int        alen;
    int        allowUnknown = 0;
  
    PROCESS_LOG(( "AP.EV chEventStartup argc %d\n", argc ));

    /*
     * create a session of each -i hostname:port parameter found
     */
    for( i = 0; i < argc; i++ )
    {
        hostname = NULL;

        /*
         * only look at -i 
         */
        if( 0 == strncmp( argv[ i ], "-i", 2 ))
        {
            if( strlen( argv[ i ] ) > 2 )
            {
                /*
                 * case -iihostname
                 */
                hostname = argv[ i ] + 2;
            }
            else if( i < argc )
            {
                /*
                 * case -i ihostname
                 */
                hostname = argv[ i + 1];
            }
            else
            {
                break;
            }
        }

        /*
         * try to get the hostname and the port of the ID server
         */
        if( hostname )
        {
            /*
             * see whether all hosts should be allowed
             */
            if( !strcmp( hostname, "all" ))
            {
                /*
                 * remember the flag for here
                 */
                allowUnknown = 1;
            
                /*
                 * tell the API server to allow session by unknown 
                 * Community Servers
                 */
                chSessionAllowAll( allowUnknown );

                /*
                 * continue with next option
                 */
                continue;
            }

            /*
             * make sure port is invalid for now
             */
            port = 0;

            /*
             * look for first occurence of the ':' separator in the hostname
             */
            ptr = strchr( hostname, ':' );
            if( ptr )
            {
                /*
                 * terminate the hostname
                 */
                *ptr = '\0';
    
                /*
                 * skip the ':', then get the port number
                 */
                ptr++;
                port = atoi( ptr );
            }

            /*
             * if the port is still 0, the caller did not supply it
             */
            if( port == 0 )
            {
                PROCESS_ERR(( "No port given for ID server! %s\n", hostname ));
                return( 110 );
            }

            /*
             * create internet address structure for ID server
             */
            alen = chSessionGetServerAddress( hostname, port, &addr );
            if( alen < 0 )
            {
                if( alen == PACKET_ERR_HOST )
                {
                   PROCESS_ERR(( "Unkown ID server host: %s\n", hostname ));
                }
                else
                {
                    PROCESS_ERR(( "Can't create address for ID server host: %s\n",
                                  hostname ));
                }
                return( 111 );
            }

            PROCESS_LOG(( "ID       = \"%s:%d\"\n", hostname, port ));

            /*
             * create the session
             */
            if(( rc = chSessionCreate( hostname, port, addr, alen )))
            {
                PROCESS_ERR(( "Failed to create sessions stucture! rc %d\n", rc ));
                return( 112 );
            }

            /*
             * make sure we clear all memory before next loop
             */
            hostname = NULL;
            free( addr );
        }
    }

    /*
     * if sessions by any hosts are not allowed and
     * if there is no session at all, the caller did not supply any hostnames
     */
    if( !allowUnknown && chSessionResetIter() )
    {
        PROCESS_ERR(( "No ID server for any session specified, going down\n" ));
        return( 113 );
    }

    return( 0 );

} /* End of Function chEventStartup */

/*  =DocStart= Function chEventShutdown
------------------------------------------------------------------------------
  FUNCTION NAME:        chEventShutdown

  DESCRIPTION:
    This function is called when the API server is terminated.

  RETURN VALUES:
    void

------------------------------------------------------------------------------
*/
void chEventShutdown(
int exitcode				/* return code for exit call        */
)
/*  =DocEnd= */
{
    int your_exit_code = 0;

    PROCESS_LOG(( "AP.EV chEventShutdown exitcode %d\n", exitcode ));

    /*
     * do not exit this process, do all the shutdown things you need to do.
     * if you want to change the exit status of the program do so by setting
     * the global process_exitrc
     */
    if( your_exit_code )
    {
        process_exitrc = your_exit_code;
    }

    return;

} /* End of Function chEventShutdown */

/*  =DocStart= Function chEventSessionReqSubscribe
------------------------------------------------------------------------------
  FUNCTION NAME:        chEventSessionReqSubscribe

  DESCRIPTION:
    This function is called when Community Server requests the API server to
    subscribe to certain events.

    This can either happen:

    - at startup time of the API server
    - in case Community Server is restarted.

    See apextern.h for a list of possible events this function can
    subscribe to.

    The code here subscribes to all events that are possible.

    The parameter "unknown" has the following meaning.

    unknown == 0: The Community Server requesting the session request subscribe
                  has been explicitly created by this application via a
                  call to chSessionCreate.

    unknown != 0: The Community Server requesting the session request subscribe
                  has not been explictly created by this application.

                  In this case the function can decide whether it wants
                  to subscribe to the Community Server in question.

                  It can either accept the session by calling chSessionAllow
                  and then registering to certain events with the
                  Community Server. Or it can ignore the session with
                  the Community Server by doing nothing.

    The case unknown != 0 will only happen if sessions by unknown Community
    Servers were enabled during startup by a call to chSessionAllowAll.
                  
  RETURN VALUES:
    void

------------------------------------------------------------------------------
*/
void chEventSessionReqSubscribe(
CHHandle_t sessionHandle,		/* r: handle for session structure  */
int        sessionUnknown		/* r: flag: session is explicit     */
)
/*  =DocEnd= */
{
    int rc;
    int allowUnknown = 0;		/* the example code never allows    */
					/* sessions by unknown Community    */
					/* Servers                          */
  
    PROCESS_LOG(( "AP.EV chEventSessionReqSubscribe \"%s:%d\", unknown %d\n",
                  chSessionGetIdserverHostname( sessionHandle ),
                  ntohs( chSessionGetIdserverPort( sessionHandle )),
                  sessionUnknown
               ));

    /*
     * the example code here ignores all requests by Community Servers
     * that were not explictly created during startup.
     */
    if( sessionUnknown )
    {
        /*
         * the community server was not created explictly
         */
        if( allowUnknown )
        {
            /*
             * give out a log for the allow of the session
             */
            PROCESS_LOG(( "AP.EV chEventSessionReqSubscribe \"%s:%d\", allowed.\n",
                          chSessionGetIdserverHostname( sessionHandle ),
                          ntohs( chSessionGetIdserverPort( sessionHandle ))
                       ));

            /*
             * call chSessionAllow if you want to allow the session
             */
            chSessionAllow( sessionHandle );
        }
        else
        {
            /*
             * give out a log for the ignore of the session
             */
            PROCESS_LOG(( "AP.EV chEventSessionReqSubscribe \"%s:%d\", ignored.\n",
                          chSessionGetIdserverHostname( sessionHandle ),
                          ntohs( chSessionGetIdserverPort( sessionHandle ))
                       ));

            /*
             * just return, in order to ignore the session by the unknown
             * community server
             */
            return;
        }
    }

    /*
     * now that we have decided to allow the session,
     * we register for all events that are known
     */
    rc = chSessionRegisterEvent( sessionHandle,
                                 apchEventUserNew,
                                 chEventUserNew );
    if( rc != 0 )
    {
        PROCESS_ERR(( "Can't register chEventUserNew, rc = %d\n", rc ));
    }

    rc = chSessionRegisterEvent( sessionHandle,
                                 apchEventUserNewLog,
                                 chEventUserNewLog );
    if( rc != 0 )
    {
        PROCESS_ERR(( "Can't register chEventUserNewLog, rc = %d\n", rc ));
    }

    rc = chSessionRegisterEvent( sessionHandle,
                                 apchEventUserDelete,
                                 chEventUserDelete );
    if( rc != 0 )
    {
        PROCESS_ERR(( "Can't register chEventUserDelete, rc = %d\n", rc ));
    }

    rc = chSessionRegisterEvent( sessionHandle,
                                 apchEventUserSetAttr,
                                 chEventUserSetAttr );
    if( rc != 0 )
    {
        PROCESS_ERR(( "Can't register chEventUserSetAttr, rc = %d\n", rc ));
    }

    rc = chSessionRegisterEvent( sessionHandle,
                                 apchEventUserSetAttrLog,
                                 chEventUserSetAttrLog );
    if( rc != 0 )
    {
        PROCESS_ERR(( "Can't register chEventUserSetAttrLog, rc = %d\n", rc ));
    }

    rc = chSessionRegisterEvent( sessionHandle,
                                 apchEventUserTextLog,
                                 chEventUserTextLog );
    if( rc != 0 )
    {
        PROCESS_ERR(( "Can't register chEventUserTextLog, rc = %d\n", rc ));
    }

    rc = chSessionRegisterEvent( sessionHandle,
                                 apchEventUserPositionLog,
                                 chEventUserPositionLog );
    if( rc != 0 )
    {
        PROCESS_ERR(( "Can't register chEventUserPositionLog, rc = %d\n", rc ));
    }

    rc = chSessionRegisterEvent( sessionHandle,
                                 apchEventUserText,
                                 chEventUserText );
    if( rc != 0 )
    {
        PROCESS_ERR(( "Can't register chEventUserText, rc = %d\n", rc ));
    }

    rc = chSessionRegisterEvent( sessionHandle,
                                 apchEventSceneNew,
                                 chEventSceneNew );
    if( rc != 0 )
    {
        PROCESS_ERR(( "Can't register chEventSceneNew, rc = %d\n", rc ));
    }

    rc = chSessionRegisterEvent( sessionHandle,
                                 apchEventSceneDelete,
                                 chEventSceneDelete );
    if( rc != 0 )
    {
        PROCESS_ERR(( "Can't register chEventSceneDelete, rc = %d\n", rc ));
    }

    /*
     * register the event handler for udp requests
     */
    rc = chSessionRegisterEvent( sessionHandle,
                                 apchEventUdpRequest,
                                 (CHHandlerFuncPtr_t)chEventUdpRequest );
    if( rc != 0 )
    {
        PROCESS_ERR(( "Can't register chEventUdpRequest, rc = %d\n", rc ));
    }

    /*
     * register the event handler for http requests,
     *
     * Note: Http requests will only happen when the http listen
     *       port to be used is specified via the command line
     *       parameter -tp
     */
    rc = chSessionRegisterEvent( sessionHandle,
                                 apchEventHttpRequest,
                                 (CHHandlerFuncPtr_t)chEventHttpRequest );
    if( rc != 0 )
    {
        PROCESS_ERR(( "Can't register chEventHttpRequest, rc = %d\n", rc ));
    }

    /*
     * subscribe the events we have registered with Community Server
     */
    rc = chSessionCmdSubscribe( sessionHandle );
    if( rc != 0 )
    {
        PROCESS_ERR(( "Can't subscribe events, rc = %d\n", rc ));
    }

    return;

} /* End of Function chEventSessionReqSubscribe */

/*  =DocStart= Function chEventSessionAckSubscribe
------------------------------------------------------------------------------
  FUNCTION NAME:        chEventSessionAckSubscribe

  DESCRIPTION:
    This function is called when Community Server acknowledges the subscription
    for a session.

  RETURN VALUES:
    void

------------------------------------------------------------------------------
*/
void chEventSessionAckSubscribe(
CHHandle_t sessionHandle,		/* r: handle for session structure  */
int        code				/* r: unused in this function       */
)
/*  =DocEnd= */
{
    PROCESS_LOG(( "AP.EV chEventSessionAckSubscribe \"%s:%d\", code %d\n",
                  chSessionGetIdserverHostname( sessionHandle ),
                  ntohs( chSessionGetIdserverPort( sessionHandle )), code ));

    return;
} /* End of Function chEventSessionAckSubscribe */

/*  =DocStart= Function chEventSessionPeriodic
------------------------------------------------------------------------------
  FUNCTION NAME:        chEventSessionPeriodic

  DESCRIPTION:
    This function is called periodically for each session existing.
   
    The default is every 60 seconds, the default can be changed by 
    calling chSessionSetPeriod.

    The minimum value possible for period is 100000 microseconds.

  RETURN VALUES:
    void

------------------------------------------------------------------------------
*/
void chEventSessionPeriodic(
CHHandle_t sessionHandle 		/* r: handle for session structure  */
)
/*  =DocEnd= */
{
    CHHandle_t sceneHandle;
    CHHandle_t userHandle;
    int        nusers = 0;

    int        sceneurllen;
    int        scenenamelen;

    const char * sceneurl;
    const char * scenename;
    const char * nickname;
    int          nicknamelen;

    /*
     * just for fun we count the clients we know about
     */
    chUserResetIter( sessionHandle );

    /*
     * an example of a run through all users of a session
     */
    for( userHandle = chUserGetNext( sessionHandle );
         ! CHEoList( userHandle );
         userHandle = chUserGetNext( sessionHandle )
       )
    {
        nusers++;
    }

    PROCESS_LOG(( "AP.LOG Session \"%s:%d\" has %d users\n", 
                  chSessionGetIdserverHostname( sessionHandle ),
                  ntohs( chSessionGetIdserverPort( sessionHandle )),
                  nusers ));

    /*
     * lets also count all scenes existing in the session
     */
    chSceneResetIter( sessionHandle );

    /* 
     * an example of a run through all scenes of a session
     */
    for( sceneHandle = chSceneGetNext( sessionHandle );
         ! CHEoList( sceneHandle );
         sceneHandle = chSceneGetNext( sessionHandle )
       )
    {   
        /*
         * read the name and the url of the scene
         */
        scenename = chSceneGetName( sceneHandle, &scenenamelen );
        sceneurl  = chSceneGetUrl( sceneHandle, &sceneurllen );

        PROCESS_LOG(( "AP.EV Scene Url \"%.*s\" Name \"%.*s\" has %d users\n",
                      sceneurllen, sceneurl,
                      scenenamelen, scenename,
                      chSceneGetNofUsers( sceneHandle ) ));

        /*
         * lets also log all users existing in the scene
         */
        chSceneUserResetIter( sceneHandle );

        /*
         * an example of a run through all users of a scene
         */
        for( userHandle = chSceneUserGetNext( sceneHandle );
             ! CHEoList( userHandle );
             userHandle = chSceneUserGetNext( sceneHandle )
           )
        {
            nickname = chUserGetNickname( userHandle, &nicknamelen );
            PROCESS_LOG(( "AP.EV User \"%.*s\"\n", nicknamelen, nickname ));
        }
    }

    return;
} /* End of Function chEventSessionPeriodic */

/*  =DocStart= Function chEventUserNew
------------------------------------------------------------------------------
  FUNCTION NAME:        chEventUserNew

  DESCRIPTION:
    Event function for a new user event.

    This function is called whenever a client tries to enter a specific scene
    with Community Server.

    This is an example function, it is only called when the event
    apchEventUserNew is registered via a call to chSessionRegisterEvent
    and chSessionCmdSubscribe.

    The function can read the values used by the client by calls to the
    chUserGet... functions.

    The function can change the values used by a client by calls to the
    chUserSet... functions. The client will not be told about this
    change of values. This is useful for example in order to make
    the client use a different avatar without further ado.

    The function can deny a client by calling the chUserSetDenied function.
    Reason codes for the deny are specified in apextern.h.

    The function can let the client enter the scene as specified by 
    doing nothing.

    Beware: This call is blocking, Community Server will only allow the client
            to enter the scene specified after this function returns.

  RETURN VALUES:
    void

------------------------------------------------------------------------------
*/
void chEventUserNew(
CHHandle_t userHandle,       /* r: handle for user structure     */
int        code              /* r: unused in this function       */
)
/*  =DocEnd= */
{
    CH_ip_t             ip;
    CH_port_t           port;
    CH_version_t        version;
    int                 encryption;
    CHInstallId_t       installid;
    CH_4byte_t          attr4;
    
    const char * attr;
    int          attrlen;

    const char * info;
    int          infolen;

    const char * nickname;
    int          nicknamelen;

    const char * url;
    int          urllen;

    const char * sceneurl;
    int          sceneurllen;

    const char * scenename;
    int          scenenamelen;

    const char * userid;
    int          useridlen;

    const char * passwd;
    int          passwdlen;

    /*
     * just log the event
     */
    PROCESS_LOG(( "AP.EV chEventUserNew \"%lx.%lx\", code %d\n",
                  userHandle.sessionid,
                  ntohl( chUserGetId( userHandle )), code ));

    /*
     * read some and log values used by the user
     */
    userid = chUserGetUserid( userHandle, &useridlen );
    if( userid && useridlen )
    {
        PROCESS_LOG(( "AP.LOG userid \"%s\"\n", userid ));
    }
    
    passwd = chUserGetPasswd( userHandle, &passwdlen );
    if( passwd && passwdlen )
    {
        PROCESS_LOG(( "AP.LOG passwd \"%s\"\n", passwd ));
    }

    sceneurl = chUserGetSceneurl( userHandle, &sceneurllen );
    if( sceneurl && sceneurllen )
    {
        PROCESS_LOG(( "AP.LOG sceneurl \"%s\"\n", sceneurl ));
    }
    
    scenename = chUserGetSceneName( userHandle, &scenenamelen );
    if( scenename && scenenamelen )
    {
        PROCESS_LOG(( "AP.LOG scenename \"%s\"\n", scenename ));
    }

    nickname = chUserGetNickname( userHandle, &nicknamelen );
    if( nickname && nicknamelen )
    {
        /* 
         * an example of what to do if you want to set a different value
         */
        if(( !strncmp( userid, "chtest", useridlen ))
         &&( !strncmp( nickname, "Reset", nicknamelen )))
        {
            /*
             * if the userid is "chtest" and the value set is "Reset"
             * we use a different value, but don't tell the user
             */
            chUserSetNickname( userHandle, "reset by API",
                               strlen( "reset by API" ));

            nickname = chUserGetNickname( userHandle, &nicknamelen );
        }
        PROCESS_LOG(( "AP.LOG nickname \"%s\"\n", nickname ));
    }
    
    url = chUserGetUrl( userHandle, &urllen );
    if( url && urllen )
    {
        /*
         * an example of what to do if you want to set a different value
         */
        if(( !strncmp( userid, "chtest", useridlen ))
         &&( !strncmp( url, "Reset", urllen )))
        {
            /*
             * if the userid is "chtest" and the value set is "Reset"
             * we use a different value, but don't tell the user
             */
            chUserSetUrl( userHandle, "reset by API",
                          strlen( "reset by API" ));
        
            url = chUserGetUrl( userHandle, &urllen );
        }
        PROCESS_LOG(( "AP.LOG url \"%s\"\n", url ));
    }

    info = chUserGetInfo( userHandle, &infolen );
    if( info && infolen )
    {
        /*
         * an example of what to do if you want to set a different value
         */
        if(( !strncmp( userid, "chtest", useridlen ))
         &&( !strncmp( info, "Reset", infolen )))
        {
            /*
             * if the userid is "chtest" and the value set is "Reset"
             * we use a different value, but don't tell the client
             */
            chUserSetInfo( userHandle, "reset by API",
                           strlen( "reset by API" )); 
        
            info = chUserGetInfo( userHandle, &infolen );
        }

        PROCESS_LOG(( "AP.LOG info \"%s\"\n", info ));
    }

    attr = chUserGetAttr( userHandle, &attrlen );
    if( attr && attrlen )
    {
        memcpy( &attr4, attr, sizeof( attr4 ));
        PROCESS_LOG(( "AP.LOG attr \"%lx\"\n", attr4 ));
    }

    ip = chUserGetIp( userHandle );
    PROCESS_LOG(( "AP.LOG IP \"%s\"\n", chSessionIp2Str( ip )));

    port = chUserGetPort( userHandle );
    PROCESS_LOG(( "AP.LOG Port \"%d\"\n", ntohs( port )));

    version = chUserGetVersion( userHandle );
    PROCESS_LOG(( "AP.LOG Version \"%ld\"\n", version ));

    encryption = chUserGetEncryption( userHandle );
    PROCESS_LOG(( "AP.LOG Encryption \"%d\"\n", encryption ));

    installid = chUserGetInstallId( userHandle );
    PROCESS_LOG(( "AP.LOG Installid \"%lx.%lx\"\n",
                  installid.installid1, installid.installid2 ));

    /*
     * this example function denies a client called "chtest" if the
     * password has specific values these are taken as reasons
     * for the deny
     */
    if( userid && useridlen && passwd && passwdlen )
    {
        /*
         * this is an example what to do if a client should be denied
         * replace it with your own access checking
         */
        if( !strncmp( userid, "chtest", useridlen ))
        {
            if( !strncmp( passwd, "AUTHFAIL", passwdlen ))
            {
                chUserSetDenied( userHandle, CH_AUTHORISATIONFAILURE );
            }
            else if( !strncmp( passwd, "BADVERSION", passwdlen ))
            {
                chUserSetDenied( userHandle, CH_BADVERSION );
            }
            else if( !strncmp( passwd, "BADSCENE", passwdlen ))
            {
                chUserSetDenied( userHandle, CH_BADSCENE );
            }
            else if( !strncmp( passwd, "NEEDAUTH", passwdlen ))
            {
                chUserSetDenied( userHandle, CH_NEEDAUTHORISATION );
            }
            else if( !strncmp( passwd, "BADURL", passwdlen ))
            {
                chUserSetDenied( userHandle, CH_BADURL );
            }
            else if( !strncmp( passwd, "NOMORE", passwdlen ))
            {
                chUserSetDenied( userHandle, CH_NOMORE );
            }
            else if( !strncmp( passwd, "BADNAME", passwdlen ))
            {
                chUserSetDenied( userHandle, CH_BADNICKNAME );
            }
        }
    }

    return;

} /* End of Function chEventUserNew */

/*  =DocStart= Function chEventUserNewLog
------------------------------------------------------------------------------
  FUNCTION NAME:        chEventUserNewLog

  DESCRIPTION:
    Event function for a EventUserNewLog event

    This function is called whenever a client has entered a specific scene
    with Community Server.

    This is an example function, it is only called when the event
    apchEventUserNewLog is registered via a call to chSessionRegisterEvent
    and chSessionCmdSubscribe.

    The function can read the values used by the client by calls to the
    chUserGet... functions.

    Community Server has already allowed the client to enter the scene when the
    function is called.

  RETURN VALUES:
    void

------------------------------------------------------------------------------
*/
void chEventUserNewLog(
CHHandle_t userHandle,			/* r: handle for user structure     */
int        code				/* r: unused in this function       */
)
/*  =DocEnd= */
{
    const char * nickname;
    int          nicknamelen;

    const char * sceneurl;
    int          sceneurllen;

    const char * scenename;
    int          scenenamelen;

    const char * userid;
    int          useridlen;

    const char * passwd;
    int          passwdlen;

    /*
     * log the event
     */
    PROCESS_LOG(( "AP.EV chEventUserNewLog \"%lx.%lx\", code %d\n",
                  userHandle.sessionid,
                  ntohl( chUserGetId( userHandle )), code ));

    /*
     * read and log some values
     */
    sceneurl = chUserGetSceneurl( userHandle, &sceneurllen );
    if( sceneurl && sceneurllen )
    {
        PROCESS_LOG(( "AP.LOG sceneurl \"%s\"\n", sceneurl ));
    }
    
    /*
     * read and log some values 
     */
    scenename = chUserGetSceneName( userHandle, &scenenamelen );
    if( scenename && scenenamelen )
    {
        PROCESS_LOG(( "AP.LOG scenename \"%s\"\n", scenename ));
    }

    nickname = chUserGetNickname( userHandle, &nicknamelen );
    if( nickname && nicknamelen )
    {
        PROCESS_LOG(( "AP.LOG nickname \"%s\"\n", nickname ));
    }
    
    userid = chUserGetUserid( userHandle, &useridlen );
    if( userid && useridlen )
    {
        PROCESS_LOG(( "AP.LOG userid \"%s\"\n", userid ));
    }
    
    passwd = chUserGetPasswd( userHandle, &passwdlen );
    if( passwd && passwdlen )
    {
        PROCESS_LOG(( "AP.LOG passwd \"%s\"\n", passwd ));
    }

    return;

} /* End of Function chEventUserNewLog */

/*  =DocStart= Function chEventUserDelete
------------------------------------------------------------------------------
  FUNCTION NAME:        chEventUserDelete

  DESCRIPTION:
    Event function for a Delete user event

    This function is called whenever a client leaves a scene at Community Server.

    This is an example function, it is only called when the event
    apchEventUserDelete is registered via a call to chSessionRegisterEvent
    and chSessionCmdSubscribe.

    The client has already left the scene specified when this function is called.

  RETURN VALUES:
    void

------------------------------------------------------------------------------
*/
void chEventUserDelete(
CHHandle_t userHandle,			/* r: handle for user structure     */
int        code				/* r: unused in this function       */
)
/*  =DocEnd= */
{
    const char * nickname;
    int          nicknamelen;

    const char * url;
    int          urllen;

    const char * sceneurl;
    int          sceneurllen;

    const char * scenename;
    int          scenenamelen;

    const char * userid;
    int          useridlen;

    const char * passwd;
    int          passwdlen;

    /*
     * just log the event
     */
    PROCESS_LOG(( "AP.EV chEventUserDelete \"%lx.%lx\", code %d\n",
                  userHandle.sessionid,
                  ntohl( chUserGetId( userHandle )), code ));

    /*
     * read some and log values used by the client
     */
    sceneurl = chUserGetSceneurl( userHandle, &sceneurllen );
    if( sceneurl && sceneurllen )
    {
        PROCESS_LOG(( "AP.LOG sceneurl \"%s\"\n", sceneurl ));
    }
    
    scenename = chUserGetSceneName( userHandle, &scenenamelen );
    if( scenename && scenenamelen )
    {
        PROCESS_LOG(( "AP.LOG scenename \"%s\"\n", scenename ));
    }

    nickname = chUserGetNickname( userHandle, &nicknamelen );
    if( nickname && nicknamelen )
    {
        PROCESS_LOG(( "AP.LOG nickname \"%s\"\n", nickname ));
    }
    
    userid = chUserGetUserid( userHandle, &useridlen );
    if( userid && useridlen )
    {
        PROCESS_LOG(( "AP.LOG userid \"%s\"\n", userid ));
    }
    
    passwd = chUserGetPasswd( userHandle, &passwdlen );
    if( passwd && passwdlen )
    {
        PROCESS_LOG(( "AP.LOG passwd \"%s\"\n", passwd ));
    }

    url = chUserGetUrl( userHandle, &urllen );
    if( url && urllen )
    {
        PROCESS_LOG(( "AP.LOG url \"%s\"\n", url ));
    }

    return;
} /* End of Function chEventUserDelete */

/*  =DocStart= Function chEventUserSetAttr
------------------------------------------------------------------------------
  FUNCTION NAME:        chEventUserSetAttr

  DESCRIPTION:
    Event function for a chEventUserSetAttr event

    This function is called whenever a client changes one of its attributes.

    This is an example function, it is only called when the event
    chEventUserSetAttr is registered via a call to chSessionRegisterEvent
    and chSessionCmdSubscribe.

    The attributes that can be changed by a client are the url of its avatar,
    its nickname, its public info and its application attributes.

    In particular if ( attr & 1 ) is true for the application attributes,
    the client is invisible to other clients in the scene.

    The function can deny the change of attributes by calling the
    chUserSetNewDataFailed function.
    Reason codes for the deny are specified in apextern.h.

    The function can allow the change of attributes by calling the
    chUserSetNewData function.

    The function can silently ignore the change of attributes by doing
    nothing. The client will not be told about this denial of
    change of values. This is useful for example in order to make
    the client not use a different avatar without further ado.

  RETURN VALUES:
    void

------------------------------------------------------------------------------
*/
void chEventUserSetAttr(
CHHandle_t userHandle,			/* r: handle for user structure     */
int        code				/* r: code which attribute changed  */
)
/*  =DocEnd= */
{
    const char * userid;
    int          useridlen;

    const char * newdata;
    int          newdatalen;

    /*
     * log the event we received
     */
    PROCESS_LOG(( "AP.EV chEventUserSetAttr \"%lx.%lx\", code %d\n",
                  userHandle.sessionid,
                  ntohl( chUserGetId( userHandle )), code ));

    userid = chUserGetUserid( userHandle, &useridlen );

    newdata = chUserGetNewData( userHandle, &newdatalen );
    if( newdata && newdatalen )
    {
        PROCESS_LOG(( "AP.LOG newdata \"%s\"\n", newdata ));
    }

    /*
     * this is just and example, to show what to do in case the client
     * should not be allowed to set its attributes
     */
    if( userid && useridlen && newdata && newdatalen )
    {
        /*
         * this is just for test purposes, replace it with your own code
         */
        if( !strncmp( userid, "chtest", useridlen ))
        {
            if( !strncmp( newdata, "AUTHFAIL", newdatalen ))
            {
                chUserSetNewDataFailed( userHandle, CH_AUTHORISATIONFAILURE );
                return;
            }
            else if( !strncmp( newdata, "NEEDAUTH", newdatalen ))
            {
                chUserSetNewDataFailed( userHandle, CH_NEEDAUTHORISATION );
                return;
            }
            else if( !strncmp( newdata, "BADURL", newdatalen ))
            {
                chUserSetNewDataFailed( userHandle, CH_BADURL );
                return;
            }
            else if( !strncmp( newdata, "BADNAME", newdatalen ))
            {
                chUserSetNewDataFailed( userHandle, CH_BADNICKNAME );
                return;
            }
            else if( !strncmp( newdata, "IGNORE", newdatalen ))
            {
                /*
                 * if we do nothing in this function, the setattr request
                 * is ignored, but the client is not told about that
                 */
                return;
            }
        }
    }

    /*
     * we allow the client to set the attributes
     */
    chUserSetNewData( userHandle, code );

    return;
} /* End of Function chEventUserSetAttr */

/*  =DocStart= Function chEventUserSetAttrLog
------------------------------------------------------------------------------
  FUNCTION NAME:        chEventUserSetAttrLog

  DESCRIPTION:
    Event function for a apchEventUserSetAttrLog event

    This function is called whenever a client changes one of its attributes.

    This is an example function, it is only called when the event
    apchEventUserSetAttrLog is registered via a call to chSessionRegisterEvent
    and chSessionCmdSubscribe.

    The function can read the new attributes.

    The new attributes are already accepted by Community Server when this function
    is called.

  RETURN VALUES:
    void

------------------------------------------------------------------------------
*/
void chEventUserSetAttrLog(
CHHandle_t userHandle,			/* r: handle for user structure     */
int        code				/* r: code which attribute changed  */
)
/*  =DocEnd= */
{
    /*
     * log the event we received
     */
    PROCESS_LOG(( "AP.EV chEventUserSetAttrLog \"%lx.%lx\", code %d\n",
                  userHandle.sessionid,
                  ntohl( chUserGetId( userHandle )), code ));

    return;

} /* End of Function chEventUserSetAttrLog */


/*  =DocStart= Function chEventUserText
------------------------------------------------------------------------------
  FUNCTION NAME:        chEventUserText

  DESCRIPTION:
    Event function for a chEventUserText event

    This function is called whenever a client sends a text.

    The function can read the text sent by calling the 
    chUserGetText function.

    The function can find out the group id of the text group the text
    was sent to by calling the chUserGetGroupID function.

    The function can find out the topic of the text group the text
    was sent to by calling the chUserGetGroupTopic function.

    The function can ignore the sending of the text by calling the
    chUserSetTextIgnore function. The text will not be forwarded to
    any other users in this case.

    The function can replace the text sent by the user with a replacement
    text by calling the chUserSetText function.

    The client will not be told that the text was ignored or changed.

    This is an example function, it is only called when the event
    chEventUserText is registered via a call to chSessionRegisterEvent
    and chSessionCmdSubscribe.

  RETURN VALUES:
    void

------------------------------------------------------------------------------
*/
void chEventUserText(
CHHandle_t userHandle,			/* r: handle for user structure     */
int        code				/* r: unused in this function       */
)
/*  =DocEnd= */
{
    const char * userid;
    int          useridlen;

    const char * text;
    int          textlen;

    const char * topic;
    int          topiclen;

    CH_id_t      groupid;

    /*
     * log the event we received
     */
    PROCESS_LOG(( "AP.EV chEventUserText \"%lx.%lx\", code %d\n",
                  userHandle.sessionid,
                  ntohl( chUserGetId( userHandle )), code ));

    userid = chUserGetUserid( userHandle, &useridlen );

    /*
     * read the text and log it
     */
    text = chUserGetText( userHandle, &textlen );
    if( text && textlen )
    {
        PROCESS_LOG(( "AP.LOG Text \"%s\"\n", text ));
    }

    /*
     * read the group id and log it
     */
    groupid = chUserGetGroupID( userHandle );

    /*
     * read the topic and log it
     */
    topic = chUserGetGroupTopic( userHandle, &topiclen );
    if( topic && topiclen )
    {
        PROCESS_LOG(( "AP.LOG Group \"%lx\" Topic \"%s\"\n", groupid, topic ));
    }

    /*
     * this is just and example, to show what to do in case the 
     * the text should be ignored, replaced or additional text should be sent.
     */
    if( userid && useridlen && text && textlen )
    {
        /*
         * this is just for test purposes, replace it with your own code
         */
        if( !strncmp( userid, "chtest", useridlen ))
        {
            if( !strncmp( text, "IGNORE", textlen ))
            {
                PROCESS_LOG(( "AP.TXT Text ignored\n" ));
                chUserSetTextIgnore( userHandle );
                return;
            }
            else if( !strncmp( text, "REPLACE", textlen ))
            {
                PROCESS_LOG(( "AP.TXT Text replaced\n" ));
                chUserSetText( userHandle, "Text replaced!",
                               strlen( "Text replaced!" ) );
                return;
            }
            else if( !strncmp( text, "SEND", textlen ))
            {
                PROCESS_LOG(( "AP.TXT Text sent by API!\n" ));
                chUserSendText( userHandle,
                                NULL,
                                "Text sent by API!",
                                strlen( "Text sent by API!" ),
                                "Public",
                                strlen( "Public" ) );

                /*
                 * ignore the text the user sent
                 */
                chUserSetTextIgnore( userHandle );
                return;
            }
            else if( !strncmp( text, "REPLY", textlen ))
            {
                PROCESS_LOG(( "AP.TXT REPLY by API!\n" ));
                chUserSendText( userHandle,
                                &userHandle,
                                "REPLY by API!",
                                strlen( "REPLY by API!" ),
                                "Public",
                                strlen( "Public" ) );

                /*
                 * ignore the text the user sent
                 */
                chUserSetTextIgnore( userHandle );
                return;
            }

        }
    }

    /*
     * if we don't do anything the text will be forwarded to all other
     * clients in the scene
     */

    return;
} /* End of Function chEventUserText */

/*  =DocStart= Function chEventUserTextLog
------------------------------------------------------------------------------
  FUNCTION NAME:        chEventUserTextLog

  DESCRIPTION:
    Event function for a apchEventUserTextLog event

    This function is called whenever a client sends a text.

    This is an example function, it is only called when the event
    apchEventUserTextLog is registered via a call to chSessionRegisterEvent
    and chSessionCmdSubscribe.

    The function can read the text sent, by calling chUserGetText.

    The function can find out the group id of the text group the text
    was sent to by calling the chUserGetGroupID function.

    The function can find out the topic of the text group the text
    was sent to by calling the chUserGetGroupTopic function.

    The text has already been forwarded to the other clients when this
    function is called.

  RETURN VALUES:
    void

------------------------------------------------------------------------------
*/
void chEventUserTextLog(
CHHandle_t userHandle,			/* r: handle for user structure     */
int        code				/* r: unused in this function       */
)
/*  =DocEnd= */
{
    /*
     * log the event we received
     */
    PROCESS_LOG(( "AP.EV chEventUserTextLog \"%lx.%lx\", code %d\n",
                  userHandle.sessionid,
                  ntohl( chUserGetId( userHandle )), code ));

    return;

} /* End of Function chEventUserTextLog */

/*  =DocStart= Function chEventUserPositionLog
------------------------------------------------------------------------------
  FUNCTION NAME:        chEventUserPositionLog

  DESCRIPTION:
    Event function for a apchEventUserPositionLog event

    This function is called whenever a client sets its position
    with the motion server.

    This is an example function, it is only called when the event
    apchEventUserPositionLog is registered via a call to chSessionRegisterEvent
    and chSessionCmdSubscribe.

    The function can read the position set, by calling chUserGetPosition.
    The function can read the orientation set, by calling chUserGetOrientation.

    The position has already been forwarded to the other clients when this
    function is called.

  RETURN VALUES:
    void

------------------------------------------------------------------------------
*/
void chEventUserPositionLog(
CHHandle_t userHandle,			/* r: handle for user structure     */
int        code                 /* r: unused in this function       */
)
/*  =DocEnd= */
{
    CHPosition_t      position;
    CHOrientation_t   orientation;

    position = chUserGetPosition( userHandle );
    orientation = chUserGetOrientation( userHandle );

    /*
     * log the event we received
     */
    PROCESS_LOG(( "AP.EV chEventUserPositionLog \"%lx.%lx\", "
                  "%ld %ld %ld, %d %d %d %u\n",
                  userHandle.sessionid,
                  ntohl( chUserGetId( userHandle )), 
                  position.x, position.y, position.z,
                  orientation.x, orientation.y, orientation.z, orientation.a
               ));

    return;

} /* End of Function chEventUserPositionLog */

/*  =DocStart= Function chEventSceneNew
------------------------------------------------------------------------------
  FUNCTION NAME:        chEventSceneNew

  DESCRIPTION:
    Event function for a EventSceneNew event

    This function is called whenever a new scene gets created by
    Community Server.

    This is an example function, it is only called when the event
    apchEventSceneNew is registered via a call to chSessionRegisterEvent
    and chSessionCmdSubscribe.

    The function can read the values set for the scene by using the
    chSceneGet... functions.

    Community Server has already created the scene when the function is called.

  RETURN VALUES:
    void

------------------------------------------------------------------------------
*/
void chEventSceneNew(
CHHandle_t sceneHandle,			/* r: handle for scene structure    */
int        code				/* r: unused in this function       */
)
/*  =DocEnd= */
{
    int sceneurllen = 0;
    int scenenamelen = 0;
  
    const char * scenename = NULL;
    const char * sceneurl  = NULL;

    
    /*
     * log the event
     */
    PROCESS_LOG(( "AP.EV chEventSceneNew \"%lx\", code %d\n",
                  sceneHandle.sessionid, code ));

    scenename = chSceneGetName( sceneHandle, &scenenamelen );
    sceneurl  = chSceneGetUrl( sceneHandle, &sceneurllen );

    PROCESS_LOG(( "AP.EV chEventSceneNew Url \"%.*s\" Name \"%.*s\"\n",
                  sceneurllen, sceneurl,
                  scenenamelen, scenename ));

    return;

} /* End of Function chEventSceneNew */

/*  =DocStart= Function chEventSceneDelete
------------------------------------------------------------------------------
  FUNCTION NAME:        chEventSceneDelete

  DESCRIPTION:
    Event function for a EventSceneDelete event

    This function is called whenever a scene gets deleted by
    Community Server.

    This is an example function, it is only called when the event
    apchEventSceneDelete is registered via a call to chSessionRegisterEvent
    and chSessionCmdSubscribe.

    The function can read the values set for the scene by using the
    chSceneGet... functions.

    Community Server has already deleted the scene when the function is called.

  RETURN VALUES:
    void

------------------------------------------------------------------------------
*/
void chEventSceneDelete(
CHHandle_t sceneHandle,			/* r: handle for scene structure    */
int        code				/* r: unused in this function       */
)
/*  =DocEnd= */
{
    int sceneurllen = 0;
    int scenenamelen = 0;
  
    const char * scenename = NULL;
    const char * sceneurl  = NULL;

    
    /*
     * log the event
     */
    PROCESS_LOG(( "AP.EV chEventSceneDelete \"%lx\", code %d\n",
                  sceneHandle.sessionid, code ));

    scenename = chSceneGetName( sceneHandle, &scenenamelen );
    sceneurl  = chSceneGetUrl( sceneHandle, &sceneurllen );

    PROCESS_LOG(( "AP.EV chEventSceneDelete Url \"%.*s\" Name \"%.*s\" %d users\n",
                  sceneurllen, sceneurl,
                  scenenamelen, scenename,
                  chSceneGetNofUsers( sceneHandle ) ));

    return;

} /* End of Function chEventSceneDelete */


/*  =DocStart= Function chEventUdpRequest
------------------------------------------------------------------------------
  FUNCTION NAME:        chEventUdpRequest

  DESCRIPTION:
    This function is called whenever a UDP client request comes in.

    This is an example function, it is only called when the event
    apchEventUdpRequest is registered via a call to chSessionRegisterEvent.
   
    Use the RA server api call raGetArg to retrieve the arguments one by
    one.

    And use raSetResult to set result strings for the client application.

    At least one result string has to be set, otherwise the client api
    call returns with the error RA_ERR_DENIED.

  RETURN VALUES:
    void

------------------------------------------------------------------------------
*/
void chEventUdpRequest(
char            *client,		/* client that sent the request     */
int              inargc,		/* number of arguments sent         */
char            *inargv[]		/* pointerarray of arguments sent   */
)
/*  =DocEnd= */
{
    int i;
    char *ptr;
    PROCESS_LOG(( "RA.LOG chEventUdpRequest %d args.\n", inargc ));

    /*
     * we just log the arguments we got
     */
    for( i = 0; i < inargc; i++ )
    {
        ptr = raGetArg( client, i );
        if( !ptr )
        {
            ptr = "NULL";
        }
        PROCESS_LOG(( "argv[ %d ] = \"%s\"\n", i, ptr ));
    }
    
    /*
     * for now we echo the arguments if the first argument is "echo"
     */
    ptr = raGetArg( client, 0 );
    if( ptr && ( 0 == strcmp( ptr, "echo" )))
    {
        for( i = 0; i < inargc; i++ )
        {
            ptr = raGetArg( client, i );
            if( !ptr )
            {
                break;
            }
            raSetResult( client, ptr );
        }
    }
    else if( ptr && ( 0 == strcmp( ptr, "ok" )))
    {
        raSetResult( client, ptr );
    }

    /*
     * we deny all other requests by doing nothing!
     */
    return;

} /* End of Function chEventUdpRequest */


/*  =DocStart= Function chEventHttpRequest
------------------------------------------------------------------------------
  FUNCTION NAME:        chEventHttpRequest

  DESCRIPTION:
    This function is called whenever a Http client request comes in.
   
    This is an example function, it is only called when the event
    apchEventHttpRequest is registered via a call to 
    chSessionRegisterEvent and the TCP listen port is specified
    via the command line parameter -tp.

    Use the RA server raGetHttp... API functions in order to read
    the data sent by the client and use raSetHttpResult to set result
    strings for the client application.

  RETURN VALUES:
    void

------------------------------------------------------------------------------
*/
void chEventHttpRequest(
char  *connection               /* r: connection that sent the request  */
)
/*  =DocEnd= */
{
    char * url = NULL;
    size_t urllen = 0;
    char * header = NULL;
    size_t headerlen = 0;
    char * content = NULL;
    size_t contentlen = 0;

    /*
     * read the url used for the request
     */
    url = raGetHttpUrl( connection, &urllen );

    PROCESS_LOG(( "RA.LOG chEventHttpRequest %s:%d Url: %.*s\n",
                  chSessionIp2Str( raGetHttpClientIp( connection )),
                  ntohs( raGetHttpClientPort(  connection )),
                  urllen, url ));

    /*
     * read header and content, they are only given in the POST case
     */
    header = raGetHttpHeader( connection, &headerlen );
    content = raGetHttpContent( connection, &contentlen );
    if( header )
    {
        /*
         * this is a post request, print the header
         */
        PROCESS_LOG(( "RA.LOG POST Content length %ld\n%.*s\n\n",
                      contentlen, headerlen, header ));
    }

    /*
     * for now we echo the arguments if the url starts with "/echo"
     */
    if( url )
    {
        if( 0 == strncmp( url, "/echo", strlen( "/echo" ) ))
        {
            raSetHttpResult( connection, "<HTML>\n<BODY>\n" );
            raSetHttpResult( connection, url + 5 );
        }

        /*
         * we also echo the content if it is given
         */
        if( content )
        {
            raSetHttpResult( connection, "\n" );
            raSetHttpResult( connection, content );
        }
    }

    /*
     * we deny all other requests by doing nothing!
     */
    return;

} /* End of Function chEventHttpRequest */

