/***************************************************************************
 *                                                                         *
 *   TLCUSER.CPP                                                           *
 *                                                                         *
 *   Copyright (c) 1997-1998  Galacticomm, Inc.   All Rights Reserved      *
 *                                                                         *
 *   Teleconference Users                                                  *
 *   Implementation                                                        *
 *                                               - Phil Henning 5/14/97    *
 *                                                                         *
 ***************************************************************************/

#include "gcomm.h"
#include "majorbbs.h"
#include "tlcapi.hpp"
#include <time.h>

#define FILREV "$Revision: 65 $"

MARKSOURCE(tlcuser);

CTlcUser::CTlcUser()
{
     m_pszName[0]=m_strLastWhispered[0]='\0';
     m_pszChannel[0]='\0';
     m_fFlags=0;
     m_iTimesSpoken=0;
     m_iType=USR_TYPE_DEFAULT;
     m_iUsrnum=-1;
     m_iChatInterval=2;
     m_pTransport=NULL;
     m_pfnacc=0;
     m_iSize=sizeof(CTlcUser);
     m_iSpeakThrottle=0;
     m_iSpeedWarn=0;
     m_iTalkCount=0;
     m_iSwitchThrottle=0;
     m_iSwitchCount=0;
     m_iSwitchWarn=0;
     m_bBeingDeleted=FALSE;
}

CTlcUser::~CTlcUser()
{
}

VOID
CTlcUser::SetName(                 // Set User's Name
const CHAR* pszUserid)             //  Name to set to
{
     stzcpy(m_pszName,pszUserid,TLCUIDSIZ);
}

const CHAR*                        //  Buffer containing name
CTlcUser::GetName()                // Get the user's name
{
     return(m_pszName);
}

CHAR                               //  Gender
CTlcUser::GetSex()                 // Get the user's gender
{
     return(m_chSex);
}

VOID
CTlcUser::SetSex(                  // Set the user's gender
CHAR sex)                          //  Gender to set to
{
     m_chSex=sex;
}

SHORT
CTlcUser::GetAge()                 // Get the user's age
{
     return(m_iAge);
}

VOID
CTlcUser::SetAge(                  //  Set the user's age
SHORT age)                         //   age to set to
{
     m_iAge=age;
}


const CHAR*                        //  Buffer containing channel name
CTlcUser::GetChannelName()         // Get the user's channel name
{
     return(m_pszChannel);
}

VOID
CTlcUser::SetChannel(              // Set the user's channel name
const CHAR* pszChannel)            //  Channel to set to
{
     stzcpy(m_pszChannel,pszChannel,CHAN_MAX_SIZE);
}

VOID
CTlcUser::SetTransport(            // Set the user's transport
CTlcTransport* pTransport)         //  Pointer to transport to set
{
     m_pTransport=pTransport;
}

CTlcTransport*                     //  Pointer to the user's transport
CTlcUser::GetTransport()           // Get the user's tranport
{
     return(m_pTransport);
}

LPUSRSTRUCT                        //  Pointer to user's structure
CTlcUser::GetUserStruct()          // Get a structure representing this user
{
     return(dynamic_cast<LPUSRSTRUCT>(this));
}

VOID
CTlcUser::SetUserStruct(           // Set user params based on structure
LPUSRSTRUCT news)                  //  Pointer to structure to use
{
     memmove(dynamic_cast<LPUSRSTRUCT>(this),news,sizeof(USRSTRUCT));
}

VOID
CTlcUser::SetLastWhispered(        // Set the last whispered user
const CHAR* pszUserid)             //  Userid to set
{
     if (pszUserid == NULL || *pszUserid == '\0') {
          *m_strLastWhispered = '\0';
     }
     else {
          stzcpy(m_strLastWhispered,pszUserid,TLCUIDSIZ);
     }
}

const CHAR*                        //  Last userid whispered to
CTlcUser::GetLastWhispered()       // Get the last whispered to user
{
     return(m_strLastWhispered);
}

GBOOL                              //  User has access
CTlcUser::CheckAccess(             // Check access based on a key
const CHAR* key)                   //  Key to check
{
     ASSERT(key != NULL);
     if (NULSTR(key)) {
          return(TRUE);
     }
     return(uhskey(m_pszName,(CHAR*)key));
}

GBOOL                              //  Listed?
CTlcUser::IsListed()               // Does user have his channel listed?
{
     return(!(m_fFlags&USR_UNLIST));
}

VOID
CTlcUser::List()                   // Toggle channel list status
{
     if (m_fFlags&USR_UNLIST) {
          m_fFlags&=~USR_UNLIST;
     }
     else {
          m_fFlags|=USR_UNLIST;
     }
}

VOID
CTlcUser::OnRecvText(              // Send text to user
const CHAR* pszUseridFrom,         //  Userid message is from
const CHAR* pszUseridTo,           //  Userid to send TO
const CHAR* pMsg,                  //  Text to send
SHORT fType,                       //  Type of text
SHORT iMsgNum,                     //  MSG file template to use
const CHAR* ptr1,                  //  extra information for MSG template
const CHAR* ptr2,                  //  extra information for MSG template
const CHAR* ptr3)                  //  extra information for MSG template
{
     ASSERT(m_pTransport != NULL);

     CTlcUser* pUserFrom=tlcAPI->usrGetByName(pszUseridFrom);
     m_pTransport->RecvText(pUserFrom,this,pMsg,fType,iMsgNum,ptr1,ptr2,ptr3);
     UNREFERENCED(pszUseridTo);
}

VOID
CTlcUser::CmdResult(               // Send output based on a command
SHORT iOperation,                  //  operation performed
GBOOL bSuccess,                    //  successful?
SHORT iReason)                     //  reason for success/failure
{
     ASSERT(m_pTransport != NULL);
     m_pTransport->CmdResult(this,iOperation,bSuccess,iReason);
}

SHORT                              //  Type code
CTlcUser::GetType(VOID)            // Get Type of user
{
     return(m_iType);
}

INT                                //  Usrnum of user
CTlcUser::GetUsrnum(VOID)          // Get usrnum of user
{
     return(m_iUsrnum);
}

VOID
CTlcUser::SetUsrnum(               // Set usrnum of user
INT iUsrnum)                       //  usrnum to set to
{
     m_iUsrnum=iUsrnum;
}

const CHAR*                        //  Message
CTlcUser::GetEntrance(             // Get entrance message
SHORT iType)                       //  Requested or current?
{
     if (iType == MSG_CURRENT) {
          return(m_pszEntCur);
     }
     else {
          return(m_pszEntReq);
     }
}

const CHAR*                        //  Message
CTlcUser::GetExit(                 // Get exit message
SHORT iType)                       //  Requested or current?
{
     if (iType == MSG_CURRENT) {
          return(m_pszExitCur);
     }
     else {
          return(m_pszExitReq);
     }
}

VOID
CTlcUser::SetEntrance(             // Set entrance message
const CHAR* pszMsg,                //  Message to set to
SHORT iType)                       //  Requested or Current?
{
     if (iType == MSG_CURRENT) {
          stzcpy(m_pszEntCur,pszMsg,EMSGSIZ);
     }
     else {
          stzcpy(m_pszEntReq,pszMsg,EMSGSIZ);
     }
}

VOID
CTlcUser::SetExit(                 // Set exit message
const CHAR* pszMsg,                //  Message to set to
SHORT iType)                       //  Requested or Current?
{
     if (iType == MSG_CURRENT) {
          stzcpy(m_pszExitCur,pszMsg,EMSGSIZ);
     }
     else {
          stzcpy(m_pszExitReq,pszMsg,EMSGSIZ);
     }
}

SHORT                              //  Default channel
CTlcUser::GetDefaultChan()         // Get default channel
{
     return(m_iChanDft);
}

VOID
CTlcUser::SetDefaultChan(          // Set default channel
SHORT iChannel)                    //  Channel to set to
{
     m_iChanDft=iChannel;
}

SHORT                              //  Chat interval
CTlcUser::GetChatInterval()        // Get chat interval
{
     return(m_iChatInterval);
}

SHORT                              //  New interval
CTlcUser::SetChatInterval(         // Set chat interval
SHORT iInterval)                   //  Interval to set
{
     if (iInterval > INTERVAL_HIGH) {
          return(INTERVAL_TOO_HIGH);
     }
     if (iInterval < INTERVAL_LOW) {
          return(INTERVAL_TOO_LOW);
     }
     return(m_iChatInterval=iInterval);
}

const CHAR*                        //  Topic
CTlcUser::GetPrivateTopic()        // Get the user's private channel topic
{
     return(m_pszPrivateTopic);
}

VOID
CTlcUser::SetPrivateTopic(         // Set the user's private channel topic
const CHAR* pszTopic)              //  Topic to set
{
     CTlcChannel* pChannel;

     pChannel=tlcAPI->chanGetByName(tlcPrivateFromUser(m_pszName));
     pChannel->SetTopic(pszTopic);
     stlcpy(m_pszPrivateTopic,pszTopic,CHAN_MAX_TOPIC_SIZE);
     if (*pszTopic != '\0') {
          pChannel->PublicSendBut(m_pszName,NULL,GetPrivateTopic(),RECV_TYPE_NEWTOPIC,MIMSG_MSG_TOPIC,NULL,NULL,NULL,GetName());
     }
}

VOID
CTlcUser::LoadRecord()             // Load user's account record from disk
{
     USRDSK tlcdsk;

     dfaSetBlk(dfaTlcUser);
     if (!dfaAcqEQ(&tlcdsk,m_pszName,0)) {
          memset(&tlcdsk,0,sizeof(USRDSK));
          tlcdsk.m_iChatInterval=2;
          stzcpy(tlcdsk.m_pszUserid,m_pszName,UIDSIZ);
          dfaInsert(&tlcdsk);
     }
     SetChatInterval(tlcdsk.m_iChatInterval);
     SetDefaultChan(tlcdsk.m_iChanDft);
     SetEntrance(tlcdsk.m_pszEntCur,MSG_CURRENT);
     SetEntrance(tlcdsk.m_pszEntReq,MSG_REQUESTED);
     SetExit(tlcdsk.m_pszExitCur,MSG_CURRENT);
     SetExit(tlcdsk.m_pszExitReq,MSG_REQUESTED);
     stlcpy(m_pszPrivateTopic,tlcdsk.m_pszPrivateTopic,CHAN_MAX_TOPIC_SIZE);
     if (CheckAccess(rawmsg(AUTKEY))) {
          if (*(tlcdsk.m_pszEntReq) != '\0') {
               SetEntrance(tlcdsk.m_pszEntReq,MSG_CURRENT);
               *m_pszEntReq = '\0';
          }
          if (*(tlcdsk.m_pszExitReq) != '\0') {
               SetExit(tlcdsk.m_pszExitReq,MSG_CURRENT);
               *m_pszExitReq = '\0';
          }
     }
     if (tlcdsk.m_bSquelched) {
          m_fFlags|=USR_SQUELCH;
     }
     else {
          m_fFlags&=~USR_SQUELCH;
     }
     if (tlcdsk.m_fFlags&USR_ENCODETAGS) {
          m_fFlags|=USR_ENCODETAGS;
     }
     if (tlcdsk.m_fFlags&USR_WEBTELJAVA) {
          m_fFlags|=USR_WEBTELJAVA;
     }
     else if(tlcdsk.m_fFlags&USR_WEBTELHTML) {
          m_fFlags|=USR_WEBTELHTML;
     }
     if (tlcdsk.m_fFlags&USR_UNLIST) {
          m_fFlags|=USR_UNLIST;
     }
     else {
          m_fFlags&=~USR_UNLIST;
     }
     dfaRstBlk();
}

VOID
CTlcUser::SaveRecord()             // Save user's account record to disk
{
     USRDSK tlcdsk;
     GBOOL update=FALSE;

     dfaSetBlk(dfaTlcUser);
     if (dfaAcqEQ(&tlcdsk,m_pszName,0)) {
          update=TRUE;
     }
     tlcdsk.m_iChatInterval=GetChatInterval();
     tlcdsk.m_iChanDft=GetDefaultChan();
     stzcpy(tlcdsk.m_pszEntCur,GetEntrance(MSG_CURRENT),EMSGSIZ);
     stzcpy(tlcdsk.m_pszEntReq,GetEntrance(MSG_REQUESTED),EMSGSIZ);
     stzcpy(tlcdsk.m_pszExitCur,GetExit(MSG_CURRENT),EMSGSIZ);
     stzcpy(tlcdsk.m_pszExitReq,GetExit(MSG_REQUESTED),EMSGSIZ);
     stzcpy(tlcdsk.m_pszUserid,m_pszName,UIDSIZ);
     stzcpy(tlcdsk.m_pszPrivateTopic,m_pszPrivateTopic,CHAN_MAX_TOPIC_SIZE);
     if (m_fFlags&USR_SQUELCH) {
          tlcdsk.m_bSquelched=TRUE;
     }
     else {
          tlcdsk.m_bSquelched=FALSE;
     }
     tlcdsk.m_fFlags=m_fFlags;
     if (update) {
          dfaUpdate(&tlcdsk);
     }
     else {
          dfaInsert(&tlcdsk);
     }
     dfaRstBlk();
}

GBOOL                              //  Invited?
CTlcUser::CheckInvite(             // Check if user is invited
CTlcUser* pUser)                   //  User to check
{
     CTlcChannel* pChannel;

     if (tlcAPI->chanCheckPrivate(m_pszName,&pChannel) == CHAN_PRIVATE_FOUND) {
          CTlcPrivateChannel* pPrivate=dynamic_cast<CTlcPrivateChannel*>(pChannel);
          return(pPrivate->CheckInvite(pUser));
     }
     return(FALSE);
}

GBOOL                              //  Invite status after toggling
CTlcUser::ToggleInvite(            // Toggle user invited
CTlcUser* pUser)                   //  User to toggle
{
     CTlcChannel* pChannel;

     if (tlcAPI->chanCheckPrivate(m_pszName,&pChannel) == CHAN_PRIVATE_FOUND) {
          CTlcPrivateChannel* pPrivate=dynamic_cast<CTlcPrivateChannel*>(pChannel);
          return(pPrivate->ToggleInvite(pUser));
     }
     return(FALSE);
}



GBOOL
CTlcUser::ToggleForget(
CTlcUser* pUser)
{
     if (pUser == NULL) {
          return(FALSE);
     }

     if (CheckForget(pUser)) {
          return(RemoveUserFromList(pUser,LIST_FORGET));
     }
     else {
          return(AddUserToList(pUser,LIST_FORGET));
     }
}

GBOOL
CTlcUser::CheckForget(
CTlcUser* pUser)
{
     if (pUser == NULL) {
          return(FALSE);
     }

     if (FindUserInList(pUser,LIST_FORGET) != NULL) {
          return(TRUE);
     }
     return(FALSE);
}

GBOOL
CTlcUser::ToggleIgnore(
CTlcUser* pUser)
{
     if (pUser == NULL) {
          return(FALSE);
     }

     if (CheckIgnore(pUser)) {
          return(RemoveUserFromList(pUser,LIST_IGNORE));
     }
     else {
          return(AddUserToList(pUser,LIST_IGNORE));
     }
}

GBOOL
CTlcUser::CheckIgnore(
CTlcUser* pUser)
{
     if (pUser == NULL) {
          return(FALSE);
     }

     if (FindUserInList(pUser,LIST_IGNORE) != NULL) {
          return(TRUE);
     }
     return(FALSE);
}


GBOOL
CTlcUser::AddUserToList(
CTlcUser* pUser,
SHORT list)
{
     ULONG user;
     USHORT board;
     GBOOL retval;

     user=pUser->GetTlcUnum();
     board=pUser->GetTlcBoardNum();

     retval=AddUserToListNUM(user,board,list);
     cschgstatus(this,pUser);
     return(retval);
}

ULONG
CTlcUser::GetTlcUnum()
{
     return(m_ulTlcUnum);
}

USHORT
CTlcUser::GetTlcBoardNum()
{
     return(m_uiTlcBoardNum);
}

VOID
CTlcUser::SetTlcUnum(
ULONG num)
{
     m_ulTlcUnum=num;
}

VOID
CTlcUser::SetTlcBoardNum(
USHORT num)
{
     m_uiTlcBoardNum=num;
}


GBOOL
CTlcUser::RemoveUserFromList(
CTlcUser* pUser,
SHORT list)
{
     ULONG user;
     USHORT board;
     GBOOL retval;

     user=pUser->GetTlcUnum();
     board=pUser->GetTlcBoardNum();

     retval=RemoveUserFromListNUM(user,board,list);
     cschgstatus(this,pUser);
     return(retval);
}
VOID*
CTlcUser::FindUserInList(
CTlcUser* pUser,
SHORT list)
{
     ULONG user;
     USHORT board;

     user=pUser->GetTlcUnum();
     board=pUser->GetTlcBoardNum();

     return(FindUserInListNUM(user,board,list));
}

LPFLIST
CTlcUser::GetForgetListPtr()
{
     InitArray();
     return(m_arForgetList);
}

LPFLIST
CTlcUser::GetIgnoreListPtr()
{
     InitArray();
     return(m_arIgnoreList);
}

LPFLIST
CTlcUser::GetInviteListPtr()
{
     CTlcChannel* pChannel;

     if (tlcAPI->chanCheckPrivate(m_pszName,&pChannel) == CHAN_PRIVATE_FOUND) {
          CTlcPrivateChannel* pPrivate=dynamic_cast<CTlcPrivateChannel*>(pChannel);
          return(pPrivate->GetInviteListPtr());
     }
     return(NULL);
}

VOID
CTlcUser::InitArray()
{
     size_t usersz;
     CHAR* ptr;

     usersz = tlcAPI->GetUserSize();
     ptr=reinterpret_cast<CHAR*>(this)+usersz;
     m_arForgetList=reinterpret_cast<LPFLIST>(ptr);
     ptr=reinterpret_cast<CHAR*>(ptr)+(sizeof(FLIST)*n2fgt);
     m_arIgnoreList=reinterpret_cast<LPFLIST>(ptr);
}


VOID
CTlcUser::ChatBegin(
SHORT context)
{
     switch (context) {
     case CHAT_ACCEPTED:
          CmdResult(CMD_CHAT,TRUE,CMD_CHAT_ACCEPTED);
          break;
     case CHAT_START:
          CmdResult(CMD_CHAT,TRUE,CMD_CHAT_BEGIN);
          break;
     }
     m_fFlags|=USR_CHATTING;
     m_chatreqtime=0;
}

VOID
CTlcUser::SetChatUser(
const CHAR* pszUserid)
{
     if (pszUserid != NULL && *pszUserid != '\0') {
          stlcpy(m_strChatUser,pszUserid,TLCUIDSIZ);

     }
     else {
          *m_strChatUser='\0';
     }
}


VOID
CTlcUser::SetChatReqTime()
{
     m_chatreqtime=time(NULL);
}

LONG
CTlcUser::GetChatReqTime()
{
     return(m_chatreqtime);
}

const CHAR*
CTlcUser::GetChatUser()
{
     return(m_strChatUser);
}

GBOOL
CTlcUser::ToggleNUM(
ULONG user,
USHORT board,
SHORT list)
{
     if (user == 0)  {
          return(FALSE);
     }

     if (CheckNUM(user,board,list)) {
          return(RemoveUserFromListNUM(user,board,list));
     }
     else {
          return(AddUserToListNUM(user,board,list));
     }
}

GBOOL
CTlcUser::CheckNUM(
ULONG user,
USHORT board,
SHORT list)
{
     if (user == 0)  {
          return(FALSE);
     }

     if (FindUserInListNUM(user,board,list) != NULL) {
          return(TRUE);
     }
     return(FALSE);
}

GBOOL
CTlcUser::AddUserToListNUM(
ULONG user,
USHORT board,
SHORT list)
{
     LPFLIST fptr;
     SHORT num;

     InitArray();
     if (list == LIST_IGNORE) {
          fptr=m_arIgnoreList;
          num=n2ign;
     }
     else {
          fptr=m_arForgetList;
          num=n2fgt;
     }

     while (fptr->user != 0 && fptr->number < num) {
          fptr++;
     }
     if (fptr->number < num) {
          fptr->user=user;
          fptr->board=board;
          return(TRUE);
     }
     return(FALSE);
}


VOID*
CTlcUser::FindUserInListNUM(
ULONG user,
USHORT board,
SHORT list)
{
     LPFLIST fptr;
     INT num;

     InitArray();
     if (list == LIST_IGNORE) {
          fptr=m_arIgnoreList;
          num=n2ign;
     }
     else {
          fptr=m_arForgetList;
          num=n2fgt;
     }
     while (fptr->user != 0 && fptr->number < num) {
          if (fptr->user == user && fptr->board == board) {
               return(fptr);
          }
          fptr++;
     }
     return(NULL);
}

GBOOL
CTlcUser::RemoveUserFromListNUM(
ULONG user,
USHORT board,
SHORT list)
{
     LPFLIST fptr;
     LPFLIST tptr;
     SHORT num;
     SHORT n2;

     if ((fptr=static_cast<LPFLIST>(FindUserInListNUM(user,board,list))) == NULL) {
          return(FALSE);
     }
     if (list == LIST_IGNORE) {
          n2=n2ign;
     }
     else {
          n2=n2fgt;
     }
     tptr=fptr;
     if (fptr->number == n2-1 || (tptr+1)->user == 0) {
          memset(fptr,0,sizeof(FLIST));
          fptr->number=n2-1;
          return(TRUE);
     }
     do {
          tptr++;
          memmove(fptr,tptr,sizeof(FLIST));
          fptr->number=tptr->number-1;
          fptr++;
     } while (fptr->user != 0 && fptr->number != n2-1);
     num=fptr->number;
     memset(fptr,0,sizeof(FLIST));
     fptr->number=num;
     return(TRUE);
}

LONG
CTlcUser::GetCredits()
{
     return(uacoff(m_iUsrnum)->creds);
}


GBOOL
CTlcUser::ChangedMsg()
{
     return(m_msgchanged);
}

VOID
CTlcUser::SetChangedMsg(GBOOL how)
{
     m_msgchanged=how;
}

SHORT
CTlcUser::IsTalkingTooFast()        // Is user talking too fast
{
     USHORT interval=(btuTicker()-m_iSpeakThrottle);
     if (interval > 10
      || (curChannel->CanAccess(this,CHAN_AXS_SUPER)
        && (curChannel->GetType()&CHAN_TYPE_PUBLIC))
      || IsTypeOf(USR_MODERATOR)) {
          m_iTalkCount=0;
          m_iSpeedWarn=0;
          m_iSpeakThrottle=btuTicker();
     }
     else if (m_iTalkCount >= tlcAPI->m_iSpeakThres
      && tlcAPI->m_iSpeakThres != 0) {
          if (m_iSpeedWarn >= tlcAPI->m_iSpeedDrop
           && tlcAPI->m_iSpeedDrop != 0) {
               return(USR_SPEED_DROP);
          }
          m_iSpeedWarn++;
          if ( m_iSpeedWarn == 1) {
               m_iSpeakThrottle=btuTicker();
          }
          return(USR_SPEED_TOOFAST);
     }
     return(USR_SPEED_OK);
}

VOID
CTlcUser::Talked()                // Increment speak counter
{
     m_iTalkCount++;
}


GBOOL
CTlcUser::IsSwitchingTooFast()        // Is user switching too fast
{
     USHORT interval=(btuTicker()-m_iSwitchThrottle);
     if (interval > 15
      || IsTypeOf(USR_MODERATOR)) {
          m_iSwitchCount=0;
          m_iSwitchWarn=0;
          m_iSwitchThrottle=btuTicker();
     }
     else if (m_iSwitchCount >= tlcAPI->m_iSwitchThres
      && tlcAPI->m_iSwitchThres != 0) {
          m_iSwitchWarn++;
          if (m_iSwitchWarn == 1) {
               m_iSwitchThrottle=btuTicker();
          }
          return(TRUE);
     }
     return(FALSE);
}

VOID
CTlcUser::Switched()                // Increment switch counter
{
     m_iSwitchCount++;
}

VOID
CTlcUser::SetSwitchCount(
USHORT sw)
{
     m_iSwitchCount=sw;
}

USHORT
CTlcUser::GetSwitchCount()
{
     return(m_iSwitchCount);
}

VOID
CTlcUser::Cleanup()
{
     m_bBeingDeleted=TRUE;
}

VOID
CTlcUser::Reinit()
{
     m_bBeingDeleted=FALSE;
}

GBOOL
CTlcUser::IsTypeOf(                // is user a certain type of user?
SHORT type)                        //   type to check
{
     GBOOL retval;
     setmbk(msgTlc);
     switch (type) {
     case USR_MODERATOR:
          retval=(CheckAccess(rawmsg(TLCOPKY)) || CheckAccess(rawmsg(TSYSKEY)));
          break;
     case USR_OPERATOR:
          retval=CheckAccess(rawmsg(TSYSKEY));
          break;
     case USR_NORMAL:
          retval=CheckAccess(rawmsg(UNLKEY));
          break;
     default:
          retval=FALSE;
     }
     rstmbk();
     return(retval);
}