Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (C) 2004-2025 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/Config.h> |
19 | | #include <znc/FileUtils.h> |
20 | | #include <znc/IRCNetwork.h> |
21 | | #include <znc/IRCSock.h> |
22 | | #include <znc/Chan.h> |
23 | | #include <znc/Query.h> |
24 | | #include <math.h> |
25 | | #include <time.h> |
26 | | #include <algorithm> |
27 | | |
28 | | #ifdef ZNC_HAVE_ARGON |
29 | | #include <argon2.h> |
30 | | #endif |
31 | | |
32 | | using std::vector; |
33 | | using std::set; |
34 | | |
35 | | class CUserTimer : public CCron { |
36 | | public: |
37 | 0 | CUserTimer(CUser* pUser) : CCron(), m_pUser(pUser) { |
38 | 0 | SetName("CUserTimer::" + m_pUser->GetUsername()); |
39 | 0 | Start(m_pUser->GetPingSlack()); |
40 | 0 | } |
41 | 0 | ~CUserTimer() override {} |
42 | | |
43 | | CUserTimer(const CUserTimer&) = delete; |
44 | | CUserTimer& operator=(const CUserTimer&) = delete; |
45 | | |
46 | | private: |
47 | | protected: |
48 | 0 | void RunJob() override { |
49 | 0 | const vector<CClient*>& vUserClients = m_pUser->GetUserClients(); |
50 | |
|
51 | 0 | for (CClient* pUserClient : vUserClients) { |
52 | 0 | if (pUserClient->GetTimeSinceLastDataTransaction() >= |
53 | 0 | m_pUser->GetPingFrequency()) { |
54 | 0 | pUserClient->PutClient("PING :ZNC"); |
55 | 0 | } |
56 | 0 | } |
57 | | |
58 | | // Restart timer for the case if the period had changed. Usually this is |
59 | | // noop |
60 | 0 | Start(m_pUser->GetPingSlack()); |
61 | 0 | } |
62 | | |
63 | | CUser* m_pUser; |
64 | | }; |
65 | | |
66 | | CUser::CUser(const CString& sUsername) |
67 | 0 | : m_sUsername(sUsername), |
68 | 0 | m_sCleanUsername(MakeCleanUsername(sUsername)), |
69 | 0 | m_sNick(m_sCleanUsername), |
70 | 0 | m_sAltNick(""), |
71 | 0 | m_sIdent(m_sCleanUsername), |
72 | 0 | m_sRealName(""), |
73 | 0 | m_sBindHost(""), |
74 | 0 | m_sDCCBindHost(""), |
75 | 0 | m_sPass(""), |
76 | 0 | m_sPassSalt(""), |
77 | 0 | m_sStatusPrefix("*"), |
78 | 0 | m_sDefaultChanModes(""), |
79 | 0 | m_sClientEncoding(""), |
80 | 0 | m_sQuitMsg(""), |
81 | 0 | m_mssCTCPReplies(), |
82 | 0 | m_sTimestampFormat("[%H:%M:%S]"), |
83 | 0 | m_sTimezone(""), |
84 | 0 | m_eHashType(HASH_NONE), |
85 | 0 | m_sUserPath(CZNC::Get().GetUserPath() + "/" + sUsername), |
86 | 0 | m_bMultiClients(true), |
87 | 0 | m_bDenyLoadMod(false), |
88 | 0 | m_bAdmin(false), |
89 | 0 | m_bDenySetBindHost(false), |
90 | 0 | m_bDenySetIdent(false), |
91 | 0 | m_bDenySetNetwork(false), |
92 | 0 | m_bDenySetRealName(false), |
93 | 0 | m_bDenySetQuitMsg(false), |
94 | 0 | m_bDenySetCTCPReplies(false), |
95 | 0 | m_bAutoClearChanBuffer(true), |
96 | 0 | m_bAutoClearQueryBuffer(true), |
97 | 0 | m_bBeingDeleted(false), |
98 | 0 | m_bAppendTimestamp(false), |
99 | 0 | m_bPrependTimestamp(true), |
100 | 0 | m_bAuthOnlyViaModule(false), |
101 | 0 | m_pUserTimer(nullptr), |
102 | 0 | m_vIRCNetworks(), |
103 | 0 | m_vClients(), |
104 | 0 | m_ssAllowedHosts(), |
105 | 0 | m_uChanBufferSize(50), |
106 | 0 | m_uQueryBufferSize(50), |
107 | 0 | m_uBytesRead(0), |
108 | 0 | m_uBytesWritten(0), |
109 | 0 | m_uMaxJoinTries(10), |
110 | 0 | m_uMaxNetworks(1), |
111 | 0 | m_uMaxQueryBuffers(50), |
112 | 0 | m_uMaxJoins(0), |
113 | 0 | m_uNoTrafficTimeout(180), |
114 | 0 | m_sSkinName(""), |
115 | 0 | m_pModules(new CModules) { |
116 | 0 | m_pUserTimer = new CUserTimer(this); |
117 | 0 | CZNC::Get().GetManager().AddCron(m_pUserTimer); |
118 | 0 | } |
119 | | |
120 | 0 | CUser::~CUser() { |
121 | | // Delete networks |
122 | 0 | while (!m_vIRCNetworks.empty()) { |
123 | 0 | delete *m_vIRCNetworks.begin(); |
124 | 0 | } |
125 | | |
126 | | // Delete clients |
127 | 0 | while (!m_vClients.empty()) { |
128 | 0 | CZNC::Get().GetManager().DelSockByAddr(m_vClients[0]); |
129 | 0 | } |
130 | 0 | m_vClients.clear(); |
131 | | |
132 | | // Delete modules (unloads all modules!) |
133 | 0 | delete m_pModules; |
134 | 0 | m_pModules = nullptr; |
135 | |
|
136 | 0 | CZNC::Get().GetManager().DelCronByAddr(m_pUserTimer); |
137 | |
|
138 | 0 | CZNC::Get().AddBytesRead(m_uBytesRead); |
139 | 0 | CZNC::Get().AddBytesWritten(m_uBytesWritten); |
140 | 0 | } |
141 | | |
142 | | namespace { |
143 | | template <class T> |
144 | | struct TOption { |
145 | | const char* name; |
146 | | void (CUser::*pSetter)(T); |
147 | | }; |
148 | | } |
149 | | |
150 | 0 | bool CUser::ParseConfig(CConfig* pConfig, CString& sError) { |
151 | 0 | TOption<const CString&> StringOptions[] = { |
152 | 0 | {"nick", &CUser::SetNick}, |
153 | 0 | {"quitmsg", &CUser::SetQuitMsg}, |
154 | 0 | {"altnick", &CUser::SetAltNick}, |
155 | 0 | {"ident", &CUser::SetIdent}, |
156 | 0 | {"realname", &CUser::SetRealName}, |
157 | 0 | {"chanmodes", &CUser::SetDefaultChanModes}, |
158 | 0 | {"bindhost", &CUser::SetBindHost}, |
159 | 0 | {"vhost", &CUser::SetBindHost}, |
160 | 0 | {"dccbindhost", &CUser::SetDCCBindHost}, |
161 | 0 | {"dccvhost", &CUser::SetDCCBindHost}, |
162 | 0 | {"timestampformat", &CUser::SetTimestampFormat}, |
163 | 0 | {"skin", &CUser::SetSkinName}, |
164 | 0 | {"clientencoding", &CUser::SetClientEncoding}, |
165 | 0 | }; |
166 | 0 | TOption<unsigned int> UIntOptions[] = { |
167 | 0 | {"jointries", &CUser::SetJoinTries}, |
168 | 0 | {"maxnetworks", &CUser::SetMaxNetworks}, |
169 | 0 | {"maxquerybuffers", &CUser::SetMaxQueryBuffers}, |
170 | 0 | {"maxjoins", &CUser::SetMaxJoins}, |
171 | 0 | {"notraffictimeout", &CUser::SetNoTrafficTimeout}, |
172 | 0 | }; |
173 | 0 | TOption<bool> BoolOptions[] = { |
174 | 0 | {"keepbuffer", |
175 | 0 | &CUser::SetKeepBuffer}, // XXX compatibility crap from pre-0.207 |
176 | 0 | {"autoclearchanbuffer", &CUser::SetAutoClearChanBuffer}, |
177 | 0 | {"autoclearquerybuffer", &CUser::SetAutoClearQueryBuffer}, |
178 | 0 | {"multiclients", &CUser::SetMultiClients}, |
179 | 0 | {"denyloadmod", &CUser::SetDenyLoadMod}, |
180 | 0 | {"admin", &CUser::SetAdmin}, |
181 | 0 | {"denysetbindhost", &CUser::SetDenySetBindHost}, |
182 | 0 | {"denysetvhost", &CUser::SetDenySetBindHost}, |
183 | 0 | {"denysetident", &CUser::SetDenySetIdent}, |
184 | 0 | {"denysetnetwork", &CUser::SetDenySetNetwork}, |
185 | 0 | {"denysetrealname", &CUser::SetDenySetRealName}, |
186 | 0 | {"denysetquitmsg", &CUser::SetDenySetQuitMsg}, |
187 | 0 | {"denysetctcpreplies", &CUser::SetDenySetCTCPReplies}, |
188 | 0 | {"appendtimestamp", &CUser::SetTimestampAppend}, |
189 | 0 | {"prependtimestamp", &CUser::SetTimestampPrepend}, |
190 | 0 | {"authonlyviamodule", &CUser::SetAuthOnlyViaModule}, |
191 | 0 | }; |
192 | |
|
193 | 0 | for (const auto& Option : StringOptions) { |
194 | 0 | CString sValue; |
195 | 0 | if (pConfig->FindStringEntry(Option.name, sValue)) |
196 | 0 | (this->*Option.pSetter)(sValue); |
197 | 0 | } |
198 | 0 | for (const auto& Option : UIntOptions) { |
199 | 0 | CString sValue; |
200 | 0 | if (pConfig->FindStringEntry(Option.name, sValue)) |
201 | 0 | (this->*Option.pSetter)(sValue.ToUInt()); |
202 | 0 | } |
203 | 0 | for (const auto& Option : BoolOptions) { |
204 | 0 | CString sValue; |
205 | 0 | if (pConfig->FindStringEntry(Option.name, sValue)) |
206 | 0 | (this->*Option.pSetter)(sValue.ToBool()); |
207 | 0 | } |
208 | |
|
209 | 0 | VCString vsList; |
210 | 0 | pConfig->FindStringVector("allow", vsList); |
211 | 0 | for (const CString& sHost : vsList) { |
212 | 0 | AddAllowedHost(sHost); |
213 | 0 | } |
214 | 0 | pConfig->FindStringVector("ctcpreply", vsList); |
215 | 0 | for (const CString& sReply : vsList) { |
216 | 0 | AddCTCPReply(sReply.Token(0), sReply.Token(1, true)); |
217 | 0 | } |
218 | |
|
219 | 0 | CString sValue; |
220 | |
|
221 | 0 | CString sDCCLookupValue; |
222 | 0 | pConfig->FindStringEntry("dcclookupmethod", sDCCLookupValue); |
223 | 0 | if (pConfig->FindStringEntry("bouncedccs", sValue)) { |
224 | 0 | if (sValue.ToBool()) { |
225 | 0 | CUtils::PrintAction("Loading Module [bouncedcc]"); |
226 | 0 | CString sModRet; |
227 | 0 | bool bModRet = GetModules().LoadModule( |
228 | 0 | "bouncedcc", "", CModInfo::UserModule, this, nullptr, sModRet); |
229 | |
|
230 | 0 | CUtils::PrintStatus(bModRet, sModRet); |
231 | 0 | if (!bModRet) { |
232 | 0 | sError = sModRet; |
233 | 0 | return false; |
234 | 0 | } |
235 | | |
236 | 0 | if (sDCCLookupValue.Equals("Client")) { |
237 | 0 | GetModules().FindModule("bouncedcc")->SetNV("UseClientIP", "1"); |
238 | 0 | } |
239 | 0 | } |
240 | 0 | } |
241 | 0 | if (pConfig->FindStringEntry("buffer", sValue)) |
242 | 0 | SetBufferCount(sValue.ToUInt(), true); |
243 | 0 | if (pConfig->FindStringEntry("chanbuffersize", sValue)) |
244 | 0 | SetChanBufferSize(sValue.ToUInt(), true); |
245 | 0 | if (pConfig->FindStringEntry("querybuffersize", sValue)) |
246 | 0 | SetQueryBufferSize(sValue.ToUInt(), true); |
247 | 0 | if (pConfig->FindStringEntry("awaysuffix", sValue)) { |
248 | 0 | CUtils::PrintMessage( |
249 | 0 | "WARNING: AwaySuffix has been deprecated, instead try -> " |
250 | 0 | "LoadModule = awaynick %nick%_" + |
251 | 0 | sValue); |
252 | 0 | } |
253 | 0 | if (pConfig->FindStringEntry("autocycle", sValue)) { |
254 | 0 | if (sValue.Equals("true")) |
255 | 0 | CUtils::PrintError( |
256 | 0 | "WARNING: AutoCycle has been removed, instead try -> " |
257 | 0 | "LoadModule = autocycle"); |
258 | 0 | } |
259 | 0 | if (pConfig->FindStringEntry("keepnick", sValue)) { |
260 | 0 | if (sValue.Equals("true")) |
261 | 0 | CUtils::PrintError( |
262 | 0 | "WARNING: KeepNick has been deprecated, instead try -> " |
263 | 0 | "LoadModule = keepnick"); |
264 | 0 | } |
265 | 0 | if (pConfig->FindStringEntry("statusprefix", sValue)) { |
266 | 0 | if (!SetStatusPrefix(sValue)) { |
267 | 0 | sError = "Invalid StatusPrefix [" + sValue + |
268 | 0 | "] Must be 1-5 chars, no spaces."; |
269 | 0 | CUtils::PrintError(sError); |
270 | 0 | return false; |
271 | 0 | } |
272 | 0 | } |
273 | 0 | if (pConfig->FindStringEntry("timezone", sValue)) { |
274 | 0 | SetTimezone(sValue); |
275 | 0 | } |
276 | 0 | if (pConfig->FindStringEntry("timezoneoffset", sValue)) { |
277 | 0 | if (fabs(sValue.ToDouble()) > 0.1) { |
278 | 0 | CUtils::PrintError( |
279 | 0 | "WARNING: TimezoneOffset has been deprecated, now you can set " |
280 | 0 | "your timezone by name"); |
281 | 0 | } |
282 | 0 | } |
283 | 0 | if (pConfig->FindStringEntry("timestamp", sValue)) { |
284 | 0 | if (!sValue.Trim_n().Equals("true")) { |
285 | 0 | if (sValue.Trim_n().Equals("append")) { |
286 | 0 | SetTimestampAppend(true); |
287 | 0 | SetTimestampPrepend(false); |
288 | 0 | } else if (sValue.Trim_n().Equals("prepend")) { |
289 | 0 | SetTimestampAppend(false); |
290 | 0 | SetTimestampPrepend(true); |
291 | 0 | } else if (sValue.Trim_n().Equals("false")) { |
292 | 0 | SetTimestampAppend(false); |
293 | 0 | SetTimestampPrepend(false); |
294 | 0 | } else { |
295 | 0 | SetTimestampFormat(sValue); |
296 | 0 | } |
297 | 0 | } |
298 | 0 | } |
299 | 0 | if (pConfig->FindStringEntry("language", sValue)) { |
300 | 0 | SetLanguage(sValue); |
301 | 0 | } |
302 | 0 | pConfig->FindStringEntry("pass", sValue); |
303 | | // There are different formats for this available: |
304 | | // Pass = <plain text> |
305 | | // Pass = <md5 hash> - |
306 | | // Pass = plain#<plain text> |
307 | | // Pass = <hash name>#<hash> |
308 | | // Pass = <hash name>#<salted hash>#<salt># |
309 | | // 'Salted hash' means hash of 'password' + 'salt' |
310 | | // Possible hashes are md5 and sha256 |
311 | 0 | if (sValue.TrimSuffix("-")) { |
312 | 0 | SetPass(sValue.Trim_n(), CUser::HASH_MD5); |
313 | 0 | } else { |
314 | 0 | CString sMethod = sValue.Token(0, false, "#"); |
315 | 0 | CString sPass = sValue.Token(1, true, "#"); |
316 | 0 | if (sMethod == "md5" || sMethod == "sha256") { |
317 | 0 | CUser::eHashType type = CUser::HASH_MD5; |
318 | 0 | if (sMethod == "sha256") type = CUser::HASH_SHA256; |
319 | |
|
320 | 0 | CString sSalt = sPass.Token(1, false, "#"); |
321 | 0 | sPass = sPass.Token(0, false, "#"); |
322 | 0 | SetPass(sPass, type, sSalt); |
323 | 0 | } else if (sMethod == "plain") { |
324 | 0 | SetPass(sPass, CUser::HASH_NONE); |
325 | 0 | } else { |
326 | 0 | SetPass(sValue, CUser::HASH_NONE); |
327 | 0 | } |
328 | 0 | } |
329 | 0 | CConfig::SubConfig subConf; |
330 | 0 | CConfig::SubConfig::const_iterator subIt; |
331 | 0 | pConfig->FindSubConfig("pass", subConf); |
332 | 0 | if (!sValue.empty() && !subConf.empty()) { |
333 | 0 | sError = "Password defined more than once"; |
334 | 0 | CUtils::PrintError(sError); |
335 | 0 | return false; |
336 | 0 | } |
337 | 0 | subIt = subConf.begin(); |
338 | 0 | if (subIt != subConf.end()) { |
339 | 0 | CConfig* pSubConf = subIt->second.m_pSubConfig; |
340 | 0 | CString sHash; |
341 | 0 | CString sMethod; |
342 | 0 | CString sSalt; |
343 | 0 | CUser::eHashType method; |
344 | 0 | pSubConf->FindStringEntry("hash", sHash); |
345 | 0 | pSubConf->FindStringEntry("method", sMethod); |
346 | 0 | pSubConf->FindStringEntry("salt", sSalt); |
347 | 0 | if (sMethod.empty() || sMethod.Equals("plain")) |
348 | 0 | method = CUser::HASH_NONE; |
349 | 0 | else if (sMethod.Equals("md5")) |
350 | 0 | method = CUser::HASH_MD5; |
351 | 0 | else if (sMethod.Equals("sha256")) |
352 | 0 | method = CUser::HASH_SHA256; |
353 | 0 | else if (sMethod.Equals("Argon2id")) { |
354 | 0 | method = CUser::HASH_ARGON2ID; |
355 | 0 | #ifndef ZNC_HAVE_ARGON |
356 | 0 | CUtils::PrintError("ZNC is built without Argon2 support, " + GetUsername() + " won't be able to authenticate"); |
357 | 0 | #endif |
358 | 0 | } else { |
359 | 0 | sError = "Invalid hash method"; |
360 | 0 | CUtils::PrintError(sError); |
361 | 0 | return false; |
362 | 0 | } |
363 | | |
364 | 0 | SetPass(sHash, method, sSalt); |
365 | 0 | if (!pSubConf->empty()) { |
366 | 0 | sError = "Unhandled lines in config!"; |
367 | 0 | CUtils::PrintError(sError); |
368 | |
|
369 | 0 | CZNC::DumpConfig(pSubConf); |
370 | 0 | return false; |
371 | 0 | } |
372 | 0 | ++subIt; |
373 | 0 | } |
374 | 0 | if (subIt != subConf.end()) { |
375 | 0 | sError = "Password defined more than once"; |
376 | 0 | CUtils::PrintError(sError); |
377 | 0 | return false; |
378 | 0 | } |
379 | | |
380 | 0 | pConfig->FindSubConfig("network", subConf); |
381 | 0 | for (subIt = subConf.begin(); subIt != subConf.end(); ++subIt) { |
382 | 0 | const CString& sNetworkName = subIt->first; |
383 | |
|
384 | 0 | CUtils::PrintMessage("Loading network [" + sNetworkName + "]"); |
385 | |
|
386 | 0 | CIRCNetwork* pNetwork = FindNetwork(sNetworkName); |
387 | |
|
388 | 0 | if (!pNetwork) { |
389 | 0 | pNetwork = new CIRCNetwork(this, sNetworkName); |
390 | 0 | } |
391 | |
|
392 | 0 | if (!pNetwork->ParseConfig(subIt->second.m_pSubConfig, sError)) { |
393 | 0 | return false; |
394 | 0 | } |
395 | 0 | } |
396 | | |
397 | 0 | if (pConfig->FindStringVector("server", vsList, false) || |
398 | 0 | pConfig->FindStringVector("chan", vsList, false) || |
399 | 0 | pConfig->FindSubConfig("chan", subConf, false)) { |
400 | 0 | CIRCNetwork* pNetwork = FindNetwork("default"); |
401 | 0 | if (!pNetwork) { |
402 | 0 | CString sErrorDummy; |
403 | 0 | pNetwork = AddNetwork("default", sErrorDummy); |
404 | 0 | } |
405 | |
|
406 | 0 | if (pNetwork) { |
407 | 0 | CUtils::PrintMessage( |
408 | 0 | "NOTICE: Found deprecated config, upgrading to a network"); |
409 | |
|
410 | 0 | if (!pNetwork->ParseConfig(pConfig, sError, true)) { |
411 | 0 | return false; |
412 | 0 | } |
413 | 0 | } |
414 | 0 | } |
415 | | |
416 | 0 | pConfig->FindStringVector("loadmodule", vsList); |
417 | 0 | for (const CString& sMod : vsList) { |
418 | 0 | CString sModName = sMod.Token(0); |
419 | 0 | CString sNotice = "Loading user module [" + sModName + "]"; |
420 | | |
421 | | // XXX Legacy crap, added in ZNC 0.089 |
422 | 0 | if (sModName == "discon_kick") { |
423 | 0 | sNotice = |
424 | 0 | "NOTICE: [discon_kick] was renamed, loading [disconkick] " |
425 | 0 | "instead"; |
426 | 0 | sModName = "disconkick"; |
427 | 0 | } |
428 | | |
429 | | // XXX Legacy crap, added in ZNC 0.099 |
430 | 0 | if (sModName == "fixfreenode") { |
431 | 0 | sNotice = |
432 | 0 | "NOTICE: [fixfreenode] doesn't do anything useful anymore, " |
433 | 0 | "ignoring it"; |
434 | 0 | CUtils::PrintMessage(sNotice); |
435 | 0 | continue; |
436 | 0 | } |
437 | | |
438 | | // XXX Legacy crap, added in ZNC 0.207 |
439 | 0 | if (sModName == "admin") { |
440 | 0 | sNotice = |
441 | 0 | "NOTICE: [admin] module was renamed, loading [controlpanel] " |
442 | 0 | "instead"; |
443 | 0 | sModName = "controlpanel"; |
444 | 0 | } |
445 | | |
446 | | // XXX Legacy crap, should have been added ZNC 0.207, but added only in |
447 | | // 1.1 :( |
448 | 0 | if (sModName == "away") { |
449 | 0 | sNotice = "NOTICE: [away] was renamed, loading [awaystore] instead"; |
450 | 0 | sModName = "awaystore"; |
451 | 0 | } |
452 | | |
453 | | // XXX Legacy crap, added in 1.1; fakeonline module was dropped in 1.0 |
454 | | // and returned in 1.1 |
455 | 0 | if (sModName == "fakeonline") { |
456 | 0 | sNotice = |
457 | 0 | "NOTICE: [fakeonline] was renamed, loading [modules_online] " |
458 | 0 | "instead"; |
459 | 0 | sModName = "modules_online"; |
460 | 0 | } |
461 | | |
462 | | // XXX Legacy crap, added in 1.3 |
463 | 0 | if (sModName == "charset") { |
464 | 0 | CUtils::PrintAction( |
465 | 0 | "NOTICE: Charset support was moved to core, importing old " |
466 | 0 | "charset module settings"); |
467 | 0 | size_t uIndex = 1; |
468 | 0 | if (sMod.Token(uIndex).Equals("-force")) { |
469 | 0 | uIndex++; |
470 | 0 | } |
471 | 0 | VCString vsClient, vsServer; |
472 | 0 | sMod.Token(uIndex).Split(",", vsClient); |
473 | 0 | sMod.Token(uIndex + 1).Split(",", vsServer); |
474 | 0 | if (vsClient.empty() || vsServer.empty()) { |
475 | 0 | CUtils::PrintStatus( |
476 | 0 | false, "charset module was loaded with wrong parameters."); |
477 | 0 | continue; |
478 | 0 | } |
479 | 0 | SetClientEncoding(vsClient[0]); |
480 | 0 | for (CIRCNetwork* pNetwork : m_vIRCNetworks) { |
481 | 0 | pNetwork->SetEncoding(vsServer[0]); |
482 | 0 | } |
483 | 0 | CUtils::PrintStatus(true, "Using [" + vsClient[0] + |
484 | 0 | "] for clients, and [" + vsServer[0] + |
485 | 0 | "] for servers"); |
486 | 0 | continue; |
487 | 0 | } |
488 | | |
489 | 0 | CString sModRet; |
490 | 0 | CString sArgs = sMod.Token(1, true); |
491 | |
|
492 | 0 | bool bModRet = LoadModule(sModName, sArgs, sNotice, sModRet); |
493 | |
|
494 | 0 | CUtils::PrintStatus(bModRet, sModRet); |
495 | 0 | if (!bModRet) { |
496 | | // XXX The awaynick module was retired in 1.6 (still available as |
497 | | // external module) |
498 | 0 | if (sModName == "awaynick") { |
499 | | // load simple_away instead, unless it's already on the list |
500 | 0 | if (std::find(vsList.begin(), vsList.end(), "simple_away") == |
501 | 0 | vsList.end()) { |
502 | 0 | sNotice = "Loading [simple_away] module instead"; |
503 | 0 | sModName = "simple_away"; |
504 | | // not a fatal error if simple_away is not available |
505 | 0 | LoadModule(sModName, sArgs, sNotice, sModRet); |
506 | 0 | } |
507 | 0 | } else { |
508 | 0 | sError = sModRet; |
509 | 0 | return false; |
510 | 0 | } |
511 | 0 | } |
512 | 0 | continue; |
513 | 0 | } |
514 | | |
515 | | // Move ircconnectenabled to the networks |
516 | 0 | if (pConfig->FindStringEntry("ircconnectenabled", sValue)) { |
517 | 0 | for (CIRCNetwork* pNetwork : m_vIRCNetworks) { |
518 | 0 | pNetwork->SetIRCConnectEnabled(sValue.ToBool()); |
519 | 0 | } |
520 | 0 | } |
521 | |
|
522 | 0 | return true; |
523 | 0 | } |
524 | | |
525 | 0 | CIRCNetwork* CUser::AddNetwork(const CString& sNetwork, CString& sErrorRet) { |
526 | 0 | if (!CIRCNetwork::IsValidNetwork(sNetwork)) { |
527 | 0 | sErrorRet = |
528 | 0 | t_s("Invalid network name. It should be alphanumeric. Not to be " |
529 | 0 | "confused with server name"); |
530 | 0 | return nullptr; |
531 | 0 | } else if (FindNetwork(sNetwork)) { |
532 | 0 | sErrorRet = t_f("Network {1} already exists")(sNetwork); |
533 | 0 | return nullptr; |
534 | 0 | } |
535 | | |
536 | 0 | CIRCNetwork* pNetwork = new CIRCNetwork(this, sNetwork); |
537 | |
|
538 | 0 | bool bCancel = false; |
539 | 0 | USERMODULECALL(OnAddNetwork(*pNetwork, sErrorRet), this, nullptr, &bCancel); |
540 | 0 | if (bCancel) { |
541 | 0 | RemoveNetwork(pNetwork); |
542 | 0 | delete pNetwork; |
543 | 0 | return nullptr; |
544 | 0 | } |
545 | | |
546 | 0 | return pNetwork; |
547 | 0 | } |
548 | | |
549 | 0 | bool CUser::AddNetwork(CIRCNetwork* pNetwork) { |
550 | 0 | if (FindNetwork(pNetwork->GetName())) { |
551 | 0 | return false; |
552 | 0 | } |
553 | | |
554 | 0 | m_vIRCNetworks.push_back(pNetwork); |
555 | |
|
556 | 0 | return true; |
557 | 0 | } |
558 | | |
559 | 0 | void CUser::RemoveNetwork(CIRCNetwork* pNetwork) { |
560 | 0 | auto it = std::find(m_vIRCNetworks.begin(), m_vIRCNetworks.end(), pNetwork); |
561 | 0 | if (it != m_vIRCNetworks.end()) { |
562 | 0 | m_vIRCNetworks.erase(it); |
563 | 0 | } |
564 | 0 | } |
565 | | |
566 | 0 | bool CUser::DeleteNetwork(const CString& sNetwork) { |
567 | 0 | CIRCNetwork* pNetwork = FindNetwork(sNetwork); |
568 | |
|
569 | 0 | if (pNetwork) { |
570 | 0 | bool bCancel = false; |
571 | 0 | USERMODULECALL(OnDeleteNetwork(*pNetwork), this, nullptr, &bCancel); |
572 | 0 | if (!bCancel) { |
573 | 0 | delete pNetwork; |
574 | 0 | return true; |
575 | 0 | } |
576 | 0 | } |
577 | | |
578 | 0 | return false; |
579 | 0 | } |
580 | | |
581 | 0 | CIRCNetwork* CUser::FindNetwork(const CString& sNetwork) const { |
582 | 0 | for (CIRCNetwork* pNetwork : m_vIRCNetworks) { |
583 | 0 | if (pNetwork->GetName().Equals(sNetwork)) { |
584 | 0 | return pNetwork; |
585 | 0 | } |
586 | 0 | } |
587 | | |
588 | 0 | return nullptr; |
589 | 0 | } |
590 | | |
591 | 0 | const vector<CIRCNetwork*>& CUser::GetNetworks() const { |
592 | 0 | return m_vIRCNetworks; |
593 | 0 | } |
594 | | |
595 | 0 | CString CUser::ExpandString(const CString& sStr) const { |
596 | 0 | CString sRet; |
597 | 0 | return ExpandString(sStr, sRet); |
598 | 0 | } |
599 | | |
600 | 0 | CString& CUser::ExpandString(const CString& sStr, CString& sRet) const { |
601 | 0 | CString sTime = CUtils::CTime(time(nullptr), m_sTimezone); |
602 | |
|
603 | 0 | sRet = sStr; |
604 | 0 | sRet.Replace("%altnick%", GetAltNick()); |
605 | 0 | sRet.Replace("%bindhost%", GetBindHost()); |
606 | 0 | sRet.Replace("%defnick%", GetNick()); |
607 | 0 | sRet.Replace("%ident%", GetIdent()); |
608 | 0 | sRet.Replace("%nick%", GetNick()); |
609 | 0 | sRet.Replace("%realname%", GetRealName()); |
610 | 0 | sRet.Replace("%time%", sTime); |
611 | 0 | sRet.Replace("%uptime%", CZNC::Get().GetUptime()); |
612 | 0 | sRet.Replace("%user%", GetUsername()); |
613 | 0 | sRet.Replace("%version%", CZNC::GetVersion()); |
614 | 0 | sRet.Replace("%vhost%", GetBindHost()); |
615 | 0 | sRet.Replace("%znc%", CZNC::GetTag(false)); |
616 | | |
617 | | // Allows for escaping ExpandString if necessary, or to prevent |
618 | | // defaults from kicking in if you don't want them. |
619 | 0 | sRet.Replace("%empty%", ""); |
620 | | // The following lines do not exist. You must be on DrUgS! |
621 | 0 | sRet.Replace("%irc%", "All your IRC are belong to ZNC"); |
622 | | // Chosen by fair zocchihedron dice roll by SilverLeo |
623 | 0 | sRet.Replace("%rand%", "42"); |
624 | |
|
625 | 0 | return sRet; |
626 | 0 | } |
627 | | |
628 | 0 | CString CUser::AddTimestamp(const CString& sStr) const { |
629 | 0 | timeval tv; |
630 | 0 | gettimeofday(&tv, nullptr); |
631 | 0 | return AddTimestamp(tv, sStr); |
632 | 0 | } |
633 | | |
634 | 0 | CString CUser::AddTimestamp(time_t tm, const CString& sStr) const { |
635 | 0 | timeval tv; |
636 | 0 | tv.tv_sec = tm; |
637 | 0 | tv.tv_usec = 0; |
638 | 0 | return AddTimestamp(tv, sStr); |
639 | 0 | } |
640 | | |
641 | 0 | CString CUser::AddTimestamp(timeval tv, const CString& sStr) const { |
642 | 0 | CString sRet = sStr; |
643 | |
|
644 | 0 | if (!GetTimestampFormat().empty() && |
645 | 0 | (m_bAppendTimestamp || m_bPrependTimestamp)) { |
646 | 0 | CString sTimestamp = |
647 | 0 | CUtils::FormatTime(tv, GetTimestampFormat(), m_sTimezone); |
648 | 0 | if (sTimestamp.empty()) { |
649 | 0 | return sRet; |
650 | 0 | } |
651 | | |
652 | 0 | if (m_bPrependTimestamp) { |
653 | 0 | sRet = sTimestamp; |
654 | 0 | sRet += " " + sStr; |
655 | 0 | } |
656 | 0 | if (m_bAppendTimestamp) { |
657 | | // From http://www.mirc.com/colors.html |
658 | | // The Control+O key combination in mIRC inserts ascii character 15, |
659 | | // which turns off all previous attributes, including color, bold, |
660 | | // underline, and italics. |
661 | | // |
662 | | // \x02 bold |
663 | | // \x03 mIRC-compatible color |
664 | | // \x04 RRGGBB color |
665 | | // \x0F normal/reset (turn off bold, colors, etc.) |
666 | | // \x12 reverse (weechat) |
667 | | // \x16 reverse (mirc, kvirc) |
668 | | // \x1D italic |
669 | | // \x1F underline |
670 | | // Also see http://www.visualirc.net/tech-attrs.php |
671 | | // |
672 | | // Keep in sync with CIRCSocket::IcuExt__UCallback |
673 | 0 | if (CString::npos != |
674 | 0 | sRet.find_first_of("\x02\x03\x04\x0F\x12\x16\x1D\x1F")) { |
675 | 0 | sRet += "\x0F"; |
676 | 0 | } |
677 | |
|
678 | 0 | sRet += " " + sTimestamp; |
679 | 0 | } |
680 | 0 | } |
681 | | |
682 | 0 | return sRet; |
683 | 0 | } |
684 | | |
685 | 0 | void CUser::BounceAllClients() { |
686 | 0 | for (CClient* pClient : m_vClients) { |
687 | 0 | pClient->BouncedOff(); |
688 | 0 | } |
689 | |
|
690 | 0 | m_vClients.clear(); |
691 | 0 | } |
692 | | |
693 | 0 | void CUser::UserConnected(CClient* pClient) { |
694 | 0 | if (!MultiClients()) { |
695 | 0 | BounceAllClients(); |
696 | 0 | } |
697 | |
|
698 | 0 | pClient->PutClient(":irc.znc.in 001 " + pClient->GetNick() + " :" + |
699 | 0 | t_s("Welcome to ZNC")); |
700 | |
|
701 | 0 | m_vClients.push_back(pClient); |
702 | 0 | } |
703 | | |
704 | 0 | void CUser::UserDisconnected(CClient* pClient) { |
705 | 0 | auto it = std::find(m_vClients.begin(), m_vClients.end(), pClient); |
706 | 0 | if (it != m_vClients.end()) { |
707 | 0 | m_vClients.erase(it); |
708 | 0 | } |
709 | 0 | } |
710 | | |
711 | 0 | void CUser::CloneNetworks(const CUser& User) { |
712 | 0 | const vector<CIRCNetwork*>& vNetworks = User.GetNetworks(); |
713 | 0 | for (CIRCNetwork* pUserNetwork : vNetworks) { |
714 | 0 | CIRCNetwork* pNetwork = FindNetwork(pUserNetwork->GetName()); |
715 | |
|
716 | 0 | if (pNetwork) { |
717 | 0 | pNetwork->Clone(*pUserNetwork); |
718 | 0 | } else { |
719 | 0 | new CIRCNetwork(this, *pUserNetwork); |
720 | 0 | } |
721 | 0 | } |
722 | |
|
723 | 0 | set<CString> ssDeleteNetworks; |
724 | 0 | for (CIRCNetwork* pNetwork : m_vIRCNetworks) { |
725 | 0 | if (!(User.FindNetwork(pNetwork->GetName()))) { |
726 | 0 | ssDeleteNetworks.insert(pNetwork->GetName()); |
727 | 0 | } |
728 | 0 | } |
729 | |
|
730 | 0 | for (const CString& sNetwork : ssDeleteNetworks) { |
731 | | // The following will move all the clients to the user. |
732 | | // So the clients are not disconnected. The client could |
733 | | // have requested the rehash. Then when we do |
734 | | // client->PutStatus("Rehashing succeeded!") we would |
735 | | // crash if there was no client anymore. |
736 | 0 | const vector<CClient*>& vClients = FindNetwork(sNetwork)->GetClients(); |
737 | |
|
738 | 0 | while (vClients.begin() != vClients.end()) { |
739 | 0 | CClient* pClient = vClients.front(); |
740 | | // This line will remove pClient from vClients, |
741 | | // because it's a reference to the internal Network's vector. |
742 | 0 | pClient->SetNetwork(nullptr); |
743 | 0 | } |
744 | |
|
745 | 0 | DeleteNetwork(sNetwork); |
746 | 0 | } |
747 | 0 | } |
748 | | |
749 | 0 | bool CUser::Clone(const CUser& User, CString& sErrorRet, bool bCloneNetworks) { |
750 | 0 | sErrorRet.clear(); |
751 | |
|
752 | 0 | if (!User.IsValid(sErrorRet, true)) { |
753 | 0 | return false; |
754 | 0 | } |
755 | | |
756 | | // user names can only specified for the constructor, changing it later |
757 | | // on breaks too much stuff (e.g. lots of paths depend on the user name) |
758 | 0 | if (GetUsername() != User.GetUsername()) { |
759 | 0 | DEBUG("Ignoring username in CUser::Clone(), old username [" |
760 | 0 | << GetUsername() << "]; New username [" << User.GetUsername() |
761 | 0 | << "]"); |
762 | 0 | } |
763 | |
|
764 | 0 | if (!User.GetPass().empty()) { |
765 | 0 | SetPass(User.GetPass(), User.GetPassHashType(), User.GetPassSalt()); |
766 | 0 | } |
767 | |
|
768 | 0 | SetNick(User.GetNick(false)); |
769 | 0 | SetAltNick(User.GetAltNick(false)); |
770 | 0 | SetIdent(User.GetIdent(false)); |
771 | 0 | SetRealName(User.GetRealName()); |
772 | 0 | SetStatusPrefix(User.GetStatusPrefix()); |
773 | 0 | SetBindHost(User.GetBindHost()); |
774 | 0 | SetDCCBindHost(User.GetDCCBindHost()); |
775 | 0 | SetQuitMsg(User.GetQuitMsg()); |
776 | 0 | SetSkinName(User.GetSkinName()); |
777 | 0 | SetDefaultChanModes(User.GetDefaultChanModes()); |
778 | 0 | SetChanBufferSize(User.GetChanBufferSize(), true); |
779 | 0 | SetQueryBufferSize(User.GetQueryBufferSize(), true); |
780 | 0 | SetJoinTries(User.JoinTries()); |
781 | 0 | SetMaxNetworks(User.MaxNetworks()); |
782 | 0 | SetMaxQueryBuffers(User.MaxQueryBuffers()); |
783 | 0 | SetMaxJoins(User.MaxJoins()); |
784 | 0 | SetNoTrafficTimeout(User.GetNoTrafficTimeout()); |
785 | 0 | SetClientEncoding(User.GetClientEncoding()); |
786 | 0 | SetLanguage(User.GetLanguage()); |
787 | | |
788 | | // Allowed Hosts |
789 | 0 | m_ssAllowedHosts.clear(); |
790 | 0 | const set<CString>& ssHosts = User.GetAllowedHosts(); |
791 | 0 | for (const CString& sHost : ssHosts) { |
792 | 0 | AddAllowedHost(sHost); |
793 | 0 | } |
794 | |
|
795 | 0 | for (CClient* pSock : m_vClients) { |
796 | 0 | if (!IsHostAllowed(pSock->GetRemoteIP())) { |
797 | 0 | pSock->PutStatusNotice( |
798 | 0 | t_s("You are being disconnected because your IP is no longer " |
799 | 0 | "allowed to connect to this user")); |
800 | 0 | pSock->Close(); |
801 | 0 | } |
802 | 0 | } |
803 | | |
804 | | // !Allowed Hosts |
805 | | |
806 | | // Networks |
807 | 0 | if (bCloneNetworks) { |
808 | 0 | CloneNetworks(User); |
809 | 0 | } |
810 | | // !Networks |
811 | | |
812 | | // CTCP Replies |
813 | 0 | m_mssCTCPReplies.clear(); |
814 | 0 | const MCString& msReplies = User.GetCTCPReplies(); |
815 | 0 | for (const auto& it : msReplies) { |
816 | 0 | AddCTCPReply(it.first, it.second); |
817 | 0 | } |
818 | | // !CTCP Replies |
819 | | |
820 | | // Flags |
821 | 0 | SetAutoClearChanBuffer(User.AutoClearChanBuffer()); |
822 | 0 | SetAutoClearQueryBuffer(User.AutoClearQueryBuffer()); |
823 | 0 | SetMultiClients(User.MultiClients()); |
824 | 0 | SetDenyLoadMod(User.DenyLoadMod()); |
825 | 0 | SetAdmin(User.IsAdmin()); |
826 | 0 | SetDenySetBindHost(User.DenySetBindHost()); |
827 | 0 | SetDenySetIdent(User.DenySetIdent()); |
828 | 0 | SetDenySetNetwork(User.DenySetNetwork()); |
829 | 0 | SetDenySetRealName(User.DenySetRealName()); |
830 | 0 | SetDenySetQuitMsg(User.DenySetQuitMsg()); |
831 | 0 | SetDenySetCTCPReplies(User.DenySetCTCPReplies()); |
832 | 0 | SetAuthOnlyViaModule(User.AuthOnlyViaModule()); |
833 | 0 | SetTimestampAppend(User.GetTimestampAppend()); |
834 | 0 | SetTimestampPrepend(User.GetTimestampPrepend()); |
835 | 0 | SetTimestampFormat(User.GetTimestampFormat()); |
836 | 0 | SetTimezone(User.GetTimezone()); |
837 | | // !Flags |
838 | | |
839 | | // Modules |
840 | 0 | set<CString> ssUnloadMods; |
841 | 0 | CModules& vCurMods = GetModules(); |
842 | 0 | const CModules& vNewMods = User.GetModules(); |
843 | |
|
844 | 0 | for (CModule* pNewMod : vNewMods) { |
845 | 0 | CString sModRet; |
846 | 0 | CModule* pCurMod = vCurMods.FindModule(pNewMod->GetModName()); |
847 | |
|
848 | 0 | if (!pCurMod) { |
849 | 0 | vCurMods.LoadModule(pNewMod->GetModName(), pNewMod->GetArgs(), |
850 | 0 | CModInfo::UserModule, this, nullptr, sModRet); |
851 | 0 | } else if (pNewMod->GetArgs() != pCurMod->GetArgs()) { |
852 | 0 | vCurMods.ReloadModule(pNewMod->GetModName(), pNewMod->GetArgs(), |
853 | 0 | this, nullptr, sModRet); |
854 | 0 | } |
855 | 0 | } |
856 | |
|
857 | 0 | for (CModule* pCurMod : vCurMods) { |
858 | 0 | CModule* pNewMod = vNewMods.FindModule(pCurMod->GetModName()); |
859 | |
|
860 | 0 | if (!pNewMod) { |
861 | 0 | ssUnloadMods.insert(pCurMod->GetModName()); |
862 | 0 | } |
863 | 0 | } |
864 | |
|
865 | 0 | for (const CString& sMod : ssUnloadMods) { |
866 | 0 | vCurMods.UnloadModule(sMod); |
867 | 0 | } |
868 | | // !Modules |
869 | |
|
870 | 0 | return true; |
871 | 0 | } |
872 | | |
873 | 0 | const set<CString>& CUser::GetAllowedHosts() const { return m_ssAllowedHosts; } |
874 | 0 | bool CUser::AddAllowedHost(const CString& sHostMask) { |
875 | 0 | if (sHostMask.empty() || |
876 | 0 | m_ssAllowedHosts.find(sHostMask) != m_ssAllowedHosts.end()) { |
877 | 0 | return false; |
878 | 0 | } |
879 | | |
880 | 0 | m_ssAllowedHosts.insert(sHostMask); |
881 | 0 | return true; |
882 | 0 | } |
883 | 0 | bool CUser::RemAllowedHost(const CString& sHostMask) { |
884 | 0 | return m_ssAllowedHosts.erase(sHostMask) > 0; |
885 | 0 | } |
886 | 0 | void CUser::ClearAllowedHosts() { m_ssAllowedHosts.clear(); } |
887 | | |
888 | 0 | bool CUser::IsHostAllowed(const CString& sHost) const { |
889 | 0 | if (m_ssAllowedHosts.empty()) { |
890 | 0 | return true; |
891 | 0 | } |
892 | | |
893 | 0 | for (const CString& sAllowedHost : m_ssAllowedHosts) { |
894 | 0 | if (CUtils::CheckCIDR(sHost, sAllowedHost)) { |
895 | 0 | return true; |
896 | 0 | } |
897 | 0 | } |
898 | | |
899 | 0 | return false; |
900 | 0 | } |
901 | | |
902 | 0 | const CString& CUser::GetTimestampFormat() const { return m_sTimestampFormat; } |
903 | 0 | bool CUser::GetTimestampAppend() const { return m_bAppendTimestamp; } |
904 | 0 | bool CUser::GetTimestampPrepend() const { return m_bPrependTimestamp; } |
905 | | |
906 | 0 | bool CUser::IsValidUsername(const CString& sUsername) { |
907 | 0 | return CUser::IsValidUserName(sUsername); |
908 | 0 | } |
909 | | |
910 | | /// @deprecated |
911 | 0 | bool CUser::IsValidUserName(const CString& sUsername) { |
912 | | // /^[a-zA-Z][a-zA-Z@._\-]*$/ |
913 | 0 | const char* p = sUsername.c_str(); |
914 | |
|
915 | 0 | if (sUsername.empty()) { |
916 | 0 | return false; |
917 | 0 | } |
918 | | |
919 | 0 | if ((*p < 'a' || *p > 'z') && (*p < 'A' || *p > 'Z')) { |
920 | 0 | return false; |
921 | 0 | } |
922 | | |
923 | 0 | while (*p) { |
924 | 0 | if (*p != '@' && *p != '.' && *p != '-' && *p != '_' && !isalnum(*p)) { |
925 | 0 | return false; |
926 | 0 | } |
927 | | |
928 | 0 | p++; |
929 | 0 | } |
930 | | |
931 | 0 | return true; |
932 | 0 | } |
933 | | |
934 | 0 | bool CUser::IsValid(CString& sErrMsg, bool bSkipPass) const { |
935 | 0 | sErrMsg.clear(); |
936 | |
|
937 | 0 | if (!bSkipPass && m_sPass.empty()) { |
938 | 0 | sErrMsg = t_s("Password is empty"); |
939 | 0 | return false; |
940 | 0 | } |
941 | | |
942 | 0 | if (m_sUsername.empty()) { |
943 | 0 | sErrMsg = t_s("Username is empty"); |
944 | 0 | return false; |
945 | 0 | } |
946 | | |
947 | 0 | if (!CUser::IsValidUsername(m_sUsername)) { |
948 | 0 | sErrMsg = t_s("Username is invalid"); |
949 | 0 | return false; |
950 | 0 | } |
951 | | |
952 | 0 | return true; |
953 | 0 | } |
954 | | |
955 | 0 | CConfig CUser::ToConfig() const { |
956 | 0 | CConfig config; |
957 | 0 | CConfig passConfig; |
958 | |
|
959 | 0 | CString sHash; |
960 | 0 | switch (m_eHashType) { |
961 | 0 | case HASH_NONE: |
962 | 0 | sHash = "Plain"; |
963 | 0 | break; |
964 | 0 | case HASH_MD5: |
965 | 0 | sHash = "MD5"; |
966 | 0 | break; |
967 | 0 | case HASH_SHA256: |
968 | 0 | sHash = "SHA256"; |
969 | 0 | break; |
970 | 0 | case HASH_ARGON2ID: |
971 | 0 | sHash = "Argon2id"; |
972 | 0 | break; |
973 | 0 | } |
974 | 0 | passConfig.AddKeyValuePair("Salt", m_sPassSalt); |
975 | 0 | passConfig.AddKeyValuePair("Method", sHash); |
976 | 0 | passConfig.AddKeyValuePair("Hash", GetPass()); |
977 | 0 | config.AddSubConfig("Pass", "password", passConfig); |
978 | |
|
979 | 0 | config.AddKeyValuePair("Nick", GetNick()); |
980 | 0 | config.AddKeyValuePair("AltNick", GetAltNick()); |
981 | 0 | config.AddKeyValuePair("Ident", GetIdent()); |
982 | 0 | config.AddKeyValuePair("RealName", GetRealName()); |
983 | 0 | config.AddKeyValuePair("BindHost", GetBindHost()); |
984 | 0 | config.AddKeyValuePair("DCCBindHost", GetDCCBindHost()); |
985 | 0 | config.AddKeyValuePair("QuitMsg", GetQuitMsg()); |
986 | 0 | if (CZNC::Get().GetStatusPrefix() != GetStatusPrefix()) |
987 | 0 | config.AddKeyValuePair("StatusPrefix", GetStatusPrefix()); |
988 | 0 | config.AddKeyValuePair("Skin", GetSkinName()); |
989 | 0 | config.AddKeyValuePair("ChanModes", GetDefaultChanModes()); |
990 | 0 | config.AddKeyValuePair("ChanBufferSize", CString(GetChanBufferSize())); |
991 | 0 | config.AddKeyValuePair("QueryBufferSize", CString(GetQueryBufferSize())); |
992 | 0 | config.AddKeyValuePair("AutoClearChanBuffer", |
993 | 0 | CString(AutoClearChanBuffer())); |
994 | 0 | config.AddKeyValuePair("AutoClearQueryBuffer", |
995 | 0 | CString(AutoClearQueryBuffer())); |
996 | 0 | config.AddKeyValuePair("MultiClients", CString(MultiClients())); |
997 | 0 | config.AddKeyValuePair("DenyLoadMod", CString(DenyLoadMod())); |
998 | 0 | config.AddKeyValuePair("Admin", CString(IsAdmin())); |
999 | 0 | config.AddKeyValuePair("DenySetBindHost", CString(DenySetBindHost())); |
1000 | 0 | config.AddKeyValuePair("DenySetIdent", CString(DenySetIdent())); |
1001 | 0 | config.AddKeyValuePair("DenySetNetwork", CString(DenySetNetwork())); |
1002 | 0 | config.AddKeyValuePair("DenySetRealName", CString(DenySetRealName())); |
1003 | 0 | config.AddKeyValuePair("DenySetQuitMsg", CString(DenySetQuitMsg())); |
1004 | 0 | config.AddKeyValuePair("DenySetCTCPReplies", CString(DenySetCTCPReplies())); |
1005 | 0 | config.AddKeyValuePair("TimestampFormat", GetTimestampFormat()); |
1006 | 0 | config.AddKeyValuePair("AppendTimestamp", CString(GetTimestampAppend())); |
1007 | 0 | config.AddKeyValuePair("PrependTimestamp", CString(GetTimestampPrepend())); |
1008 | 0 | config.AddKeyValuePair("AuthOnlyViaModule", CString(AuthOnlyViaModule())); |
1009 | 0 | config.AddKeyValuePair("Timezone", m_sTimezone); |
1010 | 0 | config.AddKeyValuePair("JoinTries", CString(m_uMaxJoinTries)); |
1011 | 0 | config.AddKeyValuePair("MaxNetworks", CString(m_uMaxNetworks)); |
1012 | 0 | config.AddKeyValuePair("MaxQueryBuffers", CString(m_uMaxQueryBuffers)); |
1013 | 0 | config.AddKeyValuePair("MaxJoins", CString(m_uMaxJoins)); |
1014 | 0 | config.AddKeyValuePair("ClientEncoding", GetClientEncoding()); |
1015 | 0 | config.AddKeyValuePair("Language", GetLanguage()); |
1016 | 0 | config.AddKeyValuePair("NoTrafficTimeout", CString(GetNoTrafficTimeout())); |
1017 | | |
1018 | | // Allow Hosts |
1019 | 0 | if (!m_ssAllowedHosts.empty()) { |
1020 | 0 | for (const CString& sHost : m_ssAllowedHosts) { |
1021 | 0 | config.AddKeyValuePair("Allow", sHost); |
1022 | 0 | } |
1023 | 0 | } |
1024 | | |
1025 | | // CTCP Replies |
1026 | 0 | if (!m_mssCTCPReplies.empty()) { |
1027 | 0 | for (const auto& itb : m_mssCTCPReplies) { |
1028 | 0 | config.AddKeyValuePair("CTCPReply", |
1029 | 0 | itb.first.AsUpper() + " " + itb.second); |
1030 | 0 | } |
1031 | 0 | } |
1032 | | |
1033 | | // Modules |
1034 | 0 | const CModules& Mods = GetModules(); |
1035 | |
|
1036 | 0 | if (!Mods.empty()) { |
1037 | 0 | for (CModule* pMod : Mods) { |
1038 | 0 | CString sArgs = pMod->GetArgs(); |
1039 | |
|
1040 | 0 | if (!sArgs.empty()) { |
1041 | 0 | sArgs = " " + sArgs; |
1042 | 0 | } |
1043 | |
|
1044 | 0 | config.AddKeyValuePair("LoadModule", pMod->GetModName() + sArgs); |
1045 | 0 | } |
1046 | 0 | } |
1047 | | |
1048 | | // Networks |
1049 | 0 | for (CIRCNetwork* pNetwork : m_vIRCNetworks) { |
1050 | 0 | config.AddSubConfig("Network", pNetwork->GetName(), |
1051 | 0 | pNetwork->ToConfig()); |
1052 | 0 | } |
1053 | |
|
1054 | 0 | return config; |
1055 | 0 | } |
1056 | | |
1057 | 0 | bool CUser::CheckPass(const CString& sPass) { |
1058 | 0 | if(AuthOnlyViaModule() || CZNC::Get().GetAuthOnlyViaModule()) { |
1059 | 0 | return false; |
1060 | 0 | } |
1061 | | |
1062 | 0 | bool bResult = false; |
1063 | 0 | bool bUpgrade = false; |
1064 | 0 | switch (m_eHashType) { |
1065 | 0 | case HASH_MD5: |
1066 | 0 | bResult = m_sPass.Equals(CUtils::SaltedMD5Hash(sPass, m_sPassSalt)); |
1067 | 0 | bUpgrade = true; |
1068 | 0 | break; |
1069 | 0 | case HASH_SHA256: |
1070 | 0 | bResult = m_sPass.Equals(CUtils::SaltedSHA256Hash(sPass, m_sPassSalt)); |
1071 | | #if ZNC_HAVE_ARGON |
1072 | | bUpgrade = true; |
1073 | | #endif |
1074 | 0 | break; |
1075 | 0 | case HASH_ARGON2ID: |
1076 | | #if ZNC_HAVE_ARGON |
1077 | | return argon2id_verify(m_sPass.c_str(), sPass.data(), sPass.length()) == ARGON2_OK; |
1078 | | #else |
1079 | 0 | CUtils::PrintError("ZNC is built without Argon2 support, " + GetUsername() + " cannot authenticate"); |
1080 | 0 | return false; |
1081 | 0 | #endif |
1082 | 0 | case HASH_NONE: |
1083 | | // Don't upgrade hash, since the only valid use case for plain are |
1084 | | // manual tests, where it's simpler this way |
1085 | 0 | return (sPass == m_sPass); |
1086 | 0 | } |
1087 | | |
1088 | 0 | if (bResult && bUpgrade) { |
1089 | 0 | CString sSalt = CUtils::GetSalt(); |
1090 | 0 | CString sHash = CUser::SaltedHash(sPass, sSalt); |
1091 | 0 | SetPass(sHash, CUser::HASH_DEFAULT, sSalt); |
1092 | 0 | } |
1093 | 0 | return bResult; |
1094 | 0 | } |
1095 | | |
1096 | | /*CClient* CUser::GetClient() { |
1097 | | // Todo: optimize this by saving a pointer to the sock |
1098 | | CSockManager& Manager = CZNC::Get().GetManager(); |
1099 | | CString sSockName = "USR::" + m_sUsername; |
1100 | | |
1101 | | for (unsigned int a = 0; a < Manager.size(); a++) { |
1102 | | Csock* pSock = Manager[a]; |
1103 | | if (pSock->GetSockName().Equals(sSockName)) { |
1104 | | if (!pSock->IsClosed()) { |
1105 | | return (CClient*) pSock; |
1106 | | } |
1107 | | } |
1108 | | } |
1109 | | |
1110 | | return (CClient*) CZNC::Get().GetManager().FindSockByName(sSockName); |
1111 | | }*/ |
1112 | | |
1113 | 0 | CString CUser::GetLocalDCCIP() const { |
1114 | 0 | if (!GetDCCBindHost().empty()) return GetDCCBindHost(); |
1115 | | |
1116 | 0 | for (CIRCNetwork* pNetwork : m_vIRCNetworks) { |
1117 | 0 | CIRCSock* pIRCSock = pNetwork->GetIRCSock(); |
1118 | 0 | if (pIRCSock) { |
1119 | 0 | return pIRCSock->GetLocalIP(); |
1120 | 0 | } |
1121 | 0 | } |
1122 | | |
1123 | 0 | if (!GetAllClients().empty()) { |
1124 | 0 | return GetAllClients()[0]->GetLocalIP(); |
1125 | 0 | } |
1126 | | |
1127 | 0 | return ""; |
1128 | 0 | } |
1129 | | |
1130 | | bool CUser::PutUser(const CString& sLine, CClient* pClient, |
1131 | 0 | CClient* pSkipClient) { |
1132 | 0 | for (CClient* pEachClient : m_vClients) { |
1133 | 0 | if ((!pClient || pClient == pEachClient) && |
1134 | 0 | pSkipClient != pEachClient) { |
1135 | 0 | pEachClient->PutClient(sLine); |
1136 | |
|
1137 | 0 | if (pClient) { |
1138 | 0 | return true; |
1139 | 0 | } |
1140 | 0 | } |
1141 | 0 | } |
1142 | | |
1143 | 0 | for (CIRCNetwork* pNetwork : m_vIRCNetworks) { |
1144 | 0 | if (pNetwork->PutUser(sLine, pClient, pSkipClient)) { |
1145 | 0 | return true; |
1146 | 0 | } |
1147 | 0 | } |
1148 | | |
1149 | 0 | return (pClient == nullptr); |
1150 | 0 | } |
1151 | | |
1152 | | bool CUser::PutStatus(const CString& sLine, CClient* pClient, |
1153 | 0 | CClient* pSkipClient) { |
1154 | 0 | vector<CClient*> vClients = GetAllClients(); |
1155 | 0 | for (CClient* pEachClient : vClients) { |
1156 | 0 | if ((!pClient || pClient == pEachClient) && |
1157 | 0 | pSkipClient != pEachClient) { |
1158 | 0 | pEachClient->PutStatus(sLine); |
1159 | |
|
1160 | 0 | if (pClient) { |
1161 | 0 | return true; |
1162 | 0 | } |
1163 | 0 | } |
1164 | 0 | } |
1165 | | |
1166 | 0 | return (pClient == nullptr); |
1167 | 0 | } |
1168 | | |
1169 | | bool CUser::PutStatusNotice(const CString& sLine, CClient* pClient, |
1170 | 0 | CClient* pSkipClient) { |
1171 | 0 | vector<CClient*> vClients = GetAllClients(); |
1172 | 0 | for (CClient* pEachClient : vClients) { |
1173 | 0 | if ((!pClient || pClient == pEachClient) && |
1174 | 0 | pSkipClient != pEachClient) { |
1175 | 0 | pEachClient->PutStatusNotice(sLine); |
1176 | |
|
1177 | 0 | if (pClient) { |
1178 | 0 | return true; |
1179 | 0 | } |
1180 | 0 | } |
1181 | 0 | } |
1182 | | |
1183 | 0 | return (pClient == nullptr); |
1184 | 0 | } |
1185 | | |
1186 | | bool CUser::PutModule(const CString& sModule, const CString& sLine, |
1187 | 0 | CClient* pClient, CClient* pSkipClient) { |
1188 | 0 | for (CClient* pEachClient : GetAllClients()) { |
1189 | 0 | if ((!pClient || pClient == pEachClient) && |
1190 | 0 | pSkipClient != pEachClient) { |
1191 | 0 | pEachClient->PutModule(sModule, sLine); |
1192 | |
|
1193 | 0 | if (pClient) { |
1194 | 0 | return true; |
1195 | 0 | } |
1196 | 0 | } |
1197 | 0 | } |
1198 | | |
1199 | 0 | return (pClient == nullptr); |
1200 | 0 | } |
1201 | | |
1202 | | bool CUser::PutModNotice(const CString& sModule, const CString& sLine, |
1203 | 0 | CClient* pClient, CClient* pSkipClient) { |
1204 | 0 | for (CClient* pEachClient : GetAllClients()) { |
1205 | 0 | if ((!pClient || pClient == pEachClient) && |
1206 | 0 | pSkipClient != pEachClient) { |
1207 | 0 | pEachClient->PutModNotice(sModule, sLine); |
1208 | |
|
1209 | 0 | if (pClient) { |
1210 | 0 | return true; |
1211 | 0 | } |
1212 | 0 | } |
1213 | 0 | } |
1214 | | |
1215 | 0 | return (pClient == nullptr); |
1216 | 0 | } |
1217 | | |
1218 | | |
1219 | 0 | CString CUser::MakeCleanUsername(const CString& sUsername) { |
1220 | 0 | return CUser::MakeCleanUserName(sUsername); |
1221 | 0 | } |
1222 | | |
1223 | | /// @deprecated |
1224 | 0 | CString CUser::MakeCleanUserName(const CString& sUsername) { |
1225 | 0 | return sUsername.Token(0, false, "@").Replace_n(".", ""); |
1226 | 0 | } |
1227 | | |
1228 | 0 | bool CUser::IsUserAttached() const { |
1229 | 0 | if (!m_vClients.empty()) { |
1230 | 0 | return true; |
1231 | 0 | } |
1232 | | |
1233 | 0 | for (const CIRCNetwork* pNetwork : m_vIRCNetworks) { |
1234 | 0 | if (pNetwork->IsUserAttached()) { |
1235 | 0 | return true; |
1236 | 0 | } |
1237 | 0 | } |
1238 | | |
1239 | 0 | return false; |
1240 | 0 | } |
1241 | | |
1242 | | bool CUser::LoadModule(const CString& sModName, const CString& sArgs, |
1243 | 0 | const CString& sNotice, CString& sError) { |
1244 | 0 | bool bModRet = true; |
1245 | 0 | CString sModRet; |
1246 | |
|
1247 | 0 | CModInfo ModInfo; |
1248 | 0 | if (!CZNC::Get().GetModules().GetModInfo(ModInfo, sModName, sModRet)) { |
1249 | 0 | sError = t_f("Unable to find modinfo {1}: {2}")(sModName, sModRet); |
1250 | 0 | return false; |
1251 | 0 | } |
1252 | | |
1253 | 0 | CUtils::PrintAction(sNotice); |
1254 | |
|
1255 | 0 | if (!ModInfo.SupportsType(CModInfo::UserModule) && |
1256 | 0 | ModInfo.SupportsType(CModInfo::NetworkModule)) { |
1257 | 0 | CUtils::PrintMessage( |
1258 | 0 | "NOTICE: Module [" + sModName + |
1259 | 0 | "] is a network module, loading module for all networks in user."); |
1260 | | |
1261 | | // Do they have old NV? |
1262 | 0 | CFile fNVFile = |
1263 | 0 | CFile(GetUserPath() + "/moddata/" + sModName + "/.registry"); |
1264 | |
|
1265 | 0 | for (CIRCNetwork* pNetwork : m_vIRCNetworks) { |
1266 | | // Check whether the network already has this module loaded (#954) |
1267 | 0 | if (pNetwork->GetModules().FindModule(sModName)) { |
1268 | 0 | continue; |
1269 | 0 | } |
1270 | | |
1271 | 0 | if (fNVFile.Exists()) { |
1272 | 0 | CString sNetworkModPath = |
1273 | 0 | pNetwork->GetNetworkPath() + "/moddata/" + sModName; |
1274 | 0 | if (!CFile::Exists(sNetworkModPath)) { |
1275 | 0 | CDir::MakeDir(sNetworkModPath); |
1276 | 0 | } |
1277 | |
|
1278 | 0 | fNVFile.Copy(sNetworkModPath + "/.registry"); |
1279 | 0 | } |
1280 | |
|
1281 | 0 | bModRet = pNetwork->GetModules().LoadModule( |
1282 | 0 | sModName, sArgs, CModInfo::NetworkModule, this, pNetwork, |
1283 | 0 | sModRet); |
1284 | 0 | if (!bModRet) { |
1285 | 0 | break; |
1286 | 0 | } |
1287 | 0 | } |
1288 | 0 | } else { |
1289 | 0 | bModRet = GetModules().LoadModule(sModName, sArgs, CModInfo::UserModule, |
1290 | 0 | this, nullptr, sModRet); |
1291 | 0 | } |
1292 | |
|
1293 | 0 | if (!bModRet) { |
1294 | 0 | sError = sModRet; |
1295 | 0 | } |
1296 | 0 | return bModRet; |
1297 | 0 | } |
1298 | | |
1299 | | // Setters |
1300 | 0 | void CUser::SetNick(const CString& s) { m_sNick = s; } |
1301 | 0 | void CUser::SetAltNick(const CString& s) { m_sAltNick = s; } |
1302 | 0 | void CUser::SetIdent(const CString& s) { m_sIdent = s; } |
1303 | 0 | void CUser::SetRealName(const CString& s) { m_sRealName = s; } |
1304 | 0 | void CUser::SetBindHost(const CString& s) { m_sBindHost = s; } |
1305 | 0 | void CUser::SetDCCBindHost(const CString& s) { m_sDCCBindHost = s; } |
1306 | 0 | void CUser::SetPass(const CString& s, eHashType eHash, const CString& sSalt) { |
1307 | 0 | m_sPass = s; |
1308 | 0 | m_eHashType = eHash; |
1309 | 0 | switch (eHash) { |
1310 | 0 | case HASH_NONE: |
1311 | 0 | case HASH_ARGON2ID: |
1312 | | // Salt is embedded in the encoded "hash" in argon |
1313 | 0 | m_sPassSalt = ""; |
1314 | 0 | break; |
1315 | 0 | case HASH_MD5: |
1316 | 0 | case HASH_SHA256: |
1317 | 0 | m_sPassSalt = sSalt; |
1318 | 0 | break; |
1319 | 0 | } |
1320 | 0 | } |
1321 | 0 | void CUser::SetMultiClients(bool b) { m_bMultiClients = b; } |
1322 | 0 | void CUser::SetDenyLoadMod(bool b) { m_bDenyLoadMod = b; } |
1323 | 0 | void CUser::SetAdmin(bool b) { m_bAdmin = b; } |
1324 | 0 | void CUser::SetDenySetBindHost(bool b) { m_bDenySetBindHost = b; } |
1325 | 0 | void CUser::SetDenySetIdent(bool b) { m_bDenySetIdent = b; } |
1326 | 0 | void CUser::SetDenySetNetwork(bool b) { m_bDenySetNetwork = b; } |
1327 | 0 | void CUser::SetDenySetRealName(bool b) { m_bDenySetRealName = b; } |
1328 | 0 | void CUser::SetDenySetQuitMsg(bool b) { m_bDenySetQuitMsg = b; } |
1329 | 0 | void CUser::SetDenySetCTCPReplies(bool b) { m_bDenySetCTCPReplies = b; } |
1330 | 0 | void CUser::SetDefaultChanModes(const CString& s) { m_sDefaultChanModes = s; } |
1331 | 0 | void CUser::SetClientEncoding(const CString& s) { |
1332 | 0 | m_sClientEncoding = CZNC::Get().FixupEncoding(s); |
1333 | 0 | for (CClient* pClient : GetAllClients()) { |
1334 | 0 | pClient->SetEncoding(m_sClientEncoding); |
1335 | 0 | } |
1336 | 0 | } |
1337 | 0 | void CUser::SetQuitMsg(const CString& s) { m_sQuitMsg = s; } |
1338 | 0 | void CUser::SetAutoClearChanBuffer(bool b) { |
1339 | 0 | for (CIRCNetwork* pNetwork : m_vIRCNetworks) { |
1340 | 0 | for (CChan* pChan : pNetwork->GetChans()) { |
1341 | 0 | pChan->InheritAutoClearChanBuffer(b); |
1342 | 0 | } |
1343 | 0 | } |
1344 | 0 | m_bAutoClearChanBuffer = b; |
1345 | 0 | } |
1346 | 0 | void CUser::SetAutoClearQueryBuffer(bool b) { m_bAutoClearQueryBuffer = b; } |
1347 | | |
1348 | 0 | bool CUser::SetBufferCount(unsigned int u, bool bForce) { |
1349 | 0 | return SetChanBufferSize(u, bForce); |
1350 | 0 | } |
1351 | | |
1352 | 0 | bool CUser::SetChanBufferSize(unsigned int u, bool bForce) { |
1353 | 0 | if (!bForce && u > CZNC::Get().GetMaxBufferSize()) return false; |
1354 | 0 | for (CIRCNetwork* pNetwork : m_vIRCNetworks) { |
1355 | 0 | for (CChan* pChan : pNetwork->GetChans()) { |
1356 | 0 | pChan->InheritBufferCount(u, bForce); |
1357 | 0 | } |
1358 | 0 | } |
1359 | 0 | m_uChanBufferSize = u; |
1360 | 0 | return true; |
1361 | 0 | } |
1362 | | |
1363 | 0 | bool CUser::SetQueryBufferSize(unsigned int u, bool bForce) { |
1364 | 0 | if (!bForce && u > CZNC::Get().GetMaxBufferSize()) return false; |
1365 | 0 | for (CIRCNetwork* pNetwork : m_vIRCNetworks) { |
1366 | 0 | for (CQuery* pQuery : pNetwork->GetQueries()) { |
1367 | 0 | pQuery->SetBufferCount(u, bForce); |
1368 | 0 | } |
1369 | 0 | } |
1370 | 0 | m_uQueryBufferSize = u; |
1371 | 0 | return true; |
1372 | 0 | } |
1373 | | |
1374 | 0 | bool CUser::AddCTCPReply(const CString& sCTCP, const CString& sReply) { |
1375 | | // Reject CTCP requests containing spaces |
1376 | 0 | if (sCTCP.find_first_of(' ') != CString::npos) { |
1377 | 0 | return false; |
1378 | 0 | } |
1379 | | // Reject empty CTCP requests |
1380 | 0 | if (sCTCP.empty()) { |
1381 | 0 | return false; |
1382 | 0 | } |
1383 | 0 | m_mssCTCPReplies[sCTCP.AsUpper()] = sReply; |
1384 | 0 | return true; |
1385 | 0 | } |
1386 | | |
1387 | 0 | bool CUser::DelCTCPReply(const CString& sCTCP) { |
1388 | 0 | return m_mssCTCPReplies.erase(sCTCP.AsUpper()) > 0; |
1389 | 0 | } |
1390 | | |
1391 | 0 | bool CUser::SetStatusPrefix(const CString& s) { |
1392 | 0 | if ((!s.empty()) && (s.length() < 6) && (!s.Contains(" "))) { |
1393 | 0 | m_sStatusPrefix = (s.empty()) ? "*" : s; |
1394 | 0 | return true; |
1395 | 0 | } |
1396 | | |
1397 | 0 | return false; |
1398 | 0 | } |
1399 | | |
1400 | 0 | bool CUser::SetLanguage(const CString& s) { |
1401 | | // They look like ru-RU |
1402 | 0 | for (char c : s) { |
1403 | 0 | if (isalpha(c) || c == '-' || c == '_') { |
1404 | 0 | } else { |
1405 | 0 | return false; |
1406 | 0 | } |
1407 | 0 | } |
1408 | 0 | m_sLanguage = s; |
1409 | | // 1.7.0 accidentally used _ instead of -, which made language |
1410 | | // non-selectable. But it's possible that someone put _ to znc.conf |
1411 | | // manually. |
1412 | | // TODO: cleanup _ some time later. |
1413 | 0 | m_sLanguage.Replace("_", "-"); |
1414 | 0 | return true; |
1415 | 0 | } |
1416 | | // !Setters |
1417 | | |
1418 | | // Getters |
1419 | 0 | vector<CClient*> CUser::GetAllClients() const { |
1420 | 0 | vector<CClient*> vClients; |
1421 | |
|
1422 | 0 | for (CIRCNetwork* pNetwork : m_vIRCNetworks) { |
1423 | 0 | for (CClient* pClient : pNetwork->GetClients()) { |
1424 | 0 | vClients.push_back(pClient); |
1425 | 0 | } |
1426 | 0 | } |
1427 | |
|
1428 | 0 | for (CClient* pClient : m_vClients) { |
1429 | 0 | vClients.push_back(pClient); |
1430 | 0 | } |
1431 | |
|
1432 | 0 | return vClients; |
1433 | 0 | } |
1434 | | |
1435 | | /// @deprecated |
1436 | 0 | const CString& CUser::GetUserName() const { return m_sUsername; } |
1437 | 0 | const CString& CUser::GetUsername() const { return m_sUsername; } |
1438 | 0 | const CString& CUser::GetCleanUserName() const { return m_sCleanUsername; } |
1439 | 0 | const CString& CUser::GetNick(bool bAllowDefault) const { |
1440 | 0 | return (bAllowDefault && m_sNick.empty()) ? GetCleanUserName() : m_sNick; |
1441 | 0 | } |
1442 | 0 | const CString& CUser::GetAltNick(bool bAllowDefault) const { |
1443 | 0 | return (bAllowDefault && m_sAltNick.empty()) ? GetCleanUserName() |
1444 | 0 | : m_sAltNick; |
1445 | 0 | } |
1446 | 0 | const CString& CUser::GetIdent(bool bAllowDefault) const { |
1447 | 0 | return (bAllowDefault && m_sIdent.empty()) ? GetCleanUserName() : m_sIdent; |
1448 | 0 | } |
1449 | 0 | CString CUser::GetRealName() const { |
1450 | | // Not include version number via GetTag() because of |
1451 | | // https://github.com/znc/znc/issues/818#issuecomment-70402820 |
1452 | 0 | return (!m_sRealName.Trim_n().empty()) ? m_sRealName |
1453 | 0 | : "ZNC - https://znc.in"; |
1454 | 0 | } |
1455 | 0 | const CString& CUser::GetBindHost() const { return m_sBindHost; } |
1456 | 0 | const CString& CUser::GetDCCBindHost() const { return m_sDCCBindHost; } |
1457 | 0 | const CString& CUser::GetPass() const { return m_sPass; } |
1458 | 0 | CUser::eHashType CUser::GetPassHashType() const { return m_eHashType; } |
1459 | 0 | const CString& CUser::GetPassSalt() const { return m_sPassSalt; } |
1460 | 0 | bool CUser::DenyLoadMod() const { return m_bDenyLoadMod; } |
1461 | 0 | bool CUser::IsAdmin() const { return m_bAdmin; } |
1462 | 0 | bool CUser::DenySetBindHost() const { return m_bDenySetBindHost; } |
1463 | 0 | bool CUser::DenySetIdent() const { return m_bDenySetIdent; } |
1464 | 0 | bool CUser::DenySetNetwork() const { return m_bDenySetNetwork; } |
1465 | 0 | bool CUser::DenySetRealName() const { return m_bDenySetRealName; } |
1466 | 0 | bool CUser::DenySetQuitMsg() const { return m_bDenySetQuitMsg; } |
1467 | 0 | bool CUser::DenySetCTCPReplies() const { return m_bDenySetCTCPReplies; } |
1468 | 0 | bool CUser::MultiClients() const { return m_bMultiClients; } |
1469 | 0 | bool CUser::AuthOnlyViaModule() const { return m_bAuthOnlyViaModule; } |
1470 | 0 | const CString& CUser::GetStatusPrefix() const { return m_sStatusPrefix; } |
1471 | 0 | const CString& CUser::GetDefaultChanModes() const { |
1472 | 0 | return m_sDefaultChanModes; |
1473 | 0 | } |
1474 | 0 | const CString& CUser::GetClientEncoding() const { return m_sClientEncoding; } |
1475 | 0 | bool CUser::HasSpaceForNewNetwork() const { |
1476 | 0 | return GetNetworks().size() < MaxNetworks(); |
1477 | 0 | } |
1478 | | |
1479 | 0 | CString CUser::GetQuitMsg() const { |
1480 | 0 | return (!m_sQuitMsg.Trim_n().empty()) ? m_sQuitMsg : "%znc%"; |
1481 | 0 | } |
1482 | 0 | const MCString& CUser::GetCTCPReplies() const { return m_mssCTCPReplies; } |
1483 | 0 | unsigned int CUser::GetBufferCount() const { return GetChanBufferSize(); } |
1484 | 0 | unsigned int CUser::GetChanBufferSize() const { return m_uChanBufferSize; } |
1485 | 0 | unsigned int CUser::GetQueryBufferSize() const { return m_uQueryBufferSize; } |
1486 | 0 | bool CUser::AutoClearChanBuffer() const { return m_bAutoClearChanBuffer; } |
1487 | 0 | bool CUser::AutoClearQueryBuffer() const { return m_bAutoClearQueryBuffer; } |
1488 | | // CString CUser::GetSkinName() const { return (!m_sSkinName.empty()) ? |
1489 | | // m_sSkinName : CZNC::Get().GetSkinName(); } |
1490 | 0 | CString CUser::GetSkinName() const { return m_sSkinName; } |
1491 | 0 | CString CUser::GetLanguage() const { return m_sLanguage; } |
1492 | 0 | const CString& CUser::GetUserPath() const { |
1493 | 0 | if (!CFile::Exists(m_sUserPath)) { |
1494 | 0 | CDir::MakeDir(m_sUserPath); |
1495 | 0 | } |
1496 | 0 | return m_sUserPath; |
1497 | 0 | } |
1498 | | // !Getters |
1499 | | |
1500 | 0 | unsigned long long CUser::BytesRead() const { |
1501 | 0 | unsigned long long uBytes = m_uBytesRead; |
1502 | 0 | for (const CIRCNetwork* pNetwork : m_vIRCNetworks) { |
1503 | 0 | uBytes += pNetwork->BytesRead(); |
1504 | 0 | } |
1505 | 0 | return uBytes; |
1506 | 0 | } |
1507 | | |
1508 | 0 | unsigned long long CUser::BytesWritten() const { |
1509 | 0 | unsigned long long uBytes = m_uBytesWritten; |
1510 | 0 | for (const CIRCNetwork* pNetwork : m_vIRCNetworks) { |
1511 | 0 | uBytes += pNetwork->BytesWritten(); |
1512 | 0 | } |
1513 | 0 | return uBytes; |
1514 | 0 | } |