/*
   tclchan.c -- handles:
     Tcl stubs for the channel-oriented commands

   dprintf'ized, 1aug96
*/
/*
   This file is part of the eggdrop source code
   copyright (c) 1997 Robey Pointer
   and is distributed according to the GNU general public license.
   For full details, read the top of 'main.c' or the file called
   COPYING that was distributed with this code.
*/
     
#if HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include "../lush.h"
#include "eggdrop.h"
#include "users.h"
#include "chan.h"
#include "proto.h"
#include "cmdt.h"
#include "tclegg.h"

extern Tcl_Interp *interp;
extern struct chanset_t *chanset;
extern char origbotname[];
extern char newserver[];
extern int newserverport;
extern char newserverpass[];
extern char cx_file[];
extern int cx_line;
extern int serv;
extern struct userrec *userlist;
extern int ban_time;

/***********************************************************************/

/* streamlined by answer */
int tcl_chanlist STDVAR
{
  memberlist *m; int f=0,atr; char s1[121],handle[21];
  struct chanset_t *chan;
  BADARGS(2,3," channel ?flags?");
  chan=findchan(argv[1]); if (chan==NULL) {
    Tcl_AppendResult(irp,"invalid channel: ",argv[1],NULL);
    return TCL_ERROR;
  }
  m=chan->channel.member;
  if (argc==2) {
    /* no flag restrictions so just whiz it thru quick */
    while (m->nick[0]) {
      Tcl_AppendElement(irp,m->nick);
      m=m->next;
    }
    return TCL_OK;
  }
  f=str2flags(argv[2]);
  /* return empty set if asked for flags but flags don't exist */
  if ((f==0) && (argv[2][0]) && (argv[2][0]!='-')) return TCL_OK;
  while (m->nick[0]) {
    if (m->user==NULL) {
      sprintf(s1,"%s!%s",m->nick,m->userhost);
      get_handle_by_host(handle,s1);  /* updates m->user */
    }
    if (m->user==NULL) atr=0; else atr=m->user->flags;
    if ((atr & f)==f) Tcl_AppendElement(irp,m->nick);
    m=m->next;
  }
  return TCL_OK;
}
  
int tcl_botisop STDVAR
{
  struct chanset_t *chan;
  BADARGS(2,2," channel");
  chan=findchan(argv[1]); if (chan==NULL) {
    Tcl_AppendResult(irp,"illegal channel: ",argv[1],NULL);
    return TCL_ERROR;
  }
  if (me_op(chan)) Tcl_AppendResult(irp,"1",NULL);
  else Tcl_AppendResult(irp,"0",NULL);
  return TCL_OK;
}

int tcl_isop STDVAR
{
  struct chanset_t *chan;
  BADARGS(3,3," nick channel");
  chan=findchan(argv[2]); if (chan==NULL) {
    Tcl_AppendResult(irp,"illegal channel: ",argv[2],NULL);
    return TCL_ERROR;
  }
  if (member_op(chan->name,argv[1])) Tcl_AppendResult(irp,"1",NULL);
  else Tcl_AppendResult(irp,"0",NULL);
  return TCL_OK;
}

int tcl_isvoice STDVAR
{
  struct chanset_t *chan;
  BADARGS(3,3," nick channel");
  chan=findchan(argv[2]); if (chan==NULL) {
    Tcl_AppendResult(irp,"illegal channel: ",argv[2],NULL);
    return TCL_ERROR;
  }
  if (member_voice(chan->name,argv[1])) Tcl_AppendResult(irp,"1",NULL);
  else Tcl_AppendResult(irp,"0",NULL);
  return TCL_OK;
}

int tcl_onchan STDVAR
{
  struct chanset_t *chan;
  BADARGS(3,3," nickname channel");
  chan=findchan(argv[2]); if (chan==NULL) {
    Tcl_AppendResult(irp,"illegal channel: ",argv[2],NULL);
    return TCL_ERROR;
  }
  if (!ischanmember(chan->name,argv[1])) Tcl_AppendResult(irp,"0",NULL);
  else Tcl_AppendResult(irp,"1",NULL);
  return TCL_OK;
}

int tcl_handonchan STDVAR
{
  struct chanset_t *chan;
  BADARGS(3,3," handle channel");
  chan=findchan(argv[2]); if (chan==NULL) {
    Tcl_AppendResult(irp,"illegal channel: ",argv[2],NULL);
    return TCL_ERROR;
  }
  if (!hand_on_chan(chan,argv[1])) Tcl_AppendResult(irp,"0",NULL);
  else Tcl_AppendResult(irp,"1",NULL);
  return TCL_OK;
}

int tcl_ischanban STDVAR
{
  struct chanset_t *chan;
  BADARGS(3,3," ban channel");
  chan=findchan(argv[2]); if (chan==NULL) {
    Tcl_AppendResult(irp,"illegal channel: ",argv[2],NULL);
    return TCL_ERROR;
  }
  if (isbanned(chan,argv[1])) Tcl_AppendResult(irp,"1",NULL);
  else Tcl_AppendResult(irp,"0",NULL);
  return TCL_OK;
}

int tcl_getchanhost STDVAR
{
  struct chanset_t *chan;
  char s[UHOSTLEN];
  BADARGS(3,3," nickname channel");
  chan=findchan(argv[2]); if (chan==NULL) {
    Tcl_AppendResult(irp,"illegal channel: ",argv[2],NULL);
    return TCL_ERROR;
  }
  getchanhost(chan->name,argv[1],s);
  Tcl_AppendResult(irp,s,NULL);
  return TCL_OK;
}

int tcl_onchansplit STDVAR
{
  struct chanset_t *chan;
  BADARGS(3,3," nickname channel");
  chan=findchan(argv[2]); if (chan==NULL) {
    Tcl_AppendResult(irp,"illegal channel: ",argv[2],NULL);
    return TCL_ERROR;
  }
  if (is_split(chan->name,argv[1])) Tcl_AppendResult(irp,"1",NULL);
  else Tcl_AppendResult(irp,"0",NULL);
  return TCL_OK;
}

int tcl_maskhost STDVAR
{
  char new[121];
  BADARGS(2,2," nick!user@host");
  maskhost(argv[1],new);
  Tcl_AppendResult(irp,new,NULL);
  return TCL_OK;
}

int tcl_delglban STDVAR
{
  struct chanset_t *chan;
  BADARGS(2,2," ban"); 
  warn_obsolete(argv[0]);
  if (delban(argv[1])>0) {
    chan=chanset; while (chan!=NULL) {
      if (me_op(chan)) add_mode(chan,'-','b',argv[1]);
      chan=chan->next;
    }
    Tcl_AppendResult(irp,"1",NULL);
  }
  else Tcl_AppendResult(irp,"0",NULL);
  return TCL_OK;
}

int tcl_delban STDVAR
{
  struct chanset_t *chan;
  BADARGS(3,3," ban channel");
  warn_obsolete(argv[0]);
  chan=findchan(argv[2]); if (chan==NULL) {
    Tcl_AppendResult(irp,"invalid channel: ",argv[2],NULL);
    return TCL_ERROR;
  }
  if (u_delban(chan->bans,argv[1])>0) {
    if (me_op(chan)) add_mode(chan,'-','b',argv[1]);
    Tcl_AppendResult(irp,"1",NULL);
  }
  else Tcl_AppendResult(irp,"0",NULL);
  return TCL_OK;
}

int tcl_killban STDVAR
{
  struct chanset_t *chan;
  BADARGS(2,2," ban"); 
  if (delban(argv[1])>0) {
    chan=chanset; while (chan!=NULL) {
      if (me_op(chan)) add_mode(chan,'-','b',argv[1]);
      chan=chan->next;
    }
    Tcl_AppendResult(irp,"1",NULL);
  }
  else Tcl_AppendResult(irp,"0",NULL);
  return TCL_OK;
}

int tcl_killchanban STDVAR
{
  struct chanset_t *chan;
  BADARGS(3,3," channel ban");
  chan=findchan(argv[1]); if (chan==NULL) {
    Tcl_AppendResult(irp,"invalid channel: ",argv[1],NULL);
    return TCL_ERROR;
  }
  if (u_delban(chan->bans,argv[2])>0) {
    if (me_op(chan)) add_mode(chan,'-','b',argv[2]);
    Tcl_AppendResult(irp,"1",NULL);
  }
  else Tcl_AppendResult(irp,"0",NULL);
  return TCL_OK;
}

int tcl_isban STDVAR
{
  struct chanset_t *chan; int ok=0;
  BADARGS(2,3," ban ?channel?");
  if (argc==3) {
    chan=findchan(argv[2]); if (chan==NULL) {
      Tcl_AppendResult(irp,"invalid channel: ",argv[2],NULL);
      return TCL_ERROR;
    }
    if (u_equals_ban(chan->bans,argv[1])) ok=1;
  }
  if (equals_ban(argv[1])) ok=1;
  if (ok) Tcl_AppendResult(irp,"1",NULL);
  else Tcl_AppendResult(irp,"0",NULL);
  return TCL_OK;
}

int tcl_ispermban STDVAR
{
  struct chanset_t *chan; int ok=0;
  BADARGS(2,3," ban ?channel?");
  if (argc==3) {
    chan=findchan(argv[2]); if (chan==NULL) {
      Tcl_AppendResult(irp,"invalid channel: ",argv[2],NULL);
      return TCL_ERROR;
    }
    if (u_equals_ban(chan->bans,argv[1])==2) ok=1;
  }
  if (equals_ban(argv[1])==2) ok=1;
  if (ok) Tcl_AppendResult(irp,"1",NULL);
  else Tcl_AppendResult(irp,"0",NULL);
  return TCL_OK;
}

int tcl_matchban STDVAR
{
  struct chanset_t *chan; int ok=0;
  BADARGS(2,3," user!nick@host ?channel?");
  if (argc==3) {
    chan=findchan(argv[2]); if (chan==NULL) {
      Tcl_AppendResult(irp,"invalid channel: ",argv[2],NULL);
      return TCL_ERROR;
    }
    if (u_match_ban(chan->bans,argv[1])) ok=1;
  }
  if (match_ban(argv[1])) ok=1;
  if (ok) Tcl_AppendResult(irp,"1",NULL);
  else Tcl_AppendResult(irp,"0",NULL);
  return TCL_OK;
}

int tcl_addban STDVAR
{
  time_t t=time(NULL); char ban[161],cmt[66]; struct chanset_t *chan;
  BADARGS(3,4," ban channel ?comment?");
  warn_obsolete(argv[0]);
  chan=findchan(argv[2]); if (chan==NULL) {
    Tcl_AppendResult(irp,"invalid channel: ",argv[2],NULL);
    return TCL_ERROR;
  }
  strncpy(ban,argv[1],160); ban[160]=0;
  if (argc==3) strcpy(cmt,"no comment");
  else { strncpy(cmt,argv[3],65); cmt[65]=0; }
  u_addban(chan->bans,ban,origbotname,cmt,t+(60*ban_time));
  if (me_op(chan)) {
    add_mode(chan,'+','b',ban);
    recheck_channel(chan);
  }
  return TCL_OK;
}

int tcl_addglban STDVAR
{
  char ban[161],cmt[71]; struct chanset_t *chan;
  BADARGS(2,3," ban ?comment?");
  warn_obsolete(argv[0]);
  strncpy(ban,argv[1],160); ban[160]=0;
  if (argc==2) strcpy(cmt,"no comment");
  else { strncpy(cmt,argv[2],65); cmt[65]=0; }
  addban(ban,origbotname,cmt,0L);
  chan=chanset; while (chan!=NULL) {
    if (me_op(chan)) {
      add_mode(chan,'+','b',ban);
      recheck_channel(chan);
    }
    chan=chan->next;
  }
  return TCL_OK;
}

int tcl_newchanban STDVAR
{
  time_t now=time(NULL),expire_time; struct chanset_t *chan;
  char ban[161],cmt[66],from[10];
  BADARGS(5,6," channel ban creator comment ?lifetime?");
  chan=findchan(argv[1]); if (chan==NULL) {
    Tcl_AppendResult(irp,"invalid channel: ",argv[1],NULL);
    return TCL_ERROR;
  }
  strncpy(ban,argv[2],160); ban[160]=0;
  strncpy(from,argv[3],9); from[9]=0;
  strncpy(cmt,argv[4],65); cmt[65]=0;
  if (argc==5) expire_time=now+(60*ban_time);
  else {
    if (atol(argv[5])==0) expire_time=0L;
    else expire_time=now+(atol(argv[5])*60);
  }
  u_addban(chan->bans,ban,from,cmt,expire_time);
  if (me_op(chan)) {
    add_mode(chan,'+','b',ban);
    recheck_channel(chan);
  }
  return TCL_OK;
}

int tcl_newban STDVAR
{
  time_t now=time(NULL),expire_time; struct chanset_t *chan;
  char ban[UHOSTLEN],cmt[66],from[10];
  BADARGS(4,5," ban creator comment ?lifetime?");
  strncpy(ban,argv[1],UHOSTLEN-1); ban[UHOSTLEN-1]=0;
  strncpy(from,argv[2],9); from[9]=0;
  strncpy(cmt,argv[3],65); cmt[65]=0;
  if (argc==4) expire_time=now+(60*ban_time);
  else {
    if (atol(argv[4])==0) expire_time=0L;
    else expire_time=now+(atol(argv[4])*60);
  }
  addban(ban,from,cmt,expire_time);
  chan=chanset; while (chan!=NULL) {
    if (me_op(chan)) {
      add_mode(chan,'+','b',ban);
      recheck_channel(chan);
    }
    chan=chan->next;
  }
  return TCL_OK;
}

int tcl_jump STDVAR
{
  BADARGS(1,4," ?server? ?port? ?pass?");
  if (argc>=2) {
    strcpy(newserver,argv[1]);
    if (argc>=3) newserverport=atoi(argv[2]);
    else newserverport=DEFAULT_PORT;
    if (argc==4) strcpy(newserverpass,argv[3]);
  }
  tprintf(serv,"QUIT :changing servers\n");
  sleep(1); killsock(serv); serv=(-1);
  return TCL_OK;
}

int tcl_getchanidle STDVAR
{
  memberlist *m; time_t now=time(NULL); struct chanset_t *chan;
  BADARGS(3,3," nickname channel");
  chan=findchan(argv[2]); if (chan==NULL) {
    Tcl_AppendResult(irp,"invalid channel: ",argv[2],NULL);
    return TCL_ERROR;
  }
  m=chan->channel.member;
  while (m->nick[0]) {
    if (strcasecmp(m->nick,argv[1])==0) {
      char s[20]; int x;
      x=(now-(m->last))/60;
      sprintf(s,"%d",x);
      Tcl_AppendResult(irp,s,NULL);
      return TCL_OK;
    }
    m=m->next;
  }
  Tcl_AppendResult(irp,"0",NULL);
  return TCL_OK;
}

int tcl_chanbans STDVAR
{
  banlist *b; struct chanset_t *chan;
  BADARGS(2,2," channel");
  chan=findchan(argv[1]); if (chan==NULL) {
    Tcl_AppendResult(irp,"illegal channel: ",argv[2],NULL);
    return TCL_ERROR;
  }
  b=chan->channel.ban;
  while (b->ban[0]) {
    Tcl_AppendElement(irp,b->ban);
    b=b->next;
  }
  return TCL_OK;
}

int tcl_hand2nick STDVAR
{
  memberlist *m; char s[161],h[21]; struct chanset_t *chan;
  BADARGS(3,3," handle channel");
  chan=findchan(argv[2]); if (chan==NULL) {
    Tcl_AppendResult(irp,"invalid channel: ",argv[2],NULL);
    return TCL_ERROR;
  }
  m=chan->channel.member;
  while (m->nick[0]) {
    sprintf(s,"%s!%s",m->nick,m->userhost);
    get_handle_by_host(h,s);
    if (strcasecmp(h,argv[1])==0) {
      Tcl_AppendResult(irp,m->nick,NULL);
      return TCL_OK;
    }
    m=m->next;
  }
  return TCL_OK;   /* blank */
}

int tcl_nick2hand STDVAR
{
  memberlist *m; char s[161],h[21]; struct chanset_t *chan;
  BADARGS(3,3," nick channel");
  chan=findchan(argv[2]); if (chan==NULL) {
    Tcl_AppendResult(irp,"invalid channel: ",argv[2],NULL);
    return TCL_ERROR;
  }
  m=ismember(chan,argv[1]); if (m==NULL) return TCL_OK;
  sprintf(s,"%s!%s",m->nick,m->userhost);
  get_handle_by_host(h,s);
  Tcl_AppendResult(irp,h,NULL);
  return TCL_OK;
}

int tcl_channel_info(irp,chan)
Tcl_Interp *irp; struct chanset_t *chan;
{
  char s[121];
  context;
  get_mode_protect(chan,s); Tcl_AppendElement(irp,s);
  sprintf(s,"%d",chan->idle_kick); Tcl_AppendElement(irp,s);
  Tcl_AppendElement(irp,chan->need_op);
  Tcl_AppendElement(irp,chan->need_invite);
  if (chan->stat&CHAN_CLEARBANS) Tcl_AppendElement(irp,"+clearbans");
  else Tcl_AppendElement(irp,"-clearbans");
  if (chan->stat&CHAN_ENFORCEBANS) Tcl_AppendElement(irp,"+enforcebans");
  else Tcl_AppendElement(irp,"-enforcebans");
  if (chan->stat&CHAN_DYNAMICBANS) Tcl_AppendElement(irp,"+dynamicbans");
  else Tcl_AppendElement(irp,"-dynamicbans");
  if (chan->stat&CHAN_NOUSERBANS) Tcl_AppendElement(irp,"-userbans");
  else Tcl_AppendElement(irp,"+userbans");
  if (chan->stat&CHAN_OPONJOIN) Tcl_AppendElement(irp,"+autoop");
  else Tcl_AppendElement(irp,"-autoop");
  if (chan->stat&CHAN_BITCH) Tcl_AppendElement(irp,"+bitch");
  else Tcl_AppendElement(irp,"-bitch");
  if (chan->stat&CHAN_GREET) Tcl_AppendElement(irp,"+greet");
  else Tcl_AppendElement(irp,"-greet");
  if (chan->stat&CHAN_PROTECTOPS) Tcl_AppendElement(irp,"+protectops");
  else Tcl_AppendElement(irp,"-protectops");
  if (chan->stat&CHAN_LOGSTATUS) Tcl_AppendElement(irp,"+statuslog");
  else Tcl_AppendElement(irp,"-statuslog");
  if (chan->stat&CHAN_STOPNETHACK) Tcl_AppendElement(irp,"+stopnethack");
  else Tcl_AppendElement(irp,"-stopnethack");
  if (chan->stat&CHAN_REVENGE) Tcl_AppendElement(irp,"+revenge");
  else Tcl_AppendElement(irp,"-revenge");
  if (chan->stat&CHAN_SECRET) Tcl_AppendElement(irp,"+secret");
  else Tcl_AppendElement(irp,"-secret");
  return TCL_OK;
}

/* parse options for a channel */
int tcl_channel_modify(irp,chan,items,item)
Tcl_Interp *irp; struct chanset_t *chan; int items; char **item;
{
  int i;
  context;
  for (i=0; i<items; i++) {
    if (strcmp(item[i],"need-op")==0) {
      i++; if (i>=items) {
	Tcl_AppendResult(irp,"channel need-op needs argument",NULL);
	return TCL_ERROR;
      }
      strncpy(chan->need_op,item[i],120); chan->need_op[120]=0;
    }
    else if (strcmp(item[i],"need-invite")==0) {
      i++; if (i>=items) {
	Tcl_AppendResult(irp,"channel need-invite needs argument",NULL);
	return TCL_ERROR;
      }
      strncpy(chan->need_invite,item[i],120); chan->need_invite[120]=0;
    }
    else if (strcmp(item[i],"chanmode")==0) {
      i++; if (i>=items) {
	Tcl_AppendResult(irp,"channel chanmode needs argument",NULL);
	return TCL_ERROR;
      }
      if (strlen(item[i])>120) item[i][120]=0;
      set_mode_protect(chan,item[i]);
    }
    else if (strcmp(item[i],"idle-kick")==0) {
      i++; if (i>=items) {
	Tcl_AppendResult(irp,"channel idle-kick needs argument",NULL);
	return TCL_ERROR;
      }
      chan->idle_kick=atoi(item[i]);
    }
    else if (strcmp(item[i],"dont-idle-kick")==0)
      chan->idle_kick=0;
    else if (strcmp(item[i],"+clearbans")==0)
      chan->stat|=CHAN_CLEARBANS;
    else if (strcmp(item[i],"-clearbans")==0)
      chan->stat&=~CHAN_CLEARBANS;
    else if (strcmp(item[i],"+enforcebans")==0)
      chan->stat|=CHAN_ENFORCEBANS;
    else if (strcmp(item[i],"-enforcebans")==0)
      chan->stat&=~CHAN_ENFORCEBANS;
    else if (strcmp(item[i],"+dynamicbans")==0)
      chan->stat|=CHAN_DYNAMICBANS;
    else if (strcmp(item[i],"-dynamicbans")==0)
      chan->stat&=~CHAN_DYNAMICBANS;
    else if (strcmp(item[i],"-userbans")==0)
      chan->stat|=CHAN_NOUSERBANS;
    else if (strcmp(item[i],"+userbans")==0)
      chan->stat&=~CHAN_NOUSERBANS;
    else if (strcmp(item[i],"+autoop")==0)
      chan->stat|=CHAN_OPONJOIN;
    else if (strcmp(item[i],"-autoop")==0)
      chan->stat&=~CHAN_OPONJOIN;
    else if (strcmp(item[i],"+bitch")==0)
      chan->stat|=CHAN_BITCH;
    else if (strcmp(item[i],"-bitch")==0)
      chan->stat&=~CHAN_BITCH;
    else if (strcmp(item[i],"+greet")==0)
      chan->stat|=CHAN_GREET;
    else if (strcmp(item[i],"-greet")==0)
      chan->stat&=~CHAN_GREET;
    else if (strcmp(item[i],"+protectops")==0)
      chan->stat|=CHAN_PROTECTOPS;
    else if (strcmp(item[i],"-protectops")==0)
      chan->stat&=~CHAN_PROTECTOPS;
    else if (strcmp(item[i],"+statuslog")==0)
      chan->stat|=CHAN_LOGSTATUS;
    else if (strcmp(item[i],"-statuslog")==0)
      chan->stat&=~CHAN_LOGSTATUS;
    else if (strcmp(item[i],"+stopnethack")==0)
      chan->stat|=CHAN_STOPNETHACK;
    else if (strcmp(item[i],"-stopnethack")==0)
      chan->stat&=~CHAN_STOPNETHACK;
    else if (strcmp(item[i],"+revenge")==0)
      chan->stat|=CHAN_REVENGE;
    else if (strcmp(item[i],"-revenge")==0)
      chan->stat&=~CHAN_REVENGE;
    else if (strcmp(item[i],"+secret")==0)
      chan->stat|=CHAN_SECRET;
    else if (strcmp(item[i],"-secret")==0)
      chan->stat&=~CHAN_SECRET;
    else {
      Tcl_AppendResult(irp,"illegal channel option: ",item[i],NULL);
      return TCL_ERROR;
    }
  }
  return TCL_OK;
}

/* create new channel and parse commands */
int tcl_channel_add(irp,newname,options)
Tcl_Interp *irp; char *newname,*options;
{
  int i; struct chanset_t *chan; int items; char **item;
  context;
  if (Tcl_SplitList(irp,options,&items,&item) != TCL_OK)
    return TCL_ERROR;
  chan=newchanset();
  chan->name[0]=0; chan->need_op[0]=0; chan->need_invite[0]=0;
  chan->mode_pls_prot=0; chan->mode_mns_prot=0; chan->limit_prot=(-1);
  chan->key_prot[0]=0;
  chan->stat=CHAN_ENFORCEBANS|CHAN_GREET|CHAN_PROTECTOPS|CHAN_LOGSTATUS|
    CHAN_STOPNETHACK;
  chan->pls[0]=0; chan->mns[0]=0; chan->key[0]=0; chan->rmkey[0]=0;
  chan->limit=(-1); chan->idle_kick=0;
  for (i=0; i<MODES_PER_LINE; i++) { 
    chan->cmode[i].op=NULL; chan->cmode[i].type=0;
  }
  chan->deopnick[0]=0; chan->deoptime=0L; chan->deops=0;
  chan->kicknick[0]=0; chan->kicktime=0L; chan->kicks=0;
  strncpy(chan->name,newname,80); chan->name[80]=0;
  if (findchan(newname)!=NULL) {
    /* could be from rehash: ignore re-definition of channel */
    nfree(chan);
    /* BUT go ahead and re-parse the settings */
    chan=findchan(newname);
    chan->stat &= ~CHANFLAGGED;   /* don't delete me! :) */
    if (tcl_channel_modify(irp,chan,items,item) != TCL_OK) {
      return TCL_ERROR;
    }
    return TCL_OK;
  }
  /* okay, parse those commands */
  if (tcl_channel_modify(irp,chan,items,item) != TCL_OK) {
    nfree(chan); return TCL_ERROR;
  }
  /* initialize chan->channel info */
  init_channel(chan);
  addchanset(chan);
  chan->bans=NULL;
  /* channel name is stored in info field for sharebot stuff */
  chan->bans=adduser(chan->bans,"null","none","-",0);
  set_handle_info(chan->bans,"null",chan->name);
  if (serv>=0) mprintf(serv,"JOIN %s %s\n",chan->name,chan->key_prot);
  return TCL_OK;
}

int tcl_channel STDVAR
{
  struct chanset_t *chan;
  context;
  BADARGS(2,999," command ?options?");
  if (strcmp(argv[1],"add")==0) {
    BADARGS(3,999," add channel-name ?options?");
    if (argc==3) return tcl_channel_add(irp,argv[2],"");
    return tcl_channel_add(irp,argv[2],argv[3]);
  }
  if (strcmp(argv[1],"set")==0) {
    BADARGS(3,999," set channel-name ?options?");
    chan=findchan(argv[2]);
    if (chan==NULL) {
      Tcl_AppendResult(irp,"no such channel record",NULL);
      return TCL_ERROR;
    }
    return tcl_channel_modify(irp,chan,argc-3,&argv[3]);
  }
  if (strcmp(argv[1],"info")==0) {
    BADARGS(3,3," info channel-name");
    chan=findchan(argv[2]);
    if (chan==NULL) {
      Tcl_AppendResult(irp,"no such channel record",NULL);
      return TCL_ERROR;
    }
    return tcl_channel_info(irp,chan);
  }
  if (strcmp(argv[1],"remove")==0) {
    BADARGS(3,3," remove channel-name");
    chan=findchan(argv[2]);
    if (chan==NULL) {
      Tcl_AppendResult(irp,"no such channel record",NULL);
      return TCL_ERROR;
    }
    if (serv>=0) mprintf(serv,"PART %s\n",chan->name);
    clear_channel(chan,0);
    freeuser(chan->bans);
    killchanset(argv[2]);
    return TCL_OK;
  }
  Tcl_AppendResult(irp,"unknown channel command: should be one of: ",
		   "add, set, info, remove",NULL);
  return TCL_ERROR;
}

int tcl_banlist STDVAR
{
  struct chanset_t *chan; struct userrec *u; struct eggqueue *q;
  char s[256],hst[UHOSTLEN],ts[21],ts1[21],ts2[21],from[81];
  char *list[6],*p; time_t t;
  context;
  BADARGS(1,2," ?channel?");
  if (argc==2) {
    chan=findchan(argv[1]); if (chan==NULL) {
      Tcl_AppendResult(irp,"invalid channel: ",argv[1],NULL);
      return TCL_ERROR;
    }
    u=chan->bans;
  }
  else u=get_user_by_handle(userlist,BAN_NAME);
  if (u==NULL) return TCL_OK;
  q=u->host; while ((q!=NULL) && (strcmp(q->item,"none")!=0)) {
    strcpy(s,q->item); splitc(hst,s,':'); splitc(ts,s,':');
    if (ts[0]=='+') {
      /* new-style expiration */
      strcpy(ts,&ts[1]);
    }
    else {
      /* old-style (convert) */
      t=(time_t)atol(ts);
      if (t!=0L) t+=(60*ban_time);
      sprintf(ts,"%lu",t);
    }
    if (s[0]=='+') {
      /* extended format */
      strcpy(s,&s[1]);
      splitc(ts1,s,':'); splitc(ts2,s,':');
    }
    else { strcpy(ts1,"0"); strcpy(ts2,"0"); }
    splitc(from,s,':');
    if (s[0]) {
      /* decode gibberish stuff */
      p=strchr(s,'~'); while (p!=NULL) { *p=' '; p=strchr(s,'~'); }
      p=strchr(s,'`'); while (p!=NULL) { *p=','; p=strchr(s,'`'); }
    }
    if (s[0]==' ') strcpy(s,&s[1]);
    list[0]=hst; list[1]=s; list[2]=ts; list[3]=ts1; list[4]=ts2;
    list[5]=from;
    p=Tcl_Merge(6,list);
    Tcl_AppendElement(irp,p); n_free(p,"",0);
    q=q->next;
  }
  return TCL_OK;
}

int tcl_channels STDVAR
{
  struct chanset_t *chan;
  context;
  BADARGS(1,1,"");
  chan=chanset; while (chan!=NULL) {
    Tcl_AppendElement(irp,chan->name);
    chan=chan->next;
  }
  return TCL_OK;
}

int tcl_getchanmode STDVAR
{
  struct chanset_t *chan;
  BADARGS(2,2," channel");
  chan=findchan(argv[1]); if (chan==NULL) {
    Tcl_AppendResult(irp,"invalid channel: ",argv[1],NULL);
    return TCL_ERROR;
  }
  Tcl_AppendResult(irp,getchanmode(chan),NULL);
  return TCL_OK;
}

/* flushmode <chan> */
int tcl_flushmode STDVAR
{
  struct chanset_t *chan;
  BADARGS(2,2," channel");
  chan=findchan(argv[1]); if (chan==NULL) {
    Tcl_AppendResult(irp,"invalid channel: ",argv[1],NULL);
    return TCL_ERROR;
  }
  flush_mode(chan,NORMAL);
  return TCL_OK;
}

int tcl_pushmode STDVAR
{
  struct chanset_t *chan; char plus,mode;
  BADARGS(3,4," channel mode ?arg?");
  chan=findchan(argv[1]); if (chan==NULL) {
    Tcl_AppendResult(irp,"invalid channel: ",argv[1],NULL);
    return TCL_ERROR;
  }
  plus=argv[2][0]; mode=argv[2][1];
  if ((plus!='+') && (plus!='-')) { mode=plus; plus='+'; }
  if ((mode < 'a') || (mode > 'z')) {
    Tcl_AppendResult(irp,"invalid mode: ",argv[2],NULL);
    return TCL_ERROR;
  }
  if ((argc<4) && (strchr("bvo",mode)!=NULL)) {
    Tcl_AppendResult(irp,"modes b/v/o require an argument",NULL);
    return TCL_ERROR;
  }
  if (argc==4) add_mode(chan,plus,mode,argv[3]);
  else add_mode(chan,plus,mode,"");
  return TCL_OK;
}

int tcl_resetchan STDVAR
{
  struct chanset_t *chan;
  BADARGS(2,2," channel");
  chan=findchan(argv[1]); if (chan==NULL) {
    Tcl_AppendResult(irp,"invalid channel ",argv[1],NULL);
    return TCL_ERROR;
  }
  reset_chan_info(chan);
  return TCL_OK;
}

int tcl_topic STDVAR
{
  struct chanset_t *chan;
  BADARGS(2,2," channel");
  chan=findchan(argv[1]); if (chan==NULL) {
    Tcl_AppendResult(irp,"invalid channel ",argv[1],NULL);
    return TCL_ERROR;
  }
  Tcl_AppendResult(irp,chan->channel.topic,NULL);
  return TCL_OK;
}
