/* **********************************************************
   session.c - Session DDE server for variable maintenance

   Placed in public domain by Horizon Technologies Inc. 1990

  ***revision history***
1 SESSION.C 29-Jan-90,10:23:54,`JMH' Base version
2 SESSION.C 19-Jun-90,10:51:18,`JMH' Version as of 6/25/90
3 SESSION.C 25-Jun-90,14:48:04,`JMH' Version as of 6/25/90
4 SESSION.C 15-Sep-90,16:19:56,`JMH' Version 1.3
  ***revision history***
********************************************************** */
#define MAIN
#define NOCOMM

#include "windows.h"
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "dde.h"
#include "ddelib.h"
#include "session.h"

/* Undocumented windows functions */
int FAR PASCAL lstrlen (LPSTR);
LPSTR FAR PASCAL lstrcpy (LPSTR, LPSTR);
LPSTR FAR PASCAL lstrcat (LPSTR, LPSTR);
int FAR PASCAL lstrcmp (LPSTR, LPSTR);

/* Globals */
static HANDLE ghInstance;
static char *gszAppName = "Session";
static FARPROC glpfnAbout;
static DDECALLBACK glpfnSession;
static HANDLE ghList = NULL;

/* Session variables */
#define MAX_VARS 20
static struct
{
   char szName[20];
   char szValue[20];
} gVars[MAX_VARS];

/* Functions */
int PASCAL WinMain (HANDLE, HANDLE, LPSTR, int);
long FAR PASCAL SessionProc (HWND, unsigned, WORD, LONG);
WORD FAR PASCAL Session (HWND, unsigned, LPSTR, HANDLE);
void SetString (LPSTR, LPSTR);
void GetString (LPSTR, LPSTR);
BOOL FAR PASCAL About (HWND, unsigned, WORD, LONG);


int PASCAL WinMain (hInstance, hPrevInstance, lpCmdLine, nCmdShow)
HANDLE hInstance;
HANDLE hPrevInstance;
LPSTR lpCmdLine;
int nCmdShow;
{
   WNDCLASS wndclass;
   MSG msg;
   HWND hWnd;

   ghInstance = hInstance;

   if ( !hPrevInstance )
      {
      wndclass.style = CS_HREDRAW | CS_VREDRAW;
      wndclass.lpfnWndProc = SessionProc;
      wndclass.cbClsExtra = 0;
      wndclass.cbWndExtra = 0;
      wndclass.hInstance = hInstance;
      wndclass.hIcon = LoadIcon ( hInstance, gszAppName );
      wndclass.hCursor = LoadCursor ( NULL, IDC_ARROW );
      wndclass.hbrBackground = (HBRUSH) GetStockObject ( WHITE_BRUSH );
      wndclass.lpszMenuName = gszAppName;
      wndclass.lpszClassName = gszAppName;

      if ( !RegisterClass ( &wndclass ) )
	 return FALSE;
      }  /* if ! hPrevInstance */

   hWnd = CreateWindow ( gszAppName, gszAppName, WS_OVERLAPPEDWINDOW,
			 CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL,
			 hInstance, NULL );

   if ( !hWnd )
      return ( NULL );

   ShowWindow ( hWnd, SW_SHOWMINIMIZED );
   UpdateWindow ( hWnd );

   while ( GetMessage ( &msg, NULL, NULL, NULL ) )
      {
      TranslateMessage ( &msg );
      DispatchMessage ( &msg );
      }  /* while GetMessage */

   return ( msg.wParam );
}  /* function WinMain */


long FAR PASCAL SessionProc (hWnd, iMessage, wParam, lParam)
HWND hWnd;
unsigned iMessage;
WORD wParam;
LONG lParam;
{
   static HANDLE hTopic;

   switch ( iMessage )
      {
      case WM_CREATE:
	 /* Provide callback addresses for DDE callback function */
	 glpfnSession = (DDECALLBACK) MakeProcInstance ( (FARPROC) Session,
							 ghInstance );

	 /* Register the DDE Topic */
	 hTopic = DDERegisterTopic ( hWnd, gszAppName, "Variables",
				     glpfnSession );
	 break;

      case WM_COMMAND:
	 switch ( wParam )
	    {
	    case IDM_EXIT:
	       SendMessage ( hWnd, WM_CLOSE, 0, 0L );
	       break;

	    case IDM_ABOUT:
	       glpfnAbout = MakeProcInstance ( About, ghInstance );
	       DialogBox ( ghInstance, "About", hWnd, glpfnAbout );
	       FreeProcInstance ( glpfnAbout );
	       break;
	    }  /* switch wParam */
	 break;

      case WM_DESTROY:
	 /* Unregister the DDE Topic */
	 DDEUnregisterTopic ( hTopic );
	 PostQuitMessage ( 0 );
	 break;

      default:
	 return DefWindowProc ( hWnd, iMessage, wParam, lParam );
      }  /* switch iMessage */

   return 0L;
}  /* function SessionProc */


/* Callback routine to notify of client activity */
WORD FAR PASCAL Session (hSession, iMessage, lpszItem, hDataIn)
HWND hSession;
unsigned iMessage;
LPSTR lpszItem;
HANDLE hDataIn;
{
   HANDLE hDataOut;
   DDEACK wAck;
   LPDDEDATA lpData;
   LPDDEPOKE lpPoke;
   LPDDEADVISE lpAdvise;
   LPSTR lpCommand;
   char szData[100];

   /* Set up return value */
   wAck.bAppReturnCode = 0;
   wAck.reserved = 0;
   wAck.fBusy = FALSE;
   wAck.fAck = TRUE;

   /* Switch on the message */
   switch ( iMessage )
      {
      case DDE_INITIATE:
	 break;

      case DDE_TERMINATE:
	 ghList = DDEUnregisterAdvise ( ghList, hSession, NULL );
	 break;

      case DDE_POKE:
	 /* Get pointer to poke structure */
	 lpPoke = (LPDDEPOKE) GlobalLock ( hDataIn );

	 /* Accept only text */
	 if ( lpPoke && lpPoke->cfFormat == CF_TEXT )
	    SetString ( lpszItem, lpPoke->Value );

	 /* Reject all else */
	 else
	    wAck.fAck = FALSE;

	 /* Clean up */
	 if ( lpPoke )
	    GlobalUnlock ( hDataIn );
	 break;

      case DDE_REQUEST:
	 /* Get pointer to advise structure */
	 lpAdvise = (LPDDEADVISE) GlobalLock ( hDataIn );

	 /* Accept only text */
	 if ( lpAdvise && lpAdvise->cfFormat == CF_TEXT )
	    {
	    /* Get the current value */
	    GetString ( lpszItem, szData );

	    /* Create a DDEDATA structure to send the client */
	    hDataOut = GlobalAlloc ( GHND | GMEM_DDESHARE, (DWORD) sizeof (
				     DDEDATA ) + strlen ( szData ) );

	    /* Set its members */
	    lpData = (LPDDEDATA) GlobalLock ( hDataOut );
	    lpData->fRelease = TRUE;
	    lpData->fAckReq = FALSE;
	    lpData->cfFormat = CF_TEXT;
	    lstrcpy ( lpData->Value, szData );
	    GlobalUnlock ( hDataOut );

	    /* Send the data */
	    DDESendData ( hSession, DDE_REQUEST, lpszItem, hDataOut );
	    }  /* if lpAdvise */

	 /* Reject all else */
	 else
	    wAck.fAck = FALSE;

	 /* Clean up */
	 if ( lpAdvise )
	    GlobalUnlock ( hDataIn );
	 break;

      case DDE_EXECUTE:
	 /* Get pointer to command string */
	 lpCommand = GlobalLock ( hDataIn );

	 /* Process command here */

	 /* Ack bad - we do not accept commands yet */
	 wAck.fAck = FALSE;

	 /* Clean up */
	 if ( lpCommand )
	    GlobalUnlock ( hDataIn );
	 break;

      case DDE_ADVISE:
	 /* Get pointer to advise structure */
	 lpAdvise = (LPDDEADVISE) GlobalLock ( hDataIn );

	 /* Accept only text */
	 if ( lpAdvise && lpAdvise->cfFormat == CF_TEXT )
	    ghList = DDERegisterAdvise ( ghList, hSession, lpszItem,
					 lpAdvise->fAckReq, lpAdvise->fDeferUpd,
					 CF_TEXT );

	 /* Reject all else */
	 else
	    wAck.fAck = FALSE;

	 /* Clean up */
	 if ( lpAdvise )
	    GlobalUnlock ( hDataIn );
	 break;

      case DDE_UNADVISE:
	 ghList = DDEUnregisterAdvise ( ghList, hSession, lpszItem );
	 break;
      }  /* switch iMessage */

   return *(WORD *) &wAck;
}  /* function Session */


/* Set the value of some string */
void SetString (lpszString, lpszValue)
LPSTR lpszString;
LPSTR lpszValue;
{
   HANDLE hData = NULL;
   LPDDEDATA lpData;
   int iVar;
   int iEmpty = MAX_VARS;
   int nItem = 0;
   HANDLE hSession;
   char szItem[20];
   int fAckReq;
   int fDeferUpd;
   int cfFormat;

   /* Replace it if it exists */
   for ( iVar = 0; iVar < MAX_VARS; iVar++ )
      {
      /* Found it: update and quit */
      if ( lstrcmp ( gVars [iVar].szName, lpszString ) == 0 )
	 {
	 lstrcpy ( gVars [iVar].szValue, lpszValue );
	 break;
	 }  /* if lstrcmp */

      /* Found an empty slot, save its position */
      if ( iEmpty == MAX_VARS && strlen ( gVars [iVar].szName ) == 0 )
	 iEmpty = iVar;
      }  /* for iVar */

   /* Add it if it does not exist and there is room */
   if ( iVar == MAX_VARS && iEmpty != MAX_VARS )
      {
      iVar = iEmpty;
      lstrcpy ( gVars [iVar].szName, lpszString );
      lstrcpy ( gVars [iVar].szValue, lpszValue );
      }  /* if iVar */

   /* Update the world */
   if ( iVar != MAX_VARS )
      {
      /* Loop through the advise list */
      while ( DDEGetAdvise ( ghList, nItem++, &hSession, szItem,
			     sizeof ( szItem ), &fAckReq, &fDeferUpd,
			     &cfFormat ) != -1 )
	 {
	 /* If advised on this variable */
	 if ( lstrcmp ( lpszString, szItem ) == 0 )
	    {
	    /* If the client asked for a "hot" link */
	    if ( ! fDeferUpd )
	       {
	       /* Create a DDEDATA structure to send the client */
	       hData = GlobalAlloc ( GHND | GMEM_DDESHARE, (DWORD) sizeof (
				     DDEDATA ) + lstrlen ( lpszValue ) );

	       /* Set its members */
	       lpData = (LPDDEDATA) GlobalLock ( hData );
	       lpData->fRelease = TRUE;
	       lpData->fAckReq = fAckReq;
	       lpData->cfFormat = cfFormat;
	       lstrcpy ( lpData->Value, lpszValue );
	       GlobalUnlock ( hData );
	       }

	    /* Send the data */
	    DDESendData ( hSession, DDE_ADVISE, lpszString, hData );
	    }  /* if lstrcmp */
	 }  /* for iAdvise */
      }  /* if iVar */
}  /* function SetString */


/* Get the value of some string */
void GetString (lpszString, lpszValue)
LPSTR lpszString;
LPSTR lpszValue;
{
   int iVar;

   /* Default to blank */
   lstrcpy ( lpszValue, "" );

   /* Find the value of some string */
   for ( iVar = 0; iVar < MAX_VARS; iVar++ )
      {
      /* Found it */
      if ( lstrcmp ( gVars [iVar].szName, lpszString ) == 0 )
	 {
	 lstrcpy ( lpszValue, gVars [iVar].szValue );
	 break;
	 }  /* if lstrcmp */
      }  /* for iVar */
}  /* function GetString */


/* About box */
BOOL FAR PASCAL About (hDlg, iMessage, wParam, lParam)
HWND hDlg;
unsigned iMessage;
WORD wParam;
LONG lParam;
{
   switch ( iMessage )
      {
      case WM_INITDIALOG:
	 break;

      case WM_COMMAND:
	 EndDialog ( hDlg, FALSE );
	 break;

      default:
	 return FALSE;
      }  /* switch iMessage */
   return TRUE;
}  /* function About */
