/*
   botcmd.c -- handles:
     commands that comes across the botnet
     userfile transfer and update commands from sharebots

   dprintf'ized, 10nov95
*/
/*
   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 "eggdrop.h"
#include "tandem.h"
#include "users.h"
#include "chan.h"
#include "proto.h"

extern tand_t tand[];
extern int tands;
extern char origbotname[];
extern int dcc_total;
extern struct dcc_t dcc[];
extern int isolate;
extern int noshare;
extern int passive;
extern int share_users;
extern char motdfile[];
extern struct userrec *userlist;
extern char ver[];
extern char os[];
extern struct chanset_t *chanset;
extern int serv;
extern int cx_line;
extern char cx_file[];
extern char ban_time;
extern char ignore_time;

/* static buffer for goofy bot stuff */
char TBUF[1024];

void fake_alert(idx)
int idx;
{
  tprintf(dcc[idx].sock,"chat %s NOTICE: Fake message rejected.\n",
	  origbotname);
}

/* chan <from> <chan> <text> */
void bot_chan(idx,par)
int idx; char *par;
{
  char *from=TBUF,*s=TBUF+512,*p; int i,chan;
  context;
  nsplit(from,par); chan=atoi(par); nsplit(NULL,par);
  /* strip annoying control chars */
  for (p=from; *p; ) {
    if ((*p<32) || (*p==127)) strcpy(p,p+1);
    else p++;
  }
  p=strchr(from,'@');
  if (p==NULL) { sprintf(s,"*** (%s) %s",from,par); p=from; }
  else {
    sprintf(s,"<%s> %s",from,par);
    *p=0; partyidle(p+1,from); *p='@'; p++;
  }
  i=nextbot(p);
  if (i!=idx) {
    fake_alert(idx);
    return;
  }
  tandout_but(idx,"chan %s %d %s\n",from,chan,par);
  if (!isolate) {
    chanout(chan,"%s\n",s);
    if (strchr(from,'@')!=NULL) check_tcl_chat(from,chan,par);
  }
}

/* chat <from> <notice>  -- only from bots */
void bot_chat(idx,par)
int idx; char *par;
{
  char *from=TBUF; int i;
  context;
  nsplit(from,par);
  if (strchr(from,'@')!=NULL) {
    fake_alert(idx);
    return;
  }
  /* make sure the bot is valid */
  i=nextbot(from); if (i!=idx) {
    fake_alert(idx);
    return;
  }
  if (!isolate) chatout("*** (%s) %s\n",from,par);
  tandout_but(idx,"chat %s %s\n",from,par);
}

/* actchan <from> <chan> <text> */
void bot_actchan(idx,par)
int idx; char *par;
{
  char *from=TBUF,*p; int i,chan;
  context;
  nsplit(from,par); p=strchr(from,'@');
  if (p==NULL) {
    /* how can a bot do an action? */
    fake_alert(idx); return;
  }
  *p=0; partyidle(p+1,from); *p='@';
  p++; i=nextbot(p); if (i!=idx) {
    fake_alert(idx); return;
  }
  chan=atoi(par); nsplit(NULL,par);
  if (!isolate) {
    chanout(chan,"* %s %s\n",from,par);
    check_tcl_act(from,chan,par);
  }
  tandout_but(idx,"actchan %s %d %s\n",from,chan,par);
}

/* priv <from> <to> <message> */
void bot_priv(idx,par)
int idx; char *par;
{
  char *from=TBUF,*p,*to=TBUF+600,*tobot=TBUF+512; int i;
  context;
  nsplit(from,par); nsplit(tobot,par); tobot[40]=0; splitc(to,tobot,'@');
  p=strchr(from,'@'); if (p!=NULL) p++; else p=from;
  i=nextbot(p); if ((i!=idx) || (!to[0])) {
    fake_alert(idx); return;
  }
  if (strcasecmp(tobot,origbotname)==0) {  /* for me! */
    if (p==from) add_note(to,from,par,-2,0);
    else {
      i=add_note(to,from,par,-1,0);
      switch(i) {
      case NOTE_ERROR:
	tprintf(dcc[idx].sock,"priv %s %s No such user %s.\n",origbotname,
		from,to);
	break;
      case NOTE_STORED:
	tprintf(dcc[idx].sock,"priv %s %s Not online; note stored.\n",
		origbotname,from);
	break;
      case NOTE_FULL:
	tprintf(dcc[idx].sock,"priv %s %s Notebox is full, sorry.\n",
		origbotname,from);
	break;
      case NOTE_AWAY:
	tprintf(dcc[idx].sock,"priv %s %s %s is away; note stored.\n",
		origbotname,from,to);
	break;
      case NOTE_TCL:
	break;  /* do nothing */
      case NOTE_OK:
	tprintf(dcc[idx].sock,"priv %s %s Note sent to %s.\n",origbotname,
		from,to);
	break;
      }
    }
  }
  else {   /* pass it on */
    i=nextbot(tobot); if (i>=0)
       tprintf(dcc[i].sock,"priv %s %s@%s %s\n",from,to,tobot,par);
  }
}

void bot_bye(idx,par)
int idx; char *par;
{
  context;
  putlog(LOG_BOTS,"*","Disconnected from: %s",dcc[idx].nick);
  chatout("*** Disconnected from: %s\n",dcc[idx].nick);
  tandout_but(idx,"unlinked %s\n",dcc[idx].nick);
  tandout_but(idx,"chat %s Disconnected from: %s\n",origbotname,dcc[idx].nick);
  cancel_user_xfer(idx);
  rembot(dcc[idx].nick,dcc[idx].nick); unvia(idx,dcc[idx].nick);
  tprintf(dcc[idx].sock,"*bye\n"); killsock(dcc[idx].sock); lostdcc(idx);
}

/* who <from@bot> <tobot> <chan#> */
void bot_who(idx,par)
int idx; char *par;
{
  char *p; char *from=TBUF,*to=TBUF+512; int i;
  context;
  nsplit(from,par); p=strchr(from,'@'); if (p==NULL) {
    sprintf(to,"%s@%s",from,dcc[idx].nick); strcpy(from,to);
  }
  nsplit(to,par);
  if (strcasecmp(to,origbotname)==0) to[0]=0;  /* (for me) */
  if (to[0]) {     /* pass it on */
    i=nextbot(to); if (i>=0)
      tprintf(dcc[i].sock,"who %s %s %s\n",from,to,par);
  }
  else {
    remote_tell_who(dcc[idx].sock,from,atoi(par));
  }
}

void bot_version(idx,par)
int idx; char *par;
{
  context;
  strcpy(dcc[idx].u.bot->version,par);
  if ((share_users) && (get_attr_handle(dcc[idx].nick)&BOT_SHARE)) {
    if (passive) {
      if (dcc[idx].u.bot->status&STAT_CALLED) {
	if (can_resync(dcc[idx].nick)) tprintf(dcc[idx].sock,"resync?\n");
	else tprintf(dcc[idx].sock,"userfile?\n");
	dcc[idx].u.bot->status|=STAT_OFFERED;
      }
    }
    else {
      if (can_resync(dcc[idx].nick)) tprintf(dcc[idx].sock,"resync?\n");
      else tprintf(dcc[idx].sock,"userfile?\n");
      dcc[idx].u.bot->status|=STAT_OFFERED;
    }
  }
}

/* who? <from@bot> <chan>  ->  whom <to@bot> <attr><nick> <bot> <host> */
void bot_whoq(idx,par)
int idx; char *par;
{
  context;
  /* ignore old-style 'whom' request */
  putlog(LOG_BOTS,"*","Outdated 'whom' request from %s (ignoring)",
	 dcc[idx].nick);
}

/* info? <from@bot>   -> send priv */
void bot_infoq(idx,par)
int idx; char *par;
{
#ifndef NO_IRC
  char s[161]; struct chanset_t *chan;
  context;
  chan=chanset; s[0]=0;
  while (chan!=NULL) {
    if (!(chan->stat & CHAN_SECRET)) {
      strcat(s,chan->name); strcat(s,", ");
    }
    chan=chan->next;
  }
  if (s[0]) {
    s[strlen(s)-2]=0;
    tprintf(dcc[idx].sock,"priv %s %s %s (%s)\n",origbotname,par,ver,s);
  }
  else
    tprintf(dcc[idx].sock,"priv %s %s %s (no channels)\n",origbotname,par,ver);
#else
  tprintf(dcc[idx].sock,"priv %s %s %s\n",origbotname,par,ver);
#endif
  tandout_but(idx,"info? %s\n",par);
}

/* whom <to@bot> <attr><nick> <bot> <etc> */
void bot_whom(idx,par)
int idx; char *par;
{
  context;
  /* scrap it */
  putlog(LOG_BOTS,"*","Outdated 'whom' request from %s (ignoring)",
	 dcc[idx].nick);
}

void bot_ping(idx,par)
int idx; char *par;
{
  context;
  tprintf(dcc[idx].sock,"pong\n");
}

void bot_pong(idx,par)
int idx; char *par;
{
  context;
  dcc[idx].u.bot->status&=~STAT_PINGED;
}

/* link <from@bot> <who> <to-whom> */
void bot_link(idx,par)
int idx; char *par;
{
  char *from=TBUF,*bot=TBUF+512,*rfrom=TBUF+41; int i;
  context;
  nsplit(from,par); nsplit(bot,par); from[40]=0;
  if (strcasecmp(bot,origbotname)==0) {
    strcpy(rfrom,from); splitc(NULL,rfrom,':');
    putlog(LOG_CMDS,"*","#%s# link %s",rfrom,par);
    if (botlink(from,-2,par))
      tprintf(dcc[idx].sock,"priv %s %s Attempting link to %s ...\n",
	      origbotname,from,par);
    else tprintf(dcc[idx].sock,"priv %s %s Can't link there.\n",origbotname,
		 from);
  }
  else {
    i=nextbot(bot); if (i>=0)
       tprintf(dcc[i].sock,"link %s %s %s\n",from,bot,par);
  }
}

/* unlink <from@bot> <linking-bot> <undesired-bot> */
void bot_unlink(idx,par)
int idx; char *par;
{
  char *from=TBUF,*bot=TBUF+512,*rfrom=TBUF+41; int i; char *p;
  context;
  nsplit(from,par); nsplit(bot,par); from[40]=0;
  if (strcasecmp(bot,origbotname)==0) {
    strcpy(rfrom,from); splitc(NULL,rfrom,':');
    putlog(LOG_CMDS,"*","#%s# unlink %s",rfrom,par);
    if (botunlink(-2,par)) {
      p=strchr(from,'@'); if (p!=NULL) {
	/* idx will change after unlink -- get new idx */
	i=nextbot(p+1);
	if (i>=0) tprintf(dcc[i].sock,"priv %s %s Unlinked %s.\n",origbotname,
			  from,par);
      }
    }
    else {
      tandout("unlinked %s\n",par);   /* just to clear trash from link lists */
      p=strchr(from,'@'); if (p!=NULL) {
	/* ditto above, about idx */
	i=nextbot(p+1);
	if (i>=0) tprintf(dcc[i].sock,"priv %s %s Can't unlink %s.\n",
			  origbotname,from,par);
      }
    }
  }
  else {
    i=nextbot(bot); if (i>=0)
       tprintf(dcc[i].sock,"unlink %s %s %s\n",from,bot,par);
  }
}

void bot_ufno(idx,par)
int idx; char *par;
{
  context;
  putlog(LOG_BOTS,"*","User file rejected by %s: %s",dcc[idx].nick,par);
  dcc[idx].u.bot->status&=~STAT_OFFERED;
  if (!(dcc[idx].u.bot->status&STAT_GETTING))
    dcc[idx].u.bot->status&=~STAT_SHARE;
}

void bot_ufyes(idx,par)
int idx; char *par;
{
  context;
  putlog(LOG_BOTS,"*","Can't send userfile to %s -- doesn't recognize new format",
	 dcc[idx].nick);
  dcc[idx].u.bot->status&=~STAT_OFFERED;
  if (!(dcc[idx].u.bot->status&STAT_GETTING))
    dcc[idx].u.bot->status&=~STAT_SHARE;
  /* drop the bot */
  tprintf(dcc[idx].sock,"error Please upgrade; I can no longer send a userfile to you.\n");
  tprintf(dcc[idx].sock,"bye\n");
  tandout_but(idx,"unlinked %s\n",dcc[idx].nick);
  tandout_but(idx,"chat %s Disconnected %s (incompatible userfile transfer)\n",
	      origbotname,dcc[idx].nick);
  chatout("*** Disconnected %s (incompatible userfile transfer)\n",
	  dcc[idx].nick);
  rembot(dcc[idx].nick,dcc[idx].nick); unvia(idx,dcc[idx].nick);
  killsock(dcc[idx].sock); lostdcc(idx);
}

void bot_ufyes2(idx,par)
int idx; char *par;
{
  context;
  if (dcc[idx].u.bot->status&STAT_OFFERED) {
    dcc[idx].u.bot->status&=~STAT_OFFERED;
    dcc[idx].u.bot->status|=STAT_SHARE;
    dcc[idx].u.bot->status|=STAT_SENDING;
    start_sending_users(idx);
    putlog(LOG_BOTS,"*","Sending user file to %s",dcc[idx].nick);
  }
}

void bot_userfileq(idx,par)
int idx; char *par;
{
  int ok=1,i;
  context;
  flush_tbuf(dcc[idx].nick);
  if (!share_users) tprintf(dcc[idx].sock,"uf-no Not sharing userfile.\n");
  else if (!passive) tprintf(dcc[idx].sock,"uf-no Aggressive mode active.\n");
  else if (!(get_attr_handle(dcc[idx].nick) & BOT_SHARE))
    tprintf(dcc[idx].sock,"uf-no You are not +s for me.\n");
  else {
    for (i=0; i<dcc_total; i++) if (dcc[i].type==DCC_BOT)
      if ((dcc[i].u.bot->status&STAT_SHARE) &&
	  (dcc[i].u.bot->status&STAT_GETTING)) ok=0;
    if (!ok) tprintf(dcc[idx].sock,"uf-no Already downloading a userfile.\n");
    else {
      tprintf(dcc[idx].sock,"uf-yes2\n");
      /* set stat-getting to avoid race condition (robey 23jun96) */
      dcc[idx].u.bot->status|=STAT_SHARE|STAT_GETTING;
      putlog(LOG_BOTS,"*","Downloading user file from %s",dcc[idx].nick);
    }
  }
}

/* ufsend <ip> <port> <length> */
void bot_ufsend(idx,par)
int idx; char *par;
{
  char *ip=TBUF,*port=TBUF+512; int i;
  context;
  if (!(dcc[idx].u.bot->status & STAT_SHARE)) {
    tprintf(dcc[idx].sock,"error You didn't ask; you just started sending.\n");
    tprintf(dcc[idx].sock,"error Ask before sending the userfile.\n");
    zapfbot(idx);
    return;
  }
  if (dcc_total==MAXDCC) {
    putlog(LOG_MISC,"*","NO MORE DCC CONNECTIONS -- can't grab userfile");
    tprintf(dcc[idx].sock,"error I can't open a DCC to you; I'm full.\n");
    zapfbot(idx);
    return;
  }
  nsplit(ip,par); nsplit(port,par);
  i=dcc_total;
  dcc[i].addr=my_atoul(ip);
  dcc[i].port=atoi(port);
  dcc[i].sock=(-1);
  dcc[i].type=DCC_FORK;
  strcpy(dcc[i].nick,"*users");
  strcpy(dcc[i].host,dcc[idx].nick);
  dcc[i].u.other=NULL; set_fork(i);
  get_xfer_ptr(&(dcc[i].u.fork->u.xfer));
  strcpy(dcc[i].u.fork->u.xfer->filename,".share.users");
  dcc[i].u.fork->u.xfer->dir[0]=0;  /* this dir */
  dcc[i].u.fork->u.xfer->length=atol(par);
  dcc[i].u.fork->u.xfer->sent=0;
  dcc[i].u.fork->u.xfer->sofar=0;
  dcc[i].u.fork->u.xfer->f=fopen(".share.users","w");
  if (dcc[i].u.fork->u.xfer->f==NULL) {
    putlog(LOG_MISC,"*","CAN'T WRITE USERFILE DOWNLOAD FILE!");
    nfree(dcc[i].u.fork->u.xfer);
    nfree(dcc[i].u.fork);
    zapfbot(idx);
    return;
  }
  dcc[idx].u.bot->status|=STAT_GETTING;
  dcc[i].u.fork->type=DCC_SEND;
  dcc_total++;
  /* don't buffer this */
  dcc[i].sock=getsock(SOCK_BINARY);
  if (open_telnet_dcc(dcc[i].sock,ip,port) < 0) {
    putlog(LOG_MISC,"*","Asynchronous connection failed!");
    tprintf(dcc[idx].sock,"error Can't connect to you!\n");
    lostdcc(i);
    zapfbot(idx);
  }
}

void bot_resyncq(idx,par)
int idx; char *par;
{
  context;
  if (!share_users) tprintf(dcc[idx].sock,"resync-no Not sharing userfile.\n");
  else if (!(get_attr_handle(dcc[idx].nick) & BOT_SHARE))
    tprintf(dcc[idx].sock,"resync-no You are not +s for me.\n");
  else if (can_resync(dcc[idx].nick)) {
    tprintf(dcc[idx].sock,"resync!\n");
    dump_resync(dcc[idx].sock,dcc[idx].nick);
    dcc[idx].u.bot->status&=~STAT_OFFERED;
    dcc[idx].u.bot->status|=STAT_SHARE;
    putlog(LOG_BOTS,"*","Resync'd user file with %s",dcc[idx].nick);
  }
  else if (passive) {
    tprintf(dcc[idx].sock,"resync!\n");
    dcc[idx].u.bot->status&=~STAT_OFFERED;
    dcc[idx].u.bot->status|=STAT_SHARE;
    putlog(LOG_BOTS,"*","Resyncing user file from %s",dcc[idx].nick);
  }
  else tprintf(dcc[idx].sock,"resync-no No resync buffer.\n");
}

void bot_resync(idx,par)
int idx; char *par;
{
  context;
  if (dcc[idx].u.bot->status&STAT_OFFERED) {
    if (can_resync(dcc[idx].nick)) {
      dump_resync(dcc[idx].sock,dcc[idx].nick);
      dcc[idx].u.bot->status&=~STAT_OFFERED;
      dcc[idx].u.bot->status|=STAT_SHARE;
      putlog(LOG_BOTS,"*","Resync'd user file with %s",dcc[idx].nick);
    }
  }
}

void bot_resync_no(idx,par)
int idx; char *par;
{
  context;
  putlog(LOG_BOTS,"*","Resync refused by %s: %s",dcc[idx].nick,par);
  flush_tbuf(dcc[idx].nick);
  tprintf(dcc[idx].sock,"userfile?\n");
}

#define CHKSEND if (!(dcc[idx].u.bot->status&STAT_SENDING)) return
#define CHKGET if (!(dcc[idx].u.bot->status&STAT_GETTING)) return
#define CHKSHARE if (!(dcc[idx].u.bot->status&STAT_SHARE)) return

void bot_chpass(idx,par)
int idx; char *par;
{
  char *hand=TBUF;
  context;
  CHKSHARE;
  shareout_but(idx,"chpass %s\n",par);
  noshare=1; nsplit(hand,par); change_pass_by_handle(hand,par); noshare=0;
  putlog(LOG_CMDS,"*","%s: newpass %s",dcc[idx].nick,hand);
}

void bot_chhand(idx,par)
int idx; char *par;
{
  char *hand=TBUF; int i;
  context;
  CHKSHARE;
  shareout_but(idx,"chhand %s\n",par);
  noshare=1; nsplit(hand,par); change_handle(hand,par); 
  notes_change(-1,hand,par);
  noshare=0;
  putlog(LOG_CMDS,"*","%s: handle %s->%s",dcc[idx].nick,hand,par);
  for (i=0; i<dcc_total; i++) if (strcasecmp(hand,dcc[i].nick)==0) {
    if ((dcc[i].type==DCC_CHAT) && (dcc[i].u.chat->channel>=0)) {
      chanout2(dcc[i].u.chat->channel,"Nick change: %s -> %s\n",
	       dcc[i].nick,par);
      tandout("part %s %s\n",origbotname,dcc[i].nick);
      tandout("join %s %s %d %c %s\n",origbotname,par,dcc[i].u.chat->channel,
	      geticon(i),dcc[i].host);
    }
    strcpy(dcc[i].nick,par);
  }
}

void bot_chattr(idx,par)
int idx; char *par;
{
  char *hand=TBUF,*s=TBUF+512; int oatr,natr;
  context;
  CHKSHARE;
  shareout_but(idx,"chattr %s\n",par);
  noshare=1; nsplit(hand,par);
  if (get_attr_handle(hand) & BOT_SHARE) {
    noshare=0;
    return;
  }
  /* don't let tandem flags be altered */
  oatr=(get_attr_handle(hand) & ~BOT_MASK);
  natr=atoi(par);
#ifdef PRIVATE_OWNER
  oatr|=(get_attr_handle(hand) & USER_OWNER);
  natr&=(~USER_OWNER);
#endif
  set_attr_handle(hand,(natr & BOT_MASK)|oatr);
  noshare=0; flags2str(get_attr_handle(hand),s);
  putlog(LOG_CMDS,"*","%s: chattr %s %s",dcc[idx].nick,hand,s);
}

void bot_newuser(idx,par)
int idx; char *par;
{
  char *etc=TBUF,*etc2=TBUF+41,*etc3=TBUF+121;
  context;
  CHKSHARE;
  shareout_but(idx,"newuser %s\n",par);
  noshare=1; nsplit(etc,par); etc[40]=0;
   /* If user already exists, ignore command */
  if(is_user(etc)) {
    noshare=0;
    return;
  }
  nsplit(etc2,par); etc2[80]=0; nsplit(etc3,par);
  userlist=adduser(userlist,etc,etc2,etc3,atoi(par)); noshare=0;
  putlog(LOG_CMDS,"*","%s: newuser %s",dcc[idx].nick,etc);
}

void bot_killuser(idx,par)
int idx; char *par;
{
  context;
  CHKSHARE;
  noshare=1;
    /* If use is a share bot, ignore command */
  if(get_attr_handle(par) & BOT_SHARE) {
    noshare=0;
    return;
  }
  if (deluser(par)) {
  noshare=0;
  shareout_but(idx,"killuser %s\n",par);
  putlog(LOG_CMDS,"*","%s: killuser %s",dcc[idx].nick,par);
 } else noshare=0;
}

void bot_pls_host(idx,par)
int idx; char *par;
{
  char *hand=TBUF;
  context;
  CHKSHARE;
  shareout_but(idx,"+host %s\n",par);
  noshare=1; nsplit(hand,par); addhost_by_handle(hand,par); noshare=0;
  putlog(LOG_CMDS,"*","%s: +host %s %s",dcc[idx].nick,hand,par);
}

void bot_pls_bothost(idx,par)
int idx; char *par;
{
  char *hand=TBUF;
  context;
  CHKSHARE;
  shareout_but(idx,"+bothost %s\n",par);
  noshare=1; nsplit(hand,par);
  /* add bot to userlist if not there */
  if (is_user(hand)) {
    if (!(get_attr_handle(hand) & USER_BOT)) {
      noshare=0; return;   /* ignore */
    }
    addhost_by_handle(hand,par);
  }
  else userlist=adduser(userlist,hand,par,"-",USER_BOT);
  noshare=0;
  putlog(LOG_CMDS,"*","%s: +host %s %s",dcc[idx].nick,hand,par);
}

void bot_mns_host(idx,par)
int idx; char *par;
{
  char *hand=TBUF;
  context;
  CHKSHARE;
  shareout_but(idx,"-host %s\n",par);
  noshare=1; nsplit(hand,par); 
    /* If user is a share bot, ignore command */
  if (get_attr_handle(hand) & BOT_SHARE) {
    noshare=0;
    return;
  }
  delhost_by_handle(hand,par); noshare=0;
  putlog(LOG_CMDS,"*","%s: -host %s %s",dcc[idx].nick,hand,par);
}

void bot_chemail(idx,par)
int idx; char *par;
{
  char *hand=TBUF;
  context;
  CHKSHARE;
  shareout_but(idx,"chemail %s\n",par);
  noshare=1; nsplit(hand,par); set_handle_email(userlist,hand,par); noshare=0;
  putlog(LOG_CMDS,"*","%s: change email %s",dcc[idx].nick,hand);
}

void bot_chdccdir(idx,par)
int idx; char *par;
{
  char *hand=TBUF;
  context;
  CHKSHARE;
  shareout_but(idx,"chdccdir %s\n",par);
  noshare=1; nsplit(hand,par); set_handle_dccdir(userlist,hand,par); noshare=0;
}

void bot_chcomment(idx,par)
int idx; char *par;
{
  char *hand=TBUF;
  context;
  CHKSHARE;
  shareout_but(idx,"chcomment %s\n",par);
  noshare=1; nsplit(hand,par); set_handle_comment(userlist,hand,par);
  noshare=0;
  putlog(LOG_CMDS,"*","%s: change comment %s",dcc[idx].nick,hand);
}

void bot_chinfo(idx,par)
int idx; char *par;
{
  char *hand=TBUF;
  context;
  CHKSHARE;
  shareout_but(idx,"chinfo %s\n",par);
  noshare=1; nsplit(hand,par); set_handle_info(userlist,hand,par); noshare=0;
  putlog(LOG_CMDS,"*","%s: change info %s",dcc[idx].nick,hand);
}

void bot_chaddr(idx,par)
int idx; char *par;
{
  char *hand=TBUF;
  context;
  CHKSHARE;
  shareout_but(idx,"chaddr %s\n",par);
  noshare=1; nsplit(hand,par);
  /* add bot to userlist if not there */
  if (!is_user(hand)) userlist=adduser(userlist,hand,"none","-",USER_BOT);
  if (!(get_attr_handle(hand) & USER_BOT)) {
    noshare=0; return;   /* ignore */
  }
  set_handle_info(userlist,hand,par); noshare=0;
  putlog(LOG_CMDS,"*","%s: change address %s",dcc[idx].nick,hand);
}

void bot_clrxtra(idx,par)
int idx; char *par;
{
  context;
  CHKSHARE;
  shareout_but(idx,"clrxtra %s\n",par);
  noshare=1; set_handle_xtra(userlist,par,""); noshare=0;
}

void bot_addxtra(idx,par)
int idx; char *par;
{
  char *hand=TBUF;
  context;
  CHKSHARE;
  shareout_but(idx,"addxtra %s\n",par);
  noshare=1; nsplit(hand,par); add_handle_xtra(userlist,hand,par); noshare=0;
}

void bot_mns_ban(idx,par)
int idx; char *par;
{
  context;
  CHKSHARE;
  shareout_but(idx,"-ban %s\n",par);
  putlog(LOG_CMDS,"*","%s: cancel ban %s",dcc[idx].nick,par);
  noshare=1; delban(par); noshare=0;
}

void bot_mns_banchan(idx,par)
int idx; char *par;
{
  char *chname=TBUF; struct chanset_t *chan;
  context;
  CHKSHARE;
  shareout_but(idx,"-banchan %s\n",par);
  nsplit(chname,par);
  putlog(LOG_CMDS,"*","%s: cancel ban %s on %s",dcc[idx].nick,par,chname);
  chan=findchan(chname); if (chan==NULL) return;
  noshare=1; u_delban(chan->bans,par); noshare=0;
}

void bot_mns_ignore(idx,par)
int idx; char *par;
{
  context;
  CHKSHARE;
  shareout_but(idx,"-ignore %s\n",par);
  putlog(LOG_CMDS,"*","%s: cancel ignore %s",dcc[idx].nick,par);
  noshare=1; delignore(par); noshare=0;
}

void bot_pls_ban(idx,par)
int idx; char *par;
{
  time_t now=time(NULL),expire_time;
  char *ban=TBUF,*tm=TBUF+512,*from=TBUF+532;
  context;
  CHKSHARE;
  shareout_but(idx,"+ban %s\n",par);
  noshare=1; nsplit(ban,par); nsplit(tm,par); nsplit(from,par);
  if (from[strlen(from)-1]==':') from[strlen(from)-1]=0;
  putlog(LOG_CMDS,"*","%s: ban %s (%s: %s)",dcc[idx].nick,ban,from,par);
  /* new format? */
  if (tm[0]=='+') {
    /* time left */
    strcpy(tm,&tm[1]);
    expire_time=(time_t)atol(tm);
    if (expire_time != 0L) expire_time+=now;
  }
  else {
    expire_time=(time_t)atol(tm);
    if (expire_time != 0L) expire_time=(now-expire_time);
  }
  addban(ban,from,par,expire_time);
  noshare=0;
}

void bot_pls_banchan(idx,par)
int idx; char *par;
{
  time_t now=time(NULL),expire_time; struct chanset_t *chan;
  char *ban=TBUF,*tm=TBUF+512,*chname=TBUF+600,*from=TBUF+700;
  context;
  CHKSHARE;
  shareout_but(idx,"+banchan %s\n",par);
  nsplit(ban,par); nsplit(tm,par); nsplit(chname,par); nsplit(from,par);
  if (from[strlen(from)-1]==':') from[strlen(from)-1]=0;
  putlog(LOG_CMDS,"*","%s: ban %s on %s (%s:%s)",dcc[idx].nick,ban,chname,
	 from,par);
  chan=findchan(chname); if (chan==NULL) return;
  noshare=1;
  /* new format? */
  if (tm[0]=='+') {
    /* time left */
    strcpy(tm,&tm[1]);
    expire_time=(time_t)atol(tm);
    if (expire_time != 0L) expire_time+=now;
  }
  else {
    expire_time=(time_t)atol(tm);
    if (expire_time != 0L) expire_time=(now-expire_time)+(60*ban_time);
  }
  u_addban(chan->bans,ban,from,par,expire_time);
  noshare=0;
}

/* +ignore <host> +<seconds-left> <from> <note> */
void bot_pls_ignore(idx,par)
int idx; char *par;
{
  time_t now=time(NULL),expire_time;
  char *ign=TBUF,*from=TBUF+256,*ts=TBUF+512;
  context;
  CHKSHARE;
  shareout_but(idx,"+ignore %s\n",par);
  noshare=1; nsplit(ign,par);
  if (par[0]=='+') {
    /* new-style */
    nsplit(ts,par); strcpy(ts,&ts[1]);
    if (atoi(ts)==0) expire_time=0L;
    else expire_time=now+atoi(ts);
    nsplit(from,par); from[10]=0; par[65]=0;
  }
  else {
    if (atoi(par)==0) expire_time=0L;
    else expire_time=now+(60*ignore_time)-atoi(par);
    strcpy(from,dcc[idx].nick); strcpy(par,"-");
  }
  putlog(LOG_CMDS,"*","%s: ignore %s (%s: %s)",dcc[idx].nick,ign,from,par);
  addignore(ign,from,par,expire_time);
  noshare=0;
}

void bot_nlinked(idx,par)
int idx; char *par;
{
  char *newbot=TBUF,*next=TBUF+512,*p; int reject=0,bogus=0,atr,i;
  context;
  nsplit(newbot,par); nsplit(next,par);
  if (strlen(newbot)>9) newbot[9]=0;
  if (strlen(next)>9) next[9]=0;
  if (!next[0]) {
    putlog(LOG_BOTS,"*","Invalid eggnet protocol from %s (zapfing)",
	   dcc[idx].nick);
    chatout("*** Disconnected %s (invalid bot)\n",dcc[idx].nick);
    tandout_but(idx,"chat %s Disconnected %s (invalid bot)\n",origbotname,
		dcc[idx].nick);
    tprintf(dcc[idx].sock,"error invalid eggnet protocol for 'nlinked'\n");
    reject=1;
  }
  else if ((in_chain(newbot)) || (strcasecmp(newbot,origbotname)==0)) {
    /* loop! */
    putlog(LOG_BOTS,"*","Detected loop: disconnecting %s (mutual: %s)",
	   dcc[idx].nick,newbot);
    chatout("*** Loop (%s): disconnected %s\n",newbot,dcc[idx].nick);
    tandout_but(idx,"chat %s Loop (%s): disconnected %s\n",origbotname,newbot,
		dcc[idx].nick);
    tprintf(dcc[idx].sock,"error Loop (%s)\n",newbot);
    reject=1;
  }
  if (!reject) {
    for (p=newbot; *p; p++) if ((*p<32) || (*p==127)) bogus=1;
    i=nextbot(next); if (i!=idx) bogus=1;
  }
  if (bogus) {
    putlog(LOG_BOTS,"*","Bogus link notice from %s!  (%s -> %s)",dcc[idx].nick,
	next,newbot);
    chatout("*** Bogus link notice: disconnecting %s\n",dcc[idx].nick);
    tandout_but(idx,"chat %s Bogus link notice: disconnecting %s\n",
		origbotname,dcc[idx].nick);
    tprintf(dcc[idx].sock,"error Bogus link notice (%s -> %s)\n",next,newbot);
    reject=1;
  }
  atr=get_attr_handle(dcc[idx].nick);
  if (atr & BOT_LEAF) {
    putlog(LOG_BOTS,"*","Disconnecting leaf %s  (linked to %s)",dcc[idx].nick,
	   newbot);
    chatout("*** Illegal link by leaf %s (to %s): disconnecting\n",
	    dcc[idx].nick,newbot);
    tandout_but(idx,"chat %s Illegal link by leaf %s (to %s): disconnecting\n",
		origbotname,dcc[idx].nick,newbot);
    tprintf(dcc[idx].sock,"error You are supposed to be a leaf!\n");
    reject=1;
  }
  if (reject) {
    tandout_but(idx,"unlinked %s\n",dcc[idx].nick);
    cancel_user_xfer(idx);
    rembot(dcc[idx].nick,dcc[idx].nick); unvia(idx,dcc[idx].nick);
    tprintf(dcc[idx].sock,"bye\n");
    killsock(dcc[idx].sock); lostdcc(idx); return;
  }
  addbot(newbot,dcc[idx].nick);
  got_tand_next(newbot,next);
  tandout_but(idx,"nlinked %s %s %s\n",newbot,next,par);
  check_tcl_link(newbot,next);
  if (get_attr_handle(newbot) & BOT_REJECT) {
    tprintf(dcc[idx].sock,"reject %s %s\n",origbotname,newbot);
    putlog(LOG_BOTS,"*","Rejecting bot %s from %s",newbot,dcc[idx].nick);
  }
}

void bot_linked(idx,par)
int idx; char *par;
{
  context;
  putlog(LOG_BOTS,"*","Older bot detected (unsupported)");
  chatout("*** Disconnected %s (outdated)\n",dcc[idx].nick);
  tandout_but(idx,"chat %s Disconnected %s (outdated)\n",origbotname,
	      dcc[idx].nick);
  tandout_but(idx,"unlinked %s\n",dcc[idx].nick);
  cancel_user_xfer(idx);
  rembot(dcc[idx].nick,dcc[idx].nick); unvia(idx,dcc[idx].nick);
  killsock(dcc[idx].sock); lostdcc(idx);
}

void bot_unlinked(idx,par)
int idx; char *par;
{
  int i;
  context;
  i=nextbot(par); if (i!=idx && i!=-1) {
    fake_alert(idx);
    return;
  }
  rembot(par,dcc[idx].nick);
  unvia(idx,par);
  tandout_but(idx,"unlinked %s\n",par);
}

void bot_trace(idx,par)
int idx; char *par;
{
  char *from=TBUF,*dest=TBUF+512; int i;
  context;
  /* trace <from@bot> <dest> <chain:chain..> */
  nsplit(from,par); nsplit(dest,par);
  if (strcasecmp(dest,origbotname)==0) {
    tprintf(dcc[idx].sock,"traced %s %s:%s\n",from,par,origbotname);
  }
  else {
    i=nextbot(dest); if (i>=0)
       tprintf(dcc[i].sock,"trace %s %s %s:%s\n",from,dest,par,origbotname);
  }
}

void bot_traced(idx,par)
int idx; char *par;
{
  char *to=TBUF,*ss=TBUF+512,*p; int i,sock;
  context;
  /* traced <to@bot> <chain:chain..> */
  nsplit(to,par); p=strchr(to,'@');
  if (p==NULL) p=to; else { *p=0; p++; }
  if (strcasecmp(p,origbotname)==0) {
    splitc(ss,to,':');
    if (ss[0]) sock=atoi(ss); else sock=(-1);
    for (i=0; i<dcc_total; i++)
      if ((dcc[i].type==DCC_CHAT) && (strcasecmp(dcc[i].nick,to)==0) &&
	  ((sock==(-1)) || (sock==dcc[i].sock)))
	dprintf(i,"Trace result -> %s\n",par);
  }
  else {
    i=nextbot(p); if (i>=0)
       tprintf(dcc[i].sock,"traced %s@%s %s\n",to,p,par);
  }
}

/* reject <from> <bot> */
void bot_reject(idx,par)
int idx; char *par;
{
  char *from=TBUF,*who=TBUF+81,*destbot=TBUF+41,*p; int i;
  context;
  nsplit(from,par); from[40]=0;
  p=strchr(from,'@'); if (p==NULL) p=from; else p++;
  i=nextbot(p); if (i!=idx) {
    fake_alert(idx);
    return;
  }
  if (strchr(par,'@')==NULL) {
    /* rejecting a bot */
    i=nextbot(par); if (i<0) {
      tprintf(dcc[idx].sock,"priv %s %s Can't reject %s (doesn't exist)\n",
	      origbotname,from,par);
    }
    else if (strcasecmp(dcc[i].nick,par)==0) {
      /* i'm the connection to the rejected bot */
      putlog(LOG_BOTS,"*","%s rejected %s",from,dcc[i].nick);
      tprintf(dcc[i].sock,"bye\n");
      tandout_but(i,"unlinked %s\n",dcc[i].nick);
      tandout_but(i,"chat %s Disconnected %s (rejected by %s)\n",origbotname,
		  dcc[i].nick,from);
      chatout("*** Disconnected %s (rejected by %s)\n",dcc[i].nick,from);
      cancel_user_xfer(i);
      rembot(dcc[i].nick,dcc[i].nick); unvia(i,dcc[i].nick);
      killsock(dcc[i].sock); lostdcc(i);
    }
    else {
      if (i<0) tandout_but(idx,"reject %s %s\n",from,par);
      else tprintf(dcc[i].sock,"reject %s %s\n",from,par);
    }
  }
  else {    /* rejecting user */
    nsplit(destbot,par); destbot[40]=0;
    splitc(who,destbot,'@');
    if (strcasecmp(destbot,origbotname)==0) {
      /* kick someone here! */
      int ok=0;
#ifdef SHAREBOT_BOOTS
      p=strchr(from,'@'); if (p==NULL) p=from; else p++;
      if (!(get_attr_handle(p) & BOT_SHARE)) {
	add_note(from,origbotname,"Remote boots are not allowed.",-1,0);
	return;
      }
#endif
#ifdef REMOTE_BOOTS
      for (i=0; (i<dcc_total) && (!ok); i++) 
	if ((strcasecmp(who,dcc[i].nick)==0) && (dcc[i].type==DCC_CHAT)) {
#ifdef OWNER
	  int atr=get_attr_handle(dcc[i].nick);
	  if (atr&USER_OWNER) {
	    add_note(from,origbotname,"Can't boot the bot owner.",-1,0);
	    return;
	  }
#endif
	  do_boot(i,from,par); ok=1;
 	  putlog(LOG_CMDS,"*","#%s# boot %s (%s)",from,dcc[i].nick,par);
	}
#else
      tprintf(dcc[idx].sock,"priv %s %s Remote boots are not allowed.\n",
	      origbotname,from);
      ok=ok;
#endif
    }
    else {
      i=nextbot(destbot);
      if (i<0) tandout_but(idx,"reject %s %s@%s %s\n",from,who,destbot,par);
      else tprintf(dcc[i].sock,"reject %s %s@%s %s\n",from,who,destbot,par);
    }
  }
}

void bot_thisbot(idx,par)
int idx; char *par;
{
  context;
  if (strcasecmp(par,dcc[idx].nick)!=0) {
    putlog(LOG_BOTS,"*","Wrong bot: wanted %s, got %s",dcc[idx].nick,par);
    tprintf(dcc[idx].sock,"bye\n");
    tandout_but(idx,"unlinked %s\n",dcc[idx].nick);
    tandout_but(idx,"chat %s Disconnected %s (imposter)\n",origbotname,
		dcc[idx].nick);
    chatout("*** Disconnected %s (imposter)\n",dcc[idx].nick);
    rembot(dcc[idx].nick,dcc[idx].nick); unvia(idx,dcc[idx].nick);
    killsock(dcc[idx].sock); lostdcc(idx); return;
  }
  if (get_attr_handle(par) & BOT_REJECT) {
    putlog(LOG_BOTS,"*","Rejecting bot %s",par);
    tprintf(dcc[idx].sock,"bye\n");
    tandout_but(idx,"unlinked %s\n",dcc[idx].nick);
    tandout_but(idx,"chat %s Disconnected %s (rejected)\n",origbotname,
		dcc[idx].nick);
    chatout("*** Disconnected %s (rejected)\n",dcc[idx].nick);
    rembot(dcc[idx].nick,dcc[idx].nick); unvia(idx,dcc[idx].nick);
    killsock(dcc[idx].sock); lostdcc(idx); return;
  }
  if (get_attr_handle(par) & BOT_LEAF)
    dcc[idx].u.bot->status|=STAT_LEAF;
  /* set capitalization the way they want it */
  noshare=1; change_handle(dcc[idx].nick,par); noshare=0;
  strcpy(dcc[idx].nick,par);
}

void bot_handshake(idx,par)
int idx; char *par;
{
  context;
  change_pass_by_handle(dcc[idx].nick,par);
  if ((dcc[idx].u.bot->status&STAT_SHARE) &&
      (!(dcc[idx].u.bot->status&STAT_GETTING)) &&
      (!(dcc[idx].u.bot->status&STAT_SENDING))) {
    noshare=1; change_pass_by_handle(origbotname,par); noshare=0;
  }
}

void bot_trying(idx,par)
int idx; char *par;
{
  context;
  tandout_but(idx,"trying %s\n",par);
  /* currently ignore */
}

void bot_end_trying(idx,par)
int idx; char *par;
{
  context;
  tandout_but(idx,"*trying %s\n",par);
  /* currently ignore */
}

/* used to send a direct msg from Tcl on one bot to Tcl on another */
/* zapf <frombot> <tobot> <code [param]> */
void bot_zapf(idx,par)
int idx; char *par;
{
  char *from=TBUF,*to=TBUF+512; int i;
  context;
  nsplit(from,par); nsplit(to,par);
  i=nextbot(from); if (i!=idx) {
    fake_alert(idx);
    return;
  }
  if (strcasecmp(to,origbotname)==0) {
    /* for me! */
    char opcode[512];
    nsplit(opcode,par);
    check_tcl_tand(from,opcode,par);
    return;
  }
  i=nextbot(to); if (i>=0)
     tprintf(dcc[i].sock,"zapf %s %s %s\n",from,to,par);
}

/* used to send a global msg from Tcl on one bot to every other bot */
/* zapf-broad <frombot> <code [param]> */
void bot_zapfbroad(idx,par)
int idx; char *par;
{
  char *from=TBUF,*opcode=TBUF+512; int i;
  context;
  nsplit(from,par);
  nsplit(opcode,par);
  i=nextbot(from); if (i!=idx) {
    fake_alert(idx); return;
  }
  check_tcl_tand(from,opcode,par);
  tandout_but(idx,"zapf-broad %s %s %s\n",from,opcode,par);
}

/* show motd to someone */
void bot_motd(idx,par)
int idx; char *par;
{
  FILE *vv; char *s=TBUF,*who=TBUF+512,*p; int i;
  context;
  nsplit(who,par);
  if ((!par[0]) || (strcasecmp(par,origbotname)==0)) {
    p=strchr(who,':'); if (p==NULL) p=who; else p++;
    putlog(LOG_CMDS,"*","#%s# motd",p);
    vv=fopen(motdfile,"r"); if (vv!=NULL) {
      tprintf(dcc[idx].sock,"priv %s %s --- MOTD file:\n",origbotname,who);
      while (!feof(vv)) {
	fgets(s,120,vv);
	if (!feof(vv)) {
	  if (s[strlen(s)-1]=='\n') s[strlen(s)-1]=0;
	  if (!s[0]) strcpy(s," ");
	  help_subst(s,who,0,0);
	  if (s[0]) tprintf(dcc[idx].sock,"priv %s %s %s\n",origbotname,who,s);
	}
      }
      fclose(vv);
    }
    else tprintf(dcc[idx].sock,"priv %s %s No MOTD file. :(\n",origbotname,
		 who);
  }
  else {
    /* pass it on */
    i=nextbot(par); if (i>=0) tprintf(dcc[i].sock,"motd %s %s\n",who,par);
  }
}

/* assoc <chan#> <name> */
void bot_assoc(idx,par)
int idx; char *par;
{
  char *s=TBUF,*s1;
  context;
  nsplit(s,par);
  if ((atoi(s)<=0) || (atoi(s)>99999)) return;
  if (par[0]=='0') {
    s1=get_assoc_name(atoi(s)); if (s1!=NULL) {
      if (s1[0]==0) kill_assoc(atoi(s));
      else add_assoc("",atoi(s));
    }
    tandout_but(idx,"assoc %s 0\n",s);
  }
  else if (get_assoc(par)!=atoi(s)) {
    /* new one i didn't know about -- pass it on */
    s1=get_assoc_name(atoi(s)); if (s1!=NULL) {
      if (s1[0]==0) {
	/* recently killed assoc */
	tandout("assoc %s 0\n",s);
	kill_assoc(atoi(s));
	return;
      }
    }
    add_assoc(par,atoi(s));
    tandout_but(idx,"assoc %s %s\n",s,par);
  }
}

/* filereject <bot:filepath> <sock:nick@bot> <reason...> */
void bot_filereject(idx,par)
int idx; char *par;
{
  char *path=TBUF,*tobot=TBUF+512,*to=TBUF+542,*p; int i;
  context;
  nsplit(path,par); nsplit(tobot,par); splitc(to,tobot,'@');
  if (strcasecmp(tobot,origbotname)==0) {  /* for me! */
    p=strchr(to,':'); if (p!=NULL) {
      *p=0;
      for (i=0; i<dcc_total; i++) {
	if (dcc[i].sock==atoi(to))
	  dprintf(i,"FILE TRANSFER REJECTED (%s): %s\n",path,par);
      }
      *p=':';
    }
    /* no ':'? malformed */
    putlog(LOG_FILES,"*","%s rejected: %s",path,par);
  }
  else {   /* pass it on */
    i=nextbot(tobot); if (i>=0)
       tprintf(dcc[i].sock,"filereject %s %s@%s %s\n",path,to,tobot,par);
  }
}

/* filreq <sock:nick@bot> <bot:file> */
void bot_filereq(idx,par)
int idx; char *par;
{
  char *from=TBUF,*tobot=TBUF+41; int i;
  context;
  nsplit(from,par); splitc(tobot,par,':');
  if (strcasecmp(tobot,origbotname)==0) {  /* for me! */
    /* process this */
    remote_filereq(idx,from,par);
  }
  else {   /* pass it on */
    i=nextbot(tobot); if (i>=0)
       tprintf(dcc[i].sock,"filereq %s %s:%s\n",from,tobot,par);
  }
}

/* filesend <bot:path> <sock:nick@bot> <IP#> <port> <size> */
void bot_filesend(idx,par)
int idx; char *par;
{
  char *botpath=TBUF,*nick=TBUF+512,*tobot=TBUF+552,*sock=TBUF+692; int i;
  char *nfn;
  context;
  nsplit(botpath,par); nsplit(tobot,par); splitc(nick,tobot,'@');
  splitc(sock,nick,':');
  if (strcasecmp(tobot,origbotname)==0) {  /* for me! */
    nfn=strrchr(botpath,'/');
    if (nfn==NULL) {
      nfn=strrchr(botpath,':');
      if (nfn==NULL) nfn=botpath;    /* that's odd. */
      else nfn++;
    }
    else nfn++;
    /* send it to 'nick' as if it's from me */
    mprintf(serv,"PRIVMSG %s :\001DCC SEND %s %s\001\n",nick,nfn,par);
  }
  else {
    i=nextbot(tobot); if (i>=0)
       tprintf(dcc[i].sock,"filesend %s %s:%s@%s %s\n",botpath,sock,nick,
		 tobot,par);
  }
}

void bot_error(idx,par)
int idx; char *par;
{
  context;
  putlog(LOG_MISC|LOG_BOTS,"*","%s: %s",dcc[idx].nick,par);
}

/* join <bot> <nick> <chan> <flag> <from> */
void bot_join(idx,par)
int idx; char *par;
{
  char *bot=TBUF,*nick=TBUF+20,*x=TBUF+40,*y=TBUF+60,*from=TBUF+70; int i;
  context;
  nsplit(bot,par); bot[9]=0;
  nsplit(nick,par); nick[9]=0;
  nsplit(x,par); x[10]=0;
  nsplit(y,par); /* only first char matters */
  if (!y[0]) y[0]='-';
  strncpy(from,par,40); from[40]=0;
  i=nextbot(bot); if (i!=idx) return;  /* garbage sent by 1.0g bot */
  addparty(bot,nick,atoi(x),y[0],from);
  tandout_but(idx,"join %s %s %d %s %s\n",bot,nick,atoi(x),y,from);
}

/* part <bot> <nick> [etc..] */
void bot_part(idx,par)
int idx; char *par;
{
  char *bot=TBUF,*nick=TBUF+20;
  context;
  nsplit(bot,par); bot[9]=0;
  nsplit(nick,par); nick[9]=0;
  remparty(bot,nick);
  tandout_but(idx,"part %s %s %s\n",bot,nick,par);
}

/* away <bot> <nick> <message> */
void bot_away(idx,par)
int idx; char *par;
{
  char *bot=TBUF,*nick=TBUF+20;
  context;
  nsplit(bot,par); bot[9]=0;
  nsplit(nick,par); nick[9]=0;
  partystat(bot,nick,PLSTAT_AWAY,0);
  partyaway(bot,nick,par);
  tandout_but(idx,"away %s %s %s\n",bot,nick,par);
}

/* unaway <bot> <nick> */
void bot_unaway(idx,par)
int idx; char *par;
{
  char *bot=TBUF,*nick=TBUF+20;
  context;
  nsplit(bot,par); bot[9]=0;
  nsplit(nick,par); nick[9]=0;
  partystat(bot,nick,0,PLSTAT_AWAY);
  tandout_but(idx,"unaway %s %s %s\n",bot,nick,par);
}

/* (a courtesy info to help during connect bursts) */
/* idle <bot> <nick> <#secs> */
void bot_idle(idx,par)
int idx; char *par;
{
  char *bot=TBUF,*nick=TBUF+20;
  context;
  nsplit(bot,par); bot[9]=0;
  nsplit(nick,par); nick[9]=0;
  partysetidle(bot,nick,atoi(par));
  tandout_but(idx,"idle %s %s %s\n",bot,nick,par);
}
