/*
 * botmsg.c -- handles:
 *   formatting of messages to be sent on the botnet
 *   sending differnet messages to different versioned bots
 *
 * by Darrin Smith (beldin@light.iinet.net.au)
 *
 * $Id: botmsg.c,v 1.35 2006-03-28 02:35:49 wcc Exp $
 */
/*
 * Copyright (C) 1997 Robey Pointer
 * Copyright (C) 1999 - 2006 Eggheads Development Team
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

#include "main.h"
#include "tandem.h"

extern struct dcc_t *dcc;
extern int dcc_total, tands;
extern char botnetnick[];
extern party_t *party;
extern Tcl_Interp *interp;
extern struct userrec *userlist;

static char OBUF[1024];


#ifndef NO_OLD_BOTNET
/* Ditto for tandem bots
 */
void tandout_but EGG_VARARGS_DEF(int, arg1)
{
  int i, x, len;
  char *format;
  char s[601];
  va_list va;

  x = EGG_VARARGS_START(int, arg1, va);
  format = va_arg(va, char *);

  egg_vsnprintf(s, 511, format, va);
  va_end(va);
  s[sizeof(s) - 1] = 0;

  len = strlen(s);

  for (i = 0; i < dcc_total; i++)
    if ((dcc[i].type == &DCC_BOT) && (i != x) && (b_numver(i) < NEAT_BOTNET))
      tputs(dcc[i].sock, s, len);
}
#endif

/* Thank you ircu :) */
static char tobase64array[64] = {
  'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
  'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
  'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
  'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
  '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
  '[', ']'
};

char *int_to_base64(unsigned int val)
{
  static char buf_base64[12];
  int i = 11;

  buf_base64[11] = 0;
  if (!val) {
    buf_base64[10] = 'A';
    return buf_base64 + 10;
  }
  while (val) {
    i--;
    buf_base64[i] = tobase64array[val & 0x3f];
    val = val >> 6;
  }
  return buf_base64 + i;
}

char *int_to_base10(int val)
{
  static char buf_base10[17];
  int p = 0;
  int i = 16;

  buf_base10[16] = 0;
  if (!val) {
    buf_base10[15] = '0';
    return buf_base10 + 15;
  }
  if (val < 0) {
    p = 1;
    val *= -1;
  }
  while (val) {
    i--;
    buf_base10[i] = '0' + (val % 10);
    val /= 10;
  }
  if (p) {
    i--;
    buf_base10[i] = '-';
  }
  return buf_base10 + i;
}

char *unsigned_int_to_base10(unsigned int val)
{
  static char buf_base10[16];
  int i = 15;

  buf_base10[15] = 0;
  if (!val) {
    buf_base10[14] = '0';
    return buf_base10 + 14;
  }
  while (val) {
    i--;
    buf_base10[i] = '0' + (val % 10);
    val /= 10;
  }
  return buf_base10 + i;
}

int simple_sprintf EGG_VARARGS_DEF(char *, arg1)
{
  char *buf, *format, *s;
  int c = 0, i;
  va_list va;

  buf = EGG_VARARGS_START(char *, arg1, va);
  format = va_arg(va, char *);

  while (*format && c < 1023) {
    if (*format == '%') {
      format++;
      switch (*format) {
      case 's':
        s = va_arg(va, char *);

        break;
      case 'd':
      case 'i':
        i = va_arg(va, int);

        s = int_to_base10(i);
        break;
      case 'D':
        i = va_arg(va, int);

        s = int_to_base64((unsigned int) i);
        break;
      case 'u':
        i = va_arg(va, unsigned int);

        s = unsigned_int_to_base10(i);
        break;
      case '%':
        buf[c++] = *format++;
        continue;
      case 'c':
        buf[c++] = (char) va_arg(va, int);

        format++;
        continue;
      default:
        continue;
      }
      if (s)
        while (*s && c < 1023)
          buf[c++] = *s++;
      format++;
    } else
      buf[c++] = *format++;
  }
  va_end(va);
  buf[c] = 0;
  return c;
}

/* Ditto for tandem bots
 */
void send_tand_but(int x, char *buf, int len)
{
  int i, iso = 0;

  if (len < 0) {
    len = -len;
    iso = 1;
  }
  for (i = 0; i < dcc_total; i++)
    if ((dcc[i].type == &DCC_BOT) && (i != x) &&
        (b_numver(i) >= NEAT_BOTNET) &&
        (!iso || !(bot_flags(dcc[i].user) & BOT_ISOLATE)))
      tputs(dcc[i].sock, buf, len);
}

void botnet_send_bye()
{
  if (tands > 0) {
    send_tand_but(-1, "bye\n", 4);
#ifndef NO_OLD_BOTNET
    tandout_but(-1, "bye\n");
#endif
  }
}

void botnet_send_chan(int idx, char *botnick, char *user, int chan, char *data)
{
  int i;

  if ((tands > 0) && (chan < GLOBAL_CHANS)) {
    if (user) {
      i = simple_sprintf(OBUF, "c %s@%s %D %s\n", user, botnick, chan, data);
    } else {
      i = simple_sprintf(OBUF, "c %s %D %s\n", botnick, chan, data);
    }
    send_tand_but(idx, OBUF, -i);
#ifndef NO_OLD_BOTNET
    tandout_but(idx, "chan %s%s%s %d %s\n", user ? user : "",
                user ? "@" : "", botnick, chan, data);
#endif
  }
}

void botnet_send_act(int idx, char *botnick, char *user, int chan, char *data)
{
  int i;

  if ((tands > 0) && (chan < GLOBAL_CHANS)) {
    if (user) {
      i = simple_sprintf(OBUF, "a %s@%s %D %s\n", user, botnick, chan, data);
    } else {
      i = simple_sprintf(OBUF, "a %s %D %s\n", botnick, chan, data);
    }
    send_tand_but(idx, OBUF, -i);
#ifndef NO_OLD_BOTNET
    tandout_but(idx, "actchan %s%s%s %d %s\n", user ? user : "",
                user ? "@" : "", botnick, chan, data);
#endif
  }
}

void botnet_send_chat(int idx, char *botnick, char *data)
{
  int i;

  if (tands > 0) {
    i = simple_sprintf(OBUF, "ct %s %s\n", botnick, data);
    send_tand_but(idx, OBUF, -i);
#ifndef NO_OLD_BOTNET
    tandout_but(idx, "chat %s %s\n", botnick, data);
#endif
  }
}

void botnet_send_ping(int idx)
{
#ifndef NO_OLD_BOTNET
  if (b_numver(idx) < NEAT_BOTNET)
    tputs(dcc[idx].sock, "ping\n", 5);
  else
#endif
    tputs(dcc[idx].sock, "pi\n", 3);
}

void botnet_send_pong(int idx)
{
#ifndef NO_OLD_BOTNET
  if (b_numver(idx) < NEAT_BOTNET)
    tputs(dcc[idx].sock, "pong\n", 5);
  else
#endif
    tputs(dcc[idx].sock, "po\n", 3);
}

void botnet_send_priv EGG_VARARGS_DEF(int, arg1)
{
  int idx, l;
  char *from, *to, *tobot, *format;
  char tbuf[1024];
  va_list va;

  idx = EGG_VARARGS_START(int, arg1, va);
  from = va_arg(va, char *);
  to = va_arg(va, char *);
  tobot = va_arg(va, char *);
  format = va_arg(va, char *);

  egg_vsnprintf(tbuf, 450, format, va);
  va_end(va);
  tbuf[sizeof(tbuf) - 1] = 0;

  if (tobot) {
#ifndef NO_OLD_BOTNET
    if (b_numver(idx) < NEAT_BOTNET)
      l = simple_sprintf(OBUF, "priv %s %s@%s %s\n", from, to, tobot, tbuf);
    else
#endif
      l = simple_sprintf(OBUF, "p %s %s@%s %s\n", from, to, tobot, tbuf);
  } else {
#ifndef NO_OLD_BOTNET
    if (b_numver(idx) < NEAT_BOTNET)
      l = simple_sprintf(OBUF, "priv %s %s %s\n", from, to, tbuf);
    else
#endif
      l = simple_sprintf(OBUF, "p %s %s %s\n", from, to, tbuf);
  }
  tputs(dcc[idx].sock, OBUF, l);
}

void botnet_send_who(int idx, char *from, char *to, int chan)
{
  int l;

#ifndef NO_OLD_BOTNET
  if (b_numver(idx) < NEAT_BOTNET)
    l = simple_sprintf(OBUF, "who %s %s %d\n", from, to, chan);
  else
#endif
    l = simple_sprintf(OBUF, "w %s %s %D\n", from, to, chan);
  tputs(dcc[idx].sock, OBUF, l);
}

void botnet_send_infoq(int idx, char *par)
{
  int i = simple_sprintf(OBUF, "i? %s\n", par);

  send_tand_but(idx, OBUF, i);
#ifndef NO_OLD_BOTNET
  tandout_but(idx, "info? %s\n", par);
#endif
}

void botnet_send_unlink(int idx, char *who, char *via, char *bot, char *reason)
{
  int l;

#ifndef NO_OLD_BOTNET
  if (b_numver(idx) < NEAT_BOTNET)
    l = simple_sprintf(OBUF, "unlink %s %s %s %s\n", who, via, bot, reason);
  else
#endif
    l = simple_sprintf(OBUF, "ul %s %s %s %s\n", who, via, bot, reason);
  tputs(dcc[idx].sock, OBUF, l);
}

void botnet_send_link(int idx, char *who, char *via, char *bot)
{
  int l;

#ifndef NO_OLD_BOTNET
  if (b_numver(idx) < NEAT_BOTNET)
    l = simple_sprintf(OBUF, "link %s %s %s\n", who, via, bot);
  else
#endif
    l = simple_sprintf(OBUF, "l %s %s %s\n", who, via, bot);
  tputs(dcc[idx].sock, OBUF, l);
}

void botnet_send_unlinked(int idx, char *bot, char *args)
{
  int l;

  if (tands > 0) {
    l = simple_sprintf(OBUF, "un %s %s\n", bot, args ? args : "");
    send_tand_but(idx, OBUF, l);
#ifndef NO_OLD_BOTNET
    if ((idx >= 0) && (b_numver(idx) >= NEAT_BOTNET) && args && args[0])
      tandout_but(idx, "chat %s %s\n", lastbot(bot), args);
    tandout_but(idx, "unlinked %s\n", bot);
#endif
  }
}

void botnet_send_nlinked(int idx, char *bot, char *next, char flag, int vernum)
{
  int l;

  if (tands > 0) {
    l = simple_sprintf(OBUF, "n %s %s %c%D\n", bot, next, flag, vernum);
    send_tand_but(idx, OBUF, l);
#ifndef NO_OLD_BOTNET
    if (flag == '!') {
      flag = '-';
      tandout_but(idx, "chat %s %s %s\n", next, NET_LINKEDTO, bot);
    }
    tandout_but(idx, "nlinked %s %s %c%d\n", bot, next, flag, vernum);
#endif
  }
}

void botnet_send_traced(int idx, char *bot, char *buf)
{
  int l;

#ifndef NO_OLD_BOTNET
  if (b_numver(idx) < NEAT_BOTNET)
    l = simple_sprintf(OBUF, "traced %s %s\n", bot, buf);
  else
#endif
    l = simple_sprintf(OBUF, "td %s %s\n", bot, buf);
  tputs(dcc[idx].sock, OBUF, l);
}

void botnet_send_trace(int idx, char *to, char *from, char *buf)
{
  int l;

#ifndef NO_OLD_BOTNET
  if (b_numver(idx) < NEAT_BOTNET)
    l = simple_sprintf(OBUF, "trace %s %s %s:%s\n", to, from, buf, botnetnick);
  else
#endif
    l = simple_sprintf(OBUF, "t %s %s %s:%s\n", to, from, buf, botnetnick);
  tputs(dcc[idx].sock, OBUF, l);
}

void botnet_send_update(int idx, tand_t *ptr)
{
  int l;

  if (tands > 0) {
    l = simple_sprintf(OBUF, "u %s %c%D\n", ptr->bot, ptr->share, ptr->ver);
    send_tand_but(idx, OBUF, l);
#ifndef NO_OLD_BOTNET
    tandout_but(idx, "update %s %c%d\n", ptr->bot, ptr->share, ptr->ver);
#endif
  }
}

void botnet_send_reject(int idx, char *fromp, char *frombot, char *top,
                        char *tobot, char *reason)
{
  int l;
  char to[NOTENAMELEN + 1], from[NOTENAMELEN + 1];

  if (!(bot_flags(dcc[idx].user) & BOT_ISOLATE)) {
    if (tobot) {
      simple_sprintf(to, "%s@%s", top, tobot);
      top = to;
    }
    if (frombot) {
      simple_sprintf(from, "%s@%s", fromp, frombot);
      fromp = from;
    }
    if (!reason)
      reason = "";
#ifndef NO_OLD_BOTNET
    if (b_numver(idx) < NEAT_BOTNET)
      l = simple_sprintf(OBUF, "reject %s %s %s\n", fromp, top, reason);
    else
#endif
      l = simple_sprintf(OBUF, "r %s %s %s\n", fromp, top, reason);
    tputs(dcc[idx].sock, OBUF, l);
  }
}

void botnet_send_zapf(int idx, char *a, char *b, char *c)
{
  int l;

#ifndef NO_OLD_BOTNET
  if (b_numver(idx) < NEAT_BOTNET)
    l = simple_sprintf(OBUF, "zapf %s %s %s\n", a, b, c);
  else
#endif
    l = simple_sprintf(OBUF, "z %s %s %s\n", a, b, c);
  tputs(dcc[idx].sock, OBUF, l);
}

void botnet_send_zapf_broad(int idx, char *a, char *b, char *c)
{
  int l;

  if (tands > 0) {
    l = simple_sprintf(OBUF, "zb %s %s%s%s\n", a, b ? b : "", b ? " " : "", c);
    send_tand_but(idx, OBUF, l);
#ifndef NO_OLD_BOTNET
    tandout_but(idx, "zapf-broad %s\n", OBUF + 3);
#endif
  }
}

void botnet_send_motd(int idx, char *from, char *to)
{
  int l;

#ifndef NO_OLD_BOTNET
  if (b_numver(idx) < NEAT_BOTNET)
    l = simple_sprintf(OBUF, "motd %s %s\n", from, to);
  else
#endif
    l = simple_sprintf(OBUF, "m %s %s\n", from, to);
  tputs(dcc[idx].sock, OBUF, l);
}

void botnet_send_filereject(int idx, char *path, char *from, char *reason)
{
  int l;

#ifndef NO_OLD_BOTNET
  if (b_numver(idx) < NEAT_BOTNET)
    l = simple_sprintf(OBUF, "filereject %s %s %s\n", path, from, reason);
  else
#endif
    l = simple_sprintf(OBUF, "f! %s %s %s\n", path, from, reason);
  tputs(dcc[idx].sock, OBUF, l);
}

void botnet_send_filesend(int idx, char *path, char *from, char *data)
{
  int l;

#ifndef NO_OLD_BOTNET
  if (b_numver(idx) < NEAT_BOTNET)
    l = simple_sprintf(OBUF, "filesend %s %s %s\n", path, from, data);
  else
#endif
    l = simple_sprintf(OBUF, "fs %s %s %s\n", path, from, data);
  tputs(dcc[idx].sock, OBUF, l);
}

void botnet_send_filereq(int idx, char *from, char *bot, char *path)
{
  int l;

#ifndef NO_OLD_BOTNET
  if (b_numver(idx) < NEAT_BOTNET)
    l = simple_sprintf(OBUF, "filereq %s %s:%s\n", from, bot, path);
  else
#endif
    l = simple_sprintf(OBUF, "fr %s %s:%s\n", from, bot, path);
  tputs(dcc[idx].sock, OBUF, l);
}

void botnet_send_idle(int idx, char *bot, int sock, int idle, char *away)
{
  int l;

  if (tands > 0) {
    l = simple_sprintf(OBUF, "i %s %D %D %s\n", bot, sock, idle,
                       away ? away : "");
    send_tand_but(idx, OBUF, -l);
#ifndef NO_OLD_BOTNET
    if (away && away[0])
      tandout_but(idx, "away %s %d %s\n", bot, sock, away);
    tandout_but(idx, "idle %s %d %d\n", bot, sock, idle);
#endif
  }
}

void botnet_send_away(int idx, char *bot, int sock, char *msg, int linking)
{
  int l;

  if (tands > 0) {
    l = simple_sprintf(OBUF, "aw %s%s %D %s\n",
                       ((idx >= 0) && linking) ? "!" : "",
                       bot, sock, msg ? msg : "");
    send_tand_but(idx, OBUF, -l);
#ifndef NO_OLD_BOTNET
    if (msg) {
      if (idx < 0) {
        tandout_but(idx, "chan %s %d %s is now away: %s.\n", bot,
                    dcc[linking].u.chat->channel, dcc[linking].nick, msg);
      } else if ((b_numver(idx) >= NEAT_BOTNET)) {
        int partyidx = getparty(bot, sock);

        if (partyidx >= 0)
          tandout_but(idx, "chan %s %d %s %s: %s.\n", bot,
                      party[partyidx].chan, party[partyidx].nick,
                      NET_AWAY, msg);
      }
      tandout_but(idx, "away %s %d %s\n", bot, sock, msg);
    } else {
      if (idx < 0) {
        tandout_but(idx, "chan %s %d %s %s.\n", bot,
                    dcc[linking].u.chat->channel, dcc[linking].nick,
                    NET_UNAWAY);
      } else if (b_numver(idx) >= NEAT_BOTNET) {
        int partyidx = getparty(bot, sock);

        if (partyidx >= 0)
          tandout_but(idx, "chan %s %d %s %s.\n", bot,
                      party[partyidx].chan, party[partyidx].nick, NET_UNAWAY);
      }
      tandout_but(idx, "unaway %s %d\n", bot, sock);
    }
#endif
  }
}

void botnet_send_join_idx(int useridx, int oldchan)
{
  int l;

  if (tands > 0) {
    l = simple_sprintf(OBUF, "j %s %s %D %c%D %s\n",
                       botnetnick, dcc[useridx].nick,
                       dcc[useridx].u.chat->channel, geticon(useridx),
                       dcc[useridx].sock, dcc[useridx].host);
    send_tand_but(-1, OBUF, -l);
#ifndef NO_OLD_BOTNET
    tandout_but(-1, "join %s %s %d %c%d %s\n", botnetnick,
                dcc[useridx].nick, dcc[useridx].u.chat->channel,
                geticon(useridx), dcc[useridx].sock, dcc[useridx].host);
    tandout_but(-1, "chan %s %d %s %s %s.\n",
                botnetnick, dcc[useridx].u.chat->channel,
                dcc[useridx].nick, NET_JOINEDTHE,
                dcc[useridx].u.chat->channel ? "channel" : "party line");
    if ((oldchan >= 0) && (oldchan < GLOBAL_CHANS)) {
      tandout_but(-1, "chan %s %d %s %s %s.\n",
                  botnetnick, oldchan,
                  dcc[useridx].nick, NET_LEFTTHE,
                  oldchan ? "channel" : "party line");
    }
#endif
  }
}

void botnet_send_join_party(int idx, int linking, int useridx, int oldchan)
{
  int l;

  if (tands > 0) {
    l = simple_sprintf(OBUF, "j %s%s %s %D %c%D %s\n", linking ? "!" : "",
                       party[useridx].bot, party[useridx].nick,
                       party[useridx].chan, party[useridx].flag,
                       party[useridx].sock,
                       party[useridx].from ? party[useridx].from : "");
    send_tand_but(idx, OBUF, -l);
#ifndef NO_OLD_BOTNET
    tandout_but(idx, "join %s %s %d %c%d %s\n", party[useridx].bot,
                party[useridx].nick, party[useridx].chan,
                party[useridx].flag, party[useridx].sock,
                party[useridx].from ? party[useridx].from : "");
    if ((idx < 0) || (!linking && (b_numver(idx) >= NEAT_BOTNET))) {
      tandout_but(idx, "chan %s %d %s %s %s.\n",
                  party[useridx].bot, party[useridx].chan,
                  party[useridx].nick, NET_JOINEDTHE,
                  party[useridx].chan ? "channel" : "party line");
    }
    if ((oldchan >= 0) && (oldchan < GLOBAL_CHANS) &&
        ((idx < 0) || (b_numver(idx) >= NEAT_BOTNET))) {
      tandout_but(idx, "chan %s %d %s %s %s.\n",
                  party[useridx].bot, oldchan, party[useridx].nick,
                  NET_LEFTTHE, party[useridx].chan ? "channel" : "party line");
    }
#endif
  }
}

void botnet_send_part_idx(int useridx, char *reason)
{
  int l = simple_sprintf(OBUF, "pt %s %s %D %s\n", botnetnick,
                         dcc[useridx].nick, dcc[useridx].sock,
                         reason ? reason : "");

  if (tands > 0) {
    send_tand_but(-1, OBUF, -l);
#ifndef NO_OLD_BOTNET
    tandout_but(-1, "part %s %s %d\n", botnetnick,
                dcc[useridx].nick, dcc[useridx].sock);
    tandout_but(-1, "chan %s %d %s has left the %s%s%s.\n",
                botnetnick, dcc[useridx].u.chat->channel,
                dcc[useridx].nick,
                dcc[useridx].u.chat->channel ? "channel" : "party line",
                reason ? ": " : "", reason ? reason : "");
#endif
  }
}

void botnet_send_part_party(int idx, int partyidx, char *reason, int silent)
{
  int l;

  if (tands > 0) {
    l = simple_sprintf(OBUF, "pt %s%s %s %D %s\n",
                       silent ? "!" : "", party[partyidx].bot,
                       party[partyidx].nick, party[partyidx].sock,
                       reason ? reason : "");
    send_tand_but(idx, OBUF, -l);
#ifndef NO_OLD_BOTNET
    tandout_but(idx, "part %s %s %d\n", party[partyidx].bot,
                party[partyidx].nick, party[partyidx].sock);
    if (((idx < 0) || (b_numver(idx) >= NEAT_BOTNET)) && !silent) {
      tandout_but(idx, "chan %s %d %s has left the %s%s%s.\n",
                  party[partyidx].bot, party[partyidx].chan,
                  party[partyidx].nick,
                  party[partyidx].chan ? "channel" : "party line",
                  reason ? ": " : "", reason ? reason : "");
    }
#endif
  }
}

void botnet_send_nkch(int useridx, char *oldnick)
{
  int l;

  if (tands > 0) {
    l = simple_sprintf(OBUF, "nc %s %D %s\n", botnetnick,
                       dcc[useridx].sock, dcc[useridx].nick);
    send_tand_but(-1, OBUF, -l);
#ifndef NO_OLD_BOTNET
    tandout_but(-1, "part %s %s %d\n", botnetnick,
                dcc[useridx].nick, dcc[useridx].sock);
    tandout_but(-1, "join %s %s %d %c%d %s\n", botnetnick,
                dcc[useridx].nick, dcc[useridx].u.chat->channel,
                geticon(useridx), dcc[useridx].sock, dcc[useridx].host);
    tandout_but(-1, "chan %s %d %s: %s -> %s.\n",
                botnetnick, dcc[useridx].u.chat->channel,
                oldnick, NET_NICKCHANGE, dcc[useridx].nick);
#endif
  }
}

void botnet_send_nkch_part(int butidx, int useridx, char *oldnick)
{
  int l;

  if (tands > 0) {
    l = simple_sprintf(OBUF, "nc %s %D %s\n", party[useridx].bot,
                       party[useridx].sock, party[useridx].nick);
    send_tand_but(butidx, OBUF, -l);
#ifndef NO_OLD_BOTNET
    tandout_but(butidx, "part %s %s %d\n", party[useridx].bot,
                party[useridx].nick, party[useridx].sock);
    tandout_but(butidx, "join %s %s %d %c%d %s\n", party[useridx].bot,
                party[useridx].nick, party[useridx].chan,
                party[useridx].flag, party[useridx].sock,
                party[useridx].from ? party[useridx].from : "");
    tandout_but(butidx, "chan %s %d %s : %s -> %s.\n",
                party[useridx].bot, party[useridx].chan,
                NET_NICKCHANGE, oldnick, party[useridx].nick);
#endif
  }
}

/* This part of add_note is more relevant to the botnet than
 * to the notes file.
 */
int add_note(char *to, char *from, char *msg, int idx, int echo)
{
  int status, i, iaway, sock;
  char *p, botf[81], ss[81], ssf[81];
  struct userrec *u;

  /* Notes have a length limit. Note + PRIVMSG header + nick + date must
   * be less than 512.
   */
  if (strlen(msg) > 450)
    msg[450] = 0;

  /* Is this a cross-bot note? If it is, 'to' will be of the format
   * 'user@bot'.
   */
  p = strchr(to, '@');
  if (p != NULL) {
    char x[21];

    *p = 0;
    strncpy(x, to, 20);
    x[20] = 0;
    *p = '@';
    p++;

    if (!egg_strcasecmp(p, botnetnick)) /* To me?? */
      return add_note(x, from, msg, idx, echo); /* Start over, dimwit. */

    if (egg_strcasecmp(from, botnetnick)) {
      if (strlen(from) > 40)
        from[40] = 0;

      if (strchr(from, '@')) {
        strcpy(botf, from);
      } else
        sprintf(botf, "%s@%s", from, botnetnick);

    } else
      strcpy(botf, botnetnick);

    i = nextbot(p);
    if (i < 0) {
      if (idx >= 0)
        dprintf(idx, BOT_NOTHERE);

      return NOTE_ERROR;
    }

    if (idx >= 0 && echo)
      dprintf(idx, "-> %s@%s: %s\n", x, p, msg);

    if (idx >= 0) {
      sprintf(ssf, "%lu:%s", dcc[idx].sock, botf);
      botnet_send_priv(i, ssf, x, p, "%s", msg);
    } else
      botnet_send_priv(i, botf, x, p, "%s", msg);

    return NOTE_OK;             /* Forwarded to the right bot */
  }

  /* Might be form "sock:nick" */
  splitc(ssf, from, ':');
  rmspace(ssf);
  splitc(ss, to, ':');
  rmspace(ss);
  if (!ss[0])
    sock = -1;
  else
    sock = atoi(ss);

  /* Don't process if there's a note binding for it */
  if (idx != -2) {            /* Notes from bots don't trigger it */
    if (check_tcl_note(from, to, msg)) {
      if (idx >= 0 && echo)
        dprintf(idx, "-> %s: %s\n", to, msg);

      return NOTE_TCL;
    }
  }

  /* Valid user? */
  u = get_user_by_handle(userlist, to);
  if (!u) {
    if (idx >= 0)
      dprintf(idx, USERF_UNKNOWN);

    return NOTE_ERROR;
  }

  /* Is the note to a bot? */
  if (is_bot(u)) {
    if (idx >= 0)
      dprintf(idx, BOT_NONOTES);

    return NOTE_ERROR;
  }

  /* Is user rejecting notes from this source? */
  if (match_noterej(u, from)) {
    if (idx >= 0)
      dprintf(idx, "%s rejected your note.\n", u->handle);

    return NOTE_REJECT;
  }

  status = NOTE_STORED;
  iaway = 0;

  /* Online right now? */
  for (i = 0; i < dcc_total; i++) {
    if ((dcc[i].type->flags & DCT_GETNOTES) &&
        (sock == -1 || sock == dcc[i].sock) &&
        !egg_strcasecmp(dcc[i].nick, to)) {
      int aok = 1;

      if (dcc[i].type == &DCC_CHAT) {

        /* Only check away if it's not from a bot. */
        if (dcc[i].u.chat->away != NULL && idx != -2) {
          aok = 0;

          if (idx >= 0)
            dprintf(idx, "%s %s: %s\n", dcc[i].nick, BOT_USERAWAY,
                    dcc[i].u.chat->away);

          if (!iaway)
            iaway = i;
          status = NOTE_AWAY;
        }
      }

      if (aok) {
        char *p, *fr = from, work[1024];
        int l = 0;

        while (*msg == '<' || *msg == '>') {
          p = newsplit(&msg);

          if (*p == '<')
            l += simple_sprintf(work + l, "via %s, ", p + 1);
          else if (*from == '@')
            fr = p + 1;
        }

        if (idx == -2 || !egg_strcasecmp(from, botnetnick))
          dprintf(i, "*** [%s] %s%s\n", fr, l ? work : "", msg);
        else
          dprintf(i, "%cNote [%s]: %s%s\n", 7, fr, l ? work : "", msg);

        if (idx >= 0 && echo)
          dprintf(idx, "-> %s: %s\n", to, msg);

        return NOTE_OK;
      }
    }
  }

  if (idx == -2)
    return NOTE_OK; /* Error msg from a tandembot: don't store. */

  /* Call 'storenote' Tcl command. */
  simple_sprintf(ss, "%d", (idx >= 0) ? dcc[idx].sock : -1);
  Tcl_SetVar(interp, "_from", from, 0);
  Tcl_SetVar(interp, "_to",   to,   0);
  Tcl_SetVar(interp, "_data", msg,  0);
  Tcl_SetVar(interp, "_idx",  ss,   0);
  if (Tcl_VarEval(interp, "storenote", " $_from $_to $_data $_idx", NULL) ==
      TCL_OK) {

    if (interp->result && interp->result[0])
      status = NOTE_FWD;

    /* User is away in all sessions -- just notify the user that a
     * message arrived and was stored (only oldest session is notified).
     */
    if (status == NOTE_AWAY)
      dprintf(iaway, "*** %s.\n", BOT_NOTEARRIVED);

    return status;
  }

  /* If we haven't returned anything else by now, assume an error occurred. */
  return NOTE_ERROR;
}
