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