]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/coremods/core_list.cpp
e4da6ddb71d1644b1108a593fdb066fd3a55e54c
[user/henk/code/inspircd.git] / src / coremods / core_list.cpp
1 /*
2  * InspIRCd -- Internet Relay Chat Daemon
3  *
4  *   Copyright (C) 2017-2020 Sadie Powell <sadie@witchery.services>
5  *   Copyright (C) 2015 Daniel Vassdal <shutter@canternet.org>
6  *   Copyright (C) 2013-2016 Attila Molnar <attilamolnar@hush.com>
7  *   Copyright (C) 2012 Robby <robby@chatbelgie.be>
8  *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
9  *   Copyright (C) 2008 Robin Burchell <robin+git@viroteck.net>
10  *   Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
11  *   Copyright (C) 2006-2007, 2010 Craig Edwards <brain@inspircd.org>
12  *
13  * This file is part of InspIRCd.  InspIRCd is free software: you can
14  * redistribute it and/or modify it under the terms of the GNU General Public
15  * License as published by the Free Software Foundation, version 2.
16  *
17  * This program is distributed in the hope that it will be useful, but WITHOUT
18  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
19  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
20  * details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
24  */
25
26
27 #include "inspircd.h"
28
29 /** Handle /LIST.
30  */
31 class CommandList : public Command
32 {
33  private:
34         ChanModeReference secretmode;
35         ChanModeReference privatemode;
36
37         /** Parses the creation time or topic set time out of a LIST parameter.
38          * @param value The parameter containing a minute count.
39          * @return The UNIX time at \p value minutes ago.
40          */
41         time_t ParseMinutes(const std::string& value)
42         {
43                 time_t minutes = ConvToNum<time_t>(value.c_str() + 2);
44                 if (!minutes)
45                         return 0;
46                 return ServerInstance->Time() - (minutes * 60);
47         }
48
49  public:
50         // Whether to show modes in the LIST response.
51         bool showmodes;
52
53         CommandList(Module* parent)
54                 : Command(parent,"LIST", 0, 0)
55                 , secretmode(creator, "secret")
56                 , privatemode(creator, "private")
57         {
58                 allow_empty_last_param = false;
59                 Penalty = 5;
60         }
61
62         /** Handle command.
63          * @param parameters The parameters to the command
64          * @param user The user issuing the command
65          * @return A value from CmdResult to indicate command success or failure.
66          */
67         CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE;
68 };
69
70
71 /** Handle /LIST
72  */
73 CmdResult CommandList::Handle(User* user, const Params& parameters)
74 {
75         // C: Searching based on creation time, via the "C<val" and "C>val" modifiers
76         // to search for a channel creation time that is lower or higher than val
77         // respectively.
78         time_t mincreationtime = 0;
79         time_t maxcreationtime = 0;
80
81         // M: Searching based on mask.
82         // N: Searching based on !mask.
83         bool match_name_topic = false;
84         bool match_inverted = false;
85         const char* match = NULL;
86
87         // T: Searching based on topic time, via the "T<val" and "T>val" modifiers to
88         // search for a topic time that is lower or higher than val respectively.
89         time_t mintopictime = 0;
90         time_t maxtopictime = 0;
91
92         // U: Searching based on user count within the channel, via the "<val" and
93         // ">val" modifiers to search for a channel that has less than or more than
94         // val users respectively.
95         size_t minusers = 0;
96         size_t maxusers = 0;
97
98         for (Params::const_iterator iter = parameters.begin(); iter != parameters.end(); ++iter)
99         {
100                 const std::string& constraint = *iter;
101                 if (constraint[0] == '<')
102                 {
103                         maxusers = ConvToNum<size_t>(constraint.c_str() + 1);
104                 }
105                 else if (constraint[0] == '>')
106                 {
107                         minusers = ConvToNum<size_t>(constraint.c_str() + 1);
108                 }
109                 else if (!constraint.compare(0, 2, "C<", 2) || !constraint.compare(0, 2, "c<", 2))
110                 {
111                         mincreationtime = ParseMinutes(constraint);
112                 }
113                 else if (!constraint.compare(0, 2, "C>", 2) || !constraint.compare(0, 2, "c>", 2))
114                 {
115                         maxcreationtime = ParseMinutes(constraint);
116                 }
117                 else if (!constraint.compare(0, 2, "T<", 2) || !constraint.compare(0, 2, "t<", 2))
118                 {
119                         mintopictime = ParseMinutes(constraint);
120                 }
121                 else if (!constraint.compare(0, 2, "T>", 2) || !constraint.compare(0, 2, "t>", 2))
122                 {
123                         maxtopictime = ParseMinutes(constraint);
124                 }
125                 else
126                 {
127                         // If the glob is prefixed with ! it is inverted.
128                         match = constraint.c_str();
129                         if (match[0] == '!')
130                         {
131                                 match_inverted = true;
132                                 match += 1;
133                         }
134
135                         // Ensure that the user didn't just run "LIST !".
136                         if (match[0])
137                                 match_name_topic = true;
138                 }
139         }
140
141         const bool has_privs = user->HasPrivPermission("channels/auspex");
142
143         user->WriteNumeric(RPL_LISTSTART, "Channel", "Users Name");
144         const chan_hash& chans = ServerInstance->GetChans();
145         for (chan_hash::const_iterator i = chans.begin(); i != chans.end(); ++i)
146         {
147                 Channel* const chan = i->second;
148
149                 // Check the user count if a search has been specified.
150                 const size_t users = chan->GetUserCounter();
151                 if ((minusers && users <= minusers) || (maxusers && users >= maxusers))
152                         continue;
153
154                 // Check the creation ts if a search has been specified.
155                 const time_t creationtime = chan->age;
156                 if ((mincreationtime && creationtime <= mincreationtime) || (maxcreationtime && creationtime >= maxcreationtime))
157                         continue;
158
159                 // Check the topic ts if a search has been specified.
160                 const time_t topictime = chan->topicset;
161                 if ((mintopictime && (!topictime || topictime <= mintopictime)) || (maxtopictime && (!topictime || topictime >= maxtopictime)))
162                         continue;
163
164                 // Attempt to match a glob pattern.
165                 if (match_name_topic)
166                 {
167                         bool matches = InspIRCd::Match(chan->name, match) || InspIRCd::Match(chan->topic, match);
168
169                         // The user specified an match that we did not match.
170                         if (!matches && !match_inverted)
171                                 continue;
172
173                         // The user specified an inverted match that we did match.
174                         if (matches && match_inverted)
175                                 continue;
176                 }
177
178                 // if the channel is not private/secret, OR the user is on the channel anyway
179                 bool n = (has_privs || chan->HasUser(user));
180
181                 // If we're not in the channel and +s is set on it, we want to ignore it
182                 if ((n) || (!chan->IsModeSet(secretmode)))
183                 {
184                         if ((!n) && (chan->IsModeSet(privatemode)))
185                         {
186                                 // Channel is private (+p) and user is outside/not privileged
187                                 user->WriteNumeric(RPL_LIST, '*', users, "");
188                         }
189                         else if (showmodes)
190                         {
191                                 // Show the list response with the modes and topic.
192                                 user->WriteNumeric(RPL_LIST, chan->name, users, InspIRCd::Format("[+%s] %s", chan->ChanModes(n), chan->topic.c_str()));
193                         }
194                         else
195                         {
196                                 // Show the list response with just the modes.
197                                 user->WriteNumeric(RPL_LIST, chan->name, users, chan->topic);
198                         }
199                 }
200         }
201         user->WriteNumeric(RPL_LISTEND, "End of channel list.");
202
203         return CMD_SUCCESS;
204 }
205
206 class CoreModList : public Module
207 {
208  private:
209         CommandList cmd;
210
211  public:
212         CoreModList()
213                 : cmd(this)
214         {
215         }
216
217         void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
218         {
219                 ConfigTag* tag = ServerInstance->Config->ConfValue("options");
220                 cmd.showmodes = tag->getBool("modesinlist", true);
221         }
222
223         void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
224         {
225                 tokens["ELIST"] = "CMNTU";
226                 tokens["SAFELIST"];
227         }
228
229         Version GetVersion() CXX11_OVERRIDE
230         {
231                 return Version("Provides the LIST command", VF_VENDOR|VF_CORE);
232         }
233 };
234
235 MODULE_INIT(CoreModList)