diff options
Diffstat (limited to 'contrib/m_operspy.c')
-rw-r--r-- | contrib/m_operspy.c | 639 |
1 files changed, 639 insertions, 0 deletions
diff --git a/contrib/m_operspy.c b/contrib/m_operspy.c new file mode 100644 index 0000000..f893bb7 --- /dev/null +++ b/contrib/m_operspy.c @@ -0,0 +1,639 @@ +/************************************************************************ + * IRC - Internet Relay Chat, contrib/m_operspy.c + * Copyright (C) 2002 William Bierman III and the Hybrid 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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id$ + */ + +/*** PLEASE READ ME ***/ +/* + * This module gives an extraordinary amount of power to the opers + * who have the access to use it. It allows for users' privacy to + * be pretty much obliterated. The Hybrid Team assumes absolutely + * no responsibility for this file's (mis)use. + * + * - billy-jon + */ + +#include "stdinc.h" +#include "list.h" +#include "irc_string.h" +#include "channel.h" +#include "channel_mode.h" +#include "client.h" +#include "ircd.h" +#include "sprintf_irc.h" +#include "numeric.h" +#include "fdlist.h" +#include "s_bsd.h" +#include "conf.h" +#include "log.h" +#include "s_serv.h" +#include "s_misc.h" +#include "send.h" +#include "parse.h" +#include "modules.h" +#include "hash.h" + +/* enable logging of OPERSPY functions */ +#define OPERSPY_LOG + +/* enable this to log all local/remote operspy usage to a logfile */ +#define OPERSPY_LOGFILE + +/* enable this to send incoming operspy usage to connected +y (UMODE_SPY) opers */ +#define OPERSPY_NOTICE + +/* enable OPERSPY version of LIST */ +#define OPERSPY_LIST + +/* enable OPERSPY version of MODE */ +#define OPERSPY_MODE + +/* enable OPERSPY version of NAMES */ +#define OPERSPY_NAMES + +/* enable OPERSPY version of WHO */ +#define OPERSPY_WHO + +/* enable OPERSPY version of WHOIS */ +#define OPERSPY_WHOIS + +/* enable OPERSPY version of TOPIC */ +#define OPERSPY_TOPIC + +#define IsOperspy(x) (HasUMode(x, UMODE_OPER) && MyClient(x) && HasUMode(x, UMODE_ADMIN)) + +/* extensions for OPERSPY WHO */ +static void do_who(struct Client *, struct Client *, char *, const char *); +static void who_global(struct Client *, char *, int); +static void do_who_on_channel(struct Client *, struct Channel *, char *); + +static void operspy_list(struct Client *, int, char *[]); +static void operspy_mode(struct Client *, int, char *[]); +static void operspy_names(struct Client *, int, char *[]); +static void operspy_topic(struct Client *, int, char *[]); +static void operspy_who(struct Client *, int, char *[]); +static void operspy_whois(struct Client *, int, char *[]); +#ifdef OPERSPY_LOG +static void operspy_log(struct Client *, const char *, const char *); +#endif + + +static const struct operspy_s { + const char *const cmd; + void (*const func_p)(struct Client *, int, char *[]); +} operspy_table[] = { +#ifdef OPERSPY_LIST + { "LIST", operspy_list }, +#endif +#ifdef OPERSPY_MODE + { "MODE", operspy_mode }, +#endif +#ifdef OPERSPY_NAMES + { "NAMES", operspy_names }, +#endif +#ifdef OPERSPY_TOPIC + { "TOPIC", operspy_topic }, +#endif +#ifdef OPERSPY_WHO + { "WHO", operspy_who }, +#endif +#ifdef OPERSPY_WHOIS + { "WHOIS", operspy_whois }, +#endif + { NULL, NULL } +}; + +static void +ms_operspy(struct Client *client_p, struct Client *source_p, + int parc, char *parv[]) +{ +#ifdef OPERSPY_LOG + operspy_log(source_p, parv[1], parv[2]); +#endif +} + +/* mo_operspy() + * parv[1] = operspy command + * parv[2] = command parameter + */ +static void +mo_operspy(struct Client *client_p, struct Client *source_p, + int parc, char *parv[]) +{ + char cmdbuf[IRCD_BUFSIZE] = "<NONE>"; /* in case everything is undef'd */ + size_t bcnt = 0; + const struct operspy_s *optr = NULL; + + if (!IsOperspy(client_p)) + { + sendto_one(client_p, form_str(ERR_NOPRIVILEGES), + me.name, client_p->name); + return; + } + + assert(client_p == source_p); + + for (optr = operspy_table; optr->cmd; ++optr) + { + if (!irccmp(optr->cmd, parv[1])) + { + (*optr->func_p)(client_p, parc, parv); + return; + } + } + + for (optr = operspy_table; optr->cmd; ++optr) + { + /* str*cat is slow and sucks */ + bcnt += strlcpy(cmdbuf+bcnt, optr->cmd, sizeof(cmdbuf)-bcnt); + if ((optr + 1)->cmd != NULL && bcnt < (sizeof(cmdbuf)-2)) + { + cmdbuf[bcnt++] = ','; + cmdbuf[bcnt++] = ' '; + } + } + + sendto_one(client_p, ":%s NOTICE %s :%s is not a valid option. Choose from %s", + me.name, client_p->name, parv[1], cmdbuf); +} + +static void +operspy_list(struct Client *client_p, int parc, char *parv[]) +{ + const dlink_node *ptr = NULL; +#ifdef OPERSPY_LOG + operspy_log(client_p, "LIST", parv[2]); +#endif + + if (*parv[2] == '\0') + return; + + sendto_one(client_p, form_str(RPL_LISTSTART), + me.name, client_p->name); + + DLINK_FOREACH(ptr, global_channel_list.head) + { + const struct Channel *chptr_list = ptr->data; + + if (!match(parv[2], chptr_list->chname)) + sendto_one(client_p, form_str(RPL_LIST), me.name, client_p->name, + chptr_list->chname, dlink_list_length(&chptr_list->members), + chptr_list->topic); + } + + sendto_one(client_p, form_str(RPL_LISTEND), + me.name, client_p->name); +} + +static void +operspy_mode(struct Client *client_p, int parc, char *parv[]) +{ + /* needed to preserve actual client status */ + int c_status = 0; + char modebuf[MODEBUFLEN]; + char parabuf[MODEBUFLEN]; + struct Channel *chptr_mode = NULL; + + if ((chptr_mode = hash_find_channel(parv[2])) == NULL) + { + /* + * according to m_mode.c, the channel *could* exist on the uplink still, + * but I don't see how. Even if it does, we won't be able to spy without + * info. + */ + sendto_one(client_p, form_str(ERR_NOSUCHCHANNEL), + me.name, client_p->name, parv[2]); + return; + } + +#ifdef OPERSPY_LOG + operspy_log(client_p, "MODE", parv[2]); +#endif + + /* + * XXX - this is a dirty nasty kludge to trick channel_modes() + * into giving us the key + */ + c_status = client_p->status; + client_p->status = STAT_SERVER; + + channel_modes(chptr_mode, client_p, modebuf, parabuf); + client_p->status = c_status; + + sendto_one(client_p, form_str(RPL_CHANNELMODEIS), + me.name, client_p->name, parv[2], modebuf, parabuf); + sendto_one(client_p, form_str(RPL_CREATIONTIME), + me.name, client_p->name, parv[2], chptr_mode->channelts); +} + +static void +operspy_names(struct Client *client_p, int parc, char *parv[]) +{ + /* as with mode, must preserve channel modes */ + struct Channel *chptr_names = NULL; + + if ((chptr_names = hash_find_channel(parv[2])) == NULL) + { + sendto_one(client_p, form_str(ERR_NOSUCHCHANNEL), + me.name, client_p->name, parv[2]); + return; + } + +#ifdef OPERSPY_LOG + operspy_log(client_p, "NAMES", parv[2]); +#endif + + /* + * the way to go with this, rather than temporarily setting -sp, + * is to temporarily add our client to the member list. then + * we can also list +i users. an unfortunate side-effect of this + * is that your nickname shows up in the list. for now, there is + * no easy way around it. + */ + if (IsMember(client_p, chptr_names)) + channel_member_names(client_p, chptr_names, 1); + else + { + add_user_to_channel(chptr_names, client_p, CHFL_CHANOP, 0); + channel_member_names(client_p, chptr_names, 1); + remove_user_from_channel(find_channel_link(client_p, chptr_names)); + } +} + +static void +operspy_topic(struct Client *client_p, int parc, char *parv[]) +{ + const struct Channel *chptr_topic = NULL; + + if ((chptr_topic = hash_find_channel(parv[2])) == NULL) + { + sendto_one(client_p, form_str(ERR_NOSUCHCHANNEL), + me.name, client_p->name, parv[2]); + return; + } + +#ifdef OPERSPY_LOG + operspy_log(client_p, "TOPIC", parv[2]); +#endif + + if (chptr_topic->topic[0] == '\0') + sendto_one(client_p, form_str(RPL_NOTOPIC), + me.name, client_p->name, parv[2]); + else + { + sendto_one(client_p, form_str(RPL_TOPIC), me.name, client_p->name, + chptr_topic->chname, chptr_topic->topic); + sendto_one(client_p, form_str(RPL_TOPICWHOTIME), me.name, + client_p->name, chptr_topic->chname, chptr_topic->topic_info, + chptr_topic->topic_time); + } +} + +static void +operspy_who(struct Client *client_p, int parc, char *parv[]) +{ + char *mask = parc > 2 ? parv[2] : NULL; + int server_oper = parc > 3 ? (*parv[3] == 'o') : 0; + struct Channel *chptr_who = NULL; + struct Client *target_p_who = NULL; + + if (mask != NULL) + { + collapse(mask); + + if (*mask == '\0') + { + sendto_one(client_p, form_str(RPL_ENDOFWHO), + me.name, client_p->name, "*"); + return; + } + } + else + { +#ifdef OPERSPY_LOG + operspy_log(client_p, "WHO", "*"); +#endif + who_global(client_p, NULL, server_oper); + sendto_one(client_p, form_str(RPL_ENDOFWHO), + me.name, client_p->name, "*"); + return; + } + + /* /who #channel */ + if (IsChanPrefix(*mask)) + { + if ((chptr_who = hash_find_channel(mask)) != NULL) + { +#ifdef OPERSPY_LOG + operspy_log(client_p, "WHO", mask); +#endif + do_who_on_channel(client_p, chptr_who, chptr_who->chname); + } + + sendto_one(client_p, form_str(RPL_ENDOFWHO), + me.name, client_p->name, mask); + return; + } + + /* /who nick */ + if ((target_p_who = find_person(client_p, mask)) != NULL) + { +#ifdef OPERSPY_LOG + /* "nick!user@host server\0" */ + char nuh[NICKLEN + 1 + USERLEN + 1 + HOSTLEN + 1 + HOSTLEN + 1]; + + snprintf(nuh, sizeof(nuh), "%s!%s@%s %s", target_p_who->name, + target_p_who->username, target_p_who->host, + target_p_who->servptr->name); + operspy_log(client_p, "WHO", nuh); +#endif + + if (target_p_who->channel.head != NULL) + { + chptr_who = + ((struct Membership *)target_p_who->channel.head->data)->chptr; + + do_who(client_p, target_p_who, chptr_who->chname, + get_member_status(target_p_who->channel.head->data, 0)); + } + else + do_who(client_p, target_p_who, NULL, ""); + + sendto_one(client_p, form_str(RPL_ENDOFWHO), + me.name, client_p->name, mask); + return; + } + +#ifdef OPERSPY_LOG + operspy_log(client_p, "WHO", parv[2]); +#endif + + /* /who 0 */ + if (!strcmp(mask, "0")) + who_global(client_p, NULL, server_oper); + else + who_global(client_p, mask, server_oper); + + /* nothing else? end of /who. */ + sendto_one(client_p, form_str(RPL_ENDOFWHO), + me.name, client_p->name, mask); +} + +static void +operspy_whois(struct Client *client_p, int parc, char *parv[]) +{ + const dlink_node *lp; + struct Channel *chptr_whois = NULL; + struct Client *a2client_p; + struct Client *target_p = NULL; + char buf[IRCD_BUFSIZE]; +#ifdef OPERSPY_LOG + /* "nick!user@host server\0" */ + char nuh[NICKLEN + 1 + USERLEN + 1 + HOSTLEN + 1 + HOSTLEN + 1]; +#endif + char *t = NULL; + int mlen, tlen; + int cur_len = 0; + int reply_to_send = 0; + + if (has_wildcards(parv[2])) + { + sendto_one(client_p, ":%s NOTICE %s :Do not use wildcards with this.", + me.name, client_p->name); + return; + } + + if ((target_p = find_person(client_p, parv[2])) == NULL) + { + sendto_one(client_p, form_str(ERR_NOSUCHNICK), + me.name, client_p->name, parv[2]); + return; + } + +#ifdef OPERSPY_LOG + snprintf(nuh, sizeof(nuh), "%s!%s@%s %s", + target_p->name, target_p->username, target_p->host, + target_p->servptr->name); + operspy_log(client_p, "WHOIS", nuh); +#endif + + a2client_p = target_p->servptr; + + sendto_one(client_p, form_str(RPL_WHOISUSER), me.name, + client_p->name, target_p->name, target_p->username, + target_p->host, target_p->info); + mlen = ircsprintf(buf, form_str(RPL_WHOISCHANNELS), me.name, + client_p->name, target_p->name, ""); + cur_len = mlen; + t = buf + mlen; + + DLINK_FOREACH(lp, target_p->channel.head) + { + chptr_whois = ((struct Membership *)lp->data)->chptr; + + if ((cur_len + strlen(chptr_whois->chname) + 2) > (IRCD_BUFSIZE - 4)) + { + sendto_one(client_p, "%s", buf); + cur_len = mlen; + t = buf + mlen; + } + + tlen = ircsprintf(t, "%s%s%s ", + ShowChannel(client_p, chptr_whois) ? "" : "%", + get_member_status((struct Membership *)lp->data, 1), + chptr_whois->chname); + t += tlen; + cur_len += tlen; + reply_to_send = 1; + } + + if (reply_to_send == 1) + sendto_one(client_p, "%s", buf); + + sendto_one(client_p, form_str(RPL_WHOISSERVER), me.name, + client_p->name, target_p->name, a2client_p->name, + a2client_p->info); + + if (HasUMode(target_p, UMODE_OPER)) + sendto_one(client_p, form_str(HasUMode(target_p, UMODE_ADMIN) ? RPL_WHOISADMIN : + RPL_WHOISOPERATOR), me.name, client_p->name, target_p->name); + + if (MyConnect(target_p)) + sendto_one(client_p, form_str(RPL_WHOISIDLE), me.name, + client_p->name, target_p->name, + CurrentTime - target_p->localClient->last_privmsg, + target_p->localClient->firsttime); + sendto_one(client_p, form_str(RPL_ENDOFWHOIS), + me.name, client_p->name, parv[2]); +} + +/* extensions for OPERSPY WHO */ +static void +do_who(struct Client *source_p, struct Client *target_p, + char *chname, const char *op_flags) +{ + char status[8]; + + snprintf(status, sizeof(status), "%c%s%s", target_p->away[0] ? 'G' : 'H', + HasUMode(target_p, UMODE_OPER) ? "*" : "", op_flags); + sendto_one(source_p, form_str(RPL_WHOREPLY), me.name, source_p->name, + (chname) ? (chname) : "*", + target_p->username, + target_p->host, target_p->servptr->name, target_p->name, + status, target_p->hopcount, target_p->info); +} + +static void +who_global(struct Client *source_p, char *mask, int server_oper) +{ + struct Client *target_p; + dlink_node *lp; + int maxmatches = 500; + + /* list all matching visible clients */ + DLINK_FOREACH(lp, global_client_list.head) + { + target_p = lp->data; + + if (!IsClient(target_p)) + continue; + + if (server_oper && !HasUMode(target_p, UMODE_OPER)) + continue; + + if (!mask || + !match(mask, target_p->name) || !match(mask, target_p->username) || + !match(mask, target_p->host) || !match(mask, target_p->servptr->name) || + !match(mask, target_p->info) || + (MyClient(target_p) && !match(mask, target_p->sockhost))) + { + if (dlink_list_length(&target_p->channel)) + { + struct Channel *chptr; + static char fl[5]; + + chptr = ((struct Membership *)(target_p->channel.head->data))->chptr; + snprintf(fl, sizeof(fl), "%s", + get_member_status((struct Membership *)(target_p->channel.head->data), 0)); + + do_who(source_p, target_p, chptr->chname, fl); + } + else + do_who(source_p, target_p, NULL, ""); + + if (maxmatches > 0) + { + if (--maxmatches == 0) + return; + } + } + } +} + +static void +do_who_on_channel(struct Client *source_p, struct Channel *chptr, + char *chname) +{ + dlink_node *ptr; + struct Membership *ms; + + DLINK_FOREACH(ptr, chptr->members.head) + { + ms = ptr->data; + do_who(source_p, ms->client_p, chname, get_member_status(ms, 0)); + } +} + +#ifdef OPERSPY_LOG +static void +operspy_log(struct Client *source_p, const char *command, const char *target) +{ + struct MaskItem *conf = NULL; +#ifdef OPERSPY_LOGFILE + FILE *operspy_fb; + dlink_node *cnode; + const char *opername = source_p->name; + char linebuf[IRCD_BUFSIZE], logfile[IRCD_BUFSIZE]; +#endif + + assert(source_p != NULL); + +#ifdef OPERSPY_LOGFILE + if (HasUMode(source_p, UMODE_OPER) && MyClient(source_p)) + { + DLINK_FOREACH(cnode, source_p->localClient->confs.head) + { + conf = cnode->data; + + if (conf->type == CONF_OPER) + opername = conf->name; + } + } + else if (!MyClient(source_p)) + opername = "remote"; + + snprintf(logfile, sizeof(logfile), "%s/operspy.%s.log", LOGPATH, opername); + if ((operspy_fb = fopen(logfile, "a")) == NULL) + return; + + snprintf(linebuf, sizeof(linebuf), "[%s] OPERSPY %s %s %s\n", + smalldate(CurrentTime), + get_oper_name(source_p), + command, target); + fputs(linebuf, operspy_fb); + fclose(operspy_fb); +#endif + +#ifdef OPERSPY_NOTICE + sendto_realops_flags(UMODE_SPY, L_ALL, SEND_NOTICE, "OPERSPY %s %s %s", + get_oper_name(source_p), command, target); +#endif + + if (MyClient(source_p)) + sendto_match_servs(source_p, "*", CAP_ENCAP, "ENCAP * OPERSPY %s :%s", + command, target); +} +#endif /* OPERSPY_LOG */ + +static struct Message operspy_msgtab = { + "OPERSPY", 0, 0, 3, MAXPARA, MFLG_SLOW, 0, + {m_ignore, m_not_oper, ms_operspy, ms_operspy, mo_operspy, m_ignore} +}; + +static void +module_init(void) +{ + mod_add_cmd(&operspy_msgtab); +} + +static void +module_exit(void) +{ + mod_del_cmd(&operspy_msgtab); +} + +struct module module_entry = { + .node = { NULL, NULL, NULL }, + .name = NULL, + .version = "$Revision$", + .handle = NULL, + .modinit = module_init, + .modexit = module_exit, + .flags = 0 +}; |