2 * InspIRCd -- Internet Relay Chat Daemon
4 * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
5 * Copyright (C) 2008 Thomas Stagner <aquanight@inspircd.org>
6 * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
8 * This file is part of InspIRCd. InspIRCd is free software: you can
9 * redistribute it and/or modify it under the terms of the GNU General Public
10 * License as published by the Free Software Foundation, version 2.
12 * This program is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #include "modules/whois.h"
29 RPL_WHOISOPERATOR = 313,
31 RPL_WHOISCHANNELS = 319,
43 // Don't split private/secret channels into a separate RPL_WHOISCHANNELS numeric.
46 // Split private/secret channels into a separate RPL_WHOISCHANNELS numeric.
49 // Split private/secret channels into a separate RPL_WHOISCHANNELS numeric with RPL_CHANNELSMSG to explain the split.
53 class WhoisContextImpl : public Whois::Context
55 Events::ModuleEventProvider& lineevprov;
58 WhoisContextImpl(LocalUser* src, User* targ, Events::ModuleEventProvider& evprov)
59 : Whois::Context(src, targ)
64 using Whois::Context::SendLine;
65 void SendLine(Numeric::Numeric& numeric) CXX11_OVERRIDE;
68 void WhoisContextImpl::SendLine(Numeric::Numeric& numeric)
71 FIRST_MOD_RESULT_CUSTOM(lineevprov, Whois::LineEventListener, OnWhoisLine, MOD_RESULT, (*this, numeric));
73 if (MOD_RESULT != MOD_RES_DENY)
74 source->WriteNumeric(numeric);
79 class CommandWhois : public SplitCommand
81 ChanModeReference secretmode;
82 ChanModeReference privatemode;
83 UserModeReference snomaskmode;
84 Events::ModuleEventProvider evprov;
85 Events::ModuleEventProvider lineevprov;
87 void DoWhois(LocalUser* user, User* dest, time_t signon, unsigned long idle);
88 void SendChanList(WhoisContextImpl& whois);
91 /** If true then all opers are shown with a generic 'is an IRC operator' line rather than the oper type. */
94 /** How to handle private/secret channels in the WHOIS response. */
95 SplitWhoisState splitwhois;
99 /** Constructor for whois.
101 CommandWhois(Module* parent)
102 : SplitCommand(parent, "WHOIS", 1)
103 , secretmode(parent, "secret")
104 , privatemode(parent, "private")
105 , snomaskmode(parent, "snomask")
106 , evprov(parent, "event/whois")
107 , lineevprov(parent, "event/whoisline")
110 syntax = "[<servername>] <nick>[,<nick>]+";
114 * @param parameters The parameters to the command
115 * @param user The user issuing the command
116 * @return A value from CmdResult to indicate command success or failure.
118 CmdResult HandleLocal(LocalUser* user, const Params& parameters) CXX11_OVERRIDE;
119 CmdResult HandleRemote(RemoteUser* target, const Params& parameters) CXX11_OVERRIDE;
122 class WhoisNumericSink
124 WhoisContextImpl& whois;
126 WhoisNumericSink(WhoisContextImpl& whoisref)
131 void operator()(Numeric::Numeric& numeric) const
133 whois.SendLine(numeric);
137 class WhoisChanListNumericBuilder : public Numeric::GenericBuilder<' ', false, WhoisNumericSink>
140 WhoisChanListNumericBuilder(WhoisContextImpl& whois)
141 : Numeric::GenericBuilder<' ', false, WhoisNumericSink>(WhoisNumericSink(whois), RPL_WHOISCHANNELS, false, whois.GetSource()->nick.size() + whois.GetTarget()->nick.size() + 1)
143 GetNumeric().push(whois.GetTarget()->nick).push(std::string());
149 const SplitWhoisState& splitwhois;
150 WhoisChanListNumericBuilder num;
151 WhoisChanListNumericBuilder secretnum;
152 std::string prefixstr;
154 void AddMember(Membership* memb, WhoisChanListNumericBuilder& out)
157 const char prefix = memb->GetPrefixChar();
159 prefixstr.push_back(prefix);
160 out.Add(prefixstr, memb->chan->name);
164 WhoisChanList(WhoisContextImpl& whois, const SplitWhoisState& sws)
171 void AddVisible(Membership* memb)
173 AddMember(memb, num);
176 void AddHidden(Membership* memb)
178 AddMember(memb, splitwhois == SPLITWHOIS_NONE ? num : secretnum);
181 void Flush(WhoisContextImpl& whois)
184 if (!secretnum.IsEmpty() && splitwhois == SPLITWHOIS_SPLITMSG)
185 whois.SendLine(RPL_CHANNELSMSG, "is on private/secret channels:");
190 void CommandWhois::SendChanList(WhoisContextImpl& whois)
192 WhoisChanList chanlist(whois, splitwhois);
194 User* const target = whois.GetTarget();
195 bool hasoperpriv = whois.GetSource()->HasPrivPermission("users/channel-spy");
196 for (User::ChanList::iterator i = target->chans.begin(); i != target->chans.end(); ++i)
198 Membership* memb = *i;
199 Channel* c = memb->chan;
201 // Anyone can view channels which are not private or secret.
202 if (!c->IsModeSet(privatemode) && !c->IsModeSet(secretmode))
203 chanlist.AddVisible(memb);
205 // Hidden channels are visible when the following conditions are true:
206 // (1) The source user and the target user are the same.
207 // (2) The source user is a member of the hidden channel.
208 // (3) The source user is an oper with the users/channel-spy privilege.
209 else if (whois.IsSelfWhois() || c->HasUser(whois.GetSource()) || hasoperpriv)
210 chanlist.AddHidden(memb);
213 chanlist.Flush(whois);
216 void CommandWhois::DoWhois(LocalUser* user, User* dest, time_t signon, unsigned long idle)
218 WhoisContextImpl whois(user, dest, lineevprov);
220 whois.SendLine(RPL_WHOISUSER, dest->ident, dest->GetDisplayedHost(), '*', dest->GetRealName());
221 if (whois.IsSelfWhois() || user->HasPrivPermission("users/auspex"))
223 whois.SendLine(RPL_WHOISHOST, InspIRCd::Format("is connecting from %s@%s %s", dest->ident.c_str(), dest->GetRealHost().c_str(), dest->GetIPString().c_str()));
228 if (!whois.IsSelfWhois() && !ServerInstance->Config->HideServer.empty() && !user->HasPrivPermission("servers/auspex"))
230 whois.SendLine(RPL_WHOISSERVER, ServerInstance->Config->HideServer, ServerInstance->Config->Network);
234 whois.SendLine(RPL_WHOISSERVER, dest->server->GetName(), dest->server->GetDesc());
239 whois.SendLine(RPL_AWAY, dest->awaymsg);
245 whois.SendLine(RPL_WHOISOPERATOR, "is an IRC operator");
247 whois.SendLine(RPL_WHOISOPERATOR, InspIRCd::Format("is %s %s on %s", (strchr("AEIOUaeiou",dest->oper->name[0]) ? "an" : "a"), dest->oper->name.c_str(), ServerInstance->Config->Network.c_str()));
250 if (whois.IsSelfWhois() || user->HasPrivPermission("users/auspex"))
252 if (dest->IsModeSet(snomaskmode))
254 whois.SendLine(RPL_WHOISMODES, InspIRCd::Format("is using modes %s %s", dest->GetModeLetters().c_str(), snomaskmode->GetUserParameter(dest).c_str()));
258 whois.SendLine(RPL_WHOISMODES, InspIRCd::Format("is using modes %s", dest->GetModeLetters().c_str()));
262 FOREACH_MOD_CUSTOM(evprov, Whois::EventListener, OnWhois, (whois));
265 * We only send these if we've been provided them. That is, if hideserver is turned off, and user is local, or
266 * if remote whois is queried, too. This is to keep the user hidden, and also since you can't reliably tell remote time. -- w00t
268 if ((idle) || (signon))
270 whois.SendLine(RPL_WHOISIDLE, idle, signon, "seconds idle, signon time");
273 whois.SendLine(RPL_ENDOFWHOIS, "End of /WHOIS list.");
276 CmdResult CommandWhois::HandleRemote(RemoteUser* target, const Params& parameters)
278 if (parameters.size() < 2)
281 User* user = ServerInstance->FindUUID(parameters[0]);
285 // User doing the whois must be on this server
286 LocalUser* localuser = IS_LOCAL(user);
290 unsigned long idle = ConvToNum<unsigned long>(parameters.back());
291 DoWhois(localuser, target, target->signon, idle);
296 CmdResult CommandWhois::HandleLocal(LocalUser* user, const Params& parameters)
299 unsigned int userindex = 0;
300 unsigned long idle = 0;
303 if (CommandParser::LoopCall(user, this, parameters, 0))
307 * If 2 paramters are specified (/whois nick nick), ignore the first one like spanningtree
308 * does, and use the second one, otherwise, use the only paramter. -- djGrrr
310 if (parameters.size() > 1)
313 dest = ServerInstance->FindNickOnly(parameters[userindex]);
315 if ((dest) && (dest->registered == REG_ALL))
318 * Okay. Umpteenth attempt at doing this, so let's re-comment...
319 * For local users (/w localuser), we show idletime if hideserver is disabled
320 * For local users (/w localuser localuser), we always show idletime, hence parameters.size() > 1 check.
321 * For remote users (/w remoteuser), we do NOT show idletime
322 * For remote users (/w remoteuser remoteuser), spanningtree will handle calling do_whois, so we can ignore this case.
323 * Thanks to djGrrr for not being impatient while I have a crap day coding. :p -- w00t
325 LocalUser* localuser = IS_LOCAL(dest);
326 if (localuser && (ServerInstance->Config->HideServer.empty() || parameters.size() > 1))
328 idle = labs((long)((localuser->idle_lastmsg)-ServerInstance->Time()));
329 signon = dest->signon;
332 DoWhois(user,dest,signon,idle);
336 /* no such nick/channel */
337 user->WriteNumeric(Numerics::NoSuchNick(!parameters[userindex].empty() ? parameters[userindex] : "*"));
338 user->WriteNumeric(RPL_ENDOFWHOIS, (!parameters[userindex].empty() ? parameters[userindex] : "*"), "End of /WHOIS list.");
345 class CoreModWhois : public Module
356 void ReadConfig(ConfigStatus&) CXX11_OVERRIDE
358 ConfigTag* tag = ServerInstance->Config->ConfValue("options");
359 const std::string splitwhois = tag->getString("splitwhois", "no");
360 SplitWhoisState newsplitstate;
361 if (stdalgo::string::equalsci(splitwhois, "no"))
362 newsplitstate = SPLITWHOIS_NONE;
363 else if (stdalgo::string::equalsci(splitwhois, "split"))
364 newsplitstate = SPLITWHOIS_SPLIT;
365 else if (stdalgo::string::equalsci(splitwhois, "splitmsg"))
366 newsplitstate = SPLITWHOIS_SPLITMSG;
368 throw ModuleException(splitwhois + " is an invalid <security:splitwhois> value, at " + tag->getTagLocation());
370 ConfigTag* security = ServerInstance->Config->ConfValue("security");
371 cmd.genericoper = security->getBool("genericoper");
372 cmd.splitwhois = newsplitstate;
375 Version GetVersion() CXX11_OVERRIDE
377 return Version("Provides the WHOIS command", VF_VENDOR|VF_CORE);
381 MODULE_INIT(CoreModWhois)