/* pmgetpw.c
   Copyright (C) 1995-1996 Eberhard Mattes

This file is part of GNU Emacs.

GNU Emacs is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.

GNU Emacs is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with GNU Emacs; see the file COPYING.  If not, write to
the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.  */


#include <stdio.h>
#include <string.h>
#define INCL_DOS
#define INCL_DOSERRORS
#define INCL_WIN
#include <os2.h>
#include "pmgetpw.h"

#define COOKIE_LEN      128
#define PASSWD_LEN      128
#define CACHE_SIZE      31

struct cache_entry
{
  char cookie[COOKIE_LEN];
  char passwd[PASSWD_LEN];
};

struct cache
{
  int n;
  PID server_pid;
  struct cache_entry a[CACHE_SIZE];
};

static char server;
static struct cache *cache;
static const char *cookie;
static HMTX hmtx;
static HEV hev;
static char const cache_name[] = "/sharemem/pmgetpw/cache";
static char const mutex_name[] = "/sem32/pmgetpw/mtx";
static char const ev_name[]    = "/sem32/pmgetpw/ev";


static void lock (void)
{
  ULONG rc;

  rc = DosRequestMutexSem (hmtx, SEM_INDEFINITE_WAIT);
}


static void unlock (void)
{
  ULONG rc;

  rc = DosReleaseMutexSem (hmtx);
}


static void answer (const char *passwd, int enter)
{
  ULONG nwritten;
  struct cache_entry *p;

  DosWrite (1, passwd, strlen (passwd), &nwritten);
  if (enter && cache != NULL)
    {
      lock ();
      if (cache->n < CACHE_SIZE)
        {
          p = &cache->a[cache->n];
          strcpy (p->cookie, cookie);
          strcpy (p->passwd, passwd);
          ++cache->n;
        }
      unlock ();
    }
}


static void dlgbox_sysmenu (HWND hwnd)
{
  HWND hwndSysMenu;
  SHORT i, n, id;
  MENUITEM mi;
  MRESULT mr;

  hwndSysMenu = WinWindowFromID (hwnd, FID_SYSMENU);
  if (hwndSysMenu == NULLHANDLE)
    return;
  mr = WinSendMsg (hwndSysMenu, MM_QUERYITEM,
                   MPFROM2SHORT (SC_SYSMENU, FALSE), (MPARAM)&mi);
  if (!SHORT1FROMMR (mr))
    return;
  hwndSysMenu = mi.hwndSubMenu;
  if (hwndSysMenu == NULLHANDLE)
    return;

  WinSendMsg (hwndSysMenu, MM_DELETEITEM,
              MPFROM2SHORT (SC_MINIMIZE, FALSE), 0L);
  WinSendMsg (hwndSysMenu, MM_DELETEITEM,
              MPFROM2SHORT (SC_MAXIMIZE, FALSE), 0L);
  WinSendMsg (hwndSysMenu, MM_DELETEITEM,
              MPFROM2SHORT (SC_SIZE, FALSE), 0L);
  WinSendMsg (hwndSysMenu, MM_DELETEITEM,
              MPFROM2SHORT (SC_RESTORE, FALSE), 0L);
  WinSendMsg (hwndSysMenu, MM_DELETEITEM,
              MPFROM2SHORT (SC_TASKMANAGER, FALSE), 0L);
  WinSendMsg (hwndSysMenu, MM_DELETEITEM,
              MPFROM2SHORT (SC_HIDE, FALSE), 0L);

redo:
  mr = WinSendMsg (hwndSysMenu, MM_QUERYITEMCOUNT, 0L, 0L);
  n = SHORT1FROMMR (mr);
  for (i = 0; i < n; ++i)
    {
      mr = WinSendMsg (hwndSysMenu, MM_ITEMIDFROMPOSITION,
                       MPFROMSHORT (i), 0L);
      id = SHORT1FROMMR (mr);
      if (id != MID_ERROR)
        {
          mr = WinSendMsg (hwndSysMenu, MM_QUERYITEM,
                           MPFROM2SHORT (id, FALSE), (MPARAM)&mi);
          if (SHORT1FROMMR (mr) && (mi.afStyle & MIS_SEPARATOR))
            {
              WinSendMsg (hwndSysMenu, MM_DELETEITEM,
                          MPFROM2SHORT (id, FALSE), 0L);
              goto redo;
            }
        }
    }
}


static void initdlg (HWND hwnd)
{
  ULONG len;
  LONG clr;
  SWP swpScreen, swpDialog;
  SWCNTRL swc;

  if (*cookie != 0)
    WinSetWindowText (hwnd, cookie);
  dlgbox_sysmenu (hwnd);
  WinSendDlgItemMsg (hwnd, IDC_PASSWORD, EM_SETTEXTLIMIT,
                     MPFROMSHORT (PASSWD_LEN), 0);
  clr = WinQuerySysColor (HWND_DESKTOP, SYSCLR_ENTRYFIELD, 0);
  WinSetPresParam (WinWindowFromID (hwnd, IDC_PASSWORD),
                   PP_FOREGROUNDCOLOR, sizeof (clr), &clr);
  clr = WinQuerySysColor (HWND_DESKTOP, SYSCLR_HILITEBACKGROUND, 0);
  WinSetPresParam (WinWindowFromID (hwnd, IDC_PASSWORD),
                   PP_HILITEFOREGROUNDCOLOR, sizeof (clr), &clr);

  WinQueryWindowPos (hwnd, &swpDialog);
  WinQueryWindowPos (HWND_DESKTOP, &swpScreen);
  WinSetWindowPos (hwnd, HWND_TOP,
                   (swpScreen.cx - swpDialog.cx) / 2,
                   (swpScreen.cy - swpDialog.cy) / 2,
                   0, 0, SWP_MOVE);

  memset (&swc, 0, sizeof (swc));
  strcpy (swc.szSwtitle, "pmgetpw");
  len = strlen (cookie);
  if (len != 0)
    {
      ULONG len0;

      strcat (swc.szSwtitle, "-");
      len0 = strlen (swc.szSwtitle);
      if (len0 + len > MAXNAMEL)
        len = MAXNAMEL - len0;
      memcpy (swc.szSwtitle + len0, cookie, len);
      swc.szSwtitle[len0+len] = 0;
    }
  swc.hwnd          = hwnd;
  swc.hwndIcon      = NULLHANDLE;
  swc.hprog         = NULLHANDLE;
  swc.idProcess     = 0;
  swc.idSession     = 0;
  swc.uchVisibility = SWL_VISIBLE;
  swc.fbJump        = SWL_JUMPABLE;
  swc.bProgType     = PROG_PM;
  WinAddSwitchEntry (&swc);
}


static MRESULT EXPENTRY DlgWndProc (HWND hwnd, ULONG msg,
                                    MPARAM mp1, MPARAM mp2)
{
  static char passwd[PASSWD_LEN+1];
  ULONG len;

  switch (msg)
    {
    case WM_INITDLG:
      initdlg (hwnd);
      return ((MRESULT)FALSE);

    case WM_COMMAND:
      switch (COMMANDMSG (&msg)->cmd)
        {
        case DID_OK:
          len = WinQueryDlgItemText (hwnd, IDC_PASSWORD,
                                     sizeof (passwd), passwd);
          passwd[len] = 0;
          answer (passwd, TRUE);
          WinDismissDlg (hwnd, 0);
          return ((MRESULT)FALSE);

        case DID_CANCEL:
          WinDismissDlg (hwnd, 0);
          return ((MRESULT)FALSE);
        }
    }
  return (WinDefDlgProc (hwnd, msg, mp1, mp2));
}


static void init_shmem (void)
{
  ULONG rc;

retry:
  rc = DosCreateMutexSem (mutex_name, &hmtx, DC_SEM_SHARED, TRUE);
  if (rc == ERROR_DUPLICATE_NAME)
    {
      rc = DosOpenMutexSem (mutex_name, &hmtx);
      if (rc == ERROR_SEM_NOT_FOUND)
        {
          DosSleep (200);
          goto retry;
        }
      else if (rc != 0)
        exit (1);
    }
  else if (rc != 0)
    exit (1);

  rc = DosGetNamedSharedMem ((PPVOID)&cache, cache_name, PAG_READ | PAG_WRITE);
  if (rc == 0)
    {
      rc = DosReleaseMutexSem (hmtx);
      return;
    }

  if (server)
    {
      rc = DosReleaseMutexSem (hmtx);
      exit (1);
    }

  rc = DosAllocSharedMem ((PPVOID)&cache, cache_name,
                          sizeof (struct cache),
                          PAG_COMMIT | PAG_READ | PAG_WRITE);
  if (rc != 0) exit (1);

  cache->n = 0;

  rc = DosReleaseMutexSem (hmtx);

  rc = DosCreateEventSem (ev_name, &hev, DC_SEM_SHARED, FALSE);
  if (rc == 0)
    {
      PTIB ptib;
      PPIB ppib;
      STARTDATA sd;
      ULONG sid;
      PID pid;
      const char *s;

      DosGetInfoBlocks (&ptib, &ppib);
      s = ppib->pib_pchenv;
      while (*s != 0)
        while (*s++ != 0)
          ;
      ++s;
      memset (&sd, 0, sizeof (sd));
      sd.Length      = 30;
      sd.Related     = SSF_RELATED_INDEPENDENT;
      sd.FgBg        = SSF_FGBG_BACK;
      sd.TraceOpt    = SSF_TRACEOPT_NONE;
      sd.PgmTitle    = NULL;
      sd.PgmName     = (PSZ)s;
      sd.PgmInputs   = "/server";
      sd.TermQ       = NULL;
      sd.Environment = NULL;
      sd.InheritOpt  = SSF_INHERTOPT_SHELL;
      rc = DosStartSession (&sd, &sid, &pid);
      if (rc == 0)
        {
          rc = DosWaitEventSem (hev, 20000);
        }
    }
}


static void do_server (void)
{
  PTIB ptib;
  PPIB ppib;
  ULONG rc;

  DosGetInfoBlocks (&ptib, &ppib);
  cache->server_pid = ppib->pib_ulpid;
  rc = DosOpenEventSem (ev_name, &hev);
  if (rc == 0)
    rc = DosPostEventSem (hev);
  for (;;)
    DosSleep ((ULONG)-1);
}


static void do_clear (void)
{
  ULONG rc;

  if (cache == NULL)
    exit (1);
  rc = DosKillProcess (DKP_PROCESS, cache->server_pid);
  if (rc != 0)
    exit (1);
  exit (0);
}


int main (int argc, char *argv[])
{
  HAB hab;
  HMQ hmq;

  if (argc == 1)
    cookie = "";
  else if (argc == 2)
    cookie = argv[1];
  else
    exit (1);
  if (strlen (cookie) >= COOKIE_LEN)
    exit (1);
  server = (strcmp (cookie, "/server") == 0);

  init_shmem ();

  if (server)
    do_server ();

  if (strcmp (cookie, "/clear") == 0)
    do_clear ();

  if (cache != NULL)
    {
      int i;

      lock ();
      for (i = 0; i < cache->n; ++i)
        if (strcmp (cache->a[i].cookie, cookie) == 0)
          {
            answer (cache->a[i].passwd, FALSE);
            unlock ();
            exit (0);
          }
      unlock ();
    }

  hab = WinInitialize (0);
  hmq = WinCreateMsgQueue (hab, 0);

  WinDlgBox (HWND_DESKTOP, NULLHANDLE, DlgWndProc, NULLHANDLE,
             IDD_MAIN, NULL);

  WinDestroyMsgQueue (hmq);
  WinTerminate (hab);
  return (0);
}
