1 /*
2  * Copyright (C) 2004-2020 ZNC, see the NOTICE file for details.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <znc/User.h>
18 #include <znc/IRCNetwork.h>
19 
20 using std::vector;
21 
22 #define MESSAGE \
23     t_s("Your account has been disabled. Contact your administrator.")
24 
25 class CBlockUser : public CModule {
26   public:
MODCONSTRUCTOR(CBlockUser)27     MODCONSTRUCTOR(CBlockUser) {
28         AddHelpCommand();
29         AddCommand("List", "", t_d("List blocked users"),
30                    [this](const CString& sLine) { OnListCommand(sLine); });
31         AddCommand("Block", t_d("<user>"), t_d("Block a user"),
32                    [this](const CString& sLine) { OnBlockCommand(sLine); });
33         AddCommand("Unblock", t_d("<user>"), t_d("Unblock a user"),
34                    [this](const CString& sLine) { OnUnblockCommand(sLine); });
35     }
36 
~CBlockUser()37     ~CBlockUser() override {}
38 
OnLoad(const CString & sArgs,CString & sMessage)39     bool OnLoad(const CString& sArgs, CString& sMessage) override {
40         VCString vArgs;
41         VCString::iterator it;
42         MCString::iterator it2;
43 
44         // Load saved settings
45         for (it2 = BeginNV(); it2 != EndNV(); ++it2) {
46             // Ignore errors
47             Block(it2->first);
48         }
49 
50         // Parse arguments, each argument is a user name to block
51         sArgs.Split(" ", vArgs, false);
52 
53         for (it = vArgs.begin(); it != vArgs.end(); ++it) {
54             if (!Block(*it)) {
55                 sMessage = t_f("Could not block {1}")(*it);
56                 return false;
57             }
58         }
59 
60         return true;
61     }
62 
63     /* If a user is on the blocked list and tries to log in, displays - MESSAGE
64     and stops their log in attempt.*/
OnLoginAttempt(std::shared_ptr<CAuthBase> Auth)65     EModRet OnLoginAttempt(std::shared_ptr<CAuthBase> Auth) override {
66         if (IsBlocked(Auth->GetUsername())) {
67             Auth->RefuseLogin(MESSAGE);
68             return HALT;
69         }
70 
71         return CONTINUE;
72     }
73 
OnModCommand(const CString & sCommand)74     void OnModCommand(const CString& sCommand) override {
75         if (!GetUser()->IsAdmin()) {
76             PutModule(t_s("Access denied"));
77         } else {
78             HandleCommand(sCommand);
79         }
80     }
81 
82     // Displays all blocked users as a list.
OnListCommand(const CString & sCommand)83     void OnListCommand(const CString& sCommand) {
84         if (BeginNV() == EndNV()) {
85             PutModule(t_s("No users are blocked"));
86             return;
87         }
88         PutModule(t_s("Blocked users:"));
89         for (auto it = BeginNV(); it != EndNV(); ++it) {
90             PutModule(it->first);
91         }
92     }
93 
94     /* Blocks a user if possible(ie not self, not already blocked).
95     Displays an error message if not possible. */
OnBlockCommand(const CString & sCommand)96     void OnBlockCommand(const CString& sCommand) {
97         CString sUser = sCommand.Token(1, true);
98 
99         if (sUser.empty()) {
100             PutModule(t_s("Usage: Block <user>"));
101             return;
102         }
103 
104         if (GetUser()->GetUsername().Equals(sUser)) {
105             PutModule(t_s("You can't block yourself"));
106             return;
107         }
108 
109         if (Block(sUser))
110             PutModule(t_f("Blocked {1}")(sUser));
111         else
112             PutModule(t_f("Could not block {1} (misspelled?)")(sUser));
113     }
114 
115     // Unblocks a user from the blocked list.
OnUnblockCommand(const CString & sCommand)116     void OnUnblockCommand(const CString& sCommand) {
117         CString sUser = sCommand.Token(1, true);
118 
119         if (sUser.empty()) {
120             PutModule(t_s("Usage: Unblock <user>"));
121             return;
122         }
123 
124         if (DelNV(sUser))
125             PutModule(t_f("Unblocked {1}")(sUser));
126         else
127             PutModule(t_s("This user is not blocked"));
128     }
129 
130     // Provides GUI to configure this module by adding a widget to user page in webadmin.
OnEmbeddedWebRequest(CWebSock & WebSock,const CString & sPageName,CTemplate & Tmpl)131     bool OnEmbeddedWebRequest(CWebSock& WebSock, const CString& sPageName,
132                               CTemplate& Tmpl) override {
133         if (sPageName == "webadmin/user" && WebSock.GetSession()->IsAdmin()) {
134             CString sAction = Tmpl["WebadminAction"];
135             if (sAction == "display") {
136                 Tmpl["Blocked"] = CString(IsBlocked(Tmpl["Username"]));
137                 Tmpl["Self"] = CString(Tmpl["Username"].Equals(
138                     WebSock.GetSession()->GetUser()->GetUsername()));
139                 return true;
140             }
141             if (sAction == "change" &&
142                 WebSock.GetParam("embed_blockuser_presented").ToBool()) {
143                 if (Tmpl["Username"].Equals(
144                         WebSock.GetSession()->GetUser()->GetUsername()) &&
145                     WebSock.GetParam("embed_blockuser_block").ToBool()) {
146                     WebSock.GetSession()->AddError(
147                         t_s("You can't block yourself"));
148                 } else if (WebSock.GetParam("embed_blockuser_block").ToBool()) {
149                     if (!WebSock.GetParam("embed_blockuser_old").ToBool()) {
150                         if (Block(Tmpl["Username"])) {
151                             WebSock.GetSession()->AddSuccess(
152                                 t_f("Blocked {1}")(Tmpl["Username"]));
153                         } else {
154                             WebSock.GetSession()->AddError(
155                                 t_f("Couldn't block {1}")(Tmpl["Username"]));
156                         }
157                     }
158                 } else if (WebSock.GetParam("embed_blockuser_old").ToBool()) {
159                     if (DelNV(Tmpl["Username"])) {
160                         WebSock.GetSession()->AddSuccess(
161                             t_f("Unblocked {1}")(Tmpl["Username"]));
162                     } else {
163                         WebSock.GetSession()->AddError(
164                             t_f("User {1} is not blocked")(Tmpl["Username"]));
165                     }
166                 }
167                 return true;
168             }
169         }
170         return false;
171     }
172 
173   private:
174     /* Iterates through all blocked users and returns true if the specified user (sUser)
175     is blocked, else returns false.*/
IsBlocked(const CString & sUser)176     bool IsBlocked(const CString& sUser) {
177         MCString::iterator it;
178         for (it = BeginNV(); it != EndNV(); ++it) {
179             if (sUser == it->first) {
180                 return true;
181             }
182         }
183         return false;
184     }
185 
Block(const CString & sUser)186     bool Block(const CString& sUser) {
187         CUser* pUser = CZNC::Get().FindUser(sUser);
188 
189         if (!pUser) return false;
190 
191         // Disconnect all clients
192         vector<CClient*> vpClients = pUser->GetAllClients();
193         vector<CClient*>::iterator it;
194         for (it = vpClients.begin(); it != vpClients.end(); ++it) {
195             (*it)->PutStatusNotice(MESSAGE);
196             (*it)->Close(Csock::CLT_AFTERWRITE);
197         }
198 
199         // Disconnect all networks from irc
200         vector<CIRCNetwork*> vNetworks = pUser->GetNetworks();
201         for (vector<CIRCNetwork*>::iterator it2 = vNetworks.begin();
202              it2 != vNetworks.end(); ++it2) {
203             (*it2)->SetIRCConnectEnabled(false);
204         }
205 
206         SetNV(pUser->GetUsername(), "");
207         return true;
208     }
209 };
210 
211 template <>
TModInfo(CModInfo & Info)212 void TModInfo<CBlockUser>(CModInfo& Info) {
213     Info.SetWikiPage("blockuser");
214     Info.SetHasArgs(true);
215     Info.SetArgsHelpText(
216         Info.t_s("Enter one or more user names. Separate them by spaces."));
217 }
218 
219 GLOBALMODULEDEFS(CBlockUser, t_s("Block certain users from logging in."))
220