/* Copyright 1998 Acorn Computers Ltd
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*
*
*  C.LMTest -- Test of LanMan SWIs
*
*  26/Apr/96
*
*  Copyright (C) Ian Harvey for Ant Limited, 1996
*
*/
#include <stdio.h>
#include "swis.h"
#include "os.h"

/*
 This demonstates the use of the two SWIs provided by LanManFS v1.68,
 LanMan_NameOp and LanMan_Transact. These are as follows:
*/
#define LanMan_SWI_base         (XOS_Bit|0x49240)
#define SWI_LanMan_NameOp   (LanMan_SWI_base + 2)
#define SWI_LanMan_Transact (LanMan_SWI_base + 3)

/*
* LanMan_NameOp is used for retrieving the names of machines and other
*  items on the network. It is called with:
*
* R0 = reason code, 0-3
* R1 = pointer to name in, or NULL (use depends on reason code)
* R2 = pointer to a 16-byte buffer which is to receive the name
*   returned from the call. Network names are up to 15 characters,
*   (generally in upper-case, although no LanManFS calls are case
*   sensitive), plus a terminating zero.
*
* If there is no error, all registers are unchanged. Else, V is set
*  and R0 points to an error block.
*
* The reason codes are as follows:
*
* R0 = 0: Get local machine name
*   R1 is ignored. The network name of this machine is copied to the
*   buffer at R2.
*
* R0 = 1: Get logged-on workgroup/domain name
*   R1 is ignored. The name of the workgroup or domain which was set
*   with the *LMLOGON command is copied to the buffer at R2.
*
* R0 = 2: Get master browser for workgroup
*   R1 is the address of a ASCIIZ string containing a workgroup name.
*   The master browser for that workgroup (a server which maintains
*   an up-to-date list of all the other servers in the group) is
*   located, and its name is copied to the buffer at R2. If R1 is NULL
*   on entry, the currently selected workgroup name (as set with *LMLogon)
*   is used. You can use this call to find the name of a server from which
*   to get lists of other servers.
*
* R0 = 3: Get domain controller
*   R1 is the address of a ASCIIZ string containing a domain name.
*   The domain controller for that domain is located, and its name is
*   copied to the buffer at R2. If R1 is NULL on entry, the currently
*   selected workgroup or domain name (as set with *LMLogon) is used.
*
* If the master browser or domain controller can't be found, the error
* 'Can't find given server' is returned.
*/

#define NAMEOP_GETLOCAL   0
#define NAMEOP_GETWG      1
#define NAMEOP_GETBROWSER 2
#define NAMEOP_GETDC      3

/*
*  LanMan_Transact
*
*  This is used to execute remote-procedure-calls on a network server.
*  Typically, the server will be able to perform all the calls in
*  the Microsoft's Lan Manager Programmer's Reference via this mechanism.
*  Although MS document the C interface to these calls, they do not
*  make available information as to how the calls are converted to
*  the network parameter format used by the Transact call. Thus, a
*  certain amount of analysis of network packets and other black arts
*  is required.
*
*  Anyway, all calls made via the Transact interface send information
*  to the server at the start of the call, and retrieve information back
*  afterwards. The information sent and returned is divided into
*  Parameters (typically 16 or 32-bit integer values) and Data (typically
*  structures, strings, etc) - exactly what each of these are varies from
*  call to call and is sometimes A Mystery. Making a call consists of
*  assembling the parameters and data into a buffer, then setting up a
*  structure containing the addresses and lengths of these, together with
*  addresses and lengths for where to put the results.
*
*  The parameters to LanMan_Transact are as follows:
*
*  R0 = ASCIIZ name of server on which to make the call (this is not
*        case sensitive; only the first 15 characters of this name are
*	 significant).
*
*  R1 = Transaction name (identifies the process on the server which is
*     	 servicing the call). For Lan Manager API transactions, this
*	 should point to the ASCIIZ string "\PIPE\LANMAN".
*
*  R2 = Pointer to parameters-and-results structure. This is a 10-word
*     	  structure as follows:
*
* [R2+0]: Length of parameters to be sent to server
* [R2+4]: Address of parameters to be sent to server
* [R2+8]: Length of data to be sent to server
* [R2+12]: Address of data to be sent to server
*
* [R2+16]: Maximum length of parameters to be returned from server
* [R2+20]: Address of buffer to receive returned parameters
* [R2+24]: this will be filled in (by LanMan_Transact) with the actual
*            length of parameters returned from the server.

* [R2+28]: Maximum length of data to be returned from server
* [R2+32]: Address of buffer to receive returned data
* [R2+36]: actual length of data returned from the server (filled in by
*             LanMan_Transact).
*
* Note that the 'returned' parameters and data can occupy the same
*  memory buffer as the 'sent' ones, if required. If any 'length'
*  field is zero, its associated pointer need not be defined.
*
* The SWI may return a variety of errors. In particular, if LanManFS
*   is not currently connected to the given server, it will have to
*   establish a connection using the user ID and password set with
*   *LMLogon. If these are not valid, you may get an 'Access denied'
*   error.
*/

typedef unsigned char uchar;

struct TransactParms
{
  int    parms_in_len;  /* Params & data sent to server */
  uchar *parms_in;
  int    data_in_len;
  uchar *data_in;

  int    parms_out_maxlen; /* Results & data returned from server */
  uchar *parms_out;
  int    parms_out_len; /* Filled in by Transact SWI */

  int    data_out_maxlen;
  uchar *data_out;
  int    data_out_len;  /* Filled in by Transact SWI */
};

/* ----------------------------------- */

#define MAX_TRANSACT_SIZE 4096
uchar TransactBuf[MAX_TRANSACT_SIZE];

struct TransactParms TP;

/* ------- */

static void addword ( int value )
{
  uchar *p = TP.parms_in + TP.parms_in_len;
  *p++ = (value & 0xFF);
  *p++ = (value >> 8 );
  TP.parms_in_len += 2;
}

/* ---------- */

static void addlong ( int value )
{
  uchar *p = TP.parms_in + TP.parms_in_len;

  *p++ = (value & 0xFF);
  *p++ = (value >> 8 );
  *p++ = (value >> 16 );
  *p++ = (value >> 24 );
  TP.parms_in_len += 4;
}

/* ---------- */

static void addstring ( char *str )
{
  uchar *p = TP.parms_in + TP.parms_in_len;
  int c;

  do
  {
    *p++ = c = *str++;
    TP.parms_in_len++;
  }
    while (c != 0);

}

/* ----------------- */

static int getword ( int offset )
{
  uchar *p = TP.parms_out + offset;
  return ( p[0] + (p[1] << 8));
}

/* ----------------- */

static int getlong ( int offset )
{
  uchar *p = TP.parms_out + offset;
  return ( p[0] + (p[1] << 8) + (p[2] << 16)+ (p[3] << 24));
}

/* ------------------- */

static uchar *getstring ( uchar *p )
{
  int ptr_val;
  /* String pointers are kept in a slightly odd format in the
     returned data. This code may be slightly inaccurate.
  */

  ptr_val = p[0] + (p[1] << 8);  /* Value returned from server */

  /* ptr_val is almost an offset into the returned data block -
     conceptually it's as though a data buffer the size of
     data_out_maxlen was allocated by the server. The normal data
     returned by the call is put at the start of the buffer, and
     all the strings are put right at the end. The string pointers
     given are the offsets from the start of the buffer. Then, when
     the data is actually sent, all the unused bytes in the middle
     of the buffer are chopped out */

  ptr_val -= TP.data_out_maxlen - TP.data_out_len;  /* Adjust it */

  if ( ptr_val <= 0 || ptr_val >= TP.data_out_len )
    return (uchar *)""; /* Null string */

  return TP.data_out + ptr_val;
}

/* -------------- */

char * ShareTypes[4] = { "Disk", "Printer", "COM port", "IPC" };

/* -------------- */

int main ( int argc, char *argv[] )
{
  char namebuf[16];
  os_error *p;
  int n;
  uchar *pb;

  (void) argc;
  (void) argv;

  /* Display some names */

  p = _swix(SWI_LanMan_NameOp, _INR(0,2), NAMEOP_GETLOCAL, NULL, (int)namebuf);
  if ( p != NULL )
    printf("Couldn't find local machine name - %s\n", p->errmess );
  else
    printf("Local machine name %s\n", namebuf );

  p = _swix(SWI_LanMan_NameOp, _INR(0,2), NAMEOP_GETWG, NULL, (int)namebuf);
  if ( p != NULL )
    printf("Couldn't find workgroup name - %s\n", p->errmess );
  else
    printf("Workgroup/domain %s\n", namebuf );

  p = _swix(SWI_LanMan_NameOp, _INR(0,2), NAMEOP_GETDC, NULL, (int)namebuf);
  if ( p != NULL )
    printf("Couldn't find local machine name - %s\n", p->errmess );
  else
    printf("Domain controller %s\n", namebuf );

  p = _swix(SWI_LanMan_NameOp, _INR(0,2), NAMEOP_GETBROWSER, NULL, (int)namebuf);
  if ( p != NULL )
  {
    printf("Couldn't find master browser - %s\n", p->errmess );
    return 0;
  }

  printf("Master browser %s\n", namebuf );

  /* Now do a Transact call */

  TP.parms_in = TransactBuf;
  TP.parms_in_len = 0;
  TP.data_in = TransactBuf;
  TP.data_in_len = 0;

  TP.parms_out = TransactBuf;
  TP.parms_out_maxlen = 8;    /* Size of data returned from this call */
  TP.data_out  = TP.parms_out + TP.parms_out_maxlen;
  TP.data_out_maxlen = MAX_TRANSACT_SIZE - TP.parms_out_maxlen;

  addword ( 0x0000 ); /* Reason code for 'NetShareEnum' */
  addstring ( "WrLeh" ); /* Sent parameters format */
  addstring ( "B13BWz" ); /* Return parameters format */
  addword ( 0x0001 ); /* 'sLevel' detail-level parameter */
  addword ( TP.data_out_maxlen ); /* Max data size returned */

  /* Do the call. The master browser name is in namebuf already */

  printf("\n\nFinding share information for '%s'\n", namebuf );
  p = _swix(SWI_LanMan_Transact, _INR(0,2), (int) namebuf, (int) "\\PIPE\\LANMAN", (int)&TP);
  if ( p != NULL )
  {
    printf("Error from Transact SWI - %s\n", p->errmess );
    return 0;
  }

  /* Check returned status */

  if ( TP.parms_out_len < 8 )
  {
    printf("Server returned insufficient result bytes\n");
    return 0;
  }

  n = getword ( 0 ); /* Return code from call */
  if ( n != 0 )
  {
    printf("Server gave error code %Xh\n", n );
    return 0;
  }

  n = getword ( 4 );  /* Number of returned records */

  if ( n*20 > TP.data_out_len )
    n = TP.data_out_len / 20;

  /* Display returned records */

  printf("%d share(s) available:\n", n);

  pb = TP.data_out;

  while ( n-- > 0 )
  {
    /* pb points to a share_info_1 structure (see Lan Manager programmer's
       reference). The first 13 bytes are an ASCIIZ share name string.
       Bytes 14..15 contains a share type field in the range 0..3. Bytes
       16..19 are a pointer to a share comment */

    printf("%-12s %-8s %s\n", pb, ShareTypes[pb[14] & 3], getstring(pb+16) );

    pb += 20;
  }



  return 0;
}