[Contents] [Index] [Help] [Retrace] [Browse <] [Browse >]

;/* shownote.c - Execute to compile with SAS/C 6.56
sc DATA=FAR NMINC STRMERGE STREQ NOSTKCHK SAVEDS IGNORE=73 shownote.c
slink FROM LIB:c.o shownote.o TO shownote LIBRARY LIB:sc.lib LIB:amiga.lib
quit
*/

/* (c)  Copyright 1992-1999 Amiga, Inc.   All rights reserved. */
/* The information contained herein is subject to change without    */
/* notice, and is provided "as is" without warranty of any kind,    */
/* either expressed or implied.  The entire risk as to the use of   */
/* this information is assumed by the user.                         */

/*
** Our Application Prototypes (specific to noter.c file)
*/
void    main( void );
void    TG_Init( void );
int     SS_Init( void );
int     DoER( char *, char *, char * );
void    AppPanic( char *, int );
void    HandleMsg( int );

/*
** Application-specific defines and globals
*/

char Version[] = "\0$VER: ShowNote 1.2 (1.12.91)";

/*
** The library bases...we need em later...
*/

struct Library *IntuitionBase, *SockBase;

/*
** All other includes and protos are indexed off our catch-all file
** note.h which both the client (sendnote.c) and server (shownote.c) include.
*/

#include    "note.h"


VOID    main( VOID )
{
    int     socket;         /* The socket */

    fd_set  sockmask,       /* Mask of open sockets */
            mask;           /* Return value of socketwait() */

    long    umask;          /* AmigaDOS signal mask */



    /*
    ** Call TG_Init to prepare the generic Amiga stuff for use...
    */

    TG_Init();

    /*
    ** ...and SS_Init for the socket-specific arrangements, keeping
    ** track of what it hands back.
    */

    socket = SS_Init();

    /*
    ** First, prepare the various masks for signal processing
    */

    FD_ZERO( &sockmask );
    FD_SET( socket, &sockmask );


    /*
    ** And we enter the event loop itself
    */

    while(1)
    {
        /*
        ** Reset the mask values for another pass
        */

        mask = sockmask;
        umask = SIGBREAKF_CTRL_C;

        /*
        ** selectwait is a combo network and Amiga Wait() rolled into
        ** a single call.  It allows the app to respond to both Amiga
        ** signals (CTRL-C in this case) and to network events.
        **
        ** Here, if the selectwait event is the SIGBREAK signal, we
        ** bail and AppPanic() but otherwise its a network event.
        ** This is a very crude way of handling the exit, but it
        ** is an effective one
        */

        if (selectwait( 2, &mask, NULL, NULL, NULL, &umask ) == -1 )
        {
            AppPanic("CTRL-C:\nProgram terminating!",0);
        }

        /*
        ** Since the contact between the client and server is so
        ** quick, an iterative server is adeqaute.  For cases where
        ** extended connections or concurrent connections are needed,
        ** either a state-machine or concurrent server would be a
        ** better choice.
        **/

        /*
        ** Here, we accept the pending connection (the only case
        ** possible with this mechanism) and dispatch to a routine
        ** which actually handles the client-server communication.
        */

        if (FD_ISSET( socket, &mask ))
        {
            HandleMsg( socket );
        }
        else
        {
            AppPanic("Network Signal Error!",0);
        }
    }
}


/*
**  AppPanic() - General Shutdown Routine
**
**  This routine serves to provide both a nice GUI way of indicating error
**  conditions to the user, and to handle all the aspects of shutting the
**  server down.  In a real-world application, you would probably separate
**  the error condition shutdown from the normal shutdown.  Since this is
**  an example, it would add needless complexity to the code.  Further,
**  as far as this server is concerned, shutting down _is_ an error state,
**  since SIGBREAKF_CTRL_C is a process-specific warning of shutdown.
*/



VOID    AppPanic( char *panictxt, int panicnum )
{
    char buffer[255];

    if (!panicnum)
    {
        DoER( APPNAME, panictxt, "OK" );
    }
    else
    {
        sprintf( (char *)&buffer, "%s\n\n%s", panictxt, strerror(panicnum));
        DoER( APPNAME, (char *)&buffer, "OK" );
    }
    if (SockBase)
    {
        cleanup_sockets();
        CloseLibrary(SockBase);
    }

    if (IntuitionBase)
    {
        CloseLibrary(IntuitionBase);
    }

    exit(RETURN_ERROR);

}

/*
**  DoER() - Attempt at a "generic" wrapper for EasyRequest()
**
**  Since EasyRequest(), though "easy", still requires some initialization
**  before it can be used, this routine acts as a wrapper to EasyRequest.
**  It also catches and provides a means to implement application-generic
**  values for what gets handed to the EasyRequest routine, making coding
**  just a wee bit easier.
*/
int     DoER( char *titletxt, char *msgtxt, char *btntxt )
{
    struct EasyStruct easy =    {
                                sizeof(struct EasyStruct),
                                NULL,
                                NULL,
                                NULL,
                                NULL
                                };

    int retval = 0;

    if (IntuitionBase)
    {
        if (titletxt)
        {
            easy.es_Title = titletxt;
        }
        else
        {
            easy.es_Title = APPNAME;
        }

        if (msgtxt)
        {
            easy.es_TextFormat = msgtxt;
        }
        else
        {
            easy.es_TextFormat = "Null message text!\nIsnt it?";
        }

        if (btntxt)
        {
            easy.es_GadgetFormat = btntxt;
        }
        else
        {
            easy.es_GadgetFormat = "Take off, eh!";
        }

        retval = EasyRequest( NULL, &easy, NULL, NULL );
        return (retval);
    }
}

/*
**  TG_Init() - Initializer of AOS/Intuition
**
**  This routine just handles opening Intuition, but would be a good
**  place to put any other initialization code which is specific to getting
**  an application's Amiga environment properly set up.
*/
VOID    TG_Init( VOID )
{
    IntuitionBase = OpenLibrary("intuition.library",36);
    if (!IntuitionBase)
        exit(RETURN_ERROR);
}
/*
**  SS_Init() - Initializer of shared socket library
**
**  SS_Init() handles the opening of socket.library, the formation of an
**  application-specific socket environment, and the creation of the initial
**  socket for the server.  It returns an identifier to the socket it has
**  prepared, which just happens to represent itself as an int.
*/
int     SS_Init( VOID )
{
    struct sockaddr_in  sockaddr;

    int snum, len = sizeof(sockaddr);

    /*
    ** Attempt to open socket library and initialize socket environ.
    ** If this fails, bail out to the non-returning AppPanic() routine.
    */

    /*
    ** The errno variable is a part of ANSI, and is defined in the c.o
    ** startup code.  Essentially, its where ANSI functions put their
    ** error codes when they fail.  For more information, consult a
    ** reference to ANSI C.
    */

    if (SockBase = OpenLibrary("inet:libs/socket.library",0L))
    {
        setup_sockets( 3, &errno );
    }
    else
    {
        AppPanic("Can't open socket.library!",0);
    }

    /*
    ** Open the initial socket on which incoming messages will queue for
    ** handling.  While the server is iterative, I do it this way so that
    ** SIGBREAKF_CTRL_C will continue to function.
    */


    if ((snum = socket( AF_INET, SOCK_STREAM, 0 )) == -1)
    {
        AppPanic("Socket Creation:",errno);
    }

    /*
    ** Here we clear and prepare the information to give our socket
    ** a real address on the system.
    */

    memset( &sockaddr, 0, len );
    sockaddr.sin_family = AF_INET;

        /*
        **  Following is commented out for ease of testing purposes!
        **
        **  {
        **      struct servent *servptr;
        **      char *serv = APPNAME;
        **
        **      if ((servptr = getservbyname( serv, "tcp" )) == NULL)
        **      {
        **          AppPanic("Service not in inet:db/services list!",0);
        **      }
        **      sockaddr.sin_port = servptr->s_port;
        **  }
        */

    sockaddr.sin_port = 8769;

    sockaddr.sin_addr.s_addr = INADDR_ANY;

    /*
    ** Having everything set up, we now attempt to allocate the port number
    ** for our socket.  If this fails, we bail.
    */

    if ( bind( snum, (struct sockaddr *)&sockaddr, len ) < 0 )
    {
        AppPanic("Socket Binding:",errno);
    }

    /*
    ** Okay, the socket is as ready as it gets.  Now all we need to do is to
    ** tell the system that the socket is open for business.  In an ideal
    ** world, this needs to be checked for errors, but for the scope of the
    ** example, it isnt necessary.  By the way, the '5' in the listen() call
    ** indicates the "queue size" for number of outstanding requests.
    */

    listen( snum, 5 );

    /*
    ** And last, we pass the socket number back to the main routine.
    */

    return snum;
}

/*
**  HandleMsg() - Handles client connection and message display
**
**  This is where 90% of the "function" of the program occurs.  This routine
**  connects the server to the client socket, gets the incoming message pkt,
**  acknowledges it, displays it, then terminates the client connection.
**  For doing all that, its small, a testament to how easily the actual work
**  can be done.
*/
void    HandleMsg( int sock )
{
    struct NetNote in;              /* Buffer for incoming packets */

    struct sockaddr_in saddr;       /* Socket address from accept() */
    struct in_addr sad;             /* Internet address component */

    struct hostent *hent;           /* Internet host information */

    int nsock,                      /* New socket from accept() */
        len,                        /* Length of addr from accept() */
        retv;                       /* Return value from DoER call */

    char rname[80],                 /* Buffer for titlebar string */
         *hname,                    /* Ptr to the hostname */
         *dd_addr;                  /* Ptr to the dotted-decimal address */

    /*
    ** We accept() the attempted connection on socket 'sock'
    ** which also yields the addr of the remote machine.  Then we
    ** attempt to convert the name to something meaningful.
    **
    ** First, we clear the stuff...
    */


    bzero( (char *)&rname, 80);
    bzero( (char *)&saddr, sizeof(struct sockaddr_in) );
    bzero( (char *)&sad, sizeof(struct in_addr) );
    len = sizeof(struct sockaddr_in);


    /*
    ** Then we accept the connection on the socket
    */
    /* Bug fixed 1/93: line below checked for wrong return value. Sorry Dale! */
    if (!(nsock = accept( sock, (struct sockaddr *)&saddr, &len )))
    {
        AppPanic("Accept:",errno);
    }

    /*
    ** Break the internet address out of the sockaddr_in structure and then
    ** create a dotted-decimal format string from it, for later use
    */

    sad = saddr.sin_addr;
    dd_addr = inet_ntoa(sad.s_addr);

    /*
    ** Use the internet address to find out the machine's name
    */

    if ( !(hent =
            gethostbyaddr( (char *)&sad.s_addr,
                           sizeof(struct in_addr),
                           AF_INET )))
    {
        AppPanic("Client resolution:\nAddress not in hosts db!", 0 );
    }
    hname = hent->h_name;

    /*
    ** Form the string which goes into the title bar using name & address
    */

    sprintf( rname, "FROM: %s (%s)", hname, dd_addr );



    /*
    ** Okay, now the waiting packet needs to be removed from the connected
    ** socket that accept() gave back to us.  Verify its of type NN_MSG and
    ** if not, set return type to NN_ERR.  If it is, then display it and
    ** return an NN_ACK message.
    */

    recv( nsock, (char *)&in, sizeof(struct NetNote), 0 );
    if (in.nn_Code == NN_MSG)
    {
        DisplayBeep(NULL);
        DisplayBeep(NULL);
        retv = DoER( rname, (char *)&in.nn_Text, (char *)&in.nn_Button );
        in.nn_Code = NN_ACK;
        in.nn_Retval = retv;
    }
    else
    {
        in.nn_Code = NN_ERR;
    }

    /*
    ** Having dealt with the message one way or the other, send the message
    ** back at the remote, then disconnect from the remote and return.
    */

    send( nsock, (char *)&in, sizeof(struct NetNote), 0 );
    s_close( nsock );

}