/* os2acl.c - access to OS/2 (LAN Server) ACLs
 *
 * Author:  Kai Uwe Rommel <rommel@ars.de>
 * Created: Mon Aug 08 1994
 *
 * This code is in the public domain.
 */

/*
 * supported 32-bit compilers:
 * - emx+gcc
 * - IBM C Set++ 2.1 or newer
 * - Watcom C/C++ 10.0 or newer
 *
 * supported 16-bit compilers:
 * - MS C 6.00A
 * - Watcom C/C++ 10.0 or newer
 *
 * supported OS/2 LAN environments:
 * - IBM LAN Server/Requester 3.0, 4.0 and 5.0 (Warp Server)
 * - IBM Peer 1.0 (Warp Connect)
 */

#ifdef KUR
   static char *rcsid =
   "$Id: os2acl.c,v 1.3 1996/04/03 19:18:27 rommel Exp rommel $";
   static char *rcsrev = "$Revision: 1.3 $";
#endif

/*
 * $Log: os2acl.c,v $
 * Revision 1.3  1996/04/03 19:18:27  rommel
 * minor fixes
 *
 * Revision 1.2  1996/03/30 22:03:52  rommel
 * avoid frequent dynamic allocation for every call
 * streamlined code
 *
 * Revision 1.1  1996/03/30 09:35:00  rommel
 * Initial revision
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <malloc.h>

#define INCL_NOPM
#define INCL_DOS
#define INCL_DOSERRORS
#include <os2.h>

#include "os2/os2acl.h"

#define UNLEN 20

#if defined(__WATCOMC__) && defined(__386__) && !defined(__32BIT__)
#define __32BIT__
#endif

#ifdef __32BIT__
typedef ULONG U_INT;
#ifdef __EMX__
#define PSTR16 _far16ptr
#define PTR16(x) _emx_32to16(x)
#else /* other 32-bit */
#define PSTR16 PCHAR16
#define PTR16(x) ((PCHAR16)(x))
#endif
#else /* 16-bit */
typedef USHORT U_INT;
#define PSTR16 PSZ
#define PTR16(x) (x)
#endif

typedef struct access_list
{
  char acl_ugname[UNLEN+1];
  char acl_pad;
  USHORT acl_access;
}
ACCLIST;

typedef struct access_info
{
  PSTR16 acc_resource_name;
  USHORT acc_attr;
  USHORT acc_count;
}
ACCINFO;

static ACCINFO *ai;
static char *path, *data;

#ifdef __32BIT__

#ifdef __EMX__

static USHORT (APIENTRY *_NetAccessGetInfo)(PSZ pszServer, PSZ pszResource,
  USHORT sLevel, PVOID pbBuffer, USHORT cbBuffer, PUSHORT pcbTotalAvail);
static USHORT (APIENTRY *_NetAccessSetInfo)(PSZ pszServer, PSZ pszResource,
  USHORT sLevel, PVOID pbBuffer, USHORT cbBuffer, USHORT sParmNum);
static USHORT (APIENTRY *_NetAccessAdd)(PSZ pszServer,
  USHORT sLevel, PVOID pbBuffer, USHORT cbBuffer);

USHORT NetAccessGetInfo(PSZ pszServer, PSZ pszResource, USHORT sLevel,
                        PVOID pbBuffer, USHORT cbBuffer, PUSHORT pcbTotalAvail)
{
  return (USHORT)
          (_THUNK_PROLOG (4+4+2+4+2+4);
           _THUNK_FLAT (pszServer);
           _THUNK_FLAT (pszResource);
           _THUNK_SHORT (sLevel);
           _THUNK_FLAT (pbBuffer);
           _THUNK_SHORT (cbBuffer);
           _THUNK_FLAT (pcbTotalAvail);
           _THUNK_CALLI (_emx_32to16(_NetAccessGetInfo)));
}

USHORT NetAccessSetInfo(PSZ pszServer, PSZ pszResource, USHORT sLevel,
                        PVOID pbBuffer, USHORT cbBuffer, USHORT sParmNum)
{
  return (USHORT)
          (_THUNK_PROLOG (4+4+2+4+2+2);
           _THUNK_FLAT (pszServer);
           _THUNK_FLAT (pszResource);
           _THUNK_SHORT (sLevel);
           _THUNK_FLAT (pbBuffer);
           _THUNK_SHORT (cbBuffer);
           _THUNK_SHORT (sParmNum);
           _THUNK_CALLI (_emx_32to16(_NetAccessSetInfo)));
}

USHORT NetAccessAdd(PSZ pszServer, USHORT sLevel,
                    PVOID pbBuffer, USHORT cbBuffer)
{
  return (USHORT)
          (_THUNK_PROLOG (4+2+4+2);
           _THUNK_FLAT (pszServer);
           _THUNK_SHORT (sLevel);
           _THUNK_FLAT (pbBuffer);
           _THUNK_SHORT (cbBuffer);
           _THUNK_CALLI (_emx_32to16(_NetAccessAdd)));
}

#else /* other 32-bit */

APIRET16 (* APIENTRY16 NetAccessGetInfo)(PCHAR16 pszServer, PCHAR16 pszResource,
  USHORT sLevel, PVOID16 pbBuffer, USHORT cbBuffer, PVOID16 pcbTotalAvail);
APIRET16 (* APIENTRY16 NetAccessSetInfo)(PCHAR16 pszServer, PCHAR16 pszResource,
  USHORT sLevel, PVOID16 pbBuffer, USHORT cbBuffer, USHORT sParmNum);
APIRET16 (* APIENTRY16 NetAccessAdd)(PCHAR16 pszServer,
  USHORT sLevel, PVOID16 pbBuffer, USHORT cbBuffer);

#define _NetAccessGetInfo NetAccessGetInfo
#define _NetAccessSetInfo NetAccessSetInfo
#define _NetAccessAdd NetAccessAdd

#if !defined(__IBMC__) || !defined(__TILED__)
#define _tmalloc malloc
#define _tfree free
#endif

#endif
#else /* 16-bit */

USHORT (APIENTRY *NetAccessGetInfo)(PSZ pszServer, PSZ pszResource,
  USHORT sLevel, PVOID pbBuffer, USHORT cbBuffer, PUSHORT pcbTotalAvail);
USHORT (APIENTRY *NetAccessSetInfo)(PSZ pszServer, PSZ pszResource,
  USHORT sLevel, PVOID pbBuffer, USHORT cbBuffer, USHORT sParmNum);
USHORT (APIENTRY *NetAccessAdd)(PSZ pszServer,
  USHORT sLevel, PVOID pbBuffer, USHORT cbBuffer);

#define _NetAccessGetInfo NetAccessGetInfo
#define _NetAccessSetInfo NetAccessSetInfo
#define _NetAccessAdd NetAccessAdd

#define _tmalloc malloc
#define _tfree free

#define DosQueryProcAddr(handle, ord, name, funcptr) \
        DosGetProcAddr(handle, name, funcptr)
#define DosQueryCurrentDir DosQCurDir
#define DosQueryCurrentDisk DosQCurDisk

#endif


static BOOL acl_init(void)
{
  static BOOL initialized, netapi_avail;
  HMODULE netapi;
  char buf[256];

  if (initialized)
    return netapi_avail;

  initialized = TRUE;

  if (DosLoadModule(buf, sizeof(buf), "NETAPI", &netapi))
    return FALSE;

  if (DosQueryProcAddr(netapi, 0, "NETACCESSGETINFO", (PFN *) &_NetAccessGetInfo) ||
      DosQueryProcAddr(netapi, 0, "NETACCESSSETINFO", (PFN *) &_NetAccessSetInfo) ||
      DosQueryProcAddr(netapi, 0, "NETACCESSADD", (PFN *) &_NetAccessAdd))
    return FALSE;

#if defined(__WATCOMC__) && defined(__386__)
  NetAccessGetInfo = (PVOID) (ULONG) (PVOID16) NetAccessGetInfo;
  NetAccessSetInfo = (PVOID) (ULONG) (PVOID16) NetAccessSetInfo;
  NetAccessAdd     = (PVOID) (ULONG) (PVOID16) NetAccessAdd;
#endif

  if ((path = _tmalloc(CCHMAXPATH)) == NULL)
    return FALSE;
  if ((data = _tmalloc(ACL_BUFFERSIZE)) == NULL)
    return FALSE;
  if ((ai = _tmalloc(sizeof(ACCINFO))) == NULL)
    return -1;

  netapi_avail = TRUE;

  return netapi_avail;
}

static void acl_mkpath(char *buffer, const char *source)
{
  char *ptr;
  static char cwd[CCHMAXPATH];
  static U_INT cwdlen;
  U_INT cdrive;
  ULONG drivemap;

  if (isalpha(source[0]) && source[1] == ':')
    buffer[0] = 0; /* fully qualified names */
  else
  {
    if (cwd[0] == 0)
    {
      DosQueryCurrentDisk(&cdrive, &drivemap);
      cwd[0] = (char)(cdrive + '@');
      cwd[1] = ':';
      cwd[2] = '\\';
      cwdlen = sizeof(cwd) - 3;
      DosQueryCurrentDir(0, cwd + 3, &cwdlen);
      cwdlen = strlen(cwd);
    }

    if (source[0] == '/' || source[0] == '\\')
    {
      if (source[1] == '/' || source[1] == '\\')
        buffer[0] = 0; /* UNC names */
      else
      {
        strncpy(buffer, cwd, 2);
        buffer[2] = 0;
      }
    }
    else
    {
      strcpy(buffer, cwd);
      if (cwd[cwdlen - 1] != '\\' && cwd[cwdlen - 1] != '/')
        strcat(buffer, "/");
    }
  }

  strcat(buffer, source);

  for (ptr = buffer; *ptr; ptr++)
    if (*ptr == '/')
      *ptr = '\\';

  if (ptr[-1] == '\\')
    ptr[-1] = 0;

  strupr(buffer);
}

static int acl_bin2text(char *data, char *text)
{
  ACCINFO *ai;
  ACCLIST *al;
  U_INT cnt, offs;

  ai = (ACCINFO *) data;
  al = (ACCLIST *) (data + sizeof(ACCINFO));

  offs = sprintf(text, "ACL1:%X,%d\n",
                 ai -> acc_attr, ai -> acc_count);

  for (cnt = 0; cnt < ai -> acc_count; cnt++)
    offs += sprintf(text + offs, "%s,%X\n",
                    al[cnt].acl_ugname, al[cnt].acl_access);

  return strlen(text);
}

int acl_get(char *server, const char *resource, char *buffer)
{
  USHORT datalen;
  PSZ srv = NULL;
  int rc;

  if (!acl_init())
    return -1;

  if (server)
    srv = server;

  acl_mkpath(path, resource);
  datalen = 0;

  rc = NetAccessGetInfo(srv, path, 1, data, ACL_BUFFERSIZE, &datalen);

  if (rc == 0)
    acl_bin2text(data, buffer);

  return rc;
}

static int acl_text2bin(char *data, char *text, char *path)
{
  ACCINFO *ai;
  ACCLIST *al;
  char *ptr, *ptr2;
  U_INT cnt;

  ai = (ACCINFO *) data;
  ai -> acc_resource_name = PTR16(path);

  if (sscanf(text, "ACL1:%hX,%hd",
             &ai -> acc_attr, &ai -> acc_count) != 2)
    return ERROR_INVALID_PARAMETER;

  al = (ACCLIST *) (data + sizeof(ACCINFO));
  ptr = strchr(text, '\n') + 1;

  for (cnt = 0; cnt < ai -> acc_count; cnt++)
  {
    ptr2 = strchr(ptr, ',');
    strncpy(al[cnt].acl_ugname, ptr, ptr2 - ptr);
    al[cnt].acl_ugname[ptr2 - ptr] = 0;
    sscanf(ptr2 + 1, "%hx", &al[cnt].acl_access);
    ptr = strchr(ptr, '\n') + 1;
  }

  return sizeof(ACCINFO) + ai -> acc_count * sizeof(ACCLIST);
}

int acl_set(char *server, const char *resource, char *buffer)
{
  USHORT datalen;
  PSZ srv = NULL;

  if (!acl_init())
    return -1;

  if (server)
    srv = server;

  acl_mkpath(path, resource);

  ai -> acc_resource_name = PTR16(path);
  ai -> acc_attr = 0;
  ai -> acc_count = 0;

  NetAccessAdd(srv, 1, ai, sizeof(ACCINFO));
  /* Ignore any errors, most probably because ACL already exists. */
  /* In any such case, try updating the existing ACL. */

  datalen = acl_text2bin(data, buffer, path);

  return NetAccessSetInfo(srv, path, 1, data, datalen, 0);
}

/* end of os2acl.c */
