/////////////////////////////////////////////////////////////////////////////
//
//   TLCCHAN.CPP
//
//   Copyright (c) 1997 Galacticomm, Inc.         All Rights Reserved
//
//   Teleconference Channel
//   Implementation
//                                               - Phil Henning 5/14/97
//
/////////////////////////////////////////////////////////////////////////////

#include "gcomm.h"
#include "majorbbs.h"
#include "tlcapi.hpp"

#define FILREV "$Revision: 67 $"

VOID (*chanActionUpdateHook)(LPCHANINFO);

MARKSOURCE(tlcchan);

VOID
dftchanActionUpdateHook(
LPCHANINFO)
{
}

CTlcChannel::CTlcChannel(CTlcAPI* ptlcAPI) : m_pTlcApi(ptlcAPI)
{
     m_strName[0]=m_strTopic[0]=m_keyVis[0]=m_keyJoin[0]=m_keySpeak[0]='\0';
     m_keyByPass[0]=m_keyMod[0]='\0';
     m_actList1[0]=m_actList2[0]='\0';
     m_flags=m_modflags=0;
     m_type=CHAN_TYPE_DEFAULT;
     m_count=0;
     m_pCustom=NULL;
     m_iSize=sizeof(CTlcChannel);
}

CTlcChannel::~CTlcChannel()
{
}

USHORT                        // Channel creation OK
CTlcChannel::Create(          // Create a Channel
const CHAR* strName,          // Name of channel
const CHAR* strTopic,         // Topic
USHORT flagChan,              // channel flags
USHORT flagMod,               // moderater flags
const CHAR* keyVis,           // key to see channel
const CHAR* keyJoin,          // key to join channel
const CHAR* keySpeak,         // key to speak in channel
const CHAR* keyByPass,        // key to by-pass channel rules
const CHAR* keyMod,           // key to be a moderator in this channel
const CHAR* actList1,         // 1st action list
const CHAR* actList2,         // 2nd action list
CTlcCmdList* pCmd,            // ptr to command list
VOID* pCustom)                // custom data for this channel
{
     USHORT retval = CHAN_CREATE_OK;

     ASSERT(strName != NULL);
     if (strlen(strName) > CHAN_MAX_SIZE-1) {
          retval|=CHAN_CHNTOOLONG;
     }
     if (strlen(strTopic) > CHAN_MAX_TOPIC_SIZE-1) {
          retval|=CHAN_TOPTOOLONG;
     }
     if (keyVis != NULL) {
          if (strlen(keyVis) > KEYSIZ) {
               retval|=CHAN_VISTOOLONG;
          }
          else {
               strcpy(m_keyVis, keyVis);
          }
     }
     if (keyJoin != NULL) {
          if (strlen(keyJoin) > KEYSIZ) {
               retval|=CHAN_JOINTOOLONG;
          }
          else {
               strcpy(m_keyJoin, keyJoin);
          }
     }
     if (keySpeak != NULL) {
          if (strlen(keySpeak) > KEYSIZ) {
               retval|=CHAN_SPEAKTOOLONG;
          }
          else {
               strcpy(m_keySpeak, keySpeak);
          }
     }
     if (keyByPass != NULL) {
          if (strlen(keyByPass) > KEYSIZ) {
               retval|=CHAN_BYPASSTOOLONG;
          }
          else {
               strcpy(m_keyByPass,keyByPass);
          }
     }
     if (keyMod != NULL) {
          if (strlen(keyMod) > KEYSIZ) {
               retval|=CHAN_MODTOOLONG;
          }
          else {
               strcpy(m_keyMod,keyMod);
          }
     }
     if (m_pTlcApi == NULL) {
          retval|=CHAN_API_NOT_INIT;
     }
     if (retval == CHAN_CREATE_OK) {
          strcpy(m_strName,strName);
          strcpy(m_strTopic,strTopic);
          m_flags=flagChan;
          m_modflags=flagMod;
          m_pfxCmds=pCmd;
          m_pCustom=pCustom;
          stzcpy(m_actList1,actList1,LISTSIZ);
          stzcpy(m_actList2,actList2,LISTSIZ);
     }
     return(retval);
}

LPCHANINFO
CTlcChannel::GetConfig()
{
     return(dynamic_cast<LPCHANINFO>(this));
}

VOID
CTlcChannel::Save()
{
     LPCHANINFO config;
     CHANDSK  cdsk;

     config=GetConfig();
     memmove(&cdsk,config,sizeof(CHANDSK));
     dfaSetBlk(dfaChan);
     if (dfaAcqEQ(NULL,cdsk.m_strName,0)) {
          dfaUpdate(&cdsk);
     }
     else {
          dfaInsert(&cdsk);
     }
     dfaRstBlk();
}

GBOOL
CTlcChannel::Load(
const CHAR* pszName)
{
     CHANINFO config;
     CHANDSK cdsk;

     dfaSetBlk(dfaChan);
     if (!dfaAcqEQ(&cdsk,pszName,0)) {
          return(FALSE);
     }
     memmove(&config,&cdsk,sizeof(CHANDSK));
     m_pfxCmds=NULL;
     Update(&config);
     return(TRUE);
}

GBOOL
CTlcChannel::Update(
LPCHANINFO pChanInfo)
{
     GBOOL actmod=FALSE;

     if (pChanInfo->m_keyVis != NULL) {
          stzcpy(m_keyVis, pChanInfo->m_keyVis, KEYSIZ);
          actmod=TRUE;
     }
     if (pChanInfo->m_keyJoin != NULL) {
          stzcpy(m_keyJoin, pChanInfo->m_keyJoin, KEYSIZ);
          actmod=TRUE;
     }
     if (pChanInfo->m_keySpeak != NULL) {
          stzcpy(m_keySpeak, pChanInfo->m_keySpeak, KEYSIZ);
          actmod=TRUE;
     }
     if (pChanInfo->m_keyByPass != NULL) {
          stzcpy(m_keyByPass,pChanInfo->m_keyByPass,KEYSIZ);
          actmod=TRUE;
     }
     if (pChanInfo->m_keyMod != NULL) {
          stzcpy(m_keyMod,pChanInfo->m_keyMod,KEYSIZ);
          actmod=TRUE;
     }
     if (pChanInfo->m_strTopic != NULL) {
          stzcpy(m_strTopic,pChanInfo->m_strTopic,CHAN_MAX_TOPIC_SIZE);
          actmod=TRUE;
     }
     if (pChanInfo->m_actList1 != NULL) {
          if (!sameas(m_actList1,pChanInfo->m_actList1)) {
               stzcpy(m_actList1,pChanInfo->m_actList1,ACTSIZ);
               actmod=TRUE;
          }
     }
     if (pChanInfo->m_actList2 != NULL) {
          if (!sameas(m_actList2,pChanInfo->m_actList2)) {
               stzcpy(m_actList2,pChanInfo->m_actList2,ACTSIZ);
               actmod=TRUE;
          }
     }
     if (m_flags != pChanInfo->m_flags) {
          m_flags=pChanInfo->m_flags;
          actmod=TRUE;
     }
     if (m_modflags != pChanInfo->m_modflags) {
          m_modflags=pChanInfo->m_modflags;
          actmod=TRUE;
     }
     m_pCustom=pChanInfo->m_pCustom;
     if (actmod) {
          chanActionUpdateHook(pChanInfo);
     }
     return(TRUE);
}

VOID
CTlcChannel::SetCommandList(
CTlcCmdList* pCmd)
{
     m_pfxCmds=pCmd;
}


USHORT
CTlcChannel::Create(
LPCHANINFO pChanInfo)
{
     m_type|=(pChanInfo->m_type);
     return(Create(
               pChanInfo->m_strName,
               pChanInfo->m_strTopic,
               pChanInfo->m_flags,
               pChanInfo->m_modflags,
               pChanInfo->m_keyVis,
               pChanInfo->m_keyJoin,
               pChanInfo->m_keySpeak,
               pChanInfo->m_keyByPass,
               pChanInfo->m_keyMod,
               pChanInfo->m_actList1,
               pChanInfo->m_actList2,
               NULL,
               pChanInfo->m_pCustom)
            );
}

SHORT
CTlcChannel::AddUser(
CTlcUser* pUser,
GBOOL bIsFirstEnter)
{
     CTlcChannel* schan;
     CHAR name[TLCUIDSIZ];

     setmbk(msgTlc);
     ASSERT(pUser != NULL);
     strcpy(name,pUser->GetName());
     if (tlcAPI->chanFindUser(GetName(),pUser->GetName())) {
          rstmbk();
          return(CHAN_JOIN_ALREADY);
     }
     schan=curChannel;
     curChannel=this;
     if (!IsUserInvis(pUser)) {
          SendJoinAnnouncement(pUser,bIsFirstEnter);
     }
     pUser=tlcAPI->usrGetByName(name);
     pUser=tlcAPI->chanAddUser(GetName(),pUser->GetName());
     if (bIsFirstEnter) {
          pUser->OnRecvText(SYSUID,NULL,NULL,RECV_TYPE_INTRO,INTRO2);
     }
     SendWelcome(pUser);
     m_count++;
     curChannel=schan;
     rstmbk();
     return(CHAN_JOIN_OK);
}

SHORT
CTlcChannel::RemoveUser(
CTlcUser* pUser,
SHORT fExitType)
{
     if (pUser != NULL) {
          if (!IsUserInvis(pUser)) {
               CTlcChannel* savchn;
               savchn=curChannel;
               curChannel=this;
               pUser->m_fFlags&=~USR_INCHAN;
               SendExitAnnouncement(pUser,fExitType);
               curChannel=savchn;
          }
          tlcAPI->chanRemoveUser(GetName(),pUser->GetName());
          m_count--;
          return (CHAN_UNJOIN_OK);
     }
     else {
          return (CHAN_UNJOIN_NOTHERE);
     }
}

INT
CTlcChannel::GetNumUsers()
{
     return(m_count);
}

CTlcUser*
CTlcChannel::GetUserByName(
const CHAR* pszUserid)
{
     ASSERT (pszUserid != NULL);
     return(tlcAPI->chanFindUser(GetName(),pszUserid));
}

CTlcUser*
CTlcChannel::GetFirstUser(VOID)
{
     return(tlcAPI->chanUserLow(GetName()));
}

CTlcUser*
CTlcChannel::GetNextUser(VOID)
{
     return(tlcAPI->chanUserNext(GetName()));
}

GBOOL
CTlcChannel::cmdRegister(
CTlcCommand* pCmd)
{
     ASSERT (m_pfxCmds != NULL);

     if (m_pfxCmds->Register(pCmd)) {
          return(TRUE);
     }
     return(FALSE);
}

CTlcCommand*
CTlcChannel::cmdGetFirst(VOID)
{
     if (m_pfxCmds == NULL) {
          return(NULL);
     }
     return(static_cast<CTlcCommand*>(m_pfxCmds->GetFirst()));
}

CTlcCommand*
CTlcChannel::cmdGetNext(VOID)
{
     if (m_pfxCmds == NULL) {
          return(NULL);
     }
     return(static_cast<CTlcCommand*>(m_pfxCmds->GetNext()));
}

const CHAR*
CTlcChannel::GetTopic(VOID)
{
     return(m_strTopic);
}

const CHAR*
CTlcChannel::GetKey(
INT access)
{
     switch (access) {
     case CHAN_AXS_SEE:
          return(m_keyVis);
     case CHAN_AXS_JOIN:
          return(m_keyJoin);
     case CHAN_AXS_SPEAK:
          return(m_keySpeak);
     case CHAN_AXS_SUPER:
          return(m_keyByPass);
     case CHAN_AXS_MODERATOR:
          return(m_keyMod);
     default:
          return("");
     }
}


SHORT
CTlcChannel::SetTopic(
const CHAR* strTopic)
{
     if (strlen(strTopic) > CHAN_MAX_TOPIC_SIZE-1) {
          return(CHAN_TOPTOOLONG);
     }
     else {
          strcpy(m_strTopic,strTopic);
          chanActionUpdateHook((LPCHANINFO)(this));
          return(0);
     }
}

SHORT
CTlcChannel::PublicSendToTrans(
CTlcTransport *pTransport,
const CHAR* pszUseridFrom,
const CHAR* pszUseridTo,
const CHAR* pMsg,
SHORT fType,
SHORT iMsgNum,
const CHAR* ptr1,
const CHAR* ptr2,
const CHAR* ptr3)
{
     ASSERT(pTransport != NULL);
     pTransport->SendAll(pszUseridFrom,pszUseridTo,pMsg,fType,this,iMsgNum,ptr1,ptr2,ptr3);
     return(0);
}

SHORT
CTlcChannel::PublicSend(
const CHAR* pszUseridFrom,
const CHAR* pszUseridTo,
const CHAR* pMsg,
SHORT fType,
SHORT iMsgNum,
const CHAR* ptr1,
const CHAR* ptr2,
const CHAR* ptr3)
{
     CTlcTransport* pTransport;

     pTransport=tlcAPI->transGetFirst();
     while (pTransport != NULL) {
          pTransport->SendAll(pszUseridFrom,pszUseridTo,pMsg,fType,this,iMsgNum,ptr1,ptr2,ptr3);
          pTransport=tlcAPI->transGetNext();
     }
     return(0);
}


SHORT
CTlcChannel::PublicSendAllChan(
const CHAR* pszUseridFrom,
const CHAR* pszUseridTo,
const CHAR* pMsg,
SHORT fType,
SHORT iMsgNum,
const CHAR* ptr1,
const CHAR* ptr2,
const CHAR* ptr3)
{
     CTlcTransport* pTransport;

     pTransport=tlcAPI->transGetFirst();
     while (pTransport != NULL && !(pTransport->m_fFlags&TRANS_NOBROADCAST)) {
          pTransport->SendAll(pszUseridFrom,pszUseridTo,pMsg,fType,NULL,iMsgNum,ptr1,ptr2,ptr3);
          pTransport=tlcAPI->transGetNext();
     }
     return(0);
}

SHORT
CTlcChannel::PublicSendAllChanNoEx(
const CHAR* pszUseridFrom,
const CHAR* pszUseridTo,
const CHAR* pMsg,
SHORT fType,
SHORT iMsgNum,
const CHAR* ptr1,
const CHAR* ptr2,
const CHAR* ptr3)
{
     CTlcTransport* pTransport;

     pTransport=tlcAPI->transGetFirst();
     while (pTransport != NULL) {
          pTransport->SendAll(pszUseridFrom,pszUseridTo,pMsg,fType,NULL,iMsgNum,ptr1,ptr2,ptr3);
          pTransport=tlcAPI->transGetNext();
     }
     return(0);
}

SHORT
CTlcChannel::PublicSendBut(
const CHAR* pszUseridFrom,
const CHAR* pszUseridTo,
const CHAR* pMsg,
SHORT fType,
SHORT iMsgNum,
const CHAR* ptr1,
const CHAR* ptr2,
const CHAR* ptr3,
const CHAR* pszUseridNot1,
const CHAR* pszUseridNot2)
{
     CTlcTransport* pTransport;

     pTransport=tlcAPI->transGetFirst();
     while (pTransport != NULL) {
          pTransport->SendAllBut(pszUseridFrom,pszUseridTo,pMsg,
               fType,this,iMsgNum,ptr1,ptr2,ptr3,pszUseridNot1,pszUseridNot2);
          pTransport=tlcAPI->transGetNext();
     }
     return(0);
}


SHORT
CTlcChannel::PrivateSend(
const CHAR* pszUseridFrom,
const CHAR* pszUseridTo,
const CHAR* pMsg,
SHORT fType,
SHORT iMsgNum,
const CHAR* ptr1,
const CHAR* ptr2,
const CHAR* ptr3)
{
     (VOID)fType;             // may be implemented in future
     (VOID)iMsgNum;

     CTlcUser* pUser;

     pUser=GetUserByName(pszUseridTo);
     if (pUser == NULL) {
          return(CHAN_WHISPER_FAIL_NOUSER);
     }
     pUser->OnRecvText(pszUseridFrom,NULL,pMsg,RECV_TYPE_WHISPER,WHMSG_WHIS_SEND,ptr1,ptr2,ptr3);
     return(CHAN_WHISPER_OK);
}

CHAR*                         // ptr to msg
CTlcChannel::FindUser(
const CHAR* pszStart,         // start of UID to check
INT* count,                   // ptr to an INT to hold number of matches
CTlcUser** pUser)             // ptr to ptr to hold user info
{
     CHAR* argend;
     GBOOL done=FALSE;
     INT lastcnt=0;
     INT mnum=0;
     CTlcUser* ptmpUser;

     for (int loop=0 ; loop < margc ; loop++) {
          margn[loop][0]='\0';
     }
     while (!done && (mnum < margc)) {
          argend=margn[mnum];
          if (*(argend-1) == ':') {
               *(argend-1)='\0';
               *count=HowManyMatch(pszStart,pUser,TRUE);
               *(argend-1)=':';
               mnum++;
               done=TRUE;
          }
          else {
               *count=HowManyMatch(pszStart,pUser,FALSE);
               margn[mnum][0]=' ';
          }
          if (*count == 0) {
               done=TRUE;
          }
          else {
               if (*count == 1) {
                    ptmpUser=*pUser;
               }
               lastcnt=*count;
          }
          if (!done) {
               mnum++;
          }
     }
     for (int loop=0 ; loop < margc ; loop++) {
          margn[loop][0]='\0';
     }
     rstrin();
     if ((*count != 1) && (lastcnt > 0)) {
          *count=lastcnt;
     }
     if (*count == 1) {
          *pUser=ptmpUser;
     }
     if (mnum == margc) {
          return("");
     }
     else {
          return(margv[mnum]);
     }
}

INT
CTlcChannel::HowManyMatch(
const CHAR* stg,
CTlcUser** ppMatchUser,
GBOOL samas)
{
     CTlcUser* pUser;
     INT cnt;

     pUser=GetFirstUser();
     cnt=0;
     while (pUser != NULL) {
          if (!(pUser->m_fFlags&USR_INVISB)
            && sameto(stg,pUser->GetName())
            && pUser->m_fFlags&USR_INCHAN) {
               cnt++;
               *ppMatchUser=pUser;
               if (samas && sameas(stg,pUser->GetName())) {
                    return(1);
               }
           }
           pUser=GetNextUser();
     }
     return(cnt);
}


GBOOL
CTlcChannel::ActionParse(
CHAR* pInput,
const CHAR* pszUseridFrom)
{
     GBOOL handled=FALSE;

     stzcpy(input,pInput,tinpsz);
     parsin();
     if ((tmpAList=tlcAPI->actGetByName(m_actList1)) != NULL) {
          if (!tmpAList->IsDeleted() && tmpAList->wordExists(margv[0])) {
               LPACTLST list=tmpAList->GetConfigPtr();
               tmpact=&(tmpAList->actionGetCurrent());
               if (curUser->CheckAccess(rawmsg(ACTKEY))
                && curUser->CheckAccess(list->usekey)
                && curUser->CheckAccess(tmpact->actkey)) {
                    handled=TRUE;
               }
          }
     }
     if (!handled && (tmpAList=tlcAPI->actGetByName(m_actList2)) != NULL) {
          if (!tmpAList->IsDeleted() && tmpAList->wordExists(margv[0])) {
               LPACTLST list=tmpAList->GetConfigPtr();
               tmpact=&(tmpAList->actionGetCurrent());
               if (curUser->CheckAccess(rawmsg(ACTKEY))
                && curUser->CheckAccess(list->usekey)
                && curUser->CheckAccess(tmpact->actkey)) {
                    handled=TRUE;
               }
          }
     }
     if (handled && !(curUser->m_fFlags&USR_NOACTIONS)) {
          if (margc == 2 && (sameas(margv[1],"help") || sameas(margv[1],"?"))) {
               acthlp();
               curUser->OnRecvText(pszUseridFrom,NULL,NULL,RECV_TYPE_PRFBUF);
          }
          else if (m_bUnlSpeak && !(curUser->m_fFlags&USR_INVISB)) {
               if (!curUser->IsTypeOf(USR_NORMAL)
                && curUser->m_iTimesSpoken >= tlcAPI->m_npaymx) {
                    return(!handled);
               }
               SHORT spd=curUser->IsTalkingTooFast();
               if (spd == USR_SPEED_TOOFAST) {
                    curUser->CmdResult(CMD_SPEAK,FALSE,CMD_SPEAK_TOOFAST);
                    return(TRUE);
               }
               else if (spd == USR_SPEED_DROP && curUser->GetUsrnum() >= 0) {
                    INT SaveUser=usrnum;
                    curusr(curUser->GetUsrnum());
                    byenow(TOOANOY);
                    curusr(SaveUser);
                    return(TRUE);
               }
               if (isempty(tmpact->complx) || margc == 1) {
                    asimpl();
                    curUser->Talked();
               }
               else {
                    acomplx();
                    curUser->Talked();
               }
          }
          else {
               return(FALSE);
          }
     }
     else {
          handled=FALSE;
     }
     return(handled);
}



GBOOL
CTlcChannel::CommandParse(
CHAR* pInput,
const CHAR* pszUseridFrom)
{
     static INT type=SPACE_DELIM;

     CTlcCommand* pCmd;
     CCommandParam pParam;
     CHAR* pszCmdParam=NULL;
     GBOOL isCommand=FALSE;
     GBOOL isAbbr;

     stzcpy(input,pInput,tinpsz);
     parsin();
     pCmd=cmdGetFirst();
     while (pCmd != NULL) {
          if (type == SPACE_DELIM && pCmd->m_bNeedSpace) {
               if (sameas(pCmd->m_pszCmd,margv[0])) {
                    if ((pszCmdParam=strchr(pInput,' ')) != 0) {
                         pszCmdParam++;
                    }
                    else {
                         pszCmdParam=pInput+strlen(pCmd->m_pszCmd);
                    }
                    isAbbr=TRUE;
                    isCommand=TRUE;
               }
               else if (pCmd->m_pszAbbr[0] == '\0'
                && sameas(pCmd->m_pszAbbr,margv[0])) {
                    if ((pszCmdParam=strchr(pInput,' ')) != 0) {
                         pszCmdParam++;
                    }
                    else {
                         pszCmdParam=pInput+strlen(pCmd->m_pszAbbr);
                    }
                    isAbbr=TRUE;
                    isCommand=TRUE;
               }
          }
          if (type == NO_DELIM && !pCmd->m_bNeedSpace) {
               if (sameas(pCmd->m_pszCmd,margv[0])
                || sameto(pCmd->m_pszCmd,pInput))       {
                    pszCmdParam=pInput+(strlen(pCmd->m_pszCmd));
                    isAbbr=TRUE;
                    isCommand=TRUE;
               }
               else if (pCmd->m_pszAbbr[0] == '\0'
                && sameto(pCmd->m_pszAbbr,pInput)) {
                    pszCmdParam=pInput+(strlen(pCmd->m_pszAbbr));
                    isAbbr=TRUE;
                    isCommand=TRUE;
               }
          }
          if (isCommand) {
               CTlcUser* pUser;
               pUser=GetUserByName(pszUseridFrom);
               pParam.m_pUser=pUser;
               pParam.m_pszCmdParam=pszCmdParam;
               type=SPACE_DELIM;
               SHORT res=pCmd->DoCmd(&pParam);
               if (res == 1) {
                    return(TRUE);
               }
               else if (res == 2 && isAbbr) {
                    stzcpy(input,pInput,tinpsz);
                    parsin();
                    isCommand=FALSE;
               }
               else {
                    return(FALSE);
               }
          }
          pCmd=cmdGetNext();
     }
     if (type == SPACE_DELIM) {
          type=NO_DELIM;
          return(CommandParse(pInput,pszUseridFrom));
     }
     else {
          type=SPACE_DELIM;
          return(FALSE);
     }
}

const CHAR*
CTlcChannel::GetName(VOID)
{
     return(m_strName);
}

SHORT
CTlcChannel::GetType(VOID)
{
     return(m_type);
}


VOID
CTlcChannel::SendJoinAnnouncement(
CTlcUser* pUser,
GBOOL bIsFirstEnter)
{
     const CHAR* pszName=pUser->GetName();
     const CHAR* pszMsg=pUser->GetEntrance(MSG_CURRENT);

     PublicSendAllChan(pszName,NULL,NULL,RECV_TYPE_USERENTER,0);
     clrprf();
     if (*pszMsg != '\0') {
          PublicSend(pszName,NULL,pszMsg,RECV_TYPE_ENTRANCE,ENMSG_ENTR1);
     }
     else if (bIsFirstEnter) {
          PublicSend(pszName,NULL,NULL,RECV_TYPE_ENTRANCE,ENMSG_ENT_TELE);
     }
     else {
          PublicSend(pszName,NULL,NULL,RECV_TYPE_ENTRANCE,ENMSG_ENT_CAMEIN);
     }

}

SHORT
CTlcChannel::GetNumVisible(VOID)
{
     SHORT count=0;
     CTlcUser* pUser;

     pUser=GetFirstUser();
     while (pUser != NULL) {
          if (!(pUser->m_fFlags&USR_INVISB) &&
              (pUser->m_fFlags&USR_INCHAN)) {
               count++;
          }
          pUser=GetNextUser();
     }
     return(count);
}

VOID
CTlcChannel::ShowVisUsers(
CTlcUser* pUser)
{
     SHORT num;
     CTlcUser* pOthUser;
     const CHAR* name;

     num=GetNumVisible();
     if (!(pUser->m_fFlags&USR_INVISB)) {
          --num;
     }
     switch (num) {
     case -1:
     case 0:
          prfmsg(BYSELF2);
          break;
     case 1:
          pOthUser=GetFirstUser();
          while ((pOthUser != NULL && (pOthUser->m_fFlags&USR_INVISB || !(pOthUser->m_fFlags&USR_INCHAN)))
           || (pUser == pOthUser) ) {
               pOthUser=GetNextUser();
          }
          prfmsg(ONEOTH2,pOthUser->GetName());
          break;
     case 2:
          pOthUser=GetFirstUser();
          while ((pOthUser != NULL && (pOthUser->m_fFlags&USR_INVISB || !(pOthUser->m_fFlags&USR_INCHAN)))
           || (pUser == pOthUser)  ) {
               pOthUser=GetNextUser();
          }
          name=pOthUser->GetName();
          pOthUser=GetNextUser();
          while ((pOthUser != NULL && (pOthUser->m_fFlags&USR_INVISB || !(pOthUser->m_fFlags&USR_INCHAN)))
           || (pUser == pOthUser)  ) {
               pOthUser=GetNextUser();
          }
          prfmsg(TWOOTH2,name,pOthUser->GetName());
          break;
     default:
          pOthUser=GetFirstUser();
          for (int i=0 ; i < (num-1) ; i++) {
               while ((pOthUser != NULL && (pOthUser->m_fFlags&USR_INVISB || !(pOthUser->m_fFlags&USR_INCHAN)))
                || (pUser == pOthUser)  ) {
                    pOthUser=GetNextUser();
               }
               prfmsg(SOMOTH2,pOthUser->GetName());
               pOthUser=GetNextUser();
          }
          while ((pOthUser != NULL && (pOthUser->m_fFlags&USR_INVISB || !(pOthUser->m_fFlags&USR_INCHAN)))
           || (pUser == pOthUser) ) {
               pOthUser=GetNextUser();
          }
          prfmsg(SEVOTH2,pOthUser->GetName());
     }
     pUser->OnRecvText(SYSUID,NULL,NULL,RECV_TYPE_USERLIST,0);
     clrprf();
}


VOID
CTlcChannel::SendWelcome(
CTlcUser* pUser)
{
     if (m_strTopic[0] != '\0') {
          pUser->OnRecvText(SYSUID,NULL,m_strTopic,RECV_TYPE_WELCOME,CHNTOP2);
     }

     ShowVisUsers(pUser);
     if ((pUser->GetUsrnum() > -1) && !(usroff(pUser->GetUsrnum())->flags&ISGCSU)) {
          pUser->OnRecvText(SYSUID,NULL,NULL,RECV_TYPE_WELCOME,IROEPI2);
     }
}

VOID
CTlcChannel::SendExitAnnouncement(
CTlcUser* pUser,
SHORT fExitType)
{
     const CHAR* pszUserid=pUser->GetName();
     const CHAR* pszMsg=pUser->GetExit(MSG_CURRENT);
     SHORT msg;

     PublicSendAllChan(pszUserid,NULL,NULL,RECV_TYPE_USEREXIT,0);
     clrprf();
     if (!sameas(pszMsg,"") && fExitType != LEAVE_PRIV_KICK) {
          PublicSend(pszUserid,NULL,pszMsg,RECV_TYPE_EXIT,EXMSG_EXTR1);
     }
     else {
          switch (fExitType) {
          case LEAVE_CHANNEL:
               msg=EXMSG_EXT_LEFTC;
               break;
          case LEAVE_TLC:
               msg=EXMSG_EXT_LEAVE;
               break;
          case LEAVE_LOGOFF:
               msg=EXMSG_EXT_HUP;
               break;
          case LEAVE_PRIV_KICK:
               msg=EXMSG_EXT_KICK;
               break;
          }
          PublicSend(pszUserid,NULL,NULL,RECV_TYPE_EXIT,msg);
     }
}

VOID
CTlcChannel::SendTabbedDbData(
const CHAR* data)
{
     CTlcTransport* pTransport;

     pTransport=tlcAPI->transGetFirst();
     while (pTransport != NULL) {
          if (pTransport->m_fFlags&TRANS_IMPLDB) {
               pTransport->SendTabbedDbData(data,this);
          }
          pTransport=tlcAPI->transGetNext();
     }
}

VOID
CTlcChannel::ToggleDb(
CTlcUser* pUser,
INT bOn)
{
     CTlcTransport* pTransport;

     if (bOn) {
          pUser->m_fFlags|=USR_DBOARD;
          prfmsg(OPNDBRD3,pUser->GetName(),pUser->GetSex() == 'F' ? "her" : "his");
     }
     else {
          pUser->m_fFlags&=~USR_DBOARD;
          prfmsg(CLSDBRD3,pUser->GetName(),pUser->GetSex() == 'F' ? "her" : "his");
     }

     pTransport=tlcAPI->transGetFirst();
     while (pTransport != NULL) {
          if (pTransport->m_fFlags&TRANS_IMPLDB) {
               pTransport->SendAll(pUser->GetName(),NULL,prfbuf,RECV_TYPE_DBTOGGLE,this);
          }
          pTransport=tlcAPI->transGetNext();
     }
     clrprf();
}

GBOOL
CTlcChannel::CanAccess(
CTlcUser* pUser,
INT access)
{
     return(pUser->CheckAccess(GetKey(access)));
}


//CTlcLocalChannel
CTlcLocalChannel::CTlcLocalChannel(
CTlcAPI* tlcAPI) : CTlcChannel(tlcAPI)
{
     m_type|=CHAN_TYPE_LOCAL;
     m_iSize=sizeof(CTlcLocalChannel);
}

CTlcLocalChannel::~CTlcLocalChannel()
{
}

//CTlcLocPublicChannel
CTlcLocPublicChannel::CTlcLocPublicChannel(
CTlcAPI* tlcAPI) : CTlcLocalChannel(tlcAPI)
{
     m_type|=CHAN_TYPE_PUBLIC;
     m_iSize=sizeof(CTlcLocPublicChannel);
}

CTlcLocPublicChannel::~CTlcLocPublicChannel()
{
}

VOID
CTlcLocPublicChannel::SendWelcome(
CTlcUser* pUser)
{
     pUser->OnRecvText(SYSUID,NULL,m_strName,RECV_TYPE_WELCOME,PUBCHN2);
     CTlcLocalChannel::SendWelcome(pUser);
}

//CTlcPrivateChannel
CTlcPrivateChannel::CTlcPrivateChannel(
CTlcAPI* tlcAPI) : CTlcChannel(tlcAPI)
{
     m_type|=CHAN_TYPE_PRIVATE;
     m_type|=CHAN_TYPE_NOEDIT;
     m_strOwner[0]='\0';
     m_arInviteList=NULL;
     m_iSize=sizeof(CTlcPrivateChannel);
}

CTlcPrivateChannel::~CTlcPrivateChannel()
{
}

GBOOL
CTlcPrivateChannel::ToggleInvite(
CTlcUser* pUser)
{
     if (pUser == NULL) {
          return(FALSE);
     }
     if (CheckInvite(pUser)) {
          csnotdelchan(this,pUser->GetUsrnum());
          return(RemoveUserFromList(pUser));
     }
     else {
          csnotaddchan(this,pUser->GetUsrnum());
          return(AddUserToList(pUser));
     }
}


GBOOL
CTlcPrivateChannel::CheckInvite(
CTlcUser* pUser)
{

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

GBOOL
CTlcPrivateChannel::AddUserToList(
CTlcUser* pUser)
{
     ULONG user;
     USHORT board;
     GBOOL retval;

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

     retval=AddUserToListNUM(user,board);
     cschgstatus(tlcAPI->usrGetByName(GetOwner()),pUser);
     return(retval);
}

GBOOL
CTlcPrivateChannel::RemoveUserFromList(
CTlcUser* pUser)
{
     ULONG user;
     USHORT board;
     GBOOL retval;

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

     retval=RemoveUserFromListNUM(user,board);
     cschgstatus(tlcAPI->usrGetByName(GetOwner()),pUser);
     return(retval);
}

VOID*
CTlcPrivateChannel::FindUserInList(
CTlcUser* pUser)
{
     ULONG user;
     USHORT board;

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

     return(FindUserInListNUM(user,board));
}


GBOOL
CTlcPrivateChannel::ToggleInviteNUM(
ULONG user,
USHORT board)
{
     if (user == 0)  {
          return(FALSE);
     }

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

GBOOL
CTlcPrivateChannel::CheckInviteNUM(
ULONG user,
USHORT board)
{
     if (user == 0)  {
          return(FALSE);
     }

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

GBOOL
CTlcPrivateChannel::AddUserToListNUM(
ULONG user,
USHORT board)
{
     LPFLIST fptr;

     InitArray();
     fptr=m_arInviteList;

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

VOID*
CTlcPrivateChannel::FindUserInListNUM(
ULONG user,
USHORT board)
{
     LPFLIST fptr;

     InitArray();
     fptr=m_arInviteList;
     while (fptr->user != 0 && fptr->number < n2inv) {
          if (fptr->user == user && fptr->board == board) {
               return(fptr);
          }
          fptr++;
     }
     return(NULL);
}

GBOOL
CTlcPrivateChannel::RemoveUserFromListNUM(
ULONG user,
USHORT board)
{
     LPFLIST fptr;
     LPFLIST tptr;

     SHORT num;

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


VOID
CTlcPrivateChannel::InitArray()
{
     size_t chansz;
     CHAR* ptr;

     chansz=tlcAPI->GetChanSize();

     ptr=reinterpret_cast<CHAR*>(this)+chansz;
     m_arInviteList=reinterpret_cast<LPFLIST>(ptr);
}

LPFLIST
CTlcPrivateChannel::GetInviteListPtr()
{
     InitArray();
     return(m_arInviteList);
}

VOID
CTlcPrivateChannel::SendWelcome(
CTlcUser* pUser)
{
     ASSERT(pUser != NULL);
     ASSERT(pUser->GetName() != NULL);

     if (sameas(m_strOwner,pUser->GetName())) {
          pUser->OnRecvText(SYSUID,NULL,NULL,RECV_TYPE_WELCOME,URTPRV2);
     }
     else {
          pUser->OnRecvText(SYSUID,NULL,m_strOwner,RECV_TYPE_WELCOME,HISPRV2);
     }
     CTlcChannel::SendWelcome(pUser);
}


VOID
CTlcPrivateChannel::SetOwner(
const CHAR* strOwner)
{
     stzcpy(m_strOwner,strOwner,UIDSIZ);
}

const CHAR*
CTlcPrivateChannel::GetOwner(VOID)
{
     return(m_strOwner);
}


