This test is done quite a bit across the tree, and the open-coded variants make it easy to have an accidental mismatch between the length of the prefix being tested and the length actually passed to strncmp(). This fixes an issue of that type comparing the server version against the prefix "u2.10", where the old code used an incorrect length of 4.
1064 lines
23 KiB
C
1064 lines
23 KiB
C
/*
|
|
* who.c -- The WHO queue. The ISON queue. The USERHOST queue.
|
|
*
|
|
* Written by Jeremy Nelson
|
|
* Copyright 1996, 1997 EPIC Software Labs
|
|
*/
|
|
|
|
#include "irc.h"
|
|
static char cvsrevision[] = "$Id$";
|
|
CVS_REVISION(who_c)
|
|
#include "struct.h"
|
|
|
|
#include "commands.h"
|
|
#include "ircaux.h"
|
|
#include "who.h"
|
|
#include "server.h"
|
|
#include "window.h"
|
|
#include "vars.h"
|
|
#include "hook.h"
|
|
#include "output.h"
|
|
#include "numbers.h"
|
|
#include "parse.h"
|
|
#include "if.h"
|
|
#include "names.h"
|
|
#include "misc.h"
|
|
#define MAIN_SOURCE
|
|
#include "modval.h"
|
|
|
|
/*
|
|
*
|
|
*
|
|
*
|
|
* WHO QUEUE
|
|
*
|
|
*
|
|
*
|
|
*/
|
|
|
|
/* flags used by who queue */
|
|
#define WHO_OPS 0x0001
|
|
#define WHO_NAME 0x0002
|
|
#define WHO_ZERO 0x0004
|
|
#define WHO_CHOPS 0x0008
|
|
#define WHO_FILE 0x0010
|
|
#define WHO_HOST 0x0020
|
|
#define WHO_SERVER 0x0040
|
|
#define WHO_HERE 0x0080
|
|
#define WHO_AWAY 0x0100
|
|
#define WHO_NICK 0x0200
|
|
#define WHO_LUSERS 0x0400
|
|
#define WHO_REAL 0x0800
|
|
#define WHO_NOCHOPS 0x1000
|
|
|
|
#define WHO_INVISIBLE 0x2000
|
|
/*
|
|
* This is tricky -- this doesnt get the LAST one, it gets the
|
|
* next to the last one. Why? Because the LAST one is the one
|
|
* asking, and they want to know who is LAST (before them)
|
|
* So it sucks. Sue me.
|
|
*/
|
|
static WhoEntry *who_previous_query (WhoEntry *me)
|
|
{
|
|
WhoEntry *what = who_queue_top(from_server);
|
|
|
|
while (what && what->next != me)
|
|
what = what->next;
|
|
|
|
return what;
|
|
}
|
|
|
|
static void who_queue_add (WhoEntry *item)
|
|
{
|
|
WhoEntry *bottom = who_queue_top(from_server);
|
|
while (bottom && bottom->next)
|
|
bottom = bottom->next;
|
|
|
|
if (!bottom)
|
|
set_who_queue_top(from_server, item);
|
|
else
|
|
bottom->next = item;
|
|
|
|
return;
|
|
}
|
|
|
|
static void delete_who_item (WhoEntry *save)
|
|
{
|
|
new_free(&save->who_target);
|
|
new_free(&save->who_name);
|
|
new_free(&save->who_host);
|
|
new_free(&save->who_server);
|
|
new_free(&save->who_nick);
|
|
new_free(&save->who_real);
|
|
new_free(&save->who_stuff);
|
|
new_free(&save->who_end);
|
|
new_free(&save->who_buff);
|
|
new_free(&save->who_args);
|
|
new_free((char **)&save);
|
|
}
|
|
|
|
static void who_queue_pop (void)
|
|
{
|
|
WhoEntry *save;
|
|
int piggyback;
|
|
|
|
do
|
|
{
|
|
if (!(save = who_queue_top(from_server)))
|
|
break;
|
|
|
|
piggyback = save->piggyback;
|
|
set_who_queue_top(from_server, save->next);
|
|
delete_who_item(save);
|
|
}
|
|
while (piggyback);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
static WhoEntry *get_new_who_entry (void)
|
|
{
|
|
WhoEntry *new_w = (WhoEntry *)new_malloc(sizeof(WhoEntry));
|
|
#if 0
|
|
no need to do this
|
|
new_w->dirty = 0;
|
|
new_w->piggyback = 0;
|
|
new_w->who_mask = 0;
|
|
new_w->who_target = NULL;
|
|
new_w->who_host = NULL;
|
|
new_w->who_name = NULL;
|
|
new_w->who_server = NULL;
|
|
new_w->who_nick = NULL;
|
|
new_w->who_real = NULL;
|
|
new_w->who_stuff = NULL;
|
|
new_w->who_end = NULL;
|
|
new_w->next = NULL;
|
|
#endif
|
|
return new_w;
|
|
}
|
|
|
|
static void who_queue_list (void)
|
|
{
|
|
WhoEntry *item = who_queue_top(from_server);
|
|
int count = 0;
|
|
|
|
while (item)
|
|
{
|
|
yell("[%d] [%d] [%s] [%s] [%s]", count,
|
|
item->who_mask,
|
|
item->who_nick ? item->who_nick : empty_string,
|
|
item->who_stuff ? item->who_stuff : empty_string,
|
|
item->who_end ? item->who_end : empty_string);
|
|
count++;
|
|
item = item->next;
|
|
}
|
|
}
|
|
|
|
static void who_queue_flush (void)
|
|
{
|
|
WhoEntry *item;
|
|
|
|
while ((item = who_queue_top(from_server)))
|
|
who_queue_pop();
|
|
|
|
yell("Who queue for server [%d] purged", from_server);
|
|
}
|
|
|
|
|
|
/*
|
|
* who: the /WHO command. Parses the who switches and sets the who_mask and
|
|
* whoo_stuff accordingly. Who_mask and who_stuff are used in whoreply() in
|
|
* parse.c
|
|
*/
|
|
BUILT_IN_COMMAND(whocmd)
|
|
{
|
|
whobase(args, NULL, NULL, NULL);
|
|
}
|
|
|
|
|
|
/*
|
|
* whobase: What does all the work.
|
|
*/
|
|
void BX_whobase(char *args, void (*line) (WhoEntry *, char *, char **), void (*end) (WhoEntry *, char *, char **), char *format, ...)
|
|
{
|
|
char *arg,
|
|
*channel = NULL;
|
|
int len;
|
|
WhoEntry *new_w, *old;
|
|
int done = 0;
|
|
|
|
/* Maybe should output a warning? */
|
|
if (from_server <= -1 || !is_server_connected(from_server))
|
|
return;
|
|
|
|
new_w = get_new_who_entry();
|
|
new_w->line = line;
|
|
new_w->end = end;
|
|
|
|
while ((arg = next_arg(args, &args)) != NULL && !done)
|
|
{
|
|
lower(arg);
|
|
|
|
if (*arg == '-' || *arg == '/')
|
|
{
|
|
arg++;
|
|
if ((len = strlen(arg)) == 0)
|
|
{
|
|
say("Unknown or missing flag");
|
|
return;
|
|
}
|
|
|
|
if (strbegins(arg, "line")) /* LINE */
|
|
{
|
|
char *line;
|
|
|
|
if ((line = next_expr(&args, '{')))
|
|
malloc_strcpy(&new_w->who_stuff, line);
|
|
else
|
|
say("Need {...} argument for -LINE argument.");
|
|
}
|
|
else if (strbegins(arg, "end")) /* END */
|
|
{
|
|
char *line;
|
|
|
|
if ((line = next_expr(&args, '{')))
|
|
malloc_strcpy(&new_w->who_end, line);
|
|
else
|
|
say("Need {...} argument for -END argument.");
|
|
}
|
|
else if (strbegins(arg, "raw")) /* RAW */
|
|
{
|
|
m_s3cat(&new_w->who_args, " ", args);
|
|
done = 1;
|
|
}
|
|
else if (strbegins(arg, "o")) /* OPS */
|
|
new_w->who_mask |= WHO_OPS;
|
|
else if (strbegins(arg, "lu")) /* LUSERS */
|
|
new_w->who_mask |= WHO_LUSERS;
|
|
else if (strbegins(arg, "ch")) /* CHOPS */
|
|
new_w->who_mask |= WHO_CHOPS;
|
|
else if (strbegins(arg, "no")) /* NOCHOPS */
|
|
new_w->who_mask |= WHO_NOCHOPS;
|
|
else if (strbegins(arg, "u-i")) /* INVISIBLE */
|
|
new_w->who_mask |= WHO_INVISIBLE;
|
|
else if (strbegins(arg, "ho")) /* HOSTS */
|
|
{
|
|
if ((arg = next_arg(args, &args)) == NULL)
|
|
{
|
|
say("WHO -HOST: missing argument");
|
|
return;
|
|
}
|
|
|
|
new_w->who_mask |= WHO_HOST;
|
|
malloc_strcpy(&new_w->who_host, arg);
|
|
channel = new_w->who_host;
|
|
}
|
|
else if (strbegins(arg, "he")) /* here */
|
|
new_w->who_mask |= WHO_HERE;
|
|
else if (strbegins(arg, "a")) /* away */
|
|
new_w->who_mask |= WHO_AWAY;
|
|
else if (strbegins(arg, "s")) /* servers */
|
|
{
|
|
if ((arg = next_arg(args, &args)) == NULL)
|
|
{
|
|
say("WHO -SERVER: missing arguement");
|
|
return;
|
|
}
|
|
|
|
new_w->who_mask |= WHO_SERVER;
|
|
malloc_strcpy(&new_w->who_server, arg);
|
|
channel = new_w->who_server;
|
|
}
|
|
else if (strbegins(arg, "na"))
|
|
{
|
|
if ((arg = next_arg(args, &args)) == NULL)
|
|
{
|
|
say("WHO -NAME: missing arguement");
|
|
return;
|
|
}
|
|
|
|
new_w->who_mask |= WHO_NAME;
|
|
malloc_strcpy(&new_w->who_name, arg);
|
|
channel = new_w->who_name;
|
|
}
|
|
else if (strbegins(arg, "re"))
|
|
{
|
|
if ((arg = next_arg(args, &args)) == NULL)
|
|
{
|
|
say("WHO -REALNAME: missing arguement");
|
|
return;
|
|
}
|
|
|
|
new_w->who_mask |= WHO_REAL;
|
|
malloc_strcpy(&new_w->who_real, arg);
|
|
channel = new_w->who_real;
|
|
}
|
|
else if (strbegins(arg, "ni"))
|
|
{
|
|
if ((arg = next_arg(args, &args)) == NULL)
|
|
{
|
|
say("WHO -NICK: missing arguement");
|
|
return;
|
|
}
|
|
|
|
new_w->who_mask |= WHO_NICK;
|
|
malloc_strcpy(&new_w->who_nick, arg);
|
|
channel = new_w->who_nick;
|
|
}
|
|
else if (strbegins(arg, "d"))
|
|
{
|
|
who_queue_list();
|
|
delete_who_item(new_w);
|
|
return;
|
|
}
|
|
else if (strbegins(arg, "f"))
|
|
{
|
|
who_queue_flush();
|
|
delete_who_item(new_w);
|
|
return;
|
|
}
|
|
else
|
|
m_s3cat(&new_w->who_args, " ", arg);
|
|
}
|
|
else if (strcmp(arg, "*") == 0)
|
|
{
|
|
channel = get_current_channel_by_refnum(0);
|
|
if (!channel || !*channel)
|
|
{
|
|
say("You are not on a channel. Use /WHO ** to see everybody.");
|
|
delete_who_item(new_w);
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
channel = arg;
|
|
}
|
|
|
|
if (!channel && (new_w->who_mask & WHO_OPS))
|
|
channel = "*.*";
|
|
|
|
if (!channel || !*channel)
|
|
{
|
|
channel = get_current_channel_by_refnum(0);
|
|
if (!channel || !*channel)
|
|
{
|
|
say("You are not on a channel. Use /WHO ** to see everybody.");
|
|
delete_who_item(new_w);
|
|
return;
|
|
}
|
|
}
|
|
|
|
who_queue_add(new_w);
|
|
new_w->who_target = m_strdup(channel);
|
|
if (format)
|
|
{
|
|
va_list arg;
|
|
char buffer[BIG_BUFFER_SIZE+1];
|
|
*buffer = 0;
|
|
va_start(arg, format);
|
|
vsnprintf(buffer, sizeof buffer, format, arg);
|
|
va_end(arg);
|
|
new_w->who_buff = m_strdup(buffer);
|
|
}
|
|
|
|
/*
|
|
* Check to see if we can piggyback
|
|
*/
|
|
old = who_previous_query(new_w);
|
|
if (old && !old->dirty && old->who_target && channel &&
|
|
!strcmp(old->who_target, channel))
|
|
{
|
|
old->piggyback = 1;
|
|
if (x_debug & DEBUG_OUTBOUND)
|
|
yell("Piggybacking this WHO onto last one.");
|
|
}
|
|
else
|
|
send_to_server("WHO %s %s%s%s", new_w->who_target,
|
|
(new_w->who_mask & WHO_OPS) ? "o" : empty_string,
|
|
(new_w->who_mask & WHO_INVISIBLE) ? "x": empty_string,
|
|
new_w->who_args ? new_w->who_args : empty_string);
|
|
}
|
|
|
|
void quote_whine(char *type)
|
|
{
|
|
yell("### Please dont do /QUOTE %s. Use /%s instead", type, type);
|
|
return;
|
|
}
|
|
|
|
static int who_whine = 0;
|
|
|
|
void whoreply (char *from, char **ArgList)
|
|
{
|
|
int ok = 1;
|
|
int voice = 0, opped = 0;
|
|
char *channel,
|
|
*user,
|
|
*host,
|
|
*server,
|
|
*nick,
|
|
*stat,
|
|
*name;
|
|
char buf_data[BIG_BUFFER_SIZE+1];
|
|
WhoEntry *new_w = who_queue_top(from_server);
|
|
|
|
if (!ArgList[5])
|
|
return; /* Fake! */
|
|
|
|
if (!new_w && !who_whine)
|
|
{
|
|
who_whine = 1;
|
|
channel = ArgList[0];
|
|
user = ArgList[1];
|
|
host = ArgList[2];
|
|
server = ArgList[3];
|
|
nick = ArgList[4];
|
|
stat = ArgList[5];
|
|
PasteArgs(ArgList, 6);
|
|
name = ArgList[6];
|
|
voice = (strchr(stat, '+') != NULL);
|
|
opped = (strchr(stat, '@') != NULL);
|
|
set_display_target(channel, LOG_CRAP);
|
|
if (do_hook(WHO_LIST, "%s %s %s %s %s %s %s", channel, nick, stat, user, host, server, name))
|
|
put_it("%s",convert_output_format(fget_string_var(FORMAT_WHO_FSET), "%s %s %s %s %s %s %s", channel, nick, stat, user, host, server, name));
|
|
reset_display_target();
|
|
return;
|
|
}
|
|
|
|
do
|
|
{
|
|
/*
|
|
* this can happen in a very likely situation. The timing is critical.
|
|
* a user joins a channel. We send a userhost, but before the server
|
|
* responds, we are kicked and rejoin the channel. A reply is still
|
|
* coming from the server but the channel sync hasn't finished,
|
|
* which means we are in limbo. This should also fix the core when
|
|
* /quote who is done.
|
|
*/
|
|
if (!new_w)
|
|
break;
|
|
/*
|
|
* We have received a reply to this query -- its too late to
|
|
* piggyback it now!
|
|
*/
|
|
new_w->dirty = 1;
|
|
/*
|
|
* We dont always want to use this function.
|
|
* If another function is supposed to do the work for us,
|
|
* we yield to them.
|
|
*/
|
|
if (new_w->line)
|
|
{
|
|
new_w->line(new_w, from, ArgList);
|
|
continue;
|
|
}
|
|
|
|
channel = ArgList[0];
|
|
user = ArgList[1];
|
|
host = ArgList[2];
|
|
server = ArgList[3];
|
|
nick = ArgList[4];
|
|
stat = ArgList[5];
|
|
PasteArgs(ArgList, 6);
|
|
name = ArgList[6];
|
|
voice = (strchr(stat, '+') != NULL);
|
|
opped = (strchr(stat, '@') != NULL);
|
|
*buf_data = 0;
|
|
strmopencat(buf_data, BIG_BUFFER_SIZE, user, "@", host, NULL);
|
|
|
|
|
|
if (*stat == 'S') /* this only true for the header WHOREPLY */
|
|
{
|
|
char buffer[1024];
|
|
|
|
channel = "Channel";
|
|
snprintf(buffer, sizeof buffer, "%s %s %s %s %s %s %s", channel,
|
|
nick, stat, user, host, server, name);
|
|
set_display_target(channel, LOG_CRAP);
|
|
if (new_w->who_stuff)
|
|
; /* munch it */
|
|
|
|
else if (do_hook(WHO_LIST, "%s", buffer))
|
|
put_it("%s",convert_output_format(fget_string_var(FORMAT_WHO_FSET), "%s %s %s %s %s %s %s", channel, nick, stat, user, host, server, name));
|
|
reset_display_target();
|
|
return;
|
|
}
|
|
|
|
if (new_w && new_w->who_mask)
|
|
{
|
|
if (new_w->who_mask & WHO_HERE)
|
|
ok = ok && (*stat == 'H');
|
|
if (new_w->who_mask & WHO_AWAY)
|
|
ok = ok && (*stat == 'G');
|
|
if (new_w->who_mask & WHO_OPS)
|
|
ok = ok && (*(stat + 1) == '*');
|
|
if (new_w->who_mask & WHO_LUSERS)
|
|
ok = ok && (*(stat + 1) != '*');
|
|
if (new_w->who_mask & WHO_CHOPS)
|
|
ok = ok && ((*(stat + 1) == '@') ||
|
|
(*(stat + 2) == '@'));
|
|
if (new_w->who_mask & WHO_NOCHOPS)
|
|
ok = ok && ((*(stat + 1) != '@') &&
|
|
(*(stat + 2) != '@') &&
|
|
(*(stat + 3) != '@'));
|
|
if (new_w->who_mask & WHO_NAME)
|
|
ok = ok && wild_match(new_w->who_name, user);
|
|
if (new_w->who_mask & WHO_NICK)
|
|
ok = ok && wild_match(new_w->who_nick, nick);
|
|
if (new_w->who_mask & WHO_HOST)
|
|
ok = ok && wild_match(new_w->who_host, host);
|
|
if (new_w->who_mask & WHO_REAL)
|
|
ok = ok && wild_match(new_w->who_real, name);
|
|
if (new_w->who_mask & WHO_SERVER)
|
|
ok = ok && wild_match(new_w->who_server, server);
|
|
}
|
|
|
|
if (ok)
|
|
{
|
|
char buffer[1024];
|
|
|
|
snprintf(buffer, sizeof buffer, "%s %s %s %s %s %s %s", channel,
|
|
nick, stat, user, host, server, name);
|
|
|
|
set_display_target(channel, LOG_CRAP);
|
|
add_to_channel(channel, nick, from_server, opped, voice, buf_data, server, stat, 0, my_atol(name));
|
|
if (new_w->who_stuff)
|
|
parse_line(NULL, new_w->who_stuff, buffer, 0, 0, 1);
|
|
else if (!in_join_list(channel, from_server) && do_hook(WHO_LIST, "%s", buffer))
|
|
{
|
|
if (do_hook(current_numeric, "%s", buffer))
|
|
{
|
|
if (!get_int_var(SHOW_WHO_HOPCOUNT_VAR))
|
|
next_arg(name, &name);
|
|
put_it("%s",convert_output_format(fget_string_var(FORMAT_WHO_FSET), "%s %s %s %s %s %s %s", channel, nick, stat, user, host, server, name));
|
|
}
|
|
}
|
|
#if WANT_NSLOOKUP
|
|
if (get_int_var(AUTO_NSLOOKUP_VAR))
|
|
do_nslookup(host, nick, user, channel, from_server, auto_nslookup, NULL);
|
|
#endif
|
|
reset_display_target();
|
|
}
|
|
}
|
|
while (new_w->piggyback && (new_w = new_w->next));
|
|
}
|
|
|
|
|
|
void who_end (char *from, char **ArgList)
|
|
{
|
|
WhoEntry *new_w = who_queue_top(from_server);
|
|
char buffer[1024];
|
|
|
|
who_whine = 0;
|
|
|
|
if (!new_w)
|
|
return;
|
|
|
|
do
|
|
{
|
|
/*
|
|
* Defer to another function, if neccesary.
|
|
*/
|
|
if (new_w->end)
|
|
{
|
|
new_w->end(new_w, from, ArgList);
|
|
}
|
|
else
|
|
{
|
|
snprintf(buffer, sizeof buffer, "%s %s %s", from, ArgList[0], ArgList[1]);
|
|
|
|
if (new_w->who_end)
|
|
parse_line(NULL, new_w->who_end, buffer, 0, 0, 1);
|
|
else if (get_int_var(SHOW_END_OF_MSGS_VAR) && do_hook(current_numeric, "%s", buffer))
|
|
put_it("%s %s", numeric_banner(), buffer);
|
|
}
|
|
} while (new_w->piggyback && (new_w = new_w->next));
|
|
|
|
who_queue_pop();
|
|
}
|
|
|
|
|
|
/*
|
|
*
|
|
*
|
|
*
|
|
* ISON QUEUE
|
|
*
|
|
*
|
|
*
|
|
*/
|
|
|
|
static void ison_queue_add (IsonEntry *item)
|
|
{
|
|
IsonEntry *bottom = ison_queue_top(from_server);
|
|
|
|
while (bottom && bottom->next)
|
|
bottom = bottom->next;
|
|
|
|
if (!bottom)
|
|
set_ison_queue_top(from_server, item);
|
|
else
|
|
bottom->next = item;
|
|
|
|
return;
|
|
}
|
|
|
|
static void ison_queue_pop (void)
|
|
{
|
|
IsonEntry *save = ison_queue_top(from_server);
|
|
if (save)
|
|
{
|
|
set_ison_queue_top(from_server, save->next);
|
|
new_free(&save->ison_asked);
|
|
new_free(&save->ison_got);
|
|
new_free((char **)&save);
|
|
}
|
|
return;
|
|
}
|
|
|
|
static IsonEntry *get_new_ison_entry (void)
|
|
{
|
|
IsonEntry *new_w = (IsonEntry *)new_malloc(sizeof(IsonEntry));
|
|
#if 0
|
|
new_w->ison_asked = NULL;
|
|
new_w->ison_got = NULL;
|
|
new_w->next = NULL;
|
|
new_w->line = NULL;
|
|
#endif
|
|
ison_queue_add(new_w);
|
|
return new_w;
|
|
}
|
|
|
|
#if 0
|
|
static void ison_queue_list (void)
|
|
{
|
|
IsonEntry *item = ison_queue_top(from_server);
|
|
int count = 0;
|
|
|
|
while (item)
|
|
{
|
|
yell("[%d] [%s] [%#x]", count, ison_asked, line);
|
|
count++;
|
|
item = item->next;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
BUILT_IN_COMMAND(isoncmd)
|
|
{
|
|
if (!args || !*args)
|
|
args = LOCAL_COPY(get_server_nickname(from_server));
|
|
|
|
isonbase(args, NULL);
|
|
}
|
|
|
|
void BX_isonbase (char *args, void (*line) (char *, char *))
|
|
{
|
|
IsonEntry *new_i;
|
|
char *next = args;
|
|
|
|
/* Maybe should output a warning? */
|
|
if (from_server <= -1 || !is_server_connected(from_server))
|
|
return;
|
|
|
|
while ((args = next))
|
|
{
|
|
new_i = get_new_ison_entry();
|
|
new_i->line = line;
|
|
if (strlen(args) > 500)
|
|
{
|
|
next = args + 500;
|
|
while (!isspace((unsigned char)*next))
|
|
next--;
|
|
*next++ = 0;
|
|
}
|
|
else
|
|
next = NULL;
|
|
|
|
malloc_strcpy(&new_i->ison_asked, args);
|
|
send_to_server("ISON %s", new_i->ison_asked);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* ison_returned: this is called when numeric 303 is received in
|
|
* numbers.c. ISON must always be the property of the WHOIS queue.
|
|
* Although we will check first that the top element expected is
|
|
* actually an ISON.
|
|
*/
|
|
void ison_returned (char *from, char **ArgList)
|
|
{
|
|
IsonEntry *new_i = ison_queue_top(from_server);
|
|
|
|
if (!new_i)
|
|
{
|
|
quote_whine("ISON");
|
|
return;
|
|
}
|
|
|
|
PasteArgs(ArgList, 0);
|
|
if (new_i->line)
|
|
new_i->line(new_i->ison_asked, ArgList[0]);
|
|
else
|
|
{
|
|
if (do_hook(current_numeric, "%s", ArgList[0]))
|
|
put_it("%s Currently online: %s", numeric_banner(), ArgList[0]);
|
|
}
|
|
|
|
ison_queue_pop();
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
* USERHOST QUEUE
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*/
|
|
|
|
|
|
static void userhost_queue_add (UserhostEntry *item)
|
|
{
|
|
UserhostEntry *bottom = userhost_queue_top(from_server);
|
|
|
|
while (bottom && bottom->next)
|
|
bottom = bottom->next;
|
|
|
|
if (!bottom)
|
|
set_userhost_queue_top(from_server, item);
|
|
else
|
|
bottom->next = item;
|
|
|
|
return;
|
|
}
|
|
|
|
static void userhost_queue_pop (void)
|
|
{
|
|
UserhostEntry *save = userhost_queue_top(from_server);
|
|
|
|
set_userhost_queue_top(from_server, save->next);
|
|
new_free(&save->userhost_asked);
|
|
new_free(&save->text);
|
|
new_free((char **)&save);
|
|
return;
|
|
}
|
|
|
|
static UserhostEntry *get_new_userhost_entry (void)
|
|
{
|
|
UserhostEntry *new_u = (UserhostEntry *)new_malloc(sizeof(UserhostEntry));
|
|
|
|
userhost_queue_add(new_u);
|
|
return new_u;
|
|
}
|
|
|
|
/*
|
|
* userhost: Does the USERHOST command. Need to split up the queries,
|
|
* since the server will only reply to 5 at a time.
|
|
*/
|
|
BUILT_IN_COMMAND(userhostcmd)
|
|
{
|
|
userhostbase(args, NULL, 1, NULL);
|
|
}
|
|
|
|
BUILT_IN_COMMAND(useripcmd)
|
|
{
|
|
userhostbase(args, NULL, 0, NULL);
|
|
}
|
|
|
|
BUILT_IN_COMMAND(usripcmd)
|
|
{
|
|
userhostbase(args, NULL, 2, NULL);
|
|
}
|
|
|
|
|
|
void BX_userhostbase(char *args, void (*line) (UserhostItem *, char *, char *), int userhost, char *format, ...)
|
|
{
|
|
int total = 0, userhost_cmd = 0, server_query_reqd = 0;
|
|
char *nick, *ptr, *next_ptr, *body = NULL;
|
|
va_list ap;
|
|
char buffer[BIG_BUFFER_SIZE], text[BIG_BUFFER_SIZE];
|
|
|
|
if (from_server < 0 || !is_server_connected(from_server))
|
|
return;
|
|
|
|
if (format)
|
|
{
|
|
va_start(ap, format);
|
|
vsnprintf(text, sizeof text, format, ap);
|
|
va_end(ap);
|
|
}
|
|
else
|
|
*text = 0;
|
|
|
|
*buffer = 0;
|
|
while ((nick = next_arg(args, &args)) != NULL)
|
|
{
|
|
if (check_nickname(nick))
|
|
{
|
|
total++;
|
|
if (!fetch_userhost(from_server, nick))
|
|
server_query_reqd++;
|
|
|
|
if (*buffer)
|
|
strlcat(buffer, space, sizeof buffer);
|
|
strlcat(buffer, nick, sizeof buffer);
|
|
}
|
|
|
|
else if (!my_strnicmp(nick, "-cmd", 2))
|
|
{
|
|
if (!total)
|
|
{
|
|
say("%s -cmd with no nicks specified", userhost == 1 ? "USERHOST" : userhost == 0 ? "USERIP" : "USRIP");
|
|
return;
|
|
}
|
|
|
|
while (my_isspace(*args))
|
|
args++;
|
|
|
|
if (!(body = next_expr_failok(&args, '{')))
|
|
body = args;
|
|
|
|
userhost_cmd = 1;
|
|
break;
|
|
}
|
|
|
|
else if (!my_strnicmp(nick, "-direct", 2))
|
|
server_query_reqd++;
|
|
}
|
|
|
|
if (!userhost_cmd && !total)
|
|
{
|
|
server_query_reqd++;
|
|
strlcpy(buffer, get_server_nickname(from_server), sizeof buffer);
|
|
}
|
|
|
|
ptr = buffer;
|
|
if (server_query_reqd || (!line && !userhost_cmd))
|
|
{
|
|
while (ptr && *ptr)
|
|
{
|
|
UserhostEntry *new_uh = get_new_userhost_entry();
|
|
|
|
move_to_abs_word(ptr, &next_ptr, 5);
|
|
if (next_ptr && *next_ptr && next_ptr > ptr)
|
|
next_ptr[-1] = 0;
|
|
|
|
new_uh->userhost_asked = m_strdup(ptr);
|
|
send_to_server("%s %s", userhost == 1 ? "USERHOST" : !userhost ? "USERIP" : "USRIP", new_uh->userhost_asked);
|
|
|
|
if (userhost_cmd)
|
|
new_uh->text = m_strdup(body);
|
|
else if (*text)
|
|
new_uh->text = m_strdup(text);
|
|
|
|
if (line)
|
|
new_uh->func = line;
|
|
else if (userhost_cmd)
|
|
new_uh->func = userhost_cmd_returned;
|
|
else
|
|
new_uh->func = NULL;
|
|
|
|
ptr = next_ptr;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
while (ptr && *ptr)
|
|
{
|
|
char *uh, *nick = next_arg(ptr, &ptr);
|
|
const char *old_uh = fetch_userhost(from_server, nick);
|
|
UserhostItem item = { 0 };
|
|
|
|
uh = LOCAL_COPY(old_uh);
|
|
item.nick = nick;
|
|
item.oper = 0;
|
|
item.connected = 1;
|
|
item.away = 0;
|
|
item.user = uh;
|
|
item.host = strchr(uh, '@');
|
|
if (item.host)
|
|
*item.host++ = 0;
|
|
else
|
|
item.host = "<UNKNOWN>";
|
|
|
|
if (line)
|
|
line(&item, nick, body ? body : *text ? text : NULL);
|
|
else if (userhost_cmd)
|
|
userhost_cmd_returned(&item, nick, body);
|
|
else
|
|
yell("Yowza! I don't know what to do here!");
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* userhost_returned: this is called when numeric 302 is received in
|
|
* numbers.c. USERHOST must always remain the property of the userhost
|
|
* queue. Sending out USERHOST requests to the server without going
|
|
* through this queue will cause it to be corrupted and the client will
|
|
* go higgledy-piggledy.
|
|
*/
|
|
void userhost_returned (char *from, char **ArgList)
|
|
{
|
|
UserhostEntry *top = userhost_queue_top(from_server);
|
|
char *ptr;
|
|
|
|
if (!top)
|
|
{
|
|
quote_whine("USERHOST");
|
|
return;
|
|
}
|
|
|
|
ptr = top->userhost_asked;
|
|
|
|
/*
|
|
* Go through the nicknames that were requested...
|
|
*/
|
|
while (ptr && *ptr)
|
|
{
|
|
/*
|
|
* Grab the next nickname
|
|
*/
|
|
char *cnick = next_arg(ptr, &ptr);
|
|
int len = strlen(cnick);
|
|
|
|
/*
|
|
* Now either it is present at the next argument
|
|
* or its not. If it is, it will match the first
|
|
* part of ArgList, and the following char will
|
|
* either be a * or an = (eg, nick*= or nick=)
|
|
*/
|
|
if (ArgList)
|
|
{
|
|
while (*(*ArgList) == ' ')
|
|
(*ArgList)++;
|
|
}
|
|
if (ArgList && *ArgList && (!my_strnicmp(cnick, *ArgList, len)
|
|
&& ((*ArgList)[len] == '*' || (*ArgList)[len] == '=')))
|
|
{
|
|
UserhostItem item;
|
|
|
|
/* Extract all the interesting info */
|
|
item.connected = 1;
|
|
item.nick = next_arg(*ArgList, ArgList);
|
|
item.user = strchr(item.nick, '=');
|
|
|
|
if (item.user[-1] == '*')
|
|
{
|
|
item.user[-1] = 0;
|
|
item.oper = 1;
|
|
}
|
|
else
|
|
item.oper = 0;
|
|
|
|
if (item.user[1] == '+')
|
|
item.away = 0;
|
|
else
|
|
item.away = 1;
|
|
|
|
*item.user++ = 0;
|
|
item.user++;
|
|
|
|
item.host = strchr(item.user, '@');
|
|
*item.host++ = 0;
|
|
|
|
|
|
/*
|
|
* If the user wanted a callback, then
|
|
* feed the callback with the info.
|
|
*/
|
|
if (top->func)
|
|
top->func(&item, cnick, top->text);
|
|
|
|
/*
|
|
* Otherwise, the user just did /userhost,
|
|
* so we offer the numeric, and if the user
|
|
* doesnt bite, we output to the screen.
|
|
*/
|
|
else if (do_hook(current_numeric, "%s %s %s %s %s",
|
|
item.nick,
|
|
item.oper ? "+" : "-",
|
|
item.away ? "-" : "+",
|
|
item.user, item.host))
|
|
put_it("%s %s is %s@%s%s%s", numeric_banner(),
|
|
item.nick, item.user, item.host,
|
|
item.oper ? " (Is an IRC operator)" : empty_string,
|
|
item.away ? " (away)" : empty_string);
|
|
}
|
|
|
|
/*
|
|
* If ArgList isnt the current nick, then the current nick
|
|
* must not be on irc. So we whip up a dummy UserhostItem
|
|
* and send it on its way. We DO NOT HOOK the 302 numeric
|
|
* with this bogus entry, because thats the historical
|
|
* behavior. This can cause a problem if you do a USERHOST
|
|
* and wait on the 302 numeric. I think waiting on the 302
|
|
* numeric is stupid, anyhow.
|
|
*/
|
|
else
|
|
{
|
|
/*
|
|
* Of course, only if the user asked for a callback
|
|
* via /userhost -cmd or a direct call to userhostbase.
|
|
* If the user just did /userhost, and the nicks arent
|
|
* on, then we just dont display anything.
|
|
*/
|
|
if (top->func)
|
|
{
|
|
UserhostItem item;
|
|
|
|
item.nick = cnick;
|
|
item.user = item.host = "<UNKNOWN>";
|
|
item.oper = item.away = 0;
|
|
item.connected = 1;
|
|
|
|
top->func(&item, cnick, top->text);
|
|
}
|
|
}
|
|
}
|
|
|
|
userhost_queue_pop();
|
|
}
|
|
|
|
void userhost_cmd_returned (UserhostItem *stuff, char *nick, char *text)
|
|
{
|
|
char args[BIG_BUFFER_SIZE + 1];
|
|
|
|
strcpy(args, stuff->nick ? stuff->nick : empty_string);
|
|
strcat(args, stuff->oper ? " + " : " - ");
|
|
strcat(args, stuff->away ? "+ " : "- ");
|
|
strcat(args, stuff->user ? stuff->user : empty_string);
|
|
strcat(args, space);
|
|
strcat(args, stuff->host ? stuff->host : empty_string);
|
|
parse_line(NULL, text, args, 0, 0, 1);
|
|
}
|
|
|
|
void clean_server_queues (int i)
|
|
{
|
|
int old_from_server = from_server;
|
|
|
|
if (i == -1 || !get_server_list() || !is_server_connected(i))
|
|
return; /* Whatever */
|
|
|
|
from_server = i;
|
|
|
|
while (who_queue_top(i))
|
|
who_queue_pop();
|
|
|
|
while (ison_queue_top(i))
|
|
ison_queue_pop();
|
|
|
|
while (userhost_queue_top(i))
|
|
userhost_queue_pop();
|
|
|
|
from_server = old_from_server;
|
|
}
|
|
|
|
|