]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/m_ojoin.cpp
Remove CMD_LOCALONLY, enforce use of GetRouting for routed commands
[user/henk/code/inspircd.git] / src / modules / m_ojoin.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  InspIRCd: (C) 2002-2009 InspIRCd Development Team
6  * See: http://wiki.inspircd.org/Credits
7  *
8  * This program is free but copyrighted software; see
9  *            the file COPYING for details.
10  *
11  * ---------------------------------------------------
12  */
13
14 /*
15  * Written for InspIRCd-1.2 by Taros on the Tel'Laerad M&D Team
16  * <http://tellaerad.net>
17  */
18
19 #include "inspircd.h"
20
21 /* $ModConfig: <ojoin prefix="!" notice="yes" op="yes">
22  *  Specify the prefix that +Y will grant here, it should be unused.
23  *  Leave prefix empty if you do not wish +Y to grant a prefix
24  *  If notice is set to on, upon ojoin, the server will notice
25  *  the channel saying that the oper is joining on network business
26  *  If op is set to on, it will give them +o along with +Y */
27 /* $ModDesc: Provides the /ojoin command, which joins a user to a channel on network business, and gives them +Y, which makes them immune to kick / deop and so on. */
28 /* $ModAuthor: Taros */
29 /* $ModAuthorMail: taros34@hotmail.com */
30
31 /* A note: This will not protect against kicks from services,
32  * ulines, or operoverride. */
33
34 #define NETWORK_VALUE 9000000
35
36 char NPrefix;
37 bool notice;
38 bool op;
39
40 /** Handle /OJOIN
41  */
42 class CommandOjoin : public Command
43 {
44  public:
45         bool active;
46         CommandOjoin (InspIRCd* Instance, Module* parent) : Command(Instance,parent,"OJOIN", "o", 1, false, 0)
47         {
48                 syntax = "<channel>";
49                 active = false;
50                 TRANSLATE3(TR_NICK, TR_TEXT, TR_END);
51         }
52
53         CmdResult Handle (const std::vector<std::string>& parameters, User *user)
54         {
55                 // Make sure the channel name is allowable.
56                 if (!ServerInstance->IsChannel(parameters[0].c_str(), ServerInstance->Config->Limits.ChanMax))
57                 {
58                         user->WriteServ("NOTICE "+std::string(user->nick)+" :*** Invalid characters in channel name or name too long");
59                         return CMD_FAILURE;
60                 }
61
62                 active = true;
63                 Channel* channel = Channel::JoinUser(ServerInstance, user, parameters[0].c_str(), false, "", false);
64                 active = false;
65
66                 if (channel)
67                 {
68                         ServerInstance->SNO->WriteGlobalSno('a', std::string(user->nick)+" used OJOIN to join "+channel->name);
69
70                         if (!NPrefix)
71                         {
72                                 std::vector<std::string> modes;
73                                 modes.push_back(parameters[0]);
74                                 modes.push_back("+Y");
75                                 modes.push_back(user->nick);
76                                 ServerInstance->SendMode(modes, ServerInstance->FakeClient);
77                                 ServerInstance->PI->SendMode(channel->name, ServerInstance->Modes->GetLastParseParams(), ServerInstance->Modes->GetLastParseTranslate());
78                         }
79
80                         if (notice)
81                         {
82                                 channel = ServerInstance->FindChan(parameters[0]);
83                                 channel->WriteChannelWithServ(ServerInstance->Config->ServerName, "NOTICE %s :%s joined on official network business.",
84                                         parameters[0].c_str(), user->nick.c_str());
85                                 ServerInstance->PI->SendChannelNotice(channel, 0, std::string(user->nick) + " joined on official network business.");
86                         }
87
88                         return CMD_SUCCESS;
89                 }
90                 else
91                 {
92                         user->WriteServ("NOTICE "+std::string(user->nick)+" :*** Could not join "+parameters[0]);
93                         return CMD_FAILURE;
94                 }
95         }
96
97         RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
98         {
99                 return ROUTE_BROADCAST;
100         }
101 };
102
103 /** Abstraction of NetworkPrefixBase for channel mode +Y
104  */
105 class NetworkPrefix : public ModeHandler
106 {
107  public:
108         NetworkPrefix(InspIRCd* Instance, Module* parent)
109                 : ModeHandler(Instance, parent, 'Y', 1, 1, true, MODETYPE_CHANNEL, false, NPrefix, 0, TR_NICK)
110 // NetworkPrefixBase(Instance,"cm_network_","official user", 388, 389) { }
111 // NetworkPrefixBase(InspIRCd* Instance, const std::string &ext, const std::string &mtype, int l, int e) :
112 //  extend(ext), type(mtype), list(l), end(e)
113         {
114         }
115
116         ModePair ModeSet(User* source, User* dest, Channel* channel, const std::string &parameter)
117         {
118                 User* x = ServerInstance->FindNick(parameter);
119                 if (x)
120                 {
121                         if (!channel->HasUser(x))
122                         {
123                                 return std::make_pair(false, parameter);
124                         }
125                         else
126                         {
127                                 std::string item = "cm_network_"+std::string(channel->name);
128                                 if (x->GetExt(item))
129                                 {
130                                         return std::make_pair(true, x->nick);
131                                 }
132                                 else
133                                 {
134                                         return std::make_pair(false, parameter);
135                                 }
136                         }
137                 }
138                 return std::make_pair(false, parameter);
139         }
140
141         void RemoveMode(Channel* channel, irc::modestacker* stack)
142         {
143                 CUList* cl = channel->GetUsers();
144                 std::string item = "cm_network_" + std::string(channel->name);
145                 std::vector<std::string> mode_junk;
146                 mode_junk.push_back(channel->name);
147                 irc::modestacker modestack(ServerInstance, false);
148                 std::deque<std::string> stackresult;
149
150                 for (CUList::iterator i = cl->begin(); i != cl->end(); i++)
151                 {
152                         if (i->first->GetExt(item))
153                         {
154                                 if (stack)
155                                         stack->Push(this->GetModeChar(), i->first->nick);
156                                 else
157                                         modestack.Push(this->GetModeChar(), i->first->nick);
158                         }
159                 }
160
161                 if (stack)
162                         return;
163
164                 while (modestack.GetStackedLine(stackresult))
165                 {
166                         mode_junk.insert(mode_junk.end(), stackresult.begin(), stackresult.end());
167                         ServerInstance->SendMode(mode_junk, ServerInstance->FakeClient);
168                         mode_junk.erase(mode_junk.begin() + 1, mode_junk.end());
169                 }
170         }
171
172         User* FindAndVerify(std::string &parameter, Channel* channel)
173         {
174                 User* theuser = ServerInstance->FindNick(parameter);
175                 if ((!theuser) || (!channel->HasUser(theuser)))
176                 {
177                         parameter.clear();
178                         return NULL;
179                 }
180                 return theuser;
181         }
182
183         ModeAction HandleChange(User* source, User* theuser, bool adding, Channel* channel, std::string &parameter)
184         {
185                 std::string item = "cm_network_" + std::string(channel->name);
186
187                 if (adding)
188                 {
189                         if (!theuser->GetExt(item))
190                         {
191                                 theuser->Extend(item);
192                                 parameter = theuser->nick;
193                                 return MODEACTION_ALLOW;
194                         }
195                 }
196                 else
197                 {
198                         if (theuser->GetExt(item))
199                         {
200                                 theuser->Shrink(item);
201                                 parameter = theuser->nick;
202                                 return MODEACTION_ALLOW;
203                         }
204                 }
205                 return MODEACTION_DENY;
206         }
207
208         unsigned int GetPrefixRank()
209         {
210                 return NETWORK_VALUE;
211         }
212
213         void RemoveMode(User* user, irc::modestacker* stack)
214         {
215         }
216
217         ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding)
218         {
219                 User* theuser = FindAndVerify(parameter, channel);
220
221                 if (!theuser)
222                         return MODEACTION_DENY;
223
224                  // source is a server, or ulined, we'll let them +-Y the user.
225                 if (source == ServerInstance->FakeClient ||
226                         ((source == theuser) && (!adding)) ||
227                         (ServerInstance->ULine(source->nick.c_str())) ||
228                         (ServerInstance->ULine(source->server)) ||
229                         (!*source->server) ||
230                         (!IS_LOCAL(source))
231                         )
232                 {
233                         return HandleChange(source, theuser, adding, channel, parameter);
234                 }
235                 else
236                 {
237                         // bzzzt, wrong answer!
238                         source->WriteNumeric(482, "%s %s :Only servers may change this mode.", source->nick.c_str(), channel->name.c_str());
239                         return MODEACTION_DENY;
240                 }
241         }
242
243 };
244
245 class ModuleOjoin : public Module
246 {
247         NetworkPrefix* np;
248         CommandOjoin mycommand;
249
250  public:
251
252         ModuleOjoin(InspIRCd* Me)
253                 : Module(Me), np(NULL), mycommand(Me, this)
254         {
255                 /* Load config stuff */
256                 OnRehash(NULL);
257
258                 /* Initialise module variables */
259                 np = new NetworkPrefix(ServerInstance, this);
260
261                 if (!ServerInstance->Modes->AddMode(np))
262                 {
263                         delete np;
264                         throw ModuleException("Could not add new mode!");
265                 }
266
267                 ServerInstance->AddCommand(&mycommand);
268
269                 Implementation eventlist[] = { I_OnUserPreJoin, I_OnUserKick, I_OnUserPart, I_OnAccessCheck, I_OnRehash };
270                 ServerInstance->Modules->Attach(eventlist, this, 5);
271         }
272
273         ModResult OnUserPreJoin(User *user, Channel *chan, const char *cname, std::string &privs, const std::string &keygiven)
274         {
275                 if (mycommand.active)
276                 {
277                         if (NPrefix)
278                                 privs += NPrefix;
279                         if (op && privs.find('@') == std::string::npos)
280                                 privs += '@';
281                         return MOD_RES_ALLOW;
282                 }
283
284                 return MOD_RES_PASSTHRU;
285         }
286
287         void OnUserKick(User* source, User* user, Channel* chan, const std::string &reason, bool &silent)
288         {
289                 user->Shrink("cm_network_"+std::string(chan->name));
290         }
291
292         void OnUserPart(User* user, Channel* channel, std::string &partreason, bool &silent)
293         {
294                 user->Shrink("cm_network_"+std::string(channel->name));
295         }
296
297         void OnRehash(User* user)
298         {
299                 ConfigReader Conf(ServerInstance);
300
301                 if (!np)
302                 {
303                         // This is done on module load only
304                         std::string npre = Conf.ReadValue("ojoin", "prefix", 0);
305                         NPrefix = npre.empty() ? 0 : npre[0];
306
307                         if (NPrefix && ServerInstance->Modes->FindPrefix(NPrefix))
308                                 throw ModuleException("Looks like the +Y prefix you picked for m_ojoin is already in use. Pick another.");
309                 }
310
311                 notice = Conf.ReadFlag("ojoin", "notice", "yes", 0);
312                 op = Conf.ReadFlag("ojoin", "op", "yes", 0);
313         }
314
315         ModResult OnAccessCheck(User* source,User* dest,Channel* channel,int access_type)
316         {
317                 // Here's where we preform access checks, and disallow any kicking/deopping
318                 // of +Y users. 
319
320                 // If there's no dest, it's not for us.
321                 if (!dest || !channel)
322                         return MOD_RES_PASSTHRU;
323
324                 // If a ulined nickname, or a server is setting the mode, let it
325                 // do whatever it wants.
326                 if ((ServerInstance->ULine(source->nick.c_str())) || (ServerInstance->ULine(source->server)) || (!*source->server))
327                         return MOD_RES_ALLOW;
328
329                 std::string network("cm_network_"+channel->name);
330
331                 // Don't do anything if they're not +Y
332                 if (!dest->GetExt(network))
333                         return MOD_RES_PASSTHRU;
334
335                 // Let them do whatever they want to themselves.
336                 if (source == dest)
337                         return MOD_RES_PASSTHRU;
338
339                 switch (access_type)
340                 {
341                         // Disallow deopping of +Y users.
342                         case AC_DEOP:
343                                 source->WriteNumeric(484, source->nick+" "+channel->name+" :Can't deop "+dest->nick+" as they're on official network business.");
344                                 return MOD_RES_DENY;
345                         break;
346
347                         // Kicking people who are here on network business is a no no.
348                         case AC_KICK:
349                                 source->WriteNumeric(484, source->nick+" "+channel->name+" :Can't kick "+dest->nick+" as they're on official network business.");
350                                 return MOD_RES_DENY;
351                         break;
352
353                         // Yes, they're immune to dehalfopping too.
354                         case AC_DEHALFOP:
355                                 source->WriteNumeric(484, source->nick+" "+channel->name+" :Can't de-halfop "+dest->nick+" as they're on official network business.");
356                                 return MOD_RES_DENY;
357                         break;
358
359                         // same with devoice.
360                         case AC_DEVOICE:
361                                 source->WriteNumeric(484, source->nick+" "+channel->name+" :Can't devoice "+dest->nick+" as they're on official network business.");
362                                 return MOD_RES_DENY;
363                         break;
364                 }
365
366                 // Some other access check that doesn't fall into the above. Let it through.
367                 return MOD_RES_PASSTHRU;
368         }
369
370         ~ModuleOjoin()
371         {
372                 ServerInstance->Modes->DelMode(np);
373                 delete np;
374         }
375
376         void Prioritize()
377         {
378                 ServerInstance->Modules->SetPriority(this, I_OnUserPreJoin, PRIORITY_FIRST);
379         }
380
381         Version GetVersion()
382         {
383                 return Version("Network Buisness Join", VF_COMMON | VF_VENDOR);
384         }
385 };
386
387 MODULE_INIT(ModuleOjoin)
388