/*
   tcl.c -- handles:
     the code for every command eggdrop adds to Tcl
     Tcl initialization
     getting and setting Tcl/eggdrop variables

   dprintf'ized, 4feb96
*/
/*
   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 "proto.h"
#include "cmdt.h"
#include "tclegg.h"

/* timezone bot is in */
char time_zone[41]="EST";

/* eggdrop always uses the same interpreter */
Tcl_Interp *interp;

extern int curserv, serv, backgrd;
extern int shtime, require_x, learn_users, share_users, share_greet, use_info,
  passive, strict_host, require_p, isolate, keep_all_logs, copy_to_tmp,
  use_stderr, upload_to_cd, never_give_up, allow_new_telnets, keepnick;
extern int telnet_port, botserverport, min_servs, default_flags, conmask,
  newserverport, dcc_block, dcc_limit, dcc_maxsize, dcc_users, save_users_at,
  notify_users_at, switch_logfiles_at, telnet_bots_only, server_timeout,
  connect_timeout, firewallport, reserved_port;
extern char *logfile[];
extern char *logchan[];
extern int logmask[];
extern int flood_thr, flood_pub_thr, flood_join_thr, ban_time, ignore_time,
  flood_ctcp_thr, flood_time, flood_pub_time, flood_join_time,
  flood_ctcp_time;
extern char botname[], origbotname[], botuser[], botrealname[], botserver[],
  dccdir[], dccin[], motdfile[], admin[], userfile[], filedb_path[],
  helpdir[], initserver[], notify_new[], notefile[], hostname[], myip[],
  botuserhost[], tempdir[], newserver[], textdir[], ctcp_version[],
  ctcp_finger[], ctcp_userinfo[], owner[], newserverpass[], newbotname[],
  altnick[], firewall[];
extern char flag1, flag2, flag3, flag4, flag5, flag6, flag7, flag8, flag9,
  flag0;
extern char cx_file[];
extern int cx_line;
extern int online;
extern struct eggqueue *serverlist;
extern struct dcc_t dcc[];
extern int dcc_total;
extern char egg_version[];
extern tcl_timer_t *timer,*utimer;

/* prototypes for tcl */
Tcl_Interp *Tcl_CreateInterp();

int expmem_tcl()
{
  int i,tot=0;
  for (i=0; i<MAXLOGS; i++) if (logfile[i]!=NULL) {
    tot+=strlen(logfile[i])+1;
    tot+=strlen(logchan[i])+1;
  }
  return tot;
}

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

/* logfile [<modes> <channel> <filename>] */
int tcl_logfile STDVAR
{
  int i; char s[151];
  BADARGS(1,4," ?logModes channel logFile?");
  if (argc==1) {
    /* they just want a list of the logfiles and modes */
    for (i=0; i<MAXLOGS; i++) if (logfile[i]!=NULL) {
      strcpy(s,masktype(logmask[i])); strcat(s," ");
      strcat(s,logchan[i]); strcat(s," ");
      strcat(s,logfile[i]);
      Tcl_AppendElement(interp,s);
    }
    return TCL_OK;
  }
  BADARGS(4,4," ?logModes channel logFile?");
  for (i=0; i<MAXLOGS; i++) 
    if ((logfile[i]!=NULL) && (strcmp(logfile[i],argv[3])==0)) {
      logmask[i]=logmodes(argv[1]);
      nfree(logchan[i]); logchan[i]=NULL;
      if (!logmask[i]) { nfree(logfile[i]); logfile[i]=NULL; }
      else {
	logchan[i]=(char *)nmalloc(strlen(argv[2])+1);
	strcpy(logchan[i],argv[2]);
      }
      Tcl_AppendResult(interp,argv[3],NULL);
      return TCL_OK;
    }
  for (i=0; i<MAXLOGS; i++) if (logfile[i]==NULL) {
    logmask[i]=logmodes(argv[1]);
    logfile[i]=(char *)nmalloc(strlen(argv[3])+1);
    strcpy(logfile[i],argv[3]);
    logchan[i]=(char *)nmalloc(strlen(argv[2])+1);
    strcpy(logchan[i],argv[2]);
    Tcl_AppendResult(interp,argv[3],NULL);
    return TCL_OK;
  }
  Tcl_AppendResult(interp,"reached max # of logfiles",NULL);
  return TCL_ERROR;
}

int findidx(z)
int z;
{
  int j;
  for (j=0; j<dcc_total; j++)
    if (dcc[j].sock==z)
      if ((dcc[j].type==DCC_CHAT) || (dcc[j].type==DCC_FILES) ||
	  (dcc[j].type==DCC_SCRIPT) || (dcc[j].type==DCC_SOCKET))
	return j;
  return -1;
}

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

/* called when someone tries to change a read-only variable */
char *tcl_readonly(cdata,irp,name1,name2,flags)
ClientData cdata; Tcl_Interp *irp; char *name1,*name2; int flags;
{
  Tcl_SetVar(interp,name1,(char *)cdata,TCL_GLOBAL_ONLY);
  return "read-only variable";
}

/* called when some script tries to change flag1..flag7 */
/* (possible that the new value will be invalid, so we ignore the change) */
char *tcl_chflag(cdata,irp,name1,name2,flags)
ClientData cdata; Tcl_Interp *irp; char *name1,*name2; int flags;
{
  char s[2],*p;
  p=Tcl_GetVar(irp,name1,TCL_GLOBAL_ONLY);
  if (p==NULL) return NULL;
  s[0]=(*p); s[1]=0;
  switch ((int)cdata) {
  case 1:
    if (!(str2flags(s) & ~USER_FLAG1)) flag1=s[0]; else s[0]=flag1; break;
  case 2:
    if (!(str2flags(s) & ~USER_FLAG2)) flag2=s[0]; else s[0]=flag2; break;
  case 3:
    if (!(str2flags(s) & ~USER_FLAG3)) flag3=s[0]; else s[0]=flag3; break;
  case 4:
    if (!(str2flags(s) & ~USER_FLAG4)) flag4=s[0]; else s[0]=flag4; break;
  case 5:
    if (!(str2flags(s) & ~USER_FLAG5)) flag5=s[0]; else s[0]=flag5; break;
  case 6:
    if (!(str2flags(s) & ~USER_FLAG6)) flag6=s[0]; else s[0]=flag6; break;
  case 7:
    if (!(str2flags(s) & ~USER_FLAG7)) flag7=s[0]; else s[0]=flag7; break;
  case 8:
    if (!(str2flags(s) & ~USER_FLAG8)) flag8=s[0]; else s[0]=flag8; break;
  case 9:
    if (!(str2flags(s) & ~USER_FLAG9)) flag9=s[0]; else s[0]=flag9; break;
  case 0:
    if (!(str2flags(s) & ~USER_FLAG0)) flag0=s[0]; else s[0]=flag0; break;
  }
  Tcl_SetVar(irp,name1,s,TCL_GLOBAL_ONLY);
  return NULL;
}

/* parse the 'servers' variable */
/* save and re-set the value of curserv */
void get_tcl_servers()
{
  int i,lc,code; char **list; char *serv;
  wipe_serverlist();
  serv=Tcl_GetVar(interp,"servers",TCL_GLOBAL_ONLY);
  if (serv==NULL) { curserv=0; return; }
  code=Tcl_SplitList(interp,serv,&lc,&list);
  if (code==TCL_ERROR) {
    putlog(LOG_MISC,"*","Tcl error parsing server list:");
    putlog(LOG_MISC,"*","%s",interp->result);
    return;
  }
  for (i=0; i<lc; i++) add_server(list[i]);
  /* tricky way to make the bot reset its server pointers */
  /* perform part of a '.jump <current-server>' */
  curserv=(-1); n_free(list,"",0);
  if (botserver[0]) next_server(&curserv,botserver,&botserverport,"");
}

/* weird, weird, weird... */
void set_tcl_servers()
{
  Tcl_DString ds; char *slist; struct eggqueue *q;
  Tcl_DStringInit(&ds); q=serverlist;
  while (q!=NULL) {
    Tcl_DStringAppendElement(&ds,q->item);
    q=q->next;
  }
  slist=Tcl_DStringValue(&ds);
  Tcl_SetVar(interp,"servers",slist,TCL_GLOBAL_ONLY);
  Tcl_DStringFree(&ds);
}

void tcl_bool(var,val)
char *var; int *val;
{
  char *s;
  s=Tcl_GetVar(interp,var,TCL_GLOBAL_ONLY);
  if (s!=NULL) {
    if (Tcl_ExprBoolean(interp,s,val)==TCL_ERROR) {
      putlog(LOG_MISC,"*","Tcl error evaluating %s:",var);
      putlog(LOG_MISC,"*","%s",interp->result);
    }
  }
}
void tcl_setbool(var,val)
char *var; int val;
{
  char s[2];
  s[1]=0; s[0]=(val?'1':'0');
  Tcl_SetVar(interp,var,s,TCL_GLOBAL_ONLY);
}

void tcl_int(var,val)
char *var; int *val;
{
  char *s; long l;
  s=Tcl_GetVar(interp,var,TCL_GLOBAL_ONLY);
  if (s!=NULL) {
    if (Tcl_ExprLong(interp,s,&l)==TCL_ERROR) {
      putlog(LOG_MISC,"*","Tcl error evaluating %s:",var);
      putlog(LOG_MISC,"*","%s",interp->result);
    }
  }
  *val=(int)l;
}
void tcl_setint(var,val)
char *var; int val;
{
  char s[40];
  sprintf(s,"%d",val);
  Tcl_SetVar(interp,var,s,TCL_GLOBAL_ONLY);
}

void tcl_str(var,val,max)
char *var,*val; int max;
{
  char *s;
  s=Tcl_GetVar(interp,var,TCL_GLOBAL_ONLY);
  if (s!=NULL) {
    if (strlen(s)>max) s[max]=0;
    strcpy(val,s);
  }
}
void tcl_setstr(var,val)
char *var,*val;
{
  Tcl_SetVar(interp,var,val,TCL_GLOBAL_ONLY);
}

void tcl_char(var,val)
char *var,*val;
{
  char *s;
  s=Tcl_GetVar(interp,var,TCL_GLOBAL_ONLY);
  if (s!=NULL) *val=s[0];
}
void tcl_setchar(var,val)
char *var,val;
{
  char s[2];
  s[0]=val; s[1]=0;
  Tcl_SetVar(interp,var,s,TCL_GLOBAL_ONLY);
}

void protect_tcl()
{
  Tcl_TraceVar(interp,"owner",TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
	       tcl_readonly,(ClientData)owner);
  Tcl_TraceVar(interp,"userfile",TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
	       tcl_readonly,(ClientData)userfile);
}

void unprotect_tcl()
{
  Tcl_UntraceVar(interp,"owner",TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
		 tcl_readonly,(ClientData)owner);
  Tcl_UntraceVar(interp,"userfile",TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
		 tcl_readonly,(ClientData)userfile);
}

/* not going through Tcl's crazy main() system (what on earth was he
   smoking?!) so we gotta initialize the Tcl interpreter */
void init_tcl()
{
  /* initialize the interpreter */
  context;
  interp=Tcl_CreateInterp();
  Tcl_Init(interp);
  init_hash();
  init_builtins();
/* see note (1) at the bottom of this file */
/*  Tcl_DeleteCommand(interp,"exec"); */
  /* put traces on the 10 flag variables */
  tcl_setchar("flag1",flag1);
  tcl_setchar("flag2",flag2);
  tcl_setchar("flag3",flag3);
  tcl_setchar("flag4",flag4);
  tcl_setchar("flag5",flag5);
  tcl_setchar("flag6",flag6);
  tcl_setchar("flag7",flag7);
  tcl_setchar("flag8",flag8);
  tcl_setchar("flag9",flag9);
  tcl_setchar("flag0",flag0);
  Tcl_TraceVar(interp,"flag1",TCL_TRACE_WRITES,tcl_chflag,(ClientData)1);
  Tcl_TraceVar(interp,"flag2",TCL_TRACE_WRITES,tcl_chflag,(ClientData)2);
  Tcl_TraceVar(interp,"flag3",TCL_TRACE_WRITES,tcl_chflag,(ClientData)3);
  Tcl_TraceVar(interp,"flag4",TCL_TRACE_WRITES,tcl_chflag,(ClientData)4);
  Tcl_TraceVar(interp,"flag5",TCL_TRACE_WRITES,tcl_chflag,(ClientData)5);
  Tcl_TraceVar(interp,"flag6",TCL_TRACE_WRITES,tcl_chflag,(ClientData)6);
  Tcl_TraceVar(interp,"flag7",TCL_TRACE_WRITES,tcl_chflag,(ClientData)7);
  Tcl_TraceVar(interp,"flag8",TCL_TRACE_WRITES,tcl_chflag,(ClientData)8);
  Tcl_TraceVar(interp,"flag9",TCL_TRACE_WRITES,tcl_chflag,(ClientData)9);
  Tcl_TraceVar(interp,"flag0",TCL_TRACE_WRITES,tcl_chflag,(ClientData)0);
  Tcl_TraceVar(interp,"version",TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
	       tcl_readonly,(ClientData)egg_version);
  /* add new commands */
  Tcl_CreateCommand(interp,"logfile",tcl_logfile,NULL,NULL);
  Tcl_CreateCommand(interp,"putserv",tcl_putserv,NULL,NULL);
  Tcl_CreateCommand(interp,"puthelp",tcl_puthelp,NULL,NULL);
  Tcl_CreateCommand(interp,"putdcc",tcl_putdcc,NULL,NULL);
  Tcl_CreateCommand(interp,"putlog",tcl_putlog,NULL,NULL);
  Tcl_CreateCommand(interp,"putcmdlog",tcl_putcmdlog,NULL,NULL);
  Tcl_CreateCommand(interp,"putxferlog",tcl_putxferlog,NULL,NULL);
  Tcl_CreateCommand(interp,"putloglev",tcl_putloglev,NULL,NULL);
  Tcl_CreateCommand(interp,"countusers",tcl_countusers,NULL,NULL);
  Tcl_CreateCommand(interp,"validuser",tcl_validuser,NULL,NULL);
  Tcl_CreateCommand(interp,"finduser",tcl_finduser,NULL,NULL);
  Tcl_CreateCommand(interp,"passwdOk",tcl_passwdOk,NULL,NULL);
  Tcl_CreateCommand(interp,"passwdok",tcl_passwdOk,NULL,NULL);
  Tcl_CreateCommand(interp,"chattr",tcl_chattr,NULL,NULL);
  Tcl_CreateCommand(interp,"matchattr",tcl_matchattr,NULL,NULL);
  Tcl_CreateCommand(interp,"botisop",tcl_botisop,NULL,NULL);
  Tcl_CreateCommand(interp,"isop",tcl_isop,NULL,NULL);
  Tcl_CreateCommand(interp,"isvoice",tcl_isvoice,NULL,NULL);
  Tcl_CreateCommand(interp,"onchan",tcl_onchan,NULL,NULL);
  Tcl_CreateCommand(interp,"handonchan",tcl_handonchan,NULL,NULL);
  Tcl_CreateCommand(interp,"ischanban",tcl_ischanban,NULL,NULL);
  Tcl_CreateCommand(interp,"getchanhost",tcl_getchanhost,NULL,NULL);
  Tcl_CreateCommand(interp,"onchansplit",tcl_onchansplit,NULL,NULL);
  Tcl_CreateCommand(interp,"chanlist",tcl_chanlist,NULL,NULL);
  Tcl_CreateCommand(interp,"bind",tcl_bind,(ClientData)0,NULL);
  Tcl_CreateCommand(interp,"unbind",tcl_bind,(ClientData)1,NULL);
  Tcl_CreateCommand(interp,"adduser",tcl_adduser,NULL,NULL);
  Tcl_CreateCommand(interp,"addbot",tcl_addbot,NULL,NULL);
  Tcl_CreateCommand(interp,"deluser",tcl_deluser,NULL,NULL);
  Tcl_CreateCommand(interp,"maskhost",tcl_maskhost,NULL,NULL);
  Tcl_CreateCommand(interp,"dccsimul",tcl_dccsimul,NULL,NULL);
  Tcl_CreateCommand(interp,"addhost",tcl_addhost,NULL,NULL);
  Tcl_CreateCommand(interp,"delhost",tcl_delhost,NULL,NULL);
  Tcl_CreateCommand(interp,"timer",tcl_timer,NULL,NULL);
  Tcl_CreateCommand(interp,"killtimer",tcl_killtimer,NULL,NULL);
  Tcl_CreateCommand(interp,"utimer",tcl_utimer,NULL,NULL);
  Tcl_CreateCommand(interp,"killutimer",tcl_killutimer,NULL,NULL);
  Tcl_CreateCommand(interp,"unixtime",tcl_unixtime,NULL,NULL);
  Tcl_CreateCommand(interp,"time",tcl_time,NULL,NULL);
  Tcl_CreateCommand(interp,"date",tcl_date,NULL,NULL);
  Tcl_CreateCommand(interp,"getinfo",tcl_getinfo,NULL,NULL);
  Tcl_CreateCommand(interp,"getaddr",tcl_getaddr,NULL,NULL);
  Tcl_CreateCommand(interp,"getdccdir",tcl_getdccdir,NULL,NULL);
  Tcl_CreateCommand(interp,"getcomment",tcl_getcomment,NULL,NULL);
  Tcl_CreateCommand(interp,"getemail",tcl_getemail,NULL,NULL);
  Tcl_CreateCommand(interp,"getxtra",tcl_getxtra,NULL,NULL);
  Tcl_CreateCommand(interp,"setinfo",tcl_setinfo,NULL,NULL);
  Tcl_CreateCommand(interp,"setdccdir",tcl_setdccdir,NULL,NULL);
  Tcl_CreateCommand(interp,"setcomment",tcl_setcomment,NULL,NULL);
  Tcl_CreateCommand(interp,"setemail",tcl_setemail,NULL,NULL);
  Tcl_CreateCommand(interp,"setxtra",tcl_setxtra,NULL,NULL);
  Tcl_CreateCommand(interp,"delban",tcl_delban,NULL,NULL);
  Tcl_CreateCommand(interp,"delglban",tcl_delglban,NULL,NULL);
  Tcl_CreateCommand(interp,"isban",tcl_isban,NULL,NULL);
  Tcl_CreateCommand(interp,"ispermban",tcl_ispermban,NULL,NULL);
  Tcl_CreateCommand(interp,"matchban",tcl_matchban,NULL,NULL);
  Tcl_CreateCommand(interp,"addban",tcl_addban,NULL,NULL);
  Tcl_CreateCommand(interp,"addglban",tcl_addglban,NULL,NULL);
  Tcl_CreateCommand(interp,"getlaston",tcl_getlaston,NULL,NULL);
  Tcl_CreateCommand(interp,"setlaston",tcl_setlaston,NULL,NULL);
  Tcl_CreateCommand(interp,"timers",tcl_timers,NULL,NULL);
  Tcl_CreateCommand(interp,"utimers",tcl_utimers,NULL,NULL);
  Tcl_CreateCommand(interp,"jump",tcl_jump,NULL,NULL);
  Tcl_CreateCommand(interp,"ctime",tcl_ctime,NULL,NULL);
  Tcl_CreateCommand(interp,"myip",tcl_myip,NULL,NULL);
  Tcl_CreateCommand(interp,"dccsend",tcl_dccsend,NULL,NULL);
  Tcl_CreateCommand(interp,"rand",tcl_rand,NULL,NULL);
  Tcl_CreateCommand(interp,"dccbroadcast",tcl_dccbroadcast,NULL,NULL);
  Tcl_CreateCommand(interp,"hand2idx",tcl_hand2idx,NULL,NULL);
  Tcl_CreateCommand(interp,"getidx",tcl_hand2idx,NULL,NULL);
  Tcl_CreateCommand(interp,"idx2hand",tcl_idx2hand,NULL,NULL);
  Tcl_CreateCommand(interp,"getchan",tcl_getchan,NULL,NULL);
  Tcl_CreateCommand(interp,"setchan",tcl_setchan,NULL,NULL);
  Tcl_CreateCommand(interp,"dccputchan",tcl_dccputchan,NULL,NULL);
  Tcl_CreateCommand(interp,"console",tcl_console,NULL,NULL);
  Tcl_CreateCommand(interp,"echo",tcl_echo,NULL,NULL);
  Tcl_CreateCommand(interp,"control",tcl_control,NULL,NULL);
  Tcl_CreateCommand(interp,"putbot",tcl_putbot,NULL,NULL);
  Tcl_CreateCommand(interp,"putallbots",tcl_putallbots,NULL,NULL);
  Tcl_CreateCommand(interp,"getchanidle",tcl_getchanidle,NULL,NULL);
  Tcl_CreateCommand(interp,"killdcc",tcl_killdcc,NULL,NULL);
  Tcl_CreateCommand(interp,"userlist",tcl_userlist,NULL,NULL);
  Tcl_CreateCommand(interp,"sendnote",tcl_sendnote,NULL,NULL);
  Tcl_CreateCommand(interp,"save",tcl_save,NULL,NULL);
  Tcl_CreateCommand(interp,"reload",tcl_reload,NULL,NULL);
  Tcl_CreateCommand(interp,"bots",tcl_bots,NULL,NULL);
  Tcl_CreateCommand(interp,"chanbans",tcl_chanbans,NULL,NULL);
  Tcl_CreateCommand(interp,"gethosts",tcl_gethosts,NULL,NULL);
  Tcl_CreateCommand(interp,"nick2hand",tcl_nick2hand,NULL,NULL);
  Tcl_CreateCommand(interp,"hand2nick",tcl_hand2nick,NULL,NULL);
  Tcl_CreateCommand(interp,"getdccidle",tcl_getdccidle,NULL,NULL);
  Tcl_CreateCommand(interp,"dcclist",tcl_dcclist,NULL,NULL);
  Tcl_CreateCommand(interp,"dccused",tcl_dccused,NULL,NULL);
  Tcl_CreateCommand(interp,"getfileq",tcl_getfileq,NULL,NULL);
  Tcl_CreateCommand(interp,"chpass",tcl_chpass,NULL,NULL);
  Tcl_CreateCommand(interp,"chnick",tcl_chhandle,NULL,NULL);
  Tcl_CreateCommand(interp,"chhandle",tcl_chhandle,NULL,NULL);
  Tcl_CreateCommand(interp,"link",tcl_link,NULL,NULL);
  Tcl_CreateCommand(interp,"unlink",tcl_unlink,NULL,NULL);
  Tcl_CreateCommand(interp,"channel",tcl_channel,NULL,NULL);
  Tcl_CreateCommand(interp,"banlist",tcl_banlist,NULL,NULL);
  Tcl_CreateCommand(interp,"channels",tcl_channels,NULL,NULL);
  Tcl_CreateCommand(interp,"getting-users",tcl_getting_users,NULL,NULL);
  Tcl_CreateCommand(interp,"getdesc",tcl_getdesc,NULL,NULL);
  Tcl_CreateCommand(interp,"getowner",tcl_getowner,NULL,NULL);
  Tcl_CreateCommand(interp,"setdesc",tcl_setdesc,NULL,NULL);
  Tcl_CreateCommand(interp,"setowner",tcl_setowner,NULL,NULL);
  Tcl_CreateCommand(interp,"getgots",tcl_getgots,NULL,NULL);
  Tcl_CreateCommand(interp,"getpwd",tcl_getpwd,NULL,NULL);
  Tcl_CreateCommand(interp,"setpwd",tcl_setpwd,NULL,NULL);
  Tcl_CreateCommand(interp,"getlink",tcl_getlink,NULL,NULL);
  Tcl_CreateCommand(interp,"setlink",tcl_setlink,NULL,NULL);
  Tcl_CreateCommand(interp,"getfiles",tcl_getfiles,NULL,NULL);
  Tcl_CreateCommand(interp,"getdirs",tcl_getdirs,NULL,NULL);
  Tcl_CreateCommand(interp,"hide",tcl_hide,NULL,NULL);
  Tcl_CreateCommand(interp,"unhide",tcl_unhide,NULL,NULL);
  Tcl_CreateCommand(interp,"share",tcl_share,NULL,NULL);
  Tcl_CreateCommand(interp,"unshare",tcl_unshare,NULL,NULL);
  Tcl_CreateCommand(interp,"filesend",tcl_filesend,NULL,NULL);
  Tcl_CreateCommand(interp,"assoc",tcl_assoc,NULL,NULL);
  Tcl_CreateCommand(interp,"killassoc",tcl_killassoc,NULL,NULL);
  Tcl_CreateCommand(interp,"getchanmode",tcl_getchanmode,NULL,NULL);
  Tcl_CreateCommand(interp,"pushmode",tcl_pushmode,NULL,NULL);
  Tcl_CreateCommand(interp,"flushmode",tcl_flushmode,NULL,NULL);
  Tcl_CreateCommand(interp,"topic",tcl_topic,NULL,NULL);
  Tcl_CreateCommand(interp,"encrypt",tcl_encrypt,NULL,NULL);
  Tcl_CreateCommand(interp,"decrypt",tcl_decrypt,NULL,NULL);
  Tcl_CreateCommand(interp,"addignore",tcl_addignore,NULL,NULL);
  Tcl_CreateCommand(interp,"addpermignore",tcl_addpermignore,NULL,NULL);
  Tcl_CreateCommand(interp,"delignore",tcl_delignore,NULL,NULL);
  Tcl_CreateCommand(interp,"isignore",tcl_isignore,NULL,NULL);
  Tcl_CreateCommand(interp,"getdccaway",tcl_getdccaway,NULL,NULL);
  Tcl_CreateCommand(interp,"setdccaway",tcl_setdccaway,NULL,NULL);
  Tcl_CreateCommand(interp,"connect",tcl_connect,NULL,NULL);
  Tcl_CreateCommand(interp,"newchanban",tcl_newchanban,NULL,NULL);
  Tcl_CreateCommand(interp,"newban",tcl_newban,NULL,NULL);
  Tcl_CreateCommand(interp,"killchanban",tcl_killchanban,NULL,NULL);
  Tcl_CreateCommand(interp,"killban",tcl_killban,NULL,NULL);
  Tcl_CreateCommand(interp,"newignore",tcl_newignore,NULL,NULL);
  Tcl_CreateCommand(interp,"killignore",tcl_killignore,NULL,NULL);
  Tcl_CreateCommand(interp,"ignorelist",tcl_ignorelist,NULL,NULL);
  Tcl_CreateCommand(interp,"whom",tcl_whom,NULL,NULL);
  Tcl_CreateCommand(interp,"dumpfile",tcl_dumpfile,NULL,NULL);
  Tcl_CreateCommand(interp,"dccdumpfile",tcl_dccdumpfile,NULL,NULL);
  Tcl_CreateCommand(interp,"resetchan",tcl_resetchan,NULL,NULL);
  Tcl_CreateCommand(interp,"die",tcl_die,NULL,NULL);
}

/* set Tcl variables to match eggdrop internal variables */
void set_tcl_vars()
{
  char s[121];
  set_tcl_servers();
  /* on/off variables */
  tcl_setbool("log-time",shtime);
  tcl_setbool("learn-users",learn_users);
  tcl_setbool("require-x",require_x);
  tcl_setbool("require-p",require_p);
  tcl_setbool("use-info",use_info);
  tcl_setbool("share-users",share_users);
  tcl_setbool("share-greet",share_greet);
  tcl_setbool("passive",passive);
  tcl_setbool("strict-host",strict_host);
  tcl_setbool("isolate",isolate);
  tcl_setbool("keep-all-logs",keep_all_logs);
  tcl_setbool("upload-to-cd",upload_to_cd);
  tcl_setbool("never-give-up",never_give_up);
  tcl_setbool("open-telnets",allow_new_telnets);
  tcl_setbool("keep-nick",keepnick);
  tcl_setbool("copy-to-tmp",copy_to_tmp);
  tcl_setbool("telnet-bots-only",telnet_bots_only);
  /* numbers */
  tcl_setint("telnet",telnet_port);
  tcl_setint("servlimit",min_servs);
  tcl_setint("ban-time",ban_time);
  tcl_setint("ignore-time",ignore_time);
  tcl_setint("dcc-limit",dcc_limit);
  tcl_setint("dcc-block",dcc_block);
  tcl_setint("dcc-maxsize",dcc_maxsize);
  tcl_setint("dcc-users",dcc_users);
  tcl_setint("save-users-at",save_users_at);
  tcl_setint("notify-users-at",notify_users_at);
  tcl_setint("switch-logfiles-at",switch_logfiles_at);
  tcl_setint("server-timeout",server_timeout);
  tcl_setint("connect-timeout",connect_timeout);
  tcl_setint("reserved-port",reserved_port);
  /* strings */
  tcl_setstr("nick",origbotname);
  tcl_setstr("altnick",altnick);
  tcl_setstr("username",botuser);
  tcl_setstr("realname",botrealname);
  tcl_setstr("userfile",userfile);
  tcl_setstr("dcc-path",dccdir);
  tcl_setstr("dcc-incoming",dccin);
  tcl_setstr("motd",motdfile);
  tcl_setstr("admin",admin);
  tcl_setstr("init-server",initserver);
  tcl_setstr("notefile",notefile);
  tcl_setstr("helpdir",helpdir);
  tcl_setstr("tempdir",tempdir);
  tcl_setstr("textdir",textdir);
  tcl_setstr("notify-newusers",notify_new);
  tcl_setstr("ctcp-version",ctcp_version);
  tcl_setstr("ctcp-finger",ctcp_finger);
  tcl_setstr("ctcp-userinfo",ctcp_userinfo);
  tcl_setstr("owner",owner);
  tcl_setstr("filedb-path",filedb_path);
  tcl_setstr("my-hostname",hostname);
  tcl_setstr("my-ip",myip);
  tcl_setstr("timezone",time_zone);
  /* weird */
  strcpy(s,masktype(conmask)); tcl_setstr("console",s);
  flags2str(default_flags,s); tcl_setstr("default-flags",s);
  if (firewall[0]) sprintf(s,"%s:%d",firewall,firewallport);
  else s[0]=0;
  tcl_setstr("firewall",s);
  sprintf(s,"%d:%d",flood_thr,flood_time);
  tcl_setstr("flood-msg",s);
  sprintf(s,"%d:%d",flood_pub_thr,flood_pub_time);
  tcl_setstr("flood-chan",s);
  sprintf(s,"%d:%d",flood_join_thr,flood_join_time);
  tcl_setstr("flood-join",s);
  sprintf(s,"%d:%d",flood_ctcp_thr,flood_ctcp_time);
  tcl_setstr("flood-ctcp",s);
  /* variables that we won't re-read... only for convenience of scripts */
  tcl_setstr("botnick",botname);
  sprintf(s,"%s:%d",botserver,botserverport); tcl_setstr("server",s);
  sprintf(s,"%s!%s",botname,botuserhost); tcl_setstr("botname",s);
  tcl_setstr("version",egg_version);
  /* cos we have to: */
  tcl_setstr("tcl_interactive","0");
}

/* set eggdrop internal variables to match Tcl variables */
void get_tcl_vars()
{
  char s[120],s1[20]; int oldisol;
  get_tcl_servers();
  /* on/off variables: */
  tcl_bool("log-time",&shtime);
  tcl_bool("learn-users",&learn_users);
  tcl_bool("require-x",&require_x);
  tcl_bool("require-p",&require_p);
  tcl_bool("use-info",&use_info);
  tcl_bool("share-users",&share_users);
  tcl_bool("share-greet",&share_greet);
  tcl_bool("passive",&passive);
  tcl_bool("strict-host",&strict_host);
  oldisol=isolate;
  tcl_bool("isolate",&isolate);
  if ((isolate) && (!oldisol) && (online)) {
    /* suddenly isolating now */
    chatout("*** Party line is now isolated.\n");
    tandout("chat %s Isolating my party line.\n",origbotname);
  }
  if ((!isolate) && (oldisol) && (online)) {
    chatout("*** Party line is now open.\n");
    tandout("chat %s Merging my party line.\n",origbotname);
  }
  tcl_bool("keep-all-logs",&keep_all_logs);
  tcl_bool("upload-to-cd",&upload_to_cd);
  tcl_bool("never-give-up",&never_give_up);
  tcl_bool("open-telnets",&allow_new_telnets);
  tcl_bool("keep-nick",&keepnick);
  tcl_bool("copy-to-tmp",&copy_to_tmp);
  tcl_bool("telnet-bots-only",&telnet_bots_only);
  /* numbers */
  oldisol=telnet_port;
  tcl_int("telnet",&telnet_port);
  tcl_int("servlimit",&min_servs);
  tcl_int("ban-time",&ban_time);
  tcl_int("ignore-time",&ignore_time);
  tcl_int("dcc-limit",&dcc_limit);
  tcl_int("dcc-block",&dcc_block);
  tcl_int("dcc-maxsize",&dcc_maxsize);
  tcl_int("dcc-users",&dcc_users);
  tcl_int("save-users-at",&save_users_at);
  tcl_int("notify-users-at",&notify_users_at);
  tcl_int("switch-logfiles-at",&switch_logfiles_at);
  tcl_int("server-timeout",&server_timeout);
  tcl_int("connect-timeout",&connect_timeout);
  tcl_int("reserved-port",&reserved_port);
  /* strings */
  strcpy(s,origbotname);
  tcl_str("nick",origbotname,9);
  if ((s[0]) && (strcasecmp(origbotname,s)!=0)) {
    /* trying to change bot's nickname */
    if (get_tands() > 0) {
      putlog(LOG_MISC,"*","* Tried to change my nickname, but I'm still linked to a botnet.");
      putlog(LOG_MISC,"*","* (Unlink and try again.)");
      strcpy(origbotname,s);
    }
    else {
      putlog(LOG_MISC,"*","* IDENTITY CHANGE: %s -> %s",s,origbotname);
      /* start all over with nick chasing: */
      strcpy(botname,origbotname); newbotname[0]=0;
      if (serv>=0) tprintf(serv,"NICK %s\n",botname);
    }
  }
  tcl_str("altnick",altnick,9);
  tcl_str("username",botuser,10);
  tcl_str("realname",botrealname,80);
  tcl_str("userfile",userfile,120);
  tcl_str("dcc-path",dccdir,120);
  if (dccdir[0]) if (dccdir[strlen(dccdir)-1]!='/') strcat(dccdir,"/");
  tcl_str("dcc-incoming",dccin,120);
  if (dccin[0]) if (dccin[strlen(dccin)-1]!='/') strcat(dccin,"/");
  tcl_str("motd",motdfile,120);
  tcl_str("admin",admin,120);
  tcl_str("init-server",initserver,120);
  tcl_str("notefile",notefile,120);
  tcl_str("helpdir",helpdir,120);
  if (helpdir[0]) if (helpdir[strlen(helpdir)-1]!='/') strcat(helpdir,"/");
  tcl_str("tempdir",tempdir,120);
  if (tempdir[0]) if (tempdir[strlen(tempdir)-1]!='/') strcat(tempdir,"/");
  tcl_str("textdir",textdir,120);
  if (textdir[0]) {
    if (textdir[strlen(textdir)-1]!='/') strcat(textdir,"/");
  }
  else strcpy(textdir,helpdir);
  tcl_str("filedb-path",filedb_path,120);
  if (filedb_path[0]) {
    if (filedb_path[strlen(filedb_path)-1]!='/') strcat(filedb_path,"/");
  }
  tcl_str("notify-newusers",notify_new,120);
  tcl_str("ctcp-version",ctcp_version,120);
  tcl_str("ctcp-finger",ctcp_finger,120);
  tcl_str("ctcp-userinfo",ctcp_userinfo,120);
  tcl_str("owner",owner,120);
  tcl_str("my-hostname",hostname,120);
  tcl_str("my-ip",myip,120);
  tcl_str("timezone",time_zone,40);
  /* wait till after loading hostname! */
  if (telnet_port!=oldisol) change_telnet_port(); 
  /* weird ones */
  s[0]=0; tcl_str("console",s,20);
  if (s[0]) conmask=logmodes(s); else conmask=LOG_MODES|LOG_MISC|LOG_CMDS;
  s[0]=0; tcl_str("default-flags",s,20); default_flags=str2flags(s);
  tcl_str("firewall",s,121);
  if (s[0]) {
    splitc(firewall,s,':');
    if (!firewall[0]) strcpy(firewall,s);
    else firewallport=atoi(s);
  }
  else firewall[0]=0;
  tcl_str("flood-msg",s,20);
  if (s[0]) {
    splitc(s1,s,':');
    if (s1[0]) { flood_thr=atoi(s1); flood_time=atoi(s); }
    else flood_thr=atoi(s);
  }
  tcl_str("flood-chan",s,20);
  if (s[0]) {
    splitc(s1,s,':');
    if (s1[0]) { flood_pub_thr=atoi(s1); flood_pub_time=atoi(s); }
    else flood_pub_thr=atoi(s);
  }
  tcl_str("flood-join",s,20);
  if (s[0]) {
    splitc(s1,s,':');
    if (s1[0]) { flood_join_thr=atoi(s1); flood_join_time=atoi(s); }
    else flood_join_thr=atoi(s);
  }
  tcl_str("flood-ctcp",s,20);
  if (s[0]) {
    splitc(s1,s,':');
    if (s1[0]) { flood_ctcp_thr=atoi(s1); flood_ctcp_time=atoi(s); }
    else flood_ctcp_thr=atoi(s);
  }
}

/* evaluate a Tcl command, send output to a dcc user */
void cmd_tcl(idx,msg)
int idx; char *msg;
{
  int code;
  context;
  set_tcl_vars();
  code=Tcl_Eval(interp,msg);
  if (code==TCL_OK) dumplots(idx,"Tcl: ",interp->result);
  else dprintf(idx,"TCL error: %s\n",interp->result);
  /* refresh internal vars */
  get_tcl_vars();
}

/* perform a 'set' command */
void cmd_set(idx,msg)
int idx; char *msg;
{
  int code; char s[512];
  context;
  putlog(LOG_CMDS,"*","#%s# set %s",dcc[idx].nick,msg);
  set_tcl_vars();
  strcpy(s,"set "); strcat(s,msg);
  if (!msg[0]) {
    strcpy(s,"info globals");
    Tcl_Eval(interp,s);
    dumplots(idx,"global vars: ",interp->result);
    return;
  }
  code=Tcl_Eval(interp,s);
  if (code==TCL_OK) {
    if (strchr(msg,' ')==NULL) dumplots(idx,"currently: ",interp->result);
    else dprintf(idx,"Ok, set.\n");
  }
  else dprintf(idx,"Error: %s\n",interp->result);
  get_tcl_vars();
}

void do_tcl(whatzit,script)
char *whatzit,*script;
{
  int code;
#ifdef EBUG_TCL
  FILE *f=fopen("DEBUG.TCL","a");
  if (f!=NULL) fprintf(f,"eval: %s\n",script);
#endif
  set_tcl_vars();
  code=Tcl_Eval(interp,script);
#ifdef EBUG_TCL
  if (f!=NULL) {
    fprintf(f,"done eval, result=%d\n",code);
    fclose(f);
  }
#endif
  if (code!=TCL_OK) {
    putlog(LOG_MISC,"*","Tcl error in script for '%s':",whatzit);
    putlog(LOG_MISC,"*","%s",interp->result);
  }
  get_tcl_vars();
}

/* read and interpret the configfile given */
/* return 1 if everything was okay */
int readtclprog(fname)
char *fname;
{
  int code; FILE *f;
  set_tcl_vars();
  f=fopen(fname,"r"); if (f==NULL) return 0;
  fclose(f);
#ifdef EBUG_TCL
  f=fopen("DEBUG.TCL","a");
  if (f!=NULL) {
    fprintf(f,"Sourcing file %s ...\n",fname);
    fclose(f);
  }
#endif
  code=Tcl_EvalFile(interp,fname);
  if (code!=TCL_OK) {
    if (use_stderr) {
      tprintf(STDERR,"Tcl error in file '%s':\n",fname);
      tprintf(STDERR,"%s\n",Tcl_GetVar(interp,"errorInfo",TCL_GLOBAL_ONLY));
    }
    else {
      putlog(LOG_MISC,"*","Tcl error in file '%s':",fname);
      putlog(LOG_MISC,"*","%s\n",Tcl_GetVar(interp,"errorInfo",TCL_GLOBAL_ONLY));
    }
    /* try to go on anyway (shrug) */
  }
  /* refresh internal variables */
  get_tcl_vars();
  return 1;
}

void warn_obsolete(s)
char *s;
{
  static warned=0;
  if (warned>=5) return;
  putlog(LOG_MISC,"*","Obsolete Tcl command '%s' used (the script %s)",
	 s,"should be updated soon, as this command will soon vanish");
  warned++;
}

/*
   note (1):
     the tcl 'exec' command is no longer removed, since it is assumed
     that the tcl command will be left at its default flag requirement,
     ie: only owners can do tcl commands directly.  also, removing the
     'exec' command doesn't block up all holes -- tcl allows you to open
     a "pipe" which really just executes a shell command and redirects
     output.  so you were never truly safe anyway.  gee.
*/
