]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/m_delayjoin.cpp
9463457f7517e23e0062555d9ea02f96e03cb85f
[user/henk/code/inspircd.git] / src / modules / m_delayjoin.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 #include "inspircd.h"
15 #include <stdarg.h>
16
17 class DelayJoinMode : public ModeHandler
18 {
19  private:
20         CUList empty;
21  public:
22         DelayJoinMode(InspIRCd* Instance, Module* Parent) : ModeHandler(Instance, Parent, 'D', 0, 0, false, MODETYPE_CHANNEL, false, 0, '@') {};
23
24         ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding);
25 };
26
27 class ModuleDelayJoin : public Module
28 {
29  private:
30         DelayJoinMode djm;
31         CUList nl;
32  public:
33         ModuleDelayJoin(InspIRCd* Me) : Module(Me), djm(Me, this)
34         {
35                 if (!ServerInstance->Modes->AddMode(&djm))
36                         throw ModuleException("Could not add new modes!");
37                 Implementation eventlist[] = { I_OnUserJoin, I_OnUserPart, I_OnUserKick, I_OnUserQuit, I_OnNamesListItem, I_OnText, I_OnHostCycle };
38                 ServerInstance->Modules->Attach(eventlist, this, 7);
39         }
40         virtual ~ModuleDelayJoin();
41         virtual Version GetVersion();
42         virtual void OnNamesListItem(User* issuer, User* user, Channel* channel, std::string &prefixes, std::string &nick);
43         virtual void OnUserJoin(User* user, Channel* channel, bool sync, bool &silent, bool created);
44         void CleanUser(User* user);
45         ModResult OnHostCycle(User* user);
46         void OnUserPart(User* user, Channel* channel, std::string &partmessage, bool &silent);
47         void OnUserKick(User* source, User* user, Channel* chan, const std::string &reason, bool &silent);
48         void OnUserQuit(User* user, const std::string &reason, const std::string &oper_message);
49         void OnText(User* user, void* dest, int target_type, const std::string &text, char status, CUList &exempt_list);
50         void WriteCommonFrom(User *user, Channel* channel, const char* text, ...) CUSTOM_PRINTF(4, 5);
51 };
52
53 /* $ModDesc: Allows for delay-join channels (+D) where users dont appear to join until they speak */
54
55 ModeAction DelayJoinMode::OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding)
56 {
57         /* no change */
58         if (channel->IsModeSet('D') == adding)
59                 return MODEACTION_DENY;
60
61         if (!adding)
62         {
63                 /*
64                  * Make all users visible, as +D is being removed. If we don't do this,
65                  * they remain permanently invisible on this channel!
66                  */
67                 CUList* names = channel->GetUsers();
68                 for (CUListIter n = names->begin(); n != names->end(); ++n)
69                         creator->OnText(n->first, channel, TYPE_CHANNEL, "", 0, empty);
70         }
71         channel->SetMode('D', adding);
72         return MODEACTION_ALLOW;
73 }
74
75 ModuleDelayJoin::~ModuleDelayJoin()
76 {
77         ServerInstance->Modes->DelMode(&djm);
78 }
79
80 Version ModuleDelayJoin::GetVersion()
81 {
82         return Version("$Id$", VF_COMMON | VF_VENDOR, API_VERSION);
83 }
84
85 void ModuleDelayJoin::OnNamesListItem(User* issuer, User* user, Channel* channel, std::string &prefixes, std::string &nick)
86 {
87         if (!channel->IsModeSet('D'))
88                 return;
89
90         if (nick.empty())
91                 return;
92
93         /* don't prevent the user from seeing themself */
94         if (issuer == user)
95                 return;
96
97         /* If the user is hidden by delayed join, hide them from the NAMES list */
98         if (user->GetExt("delayjoin_"+channel->name))
99                 nick.clear();
100 }
101
102 void ModuleDelayJoin::OnUserJoin(User* user, Channel* channel, bool sync, bool &silent, bool created)
103 {
104         if (channel->IsModeSet('D'))
105         {
106                 silent = true;
107                 /* Because we silenced the event, make sure it reaches the user whos joining (but only them of course) */
108                 user->WriteFrom(user, "JOIN %s", channel->name.c_str());
109
110                 /* This metadata tells the module the user is delayed join on this specific channel */
111                 user->Extend("delayjoin_"+channel->name);
112
113                 /* This metadata tells the module the user is delayed join on at least one (or more) channels.
114                  * It is only cleared when the user is no longer on ANY +D channels.
115                  */
116                 if (!user->GetExt("delayjoin"))
117                         user->Extend("delayjoin");
118         }
119 }
120
121 void ModuleDelayJoin::CleanUser(User* user)
122 {
123         /* Check if the user is hidden on any other +D channels, if so don't take away the
124          * metadata that says they're hidden on one or more channels
125          */
126         for (UCListIter f = user->chans.begin(); f != user->chans.end(); f++)
127                 if (user->GetExt("delayjoin_" + f->first->name))
128                         return;
129
130         user->Shrink("delayjoin");
131 }
132
133 void ModuleDelayJoin::OnUserPart(User* user, Channel* channel, std::string &partmessage, bool &silent)
134 {
135         if (!channel->IsModeSet('D'))
136                 return;
137         if (user->GetExt("delayjoin_"+channel->name))
138         {
139                 user->Shrink("delayjoin_"+channel->name);
140                 silent = true;
141                 /* Because we silenced the event, make sure it reaches the user whos leaving (but only them of course) */
142                 user->WriteFrom(user, "PART %s%s%s", channel->name.c_str(), partmessage.empty() ? "" : " :", partmessage.empty() ? "" : partmessage.c_str());
143                 CleanUser(user);
144         }
145 }
146
147 void ModuleDelayJoin::OnUserKick(User* source, User* user, Channel* chan, const std::string &reason, bool &silent)
148 {
149         if (!chan->IsModeSet('D'))
150                 return;
151         /* Send silenced event only to the user being kicked and the user doing the kick */
152         if (user->GetExt("delayjoin_"+chan->name))
153         {
154                 user->Shrink("delayjoin_"+chan->name);
155                 silent = true;
156                 user->WriteFrom(source, "KICK %s %s %s", chan->name.c_str(), user->nick.c_str(), reason.c_str());
157                 CleanUser(user);
158         }
159 }
160
161 ModResult ModuleDelayJoin::OnHostCycle(User* user)
162 {
163         return user->GetExt("delayjoin") ? MOD_RES_DENY : MOD_RES_PASSTHRU;
164 }
165
166 void ModuleDelayJoin::OnUserQuit(User* user, const std::string &reason, const std::string &oper_message)
167 {
168         Command* parthandler = ServerInstance->Parser->GetHandler("PART");
169         if (parthandler && user->GetExt("delayjoin"))
170         {
171                 for (UCListIter f = user->chans.begin(); f != user->chans.end(); f++)
172                 {
173                         Channel* chan = f->first;
174                         if (user->GetExt("delayjoin_"+chan->name))
175                         {
176                                 std::vector<std::string> parameters;
177                                 parameters.push_back(chan->name);
178                                 /* Send a fake PART from the channel, which will be silent */
179                                 parthandler->Handle(parameters, user);
180                         }
181                 }
182                 user->Shrink("delayjoin");
183         }
184 }
185
186 void ModuleDelayJoin::OnText(User* user, void* dest, int target_type, const std::string &text, char status, CUList &exempt_list)
187 {
188         /* Server origin */
189         if (!user)
190                 return;
191
192         if (target_type != TYPE_CHANNEL)
193                 return;
194
195         Channel* channel = (Channel*) dest;
196
197         if (!user->GetExt("delayjoin_"+channel->name))
198                 return;
199
200         /* Display the join to everyone else (the user who joined got it earlier) */
201         this->WriteCommonFrom(user, channel, "JOIN %s", channel->name.c_str());
202
203         std::string n = this->ServerInstance->Modes->ModeString(user, channel);
204         if (n.length() > 0)
205                 this->WriteCommonFrom(user, channel, "MODE %s +%s", channel->name.c_str(), n.c_str());
206
207         /* Shrink off the neccessary metadata for a specific channel */
208         user->Shrink("delayjoin_"+channel->name);
209         CleanUser(user);
210 }
211
212 // .. is there a real need to duplicate WriteCommonExcept?
213 void ModuleDelayJoin::WriteCommonFrom(User *user, Channel* channel, const char* text, ...)
214 {
215         va_list argsPtr;
216         char textbuffer[MAXBUF];
217         char tb[MAXBUF];
218
219         va_start(argsPtr, text);
220         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
221         va_end(argsPtr);
222         snprintf(tb,MAXBUF,":%s %s",user->GetFullHost().c_str(), textbuffer);
223
224         CUList *ulist = channel->GetUsers();
225
226         for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++)
227         {
228                 /* User doesnt get a JOIN sent to themselves */
229                 if (user == i->first)
230                         continue;
231
232                 /* Users with a visibility state that hides them dont appear */
233                 if (user->Visibility && !user->Visibility->VisibleTo(i->first))
234                         continue;
235
236                 i->first->Write(std::string(tb));
237         }
238 }
239
240 MODULE_INIT(ModuleDelayJoin)
241