1 /* +------------------------------------+
\r * | Inspire Internet Relay Chat Daemon |
\r * +------------------------------------+
\r *
\r * InspIRCd: (C) 2002-2007 InspIRCd Development Team
\r * See: http://www.inspircd.org/wiki/index.php/Credits
\r *
\r * This program is free but copyrighted software; see
\r * the file COPYING for details.
\r *
\r * ---------------------------------------------------
\r */
\r\r#include "inspircd.h"
\r#include "configreader.h"
\r#include "users.h"
\r#include "modules.h"
\r#include "wildcard.h"
\r#include "commands/cmd_who.h"
\r\r/* get the last 'visible' chan of a user */
\rstatic char *getlastchanname(userrec *u)
\r{
\r UCListIter i = u->chans.begin();
\r if (i != u->chans.end())
\r {
\r if (!i->first->IsModeSet('s'))
\r return i->first->name;
\r }
\r\r return "*";
\r}
\r\rbool cmd_who::whomatch(userrec* user, const char* matchtext)
\r{
\r bool realhost = false;
\r bool realname = false;
\r bool positive = true;
\r bool metadata = false;
\r bool ident = false;
\r bool away = false;
\r bool port = false;
\r char* dummy = NULL;
\r\r if (user->registered != REG_ALL)
\r return false;
\r\r if (opt_local && !IS_LOCAL(user))
\r return false;
\r else if (opt_far && IS_LOCAL(user))
\r return false;
\r\r if (opt_mode)
\r {
\r for (const char* n = matchtext; *n; n++)
\r {
\r if (*n == '+')
\r {
\r positive = true;
\r continue;
\r }
\r else if (*n == '-')
\r {
\r positive = false;
\r continue;
\r }
\r if (user->IsModeSet(*n) != positive)
\r return false;
\r }
\r return true;
\r }
\r else
\r {
\r\r if (opt_metadata)
\r metadata = user->GetExt(matchtext, dummy);
\r else
\r {
\r if (opt_realname)
\r realname = match(user->fullname, matchtext);
\r else
\r {
\r if (opt_showrealhost)
\r realhost = match(user->host, matchtext);
\r else
\r {
\r if (opt_ident)
\r ident = match(user->ident, matchtext);
\r else
\r {
\r if (opt_port)
\r {
\r irc::portparser portrange(matchtext, false);
\r long portno = -1;
\r while ((portno = portrange.GetToken()))
\r if (portno == user->GetPort())
\r port = true;
\r }
\r else
\r {
\r if (opt_away)
\r away = match(user->awaymsg, matchtext);
\r }
\r }
\r }
\r }
\r }
\r return ((port) || (away) || (ident) || (metadata) || (realname) || (realhost) || (match(user->dhost, matchtext)) || (match(user->nick, matchtext)) || (match(user->server, matchtext)));
\r }
\r}
\r\r\r\rextern "C" DllExport command_t* init_command(InspIRCd* Instance)
\r{
\r return new cmd_who(Instance);
\r}
\r\rbool cmd_who::CanView(chanrec* chan, userrec* user)
\r{
\r if (!user || !chan)
\r return false;
\r\r /* Execute items in fastest-to-execute first order */
\r\r /* Opers see all */
\r if (IS_OPER(user))
\r return true;
\r else if (!chan->IsModeSet('s') && !chan->IsModeSet('p'))
\r return true;
\r else if (chan->HasUser(user))
\r return true;
\r\r return false;
\r}
\r\rvoid cmd_who::SendWhoLine(userrec* user, const std::string &initial, chanrec* ch, userrec* u, std::vector<std::string> &whoresults)
\r{
\r std::string lcn = getlastchanname(u);
\r chanrec* chlast = ServerInstance->FindChan(lcn);
\r\r /* Not visible to this user */
\r if (u->Visibility && !u->Visibility->VisibleTo(user))
\r return;
\r\r std::string wholine = initial + (ch ? ch->name : lcn) + " " + u->ident + " " + (opt_showrealhost ? u->host : u->dhost) + " " +
\r ((*ServerInstance->Config->HideWhoisServer && !IS_OPER(user)) ? ServerInstance->Config->HideWhoisServer : u->server) +
\r " " + u->nick + " ";
\r\r /* away? */
\r if (IS_AWAY(u))
\r {
\r wholine.append("G");
\r }
\r else
\r {
\r wholine.append("H");
\r }
\r\r /* oper? */
\r if (IS_OPER(u))
\r {
\r wholine.append("*");
\r }
\r\r wholine = wholine + (ch ? ch->GetPrefixChar(u) : (chlast ? chlast->GetPrefixChar(u) : "")) + " :0 " + u->fullname;
\r whoresults.push_back(wholine);
\r}
\r\rCmdResult cmd_who::Handle (const char** parameters, int pcnt, userrec *user)
\r{
\r /*
\r * XXX - RFC says:
\r * The <name> passed to WHO is matched against users' host, server, real
\r * name and nickname
\r * Currently, we support WHO #chan, WHO nick, WHO 0, WHO *, and the addition of a 'o' flag, as per RFC.
\r */
\r\r /* WHO options */
\r opt_viewopersonly = false;
\r opt_showrealhost = false;
\r opt_unlimit = false;
\r opt_realname = false;
\r opt_mode = false;
\r opt_ident = false;
\r opt_metadata = false;
\r opt_port = false;
\r opt_away = false;
\r opt_local = false;
\r opt_far = false;
\r\r chanrec *ch = NULL;
\r std::vector<std::string> whoresults;
\r std::string initial = "352 " + std::string(user->nick) + " ";
\r\r const char* matchtext = NULL;
\r\r /* Change '0' into '*' so the wildcard matcher can grok it */
\r matchtext = parameters[0];
\r if (!strcmp(matchtext,"0"))
\r matchtext = "*";
\r\r if (pcnt > 1)
\r {
\r /* parse flags */
\r const char *iter = parameters[1];
\r\r while (*iter)
\r {
\r switch (*iter)
\r {
\r case 'o':
\r opt_viewopersonly = true;
\r break;
\r case 'h':
\r if (IS_OPER(user))
\r opt_showrealhost = true;
\r break;
\r case 'u':
\r if (IS_OPER(user))
\r opt_unlimit = true;
\r break;
\r case 'r':
\r opt_realname = true;
\r break;
\r case 'm':
\r opt_mode = true;
\r break;
\r case 'M':
\r opt_metadata = true;
\r break;
\r case 'i':
\r opt_ident = true;
\r break;
\r case 'p':
\r opt_port = true;
\r break;
\r case 'a':
\r opt_away = true;
\r break;
\r case 'l':
\r opt_local = true;
\r break;
\r case 'f':
\r opt_far = true;
\r break;
\r }
\r\r *iter++;
\r }
\r }
\r\r\r /* who on a channel? */
\r ch = ServerInstance->FindChan(matchtext);
\r\r if (ch)
\r {
\r if (CanView(ch,user))
\r {
\r bool inside = ch->HasUser(user);
\r \r /* who on a channel. */
\r CUList *cu = ch->GetUsers();
\r \r for (CUList::iterator i = cu->begin(); i != cu->end(); i++)
\r {
\r /* opers only, please */
\r if (opt_viewopersonly && !IS_OPER(i->first))
\r continue;
\r \r /* If we're not inside the channel, hide +i users */
\r if (i->first->IsModeSet('i') && !inside)
\r continue;
\r \r SendWhoLine(user, initial, ch, i->first, whoresults);
\r }
\r }
\r }
\r else
\r {
\r /* Match against wildcard of nick, server or host */
\r\r if (opt_viewopersonly)
\r {
\r /* Showing only opers */
\r for (std::vector<userrec*>::iterator i = ServerInstance->all_opers.begin(); i != ServerInstance->all_opers.end(); i++)
\r {
\r userrec* oper = *i;
\r\r if (whomatch(oper, matchtext))
\r {
\r if ((!oper->IsModeSet('i')) && (!IS_OPER(user)))
\r continue;
\r\r SendWhoLine(user, initial, NULL, oper, whoresults);
\r }
\r }
\r }
\r else
\r {
\r for (user_hash::iterator i = ServerInstance->clientlist->begin(); i != ServerInstance->clientlist->end(); i++)
\r {
\r if (whomatch(i->second, matchtext))
\r {
\r if ((i->second->IsModeSet('i')) && (!IS_OPER(user)))
\r continue;
\r\r SendWhoLine(user, initial, NULL, i->second, whoresults);
\r }
\r }
\r }
\r }
\r /* Send the results out */
\r if ((ServerInstance->Config->MaxWhoResults && (whoresults.size() <= (size_t)ServerInstance->Config->MaxWhoResults)) || opt_unlimit)
\r {
\r for (std::vector<std::string>::const_iterator n = whoresults.begin(); n != whoresults.end(); n++)
\r user->WriteServ(*n);
\r user->WriteServ("315 %s %s :End of /WHO list.",user->nick, *parameters[0] ? parameters[0] : "*");
\r return CMD_SUCCESS;
\r }
\r else
\r {
\r /* BZZT! Too many results. */
\r user->WriteServ("315 %s %s :Too many results",user->nick, parameters[0]);
\r return CMD_FAILURE;
\r }
\r}
\r