//
//  FINGER.C
//  Pegasus Mail for Windows extension providing a FINGER
//  interface.
//
//  Copyright (c) 1994, David Harris, All Rights Reserved.
//
//  The author grants explicit permission for this source code to be
//  used or modified as required, subject only to the conditions that
//  the copyright notices above are preserved and that by using this
//  code you agree that the code is provided without warranty of any
//  kind, either explicit or implied, and you use it at your own
//  risk.
//

#define STRICT
#include <windows.h>
#include <bwcc.h>
#include <stdio.h>
#include <string.h>
#include "..\wpmforms.h"
#include "finger.h"        //  Dialog control item IDs

//  Form dialogs and callbacks: a form DLL can export a function
//  which WinPMail's form manager will call with Windows messages
//  destined for the form's dialog or window (such as menu selections
//  and so forth). The function takes the following form:

typedef long pascal far (*FORM_CALLBACK) (HWND hWnd, WORD wMsg,
   WPARAM wParam, LPARAM lParam);

//  The return from a form callback is passed back through the
//  Windows call chain if non-zero, otherwise the default window
//  proc is called. The reasons we call this function instead of
//  simply sending messages to the dialog itself are:
//
//   1: Dialogs can only return Boolean values, whereas the callback
//      can provide the long value Windows expects to pass back
//      through the calling chain.
//
//   2: Having a separate callback means that it is potentially
//      possible to have a form which does not create a dialog.
//
//
//  Minimum interface: the minimum interface a form DLL must provide
//  is a routine called "forminit" which is exported by name. "forminit"
//  has the following prototype:
//
//  WORD FAR PASCAL _export FORMINIT (WORD version, int variant, HWND hParent,
//      char *data, HWND *hDialog, char *callback_name);
//
//  "version" is passed in with the version of the WinPMail forms
//     manager which is running.
//  "variant" indicates what type of form is required - the following
//     values are currently defined:
//       0: Create a form for composing a message
//       1: Create a form for reading a message
//  "hParent" contains the handle of the WinPMail MDI child window
//     which is to contain the form.
//  "data" contains any string defined as being required for the
//     form in the menu interface.
//  "hDialog" should be filled in with the window handle of the
//     modeless dialog created within the MDI child window.
//  "callback_name" (optional) should be filled in with the name of the
//     function in the DLL of the exported function to which messages
//     should be sent or NULL if there is none. If NULL, messages are
//     sent to the dialog returned in "hDialog". You will use an
//     indirect callback of this kind when your extension does not
//     create a dialog within the enclosing parent window.
//
//  When forminit is called, the DLL should register any window
//  classes it needs then create the dialog within the MDI parent
//  window and size it to the correct size. On return WinPMail will
//  resize the parent window to enclose the dialog correctly. The
//  DLL should NOT make the dialog visible - WinPMail will do that
//  as required.

#define WM_MARGIN (WM_USER + 1099)
#define WM_SETDIR (WM_USER + 400)
#define WM_ADDFILE (WM_USER + 401)
#define WM_STARTUP (WM_USER + 403)
#define WM_GET_ALIST (WM_USER + 404)

#define IDC_QUICKHELP_TO   970
#define IDC_QUICKHELP_CC   971


int register_form_classes (void);


HFONT hFingerFont;
HINSTANCE  hLibInstance;            // set in LibMain, used throughout the DLL
char szFormDlgName [] = "FINGER";   // Name of dialog resource template.
char szFormDlgClassName [] =
   "bordlg_fn";                  // Class name for the form dialog.
HWND last_focus;
char no_tcp [] =
   "This extension can only run on systems where WinPMail "
   "has access to Winsock TCP/IP services.";

#pragma warn -par


WORD FAR PASCAL _export FORMINIT (WORD version, int variant, HWND hParent,
   char *data, HWND *hDialog, FORM_CALLBACK *callback)
   {
   RECT r;
   char szBuf [80];
   int i;
   HWND hControl;
   BYTE tm [7];

   //  First, check to see if the version of the form manager is
   //  one we can work with. This check is pretty arbitrary - you
   //  should probably assume that you may not work with any version
   //  where the major version number is higher than the original
   //  version you targeted.

   if ((version & 0xFF00) > 0x100) return 0;

   //  Now check the variant number; for the Finger Client we only
   //  provide a COMPOSER format.

   if (variant != 0) return 0;

   (*hDialog) = CreateDialog (hLibInstance, (LPCSTR) szFormDlgName, hParent, NULL);
   if ((*hDialog) == NULL) return 0;
   GetClientRect (*hDialog, &r);
   MoveWindow (*hDialog, 0, 0, r.right, r.bottom, FALSE);

   hFingerFont = CreateFont (14, 0, 0, 0, 400, FALSE,
      FALSE, FALSE, ANSI_CHARSET, OUT_DEFAULT_PRECIS,
      CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
      DEFAULT_PITCH | FF_DONTCARE, "Arial");
   SendMessage (GetDlgItem (*hDialog, 103), WM_SETFONT,
      (WPARAM) hFingerFont, (LPARAM) TRUE);

   //  Some default values can be passed in the "data" parameter
   if ((data != NULL) && (*data))
     SetDlgItemText (*hDialog, 101, data);      // Default host
   return 1;
   }


void finger (HWND hParent, HWND hControl, char *host, char *search)
   {
   //  Despatch a query to a finger server based on the hostname
   //  and search string supplied by the user. The finger protocol
   //  is one of the older protocols on the Internet and is pretty
   //  basic - you just open port 79 and shove a CRLF delimited
   //  string down its throat... It will then do whatever matching
   //  it thinks is appropriate and return an arbitrary amount of
   //  arbitrarily-formatted data to you. The data returned is
   //  almost universally unparseable so we simply offer it to the
   //  user as-is where-is.

   char buffer [256];

   if (SendMessage (hParent, WM_F_TCPOPEN, 79, (LPARAM) host) < 1)
      return;

   SendMessage (hControl, EM_SETSEL, 0, 0x7FFF0000);
   sprintf (buffer, "%s\r\n", search);
   if (SendMessage (hParent, WM_F_TCPPUTS, 0, (LPARAM) buffer) > 0)
      {
      for (;;)
         {
         buffer [0] = '\0';
         if (SendMessage (hParent, WM_F_TCPGETS, sizeof (buffer), (LPARAM) buffer) < 1)
            break;
         if (buffer [0] == '\0') break;
         SendMessage (hControl, EM_REPLACESEL, 0, (LPARAM) buffer);
         }
      }
   SendMessage (hParent, WM_F_TCPCLOSE, 0, 0);
   }


LONG FAR PASCAL _export FormProc (HWND hWnd, WORD wMsg, 
   WORD wParam, LONG lParam)
   {
   //  Service routine for the form's enclosed dialog. This is a
   //  standard windows modeless WndProc.

   DWORD dwResult = 0;
   BOOL fCallDefProc = TRUE;
   char host [80], search [80];

   switch (wMsg)
      {
      case WM_FM_INIT :
         //  Check to see that TCP/IP services are actually
         //  available on this system. If they aren't, we simply
         //  post a close message to the parent.

         if (SendMessage (GetParent (hWnd), WM_F_TCPPRESENT, 0, 0) == 0)
            {
            MessageBox (hWnd, no_tcp, " Finger Client", MB_OK | MB_ICONSTOP);
            PostMessage (GetParent (hWnd), WM_CLOSE, 0, 0);
            }
         break;

      case WM_FM_INITFOCUS :
         SetFocus (GetDlgItem (hWnd, 101));
         break;

      case WM_FM_RESTOREFOCUS :
         //  There's a glitch in the way the Windows MDI manager
         //  seems to manage focus which means that we can have
         //  focus taken away and not returned when the user does
         //  anything outside our window. WinPMail has some quite
         //  complex logic to deal with this case and will send
         //  this message whenever focus should be restored in
         //  our window. We set focus to the last active control
         //  (which we know from trapping EN_SETFOCUS messages).

         if (last_focus) SetFocus (last_focus);
         break;

      case WM_DESTROY :
         if (hFingerFont) DeleteObject (hFingerFont);
         break;

      case WM_COMMAND :
         fCallDefProc = FALSE;
         if (HIWORD (lParam) == EN_SETFOCUS)
            {
            //  We have to trap EN_SETFOCUS messages so we know which
            //  control was active last. When a menu selection is made
            //  our current control will lose focus and because of a
            //  curiosity in Windows' MDI management, we won't get it
            //  back. Instead, WinPMail generates a WM_FM_RESTOREFOCUS
            //  message which signals to us that we should set focus to
            //  whatever the last active control was.

            last_focus = (HWND) LOWORD (lParam);
            break;
            }

         switch (wParam)
            {
            case 139 :
               GetDlgItemText (hWnd, 101, host, sizeof (host));
               GetDlgItemText (hWnd, 102, search, sizeof (search));
               finger (GetParent (hWnd), GetDlgItem (hWnd, 103), host, search);
               break;
            }
         break;
      }

   if (fCallDefProc)
      dwResult = BWCCDefDlgProc (hWnd, wMsg, wParam, lParam);

   return dwResult;
   }


#pragma warn -sus

void unregister_form_classes (void)
   {
   //  Remove any classes associated with the form; we have the
   //  same problem here as we do with registering the classes
   //  for the DLL - we only want to deregister the classes on
   //  the last time we're unloaded.

   if (GetModuleUsage (hLibInstance) > 1) return;      //  Not a problem
   UnregisterClass (szFormDlgClassName, hLibInstance);
   }


BOOL FAR PASCAL LibMain (HINSTANCE hInst, WORD wDataSeg, WORD cbHeapSize, LPSTR lpszCmdLine)
   {
   WNDCLASS wc;

   if (! hLibInstance)
      {
      hLibInstance = hInst;
      BWCCGetVersion ();      //  Forces BWCC to be dynamically loaded.

      //  Register any window classes used by the form. Forms will usually
      //  register either one or occasionally two classes which define
      //  the composition and reader dialogs created by the DLL.
      //
      //  There's a gotcha here, of course (aren't there always, in
      //  Windows?)... You can't register a window class more than once,
      //  so if the DLL has already been loaded and the user asks to
      //  create a second instance of the form, we have to be careful
      //  not to re-register the class. We do this by checking the
      //  instance usage counter of the DLL - if it's greater than 1,
      //  then we DON'T register the classes.

      wc.style          = WS_CHILD;
      wc.lpfnWndProc    = FormProc;
      wc.cbClsExtra     = 0;
      wc.cbWndExtra     = DLGWINDOWEXTRA;
	   wc.hInstance      = hLibInstance;
	   wc.hIcon          = NULL;
      wc.hCursor        = LoadCursor (NULL, IDC_ARROW);
      wc.hbrBackground  = (HBRUSH) (COLOR_WINDOW + 1);
      wc.lpszMenuName   = NULL;
      wc.lpszClassName  = szFormDlgClassName;
      if (! RegisterClass (&wc))
         MessageBeep (0);
      }

   return (TRUE);             // Initialization went OK
   }

