home *** CD-ROM | disk | FTP | other *** search
/ Amiga Developer CD v1.2 / amidev_cd_12.iso / reference / amiga_mail_vol2 / viii-31 / shownote.c < prev   
C/C++ Source or Header  |  1996-01-30  |  12KB  |  451 lines

  1. ;/* shownote.c - Execute to compile with SAS/C 6.56
  2. sc DATA=FAR NMINC STRMERGE STREQ NOSTKCHK SAVEDS IGNORE=73 shownote.c
  3. slink FROM LIB:c.o shownote.o TO shownote LIBRARY LIB:sc.lib LIB:amiga.lib
  4. quit
  5. */
  6.  
  7. /* (c)  Copyright 1992-93 Commodore-Amiga, Inc.   All rights reserved. */
  8. /* The information contained herein is subject to change without    */
  9. /* notice, and is provided "as is" without warranty of any kind,    */
  10. /* either expressed or implied.  The entire risk as to the use of   */
  11. /* this information is assumed by the user.                         */
  12.  
  13. /*
  14. ** Our Application Prototypes (specific to noter.c file)
  15. */
  16. void    main( void );
  17. void    TG_Init( void );
  18. int     SS_Init( void );
  19. int     DoER( char *, char *, char * );
  20. void    AppPanic( char *, int );
  21. void    HandleMsg( int );
  22.  
  23. /*
  24. ** Application-specific defines and globals
  25. */
  26.  
  27. char Version[] = "\0$VER: ShowNote 1.2 (1.12.91)";
  28.  
  29. /*
  30. ** The library bases...we need em later...
  31. */
  32.  
  33. struct Library *IntuitionBase, *SockBase;
  34.  
  35. /*
  36. ** All other includes and protos are indexed off our catch-all file
  37. ** note.h which both the client (sendnote.c) and server (shownote.c) include.
  38. */
  39.  
  40. #include    "note.h"
  41.  
  42.  
  43. VOID    main( VOID )
  44. {
  45.     int     socket;         /* The socket */
  46.  
  47.     fd_set  sockmask,       /* Mask of open sockets */
  48.             mask;           /* Return value of socketwait() */
  49.  
  50.     long    umask;          /* AmigaDOS signal mask */
  51.  
  52.  
  53.  
  54.     /*
  55.     ** Call TG_Init to prepare the generic Amiga stuff for use...
  56.     */
  57.  
  58.     TG_Init();
  59.  
  60.     /*
  61.     ** ...and SS_Init for the socket-specific arrangements, keeping
  62.     ** track of what it hands back.
  63.     */
  64.  
  65.     socket = SS_Init();
  66.  
  67.     /*
  68.     ** First, prepare the various masks for signal processing
  69.     */
  70.  
  71.     FD_ZERO( &sockmask );
  72.     FD_SET( socket, &sockmask );
  73.  
  74.  
  75.     /*
  76.     ** And we enter the event loop itself
  77.     */
  78.  
  79.     while(1)
  80.     {
  81.         /*
  82.         ** Reset the mask values for another pass
  83.         */
  84.  
  85.         mask = sockmask;
  86.         umask = SIGBREAKF_CTRL_C;
  87.  
  88.         /*
  89.         ** selectwait is a combo network and Amiga Wait() rolled into
  90.         ** a single call.  It allows the app to respond to both Amiga
  91.         ** signals (CTRL-C in this case) and to network events.
  92.         **
  93.         ** Here, if the selectwait event is the SIGBREAK signal, we
  94.         ** bail and AppPanic() but otherwise its a network event.
  95.         ** This is a very crude way of handling the exit, but it
  96.         ** is an effective one
  97.         */
  98.  
  99.         if (selectwait( 2, &mask, NULL, NULL, NULL, &umask ) == -1 )
  100.         {
  101.             AppPanic("CTRL-C:\nProgram terminating!",0);
  102.         }
  103.  
  104.         /*
  105.         ** Since the contact between the client and server is so
  106.         ** quick, an iterative server is adeqaute.  For cases where
  107.         ** extended connections or concurrent connections are needed,
  108.         ** either a state-machine or concurrent server would be a
  109.         ** better choice.
  110.         **/
  111.  
  112.         /*
  113.         ** Here, we accept the pending connection (the only case
  114.         ** possible with this mechanism) and dispatch to a routine
  115.         ** which actually handles the client-server communication.
  116.         */
  117.  
  118.         if (FD_ISSET( socket, &mask ))
  119.         {
  120.             HandleMsg( socket );
  121.         }
  122.         else
  123.         {
  124.             AppPanic("Network Signal Error!",0);
  125.         }
  126.     }
  127. }
  128.  
  129.  
  130. /*
  131. **  AppPanic() - General Shutdown Routine
  132. **
  133. **  This routine serves to provide both a nice GUI way of indicating error
  134. **  conditions to the user, and to handle all the aspects of shutting the
  135. **  server down.  In a real-world application, you would probably separate
  136. **  the error condition shutdown from the normal shutdown.  Since this is
  137. **  an example, it would add needless complexity to the code.  Further,
  138. **  as far as this server is concerned, shutting down _is_ an error state,
  139. **  since SIGBREAKF_CTRL_C is a process-specific warning of shutdown.
  140. */
  141.  
  142.  
  143.  
  144. VOID    AppPanic( char *panictxt, int panicnum )
  145. {
  146.     char buffer[255];
  147.  
  148.     if (!panicnum)
  149.     {
  150.         DoER( APPNAME, panictxt, "OK" );
  151.     }
  152.     else
  153.     {
  154.         sprintf( (char *)&buffer, "%s\n\n%s", panictxt, strerror(panicnum));
  155.         DoER( APPNAME, (char *)&buffer, "OK" );
  156.     }
  157.     if (SockBase)
  158.     {
  159.         cleanup_sockets();
  160.         CloseLibrary(SockBase);
  161.     }
  162.  
  163.     if (IntuitionBase)
  164.     {
  165.         CloseLibrary(IntuitionBase);
  166.     }
  167.  
  168.     exit(RETURN_ERROR);
  169.  
  170. }
  171.  
  172. /*
  173. **  DoER() - Attempt at a "generic" wrapper for EasyRequest()
  174. **
  175. **  Since EasyRequest(), though "easy", still requires some initialization
  176. **  before it can be used, this routine acts as a wrapper to EasyRequest.
  177. **  It also catches and provides a means to implement application-generic
  178. **  values for what gets handed to the EasyRequest routine, making coding
  179. **  just a wee bit easier.
  180. */
  181. int     DoER( char *titletxt, char *msgtxt, char *btntxt )
  182. {
  183.     struct EasyStruct easy =    {
  184.                                 sizeof(struct EasyStruct),
  185.                                 NULL,
  186.                                 NULL,
  187.                                 NULL,
  188.                                 NULL
  189.                                 };
  190.  
  191.     int retval = 0;
  192.  
  193.     if (IntuitionBase)
  194.     {
  195.         if (titletxt)
  196.         {
  197.             easy.es_Title = titletxt;
  198.         }
  199.         else
  200.         {
  201.             easy.es_Title = APPNAME;
  202.         }
  203.  
  204.         if (msgtxt)
  205.         {
  206.             easy.es_TextFormat = msgtxt;
  207.         }
  208.         else
  209.         {
  210.             easy.es_TextFormat = "Null message text!\nIsnt it?";
  211.         }
  212.  
  213.         if (btntxt)
  214.         {
  215.             easy.es_GadgetFormat = btntxt;
  216.         }
  217.         else
  218.         {
  219.             easy.es_GadgetFormat = "Take off, eh!";
  220.         }
  221.  
  222.         retval = EasyRequest( NULL, &easy, NULL, NULL );
  223.         return (retval);
  224.     }
  225. }
  226.  
  227. /*
  228. **  TG_Init() - Initializer of AOS/Intuition
  229. **
  230. **  This routine just handles opening Intuition, but would be a good
  231. **  place to put any other initialization code which is specific to getting
  232. **  an application's Amiga environment properly set up.
  233. */
  234. VOID    TG_Init( VOID )
  235. {
  236.     IntuitionBase = OpenLibrary("intuition.library",36);
  237.     if (!IntuitionBase)
  238.         exit(RETURN_ERROR);
  239. }
  240. /*
  241. **  SS_Init() - Initializer of shared socket library
  242. **
  243. **  SS_Init() handles the opening of socket.library, the formation of an
  244. **  application-specific socket environment, and the creation of the initial
  245. **  socket for the server.  It returns an identifier to the socket it has
  246. **  prepared, which just happens to represent itself as an int.
  247. */
  248. int     SS_Init( VOID )
  249. {
  250.     struct sockaddr_in  sockaddr;
  251.  
  252.     int snum, len = sizeof(sockaddr);
  253.  
  254.     /*
  255.     ** Attempt to open socket library and initialize socket environ.
  256.     ** If this fails, bail out to the non-returning AppPanic() routine.
  257.     */
  258.  
  259.     /*
  260.     ** The errno variable is a part of ANSI, and is defined in the c.o
  261.     ** startup code.  Essentially, its where ANSI functions put their
  262.     ** error codes when they fail.  For more information, consult a
  263.     ** reference to ANSI C.
  264.     */
  265.  
  266.     if (SockBase = OpenLibrary("inet:libs/socket.library",0L))
  267.     {
  268.         setup_sockets( 3, &errno );
  269.     }
  270.     else
  271.     {
  272.         AppPanic("Can't open socket.library!",0);
  273.     }
  274.  
  275.     /*
  276.     ** Open the initial socket on which incoming messages will queue for
  277.     ** handling.  While the server is iterative, I do it this way so that
  278.     ** SIGBREAKF_CTRL_C will continue to function.
  279.     */
  280.  
  281.  
  282.     if ((snum = socket( AF_INET, SOCK_STREAM, 0 )) == -1)
  283.     {
  284.         AppPanic("Socket Creation:",errno);
  285.     }
  286.  
  287.     /*
  288.     ** Here we clear and prepare the information to give our socket
  289.     ** a real address on the system.
  290.     */
  291.  
  292.     memset( &sockaddr, 0, len );
  293.     sockaddr.sin_family = AF_INET;
  294.  
  295.     /*
  296.     **  Following is commented out for ease of testing purposes!
  297.     **
  298.     **  {
  299.     **      struct servent *servptr;
  300.     **      char *serv = APPNAME;
  301.     **
  302.     **      if ((servptr = getservbyname( serv, "tcp" )) == NULL)
  303.     **      {
  304.     **          AppPanic("Service not in inet:db/services list!",0);
  305.     **      }
  306.     **      sockaddr.sin_port = servptr->s_port;
  307.     **  }
  308.     */
  309.  
  310.     sockaddr.sin_port = 8769;
  311.  
  312.     sockaddr.sin_addr.s_addr = INADDR_ANY;
  313.  
  314.     /*
  315.     ** Having everything set up, we now attempt to allocate the port number
  316.     ** for our socket.  If this fails, we bail.
  317.     */
  318.  
  319.     if ( bind( snum, (struct sockaddr *)&sockaddr, len ) < 0 )
  320.     {
  321.         AppPanic("Socket Binding:",errno);
  322.     }
  323.  
  324.     /*
  325.     ** Okay, the socket is as ready as it gets.  Now all we need to do is to
  326.     ** tell the system that the socket is open for business.  In an ideal
  327.     ** world, this needs to be checked for errors, but for the scope of the
  328.     ** example, it isnt necessary.  By the way, the '5' in the listen() call
  329.     ** indicates the "queue size" for number of outstanding requests.
  330.     */
  331.  
  332.     listen( snum, 5 );
  333.  
  334.     /*
  335.     ** And last, we pass the socket number back to the main routine.
  336.     */
  337.  
  338.     return snum;
  339. }
  340.  
  341. /*
  342. **  HandleMsg() - Handles client connection and message display
  343. **
  344. **  This is where 90% of the "function" of the program occurs.  This routine
  345. **  connects the server to the client socket, gets the incoming message pkt,
  346. **  acknowledges it, displays it, then terminates the client connection.
  347. **  For doing all that, its small, a testament to how easily the actual work
  348. **  can be done.
  349. */
  350. void    HandleMsg( int sock )
  351. {
  352.     struct NetNote in;              /* Buffer for incoming packets */
  353.  
  354.     struct sockaddr_in saddr;       /* Socket address from accept() */
  355.     struct in_addr sad;             /* Internet address component */
  356.  
  357.     struct hostent *hent;           /* Internet host information */
  358.  
  359.     int nsock,                      /* New socket from accept() */
  360.         len,                        /* Length of addr from accept() */
  361.         retv;                       /* Return value from DoER call */
  362.  
  363.     char rname[80],                 /* Buffer for titlebar string */
  364.          *hname,                    /* Ptr to the hostname */
  365.          *dd_addr;                  /* Ptr to the dotted-decimal address */
  366.  
  367.     /*
  368.     ** We accept() the attempted connection on socket 'sock'
  369.     ** which also yields the addr of the remote machine.  Then we
  370.     ** attempt to convert the name to something meaningful.
  371.     **
  372.     ** First, we clear the stuff...
  373.     */
  374.  
  375.  
  376.     bzero( (char *)&rname, 80);
  377.     bzero( (char *)&saddr, sizeof(struct sockaddr_in) );
  378.     bzero( (char *)&sad, sizeof(struct in_addr) );
  379.     len = sizeof(struct sockaddr_in);
  380.  
  381.  
  382.     /*
  383.     ** Then we accept the connection on the socket
  384.     */
  385.     /* Bug fixed 1/93: line below checked for wrong return value. Sorry Dale! */
  386.     if (!(nsock = accept( sock, (struct sockaddr *)&saddr, &len )))
  387.     {
  388.         AppPanic("Accept:",errno);
  389.     }
  390.  
  391.     /*
  392.     ** Break the internet address out of the sockaddr_in structure and then
  393.     ** create a dotted-decimal format string from it, for later use
  394.     */
  395.  
  396.     sad = saddr.sin_addr;
  397.     dd_addr = inet_ntoa(sad.s_addr);
  398.  
  399.     /*
  400.     ** Use the internet address to find out the machine's name
  401.     */
  402.  
  403.     if ( !(hent =
  404.             gethostbyaddr( (char *)&sad.s_addr,
  405.                            sizeof(struct in_addr),
  406.                            AF_INET )))
  407.     {
  408.         AppPanic("Client resolution:\nAddress not in hosts db!", 0 );
  409.     }
  410.     hname = hent->h_name;
  411.  
  412.     /*
  413.     ** Form the string which goes into the title bar using name & address
  414.     */
  415.  
  416.     sprintf( rname, "FROM: %s (%s)", hname, dd_addr );
  417.  
  418.  
  419.  
  420.     /*
  421.     ** Okay, now the waiting packet needs to be removed from the connected
  422.     ** socket that accept() gave back to us.  Verify its of type NN_MSG and
  423.     ** if not, set return type to NN_ERR.  If it is, then display it and
  424.     ** return an NN_ACK message.
  425.     */
  426.  
  427.     recv( nsock, (char *)&in, sizeof(struct NetNote), 0 );
  428.     if (in.nn_Code == NN_MSG)
  429.     {
  430.         DisplayBeep(NULL);
  431.         DisplayBeep(NULL);
  432.         retv = DoER( rname, (char *)&in.nn_Text, (char *)&in.nn_Button );
  433.         in.nn_Code = NN_ACK;
  434.         in.nn_Retval = retv;
  435.     }
  436.     else
  437.     {
  438.         in.nn_Code = NN_ERR;
  439.     }
  440.  
  441.     /*
  442.     ** Having dealt with the message one way or the other, send the message
  443.     ** back at the remote, then disconnect from the remote and return.
  444.     */
  445.  
  446.     send( nsock, (char *)&in, sizeof(struct NetNote), 0 );
  447.     s_close( nsock );
  448.  
  449. }
  450.  
  451.