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