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