/* 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.
 */
/*
*  Lan Manager client
*
*  Logon.C -- "Logon" interface to Omniclient
*
*  This module produces a "Log on to Lan Manager" icon in the FS List
*    !Omni window, which lets the user log on (i.e. set a default
*    workgroup/domain, user name, and password).
*
*  Versions
*  29-05-96 INH Original, for LanMan 1.69
*  10-06-96 NAS Minor fixes for domain logon/OmniClient interface to work
*  12-06-96 INH "Dismount" call deleted, OmniOp 17 made instead.
*  25-07-96 INH Home directory stuff added
*/

#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "kernel.h"
#include "swis.h"

#include "stdtypes.h"
#include "lanman.h"
#include "lmvars.h"
#include "omni.h"
#include "rpc.h"
#include "logon.h"
#include "xlate.h"

#include "LanMan_MH.h"

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

static void SetPassword ( char * pwd )
{
  static char pass_buf[NAME_LIMIT];
  if ( pwd == NULL ) pwd = "";

  strcpyn ( pass_buf, pwd, NAME_LIMIT );
  Xlt_Jumble ( pass_buf );
  LM_Vars.password_ptr = pass_buf;
}

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

static void SetOSVar ( char *varname, char *value )
{
  _kernel_swi_regs R;

  R.r[0] = (int) varname;
  R.r[1] = (int) value;

  if ( value == NULL )  /* Unset it */
    R.r[2] = -1;
  else
    R.r[2] = strlen (value);

  R.r[3] = 0;
  R.r[4] = 4; /* Literal string */
  _kernel_swi ( OS_SetVarVal, &R, &R );
}

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

char Lgn_PrimaryDCName[NAME_LIMIT];  /* For debugging */
char Lgn_HomeServerName[NAME_LIMIT];
char Lgn_HomeShareName[NAME_LIMIT];
char Lgn_HomeDirName[DOS_NAME_LEN];

static bool get_path ( char **ptr_in_out, char *result )
{
  int i;
  char *p = *ptr_in_out;

  if (*p++ != '\\') return false;

  i=0;
  while ( *p != '\\' && *p != 0 )
  {
    if ( i < NAME_LIMIT-1 )
      result[i++] = *p;
    p++;
  }

  result[i] = 0;
  *ptr_in_out = p;
  return (bool) (i>0);
}

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

static err_t ParseHomeDir ( char *name )
{
  char *p;
  char leaf_name[NAME_LIMIT];


  /* We expect the string to be of the form \\servername\sharename,
     with optionally a path on the end of it, which will be
     translated to RISCOS format. */

  while ( isspace(*name) ) name++; /* Skip leading whitespace */

  if ( name[0] != '\\' || name[1] != '\\' ) /* Doesn't start '\\' */
    return EHOMEDIRNAME;

  name++;  /* Make name \SERVER\SHARE[\path] */

  if ( !get_path ( &name, Lgn_HomeServerName ) ||
       !get_path ( &name, Lgn_HomeShareName ) )
    return EHOMEDIRNAME;

  sprintf ( Lgn_HomeDirName, "%s::Home.$", FilingSystemName );
  p = Lgn_HomeDirName + strlen(Lgn_HomeDirName);
  while ( get_path ( &name, leaf_name ) )
  {
    *p++ = '.';
    Xlt_NameDOStoRO ( p, leaf_name );
    p += strlen(p);
  }

  return OK;
}


/* ----------------------------------- */
/* This function should check for CMOS default values, *
 * and use them if present - RCE 980122                *
 * Achieved by modifying its caller 980127:RCE         *
 */

err_t Lgn_Init ( void )
{
  strcpy ( LM_Vars.workgroup, "WORKGROUP" );
  strcpy ( LM_Vars.username,  "GUEST" );
  SetPassword ( NULL );
  LM_Vars.logged_on = true;
  return  OK;
}

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

err_t Lgn_Logon ( char *wkgrp, char *userid, char *pwd )
{
  char *serv_name;
  char *dir_name;
  int   tmp;
  err_t res;

  if ( userid == NULL || userid[0] == 0 )
    return EUSERUNKNOWN;

  if ( wkgrp != NULL && strlen(wkgrp) > 0 )
    strcpyn_upper ( LM_Vars.workgroup, wkgrp, NAME_LIMIT );

  /* pwd may be NULL */

  strcpyn ( LM_Vars.username,  userid, NAME_LIMIT );
  SetPassword ( pwd );
  LM_Vars.logged_on = true;

  SetOSVar("Omni$UserLanMan", LM_Vars.username);
  SetOSVar("LanMan$HomeDir", NULL );
  Lgn_HomeDirName[0] = 0;

  Omni_RecheckInfo (RI_MOUNTS);
  Omni_RecheckInfo (RI_SERVERS);
  Omni_RecheckInfo (RI_PRINTERS);

  /* Now attempt to find home directory */

  serv_name = RPC_GetDomainController ( LM_Vars.workgroup );
  if ( serv_name == NULL ) /* No DC: Don't worry about it */
  {
    strcpy ( Lgn_PrimaryDCName, "<not found>");
    return OK;
  }

  strcpy ( Lgn_PrimaryDCName, serv_name );

  /* Report bad passwords & other errors, ignore unset home directories */


  res = RPC_LogonUser ( serv_name, LM_Vars.username, pwd, &dir_name );
  if ( res == ENOTPRESENT ) /* Server doesn't support it */
    return OK;

  if ( res != OK )
    return res;

  /* Extract fields from name, & try to connect to it */

  if ( dir_name == NULL || dir_name[0] == 0 )
    return OK;

  res = ParseHomeDir ( dir_name );
  if ( res != OK )
    return res;

  /* Set the home directory name so it is available to the boot file */

  SetOSVar ( "LanMan$HomeDir", Lgn_HomeDirName );

  res =  Omni_MountServer ( Lgn_HomeServerName,
                            LM_Vars.username,
                            pwd,
                            "Home", /* Mountname */
                            Lgn_HomeShareName,
                            &tmp ); /* MountID_out */

  /* Caution - this could potentially recurse, as the !ARMBOOT file
     may contain an LMLogon command!
  */

  if ( res == OK || res == ECONNEXISTS )
    return OK;
  else
    return EHOMEDIRCONN;
}

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

err_t Lgn_Logoff ( void )
{
  strcpy ( LM_Vars.username, "" );
  SetPassword(NULL);
  LM_Vars.logged_on = false;
  Omni_ClearLists();

  SetOSVar ( "Omni$UserLanMan", NULL );
  SetOSVar ( "LanMan$HomeDir", NULL );

  Omni_RecheckInfo ( RI_SERVERS );

  return OK;
}

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

void Lgn_Register(void)
{
  (void) _swix( SWI_Omni_RegisterClient, _INR(0,6),
  	LanMan_LogonOp, /* Client ID */
  	RC_LOGON_TYPE |  RC_NEEDS_AUTHSERV | RC_NEEDS_USERID | RC_NEEDS_PASSWD,
  	"lmlgni",
  	"LAN Manager",        /* Needs to be reasonably short. NAS */
  	"LanManFS Logon bit", /* Info string - shouldn't appear*/
  	0, /* Site ID */
  	"LanMan+"); /* Needs to be unique from LanMan FS name. NAS */
}

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

static err_t Lgn_ListServers ( char *buf_ptr, int buf_size, int token_in,
              char * *pNextByte_out, int *pToken_out )
{
  char *shortname = "LanManager";  /* Needs to be reasonably short. NAS */
  char *longname  = "LanManager";  /* Needs to be reasonably short. NAS */
  char *extradetails = "Logon Network\n \n ";
  int   serverid  = (int) "?"; /* A unique memory address */

  int   tot_size;

  tot_size = sizeof ( serverid ) + strlen(shortname) + 1 +
                                   strlen (longname ) + 1 +
                                   strlen (extradetails) + 1;

  tot_size = (tot_size + 3) & ~3;  /* Round to dword boundary */

  /* We only ever write one record */

  if ( token_in != 0 || tot_size > buf_size )
  {
    *pNextByte_out = buf_ptr;  /* No records written */
    *pToken_out = 0;           /* All done */
    return OK;
  }

  *pNextByte_out = buf_ptr + tot_size;
  *pToken_out = 0;

  memcpy ( buf_ptr, &serverid, sizeof(serverid) );
  buf_ptr += sizeof(serverid);
  strcpy ( buf_ptr, shortname );
  buf_ptr += strlen(shortname)+1;
  strcpy ( buf_ptr, longname );
  buf_ptr += strlen(longname)+1;
  strcpy ( buf_ptr, extradetails );

  return OK;
}





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

_kernel_oserror *Lgn_LogonOp_SWI ( _kernel_swi_regs *R )
{
  err_t res;

  debug1(" Omni Op(%d):", R->r[0]);

#define Rin_int(a)   (R->r[a])
#define Rout_int(a) &(R->r[a])
#define Rin_chr(a)   (char *)(R->r[a])
#define Rout_chr(a)  (char **)&(R->r[a])

  switch ( R->r[0] )
  {
    case 0:  /* Mount */
      res = Lgn_Logon ( Rin_chr(1), /* "Server" => domain/workgroup */
                        Rin_chr(2), /* User ID */
                        Rin_chr(3) ); /* Password */
      if ( res == OK || res == EHOMEDIRCONN || res == EHOMEDIRNAME)
        R->r[1] = 1; /* Keep the Smith happy */
      else
        R->r[1] = 0; /* NAS 05/Dec/96: needs to return mount_id of 0 if error */
      break;


    case 3:  /* Enumerate servers */
      res = Lgn_ListServers ( Rin_chr(1),
                               Rin_int(2),
                               Rin_int(3),
                               Rout_chr(1),
                               Rout_int(3) );
      break;

   case 9:   /* Get mount info - (mild bodge - say it's not there!) */
     R->r[3] = 0;
     res = OK;
     break;


   case 17:  /* Log out of domain */
     res = Lgn_Logoff(); /* Clear all user ID's & passwords */
     break;

   default:
     res = ENOTPRESENT;
  }

  return Xlt_Error(res);
}