]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/m_ojoin.cpp
4e9477af155abadf66c7d6b47f3f53bdb73627c4
[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                 else
89                 {
90                         ServerInstance->SNO->WriteGlobalSno('a', std::string(user->nick)+" used OJOIN in "+parameters[0]);
91                         // they're already in the channel
92                         std::vector<std::string> modes;
93                         modes.push_back(parameters[0]);
94                         modes.push_back("+Y");
95                         modes.push_back(user->nick);
96                         ServerInstance->SendMode(modes, ServerInstance->FakeClient);
97                         ServerInstance->PI->SendMode(parameters[0], ServerInstance->Modes->GetLastParseParams(), ServerInstance->Modes->GetLastParseTranslate());
98                 }
99                 return CMD_SUCCESS;
100         }
101 };
102
103 /** 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         {
111         }
112
113         ModePair ModeSet(User* source, User* dest, Channel* channel, const std::string &parameter)
114         {
115                 User* x = ServerInstance->FindNick(parameter);
116                 if (x)
117                 {
118                         if (!channel->HasUser(x))
119                         {
120                                 return std::make_pair(false, parameter);
121                         }
122                         else
123                         {
124                                 std::string item = "cm_network_"+std::string(channel->name);
125                                 if (x->GetExt(item))
126                                 {
127                                         return std::make_pair(true, x->nick);
128                                 }
129                                 else
130                                 {
131                                         return std::make_pair(false, parameter);
132                                 }
133                         }
134                 }
135                 return std::make_pair(false, parameter);
136         }
137
138         void RemoveMode(Channel* channel, irc::modestacker* stack)
139         {
140                 CUList* cl = channel->GetUsers();
141                 std::string item = "cm_network_" + std::string(channel->name);
142                 std::vector<std::string> mode_junk;
143                 mode_junk.push_back(channel->name);
144                 irc::modestacker modestack(ServerInstance, false);
145                 std::deque<std::string> stackresult;
146
147                 for (CUList::iterator i = cl->begin(); i != cl->end(); i++)
148                 {
149                         if (i->first->GetExt(item))
150                         {
151                                 if (stack)
152                                         stack->Push(this->GetModeChar(), i->first->nick);
153                                 else
154                                         modestack.Push(this->GetModeChar(), i->first->nick);
155                         }
156                 }
157
158                 if (stack)
159                         return;
160
161                 while (modestack.GetStackedLine(stackresult))
162                 {
163                         mode_junk.insert(mode_junk.end(), stackresult.begin(), stackresult.end());
164                         ServerInstance->SendMode(mode_junk, ServerInstance->FakeClient);
165                         mode_junk.erase(mode_junk.begin() + 1, mode_junk.end());
166                 }
167         }
168
169         User* FindAndVerify(std::string &parameter, Channel* channel)
170         {
171                 User* theuser = ServerInstance->FindNick(parameter);
172                 if ((!theuser) || (!channel->HasUser(theuser)))
173                 {
174                         parameter.clear();
175                         return NULL;
176                 }
177                 return theuser;
178         }
179
180         ModeAction HandleChange(User* source, User* theuser, bool adding, Channel* channel, std::string &parameter)
181         {
182                 std::string item = "cm_network_" + std::string(channel->name);
183
184                 if (adding)
185                 {
186                         if (!theuser->GetExt(item))
187                         {
188                                 theuser->Extend(item);
189                                 parameter = theuser->nick;
190                                 return MODEACTION_ALLOW;
191                         }
192                 }
193                 else
194                 {
195                         if (theuser->GetExt(item))
196                         {
197                                 theuser->Shrink(item);
198                                 parameter = theuser->nick;
199                                 return MODEACTION_ALLOW;
200                         }
201                 }
202                 return MODEACTION_DENY;
203         }
204
205         unsigned int GetPrefixRank()
206         {
207                 return NETWORK_VALUE;
208         }
209
210         void RemoveMode(User* user, irc::modestacker* stack)
211         {
212         }
213
214         ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding)
215         {
216                 User* theuser = FindAndVerify(parameter, channel);
217
218                 if (!theuser)
219                         return MODEACTION_DENY;
220
221                  // source is a server, or ulined, we'll let them +-Y the user.
222                 if (source == ServerInstance->FakeClient ||
223                         ((source == theuser) && (!adding)) ||
224                         (ServerInstance->ULine(source->nick.c_str())) ||
225                         (ServerInstance->ULine(source->server)) ||
226                         (!*source->server) ||
227                         (!IS_LOCAL(source))
228                         )
229                 {
230                         return HandleChange(source, theuser, adding, channel, parameter);
231                 }
232                 else
233                 {
234                         // bzzzt, wrong answer!
235                         source->WriteNumeric(482, "%s %s :Only servers may change this mode.", source->nick.c_str(), channel->name.c_str());
236                         return MODEACTION_DENY;
237                 }
238         }
239
240 };
241
242 class ModuleOjoin : public Module
243 {
244         NetworkPrefix* np;
245         CommandOjoin mycommand;
246
247  public:
248
249         ModuleOjoin(InspIRCd* Me)
250                 : Module(Me), np(NULL), mycommand(Me, this)
251         {
252                 /* Load config stuff */
253                 OnRehash(NULL);
254
255                 /* Initialise module variables */
256                 np = new NetworkPrefix(Me, this);
257
258                 if (!ServerInstance->Modes->AddMode(np))
259                 {
260                         delete np;
261                         throw ModuleException("Could not add new mode!");
262                 }
263
264                 ServerInstance->AddCommand(&mycommand);
265
266                 Implementation eventlist[] = { I_OnUserPreJoin, I_OnUserKick, I_OnUserPart, I_OnAccessCheck, I_OnRehash };
267                 ServerInstance->Modules->Attach(eventlist, this, 5);
268         }
269
270         ModResult OnUserPreJoin(User *user, Channel *chan, const char *cname, std::string &privs, const std::string &keygiven)
271         {
272                 if (mycommand.active)
273                 {
274                         if (NPrefix)
275                                 privs += NPrefix;
276                         if (op && privs.find('@') == std::string::npos)
277                                 privs += '@';
278                         return MOD_RES_ALLOW;
279                 }
280
281                 return MOD_RES_PASSTHRU;
282         }
283
284         void OnUserKick(User* source, User* user, Channel* chan, const std::string &reason, bool &silent)
285         {
286                 user->Shrink("cm_network_"+std::string(chan->name));
287         }
288
289         void OnUserPart(User* user, Channel* channel, std::string &partreason, bool &silent)
290         {
291                 user->Shrink("cm_network_"+std::string(channel->name));
292         }
293
294         void OnRehash(User* user)
295         {
296                 ConfigReader Conf(ServerInstance);
297
298                 if (!np)
299                 {
300                         // This is done on module load only
301                         std::string npre = Conf.ReadValue("ojoin", "prefix", 0);
302                         NPrefix = npre.empty() ? 0 : npre[0];
303
304                         if (NPrefix && ServerInstance->Modes->FindPrefix(NPrefix))
305                                 throw ModuleException("Looks like the +Y prefix you picked for m_ojoin is already in use. Pick another.");
306                 }
307
308                 notice = Conf.ReadFlag("ojoin", "notice", "yes", 0);
309                 op = Conf.ReadFlag("ojoin", "op", "yes", 0);
310         }
311
312         ModResult OnAccessCheck(User* source,User* dest,Channel* channel,int access_type)
313         {
314                 // Here's where we preform access checks, and disallow any kicking/deopping
315                 // of +Y users. 
316
317                 // If there's no dest, it's not for us.
318                 if (!dest || !channel)
319                         return MOD_RES_PASSTHRU;
320
321                 // If a ulined nickname, or a server is setting the mode, let it
322                 // do whatever it wants.
323                 if ((ServerInstance->ULine(source->nick.c_str())) || (ServerInstance->ULine(source->server)) || (!*source->server))
324                         return MOD_RES_ALLOW;
325
326                 std::string network("cm_network_"+channel->name);
327
328                 // Don't do anything if they're not +Y
329                 if (!dest->GetExt(network))
330                         return MOD_RES_PASSTHRU;
331
332                 // Let them do whatever they want to themselves.
333                 if (source == dest)
334                         return MOD_RES_PASSTHRU;
335
336                 switch (access_type)
337                 {
338                         // Disallow deopping of +Y users.
339                         case AC_DEOP:
340                                 source->WriteNumeric(484, source->nick+" "+channel->name+" :Can't deop "+dest->nick+" as they're on official network business.");
341                                 return MOD_RES_DENY;
342                         break;
343
344                         // Kicking people who are here on network business is a no no.
345                         case AC_KICK:
346                                 source->WriteNumeric(484, source->nick+" "+channel->name+" :Can't kick "+dest->nick+" as they're on official network business.");
347                                 return MOD_RES_DENY;
348                         break;
349
350                         // Yes, they're immune to dehalfopping too.
351                         case AC_DEHALFOP:
352                                 source->WriteNumeric(484, source->nick+" "+channel->name+" :Can't de-halfop "+dest->nick+" as they're on official network business.");
353                                 return MOD_RES_DENY;
354                         break;
355
356                         // same with devoice.
357                         case AC_DEVOICE:
358                                 source->WriteNumeric(484, source->nick+" "+channel->name+" :Can't devoice "+dest->nick+" as they're on official network business.");
359                                 return MOD_RES_DENY;
360                         break;
361                 }
362
363                 // Some other access check that doesn't fall into the above. Let it through.
364                 return MOD_RES_PASSTHRU;
365         }
366
367         ~ModuleOjoin()
368         {
369                 ServerInstance->Modes->DelMode(np);
370                 delete np;
371         }
372
373         void Prioritize()
374         {
375                 ServerInstance->Modules->SetPriority(this, I_OnUserPreJoin, PRIORITY_FIRST);
376         }
377
378         Version GetVersion()
379         {
380                 return Version("Network Buisness Join", VF_COMMON | VF_VENDOR);
381         }
382 };
383
384 MODULE_INIT(ModuleOjoin)
385