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 #ifndef ZNC_CLIENT_H 18 #define ZNC_CLIENT_H 19 20 #include <znc/zncconfig.h> 21 #include <znc/Socket.h> 22 #include <znc/Utils.h> 23 #include <znc/Message.h> 24 #include <znc/main.h> 25 #include <memory> 26 #include <functional> 27 28 // Forward Declarations 29 class CZNC; 30 class CUser; 31 class CIRCNetwork; 32 class CIRCSock; 33 class CClient; 34 class CMessage; 35 class CChan; 36 // !Forward Declarations 37 38 class CAuthBase : private CCoreTranslationMixin { 39 public: CAuthBase(const CString & sUsername,const CString & sPassword,CZNCSock * pSock)40 CAuthBase(const CString& sUsername, const CString& sPassword, 41 CZNCSock* pSock) 42 : m_sUsername(sUsername), m_sPassword(sPassword), m_pSock(pSock) {} 43 ~CAuthBase()44 virtual ~CAuthBase() {} 45 46 CAuthBase(const CAuthBase&) = delete; 47 CAuthBase& operator=(const CAuthBase&) = delete; 48 SetLoginInfo(const CString & sUsername,const CString & sPassword,CZNCSock * pSock)49 virtual void SetLoginInfo(const CString& sUsername, 50 const CString& sPassword, CZNCSock* pSock) { 51 m_sUsername = sUsername; 52 m_sPassword = sPassword; 53 m_pSock = pSock; 54 } 55 56 void AcceptLogin(CUser& User); 57 void RefuseLogin(const CString& sReason); 58 GetUsername()59 const CString& GetUsername() const { return m_sUsername; } GetPassword()60 const CString& GetPassword() const { return m_sPassword; } GetSocket()61 Csock* GetSocket() const { return m_pSock; } 62 CString GetRemoteIP() const; 63 64 // Invalidate this CAuthBase instance which means it will no longer use 65 // m_pSock and AcceptLogin() or RefusedLogin() will have no effect. 66 virtual void Invalidate(); 67 68 protected: 69 virtual void AcceptedLogin(CUser& User) = 0; 70 virtual void RefusedLogin(const CString& sReason) = 0; 71 72 private: 73 CString m_sUsername; 74 CString m_sPassword; 75 CZNCSock* m_pSock; 76 }; 77 78 class CClientAuth : public CAuthBase { 79 public: 80 CClientAuth(CClient* pClient, const CString& sUsername, 81 const CString& sPassword); ~CClientAuth()82 virtual ~CClientAuth() {} 83 84 CClientAuth(const CClientAuth&) = delete; 85 CClientAuth& operator=(const CClientAuth&) = delete; 86 Invalidate()87 void Invalidate() override { 88 m_pClient = nullptr; 89 CAuthBase::Invalidate(); 90 } 91 void AcceptedLogin(CUser& User) override; 92 void RefusedLogin(const CString& sReason) override; 93 94 private: 95 protected: 96 CClient* m_pClient; 97 }; 98 99 class CClient : public CIRCSocket { 100 public: CClient()101 CClient() 102 : CIRCSocket(), 103 m_bGotPass(false), 104 m_bGotNick(false), 105 m_bGotUser(false), 106 m_bInCap(false), 107 m_bCapNotify(false), 108 m_bAwayNotify(false), 109 m_bAccountNotify(false), 110 m_bExtendedJoin(false), 111 m_bNamesx(false), 112 m_bUHNames(false), 113 m_bAway(false), 114 m_bServerTime(false), 115 m_bBatch(false), 116 m_bEchoMessage(false), 117 m_bSelfMessage(false), 118 m_bPlaybackActive(false), 119 m_pUser(nullptr), 120 m_pNetwork(nullptr), 121 m_sNick("unknown-nick"), 122 m_sPass(""), 123 m_sUser(""), 124 m_sNetwork(""), 125 m_sIdentifier(""), 126 m_spAuth(), 127 m_ssAcceptedCaps(), 128 m_ssSupportedTags(), 129 m_mCoreCaps({ 130 {"multi-prefix", 131 {false, [this](bool bVal) { m_bNamesx = bVal; }}}, 132 {"userhost-in-names", 133 {false, [this](bool bVal) { m_bUHNames = bVal; }}}, 134 {"echo-message", 135 {false, [this](bool bVal) { m_bEchoMessage = bVal; }}}, 136 {"server-time", 137 {false, [this](bool bVal) { 138 m_bServerTime = bVal; 139 SetTagSupport("time", bVal); 140 }}}, 141 {"batch", {false, [this](bool bVal) { 142 m_bBatch = bVal; 143 SetTagSupport("batch", bVal); 144 }}}, 145 {"cap-notify", 146 {false, [this](bool bVal) { m_bCapNotify = bVal; }}}, 147 {"away-notify", 148 {true, [this](bool bVal) { m_bAwayNotify = bVal; }}}, 149 {"account-notify", 150 {true, [this](bool bVal) { m_bAccountNotify = bVal; }}}, 151 {"extended-join", 152 {true, [this](bool bVal) { m_bExtendedJoin = bVal; }}}, 153 }) { 154 EnableReadLine(); 155 // RFC says a line can have 512 chars max, but we are 156 // a little more gentle ;) 157 SetMaxBufferThreshold(1024); 158 159 // For compatibility with older clients 160 m_mCoreCaps["znc.in/server-time-iso"] = m_mCoreCaps["server-time"]; 161 m_mCoreCaps["znc.in/batch"] = m_mCoreCaps["batch"]; 162 m_mCoreCaps["znc.in/self-message"] = { 163 false, [this](bool bVal) { m_bSelfMessage = bVal; }}; 164 } 165 166 virtual ~CClient(); 167 168 CClient(const CClient&) = delete; 169 CClient& operator=(const CClient&) = delete; 170 171 void SendRequiredPasswordNotice(); 172 void AcceptLogin(CUser& User); 173 void RefuseLogin(const CString& sReason); 174 175 CString GetNick(bool bAllowIRCNick = true) const; 176 CString GetNickMask() const; GetIdentifier()177 CString GetIdentifier() const { return m_sIdentifier; } HasCapNotify()178 bool HasCapNotify() const { return m_bCapNotify; } HasAwayNotify()179 bool HasAwayNotify() const { return m_bAwayNotify; } HasAccountNotify()180 bool HasAccountNotify() const { return m_bAccountNotify; } HasExtendedJoin()181 bool HasExtendedJoin() const { return m_bExtendedJoin; } HasNamesx()182 bool HasNamesx() const { return m_bNamesx; } HasUHNames()183 bool HasUHNames() const { return m_bUHNames; } IsAway()184 bool IsAway() const { return m_bAway; } HasServerTime()185 bool HasServerTime() const { return m_bServerTime; } HasBatch()186 bool HasBatch() const { return m_bBatch; } HasEchoMessage()187 bool HasEchoMessage() const { return m_bEchoMessage; } HasSelfMessage()188 bool HasSelfMessage() const { return m_bSelfMessage; } 189 190 static bool IsValidIdentifier(const CString& sIdentifier); 191 192 void UserCommand(CString& sLine); 193 void UserPortCommand(CString& sLine); 194 void StatusCTCP(const CString& sCommand); 195 void BouncedOff(); IsAttached()196 bool IsAttached() const { return m_pUser != nullptr; } 197 IsPlaybackActive()198 bool IsPlaybackActive() const { return m_bPlaybackActive; } SetPlaybackActive(bool bActive)199 void SetPlaybackActive(bool bActive) { m_bPlaybackActive = bActive; } 200 201 void PutIRC(const CString& sLine); 202 /** Sends a raw data line to the client. 203 * @param sLine The line to be sent. 204 * 205 * The line is first passed \e unmodified to the \ref 206 * CModule::OnSendToClient() module hook. If no module halts the process, 207 * the line is then sent to the client. 208 * 209 * These lines appear in the debug output in the following syntax: 210 * \code [time] (user/network) ZNC -> CLI [line] \endcode 211 * 212 * Prefer \l PutClient() instead. 213 */ 214 bool PutClientRaw(const CString& sLine); 215 /** Sends a message to the client. 216 * See \l PutClient(const CMessage&) for details. 217 */ 218 void PutClient(const CString& sLine); 219 /** Sends a message to the client. 220 * @param Message The message to be sent. 221 * @note Only known and compatible messages and tags are sent. 222 * @return \c true if the message was sent, or \c false if it was ignored. 223 * 224 * This method ensures that only messages and tags, that the client has 225 * explicitly requested, are sent. Not all IRC clients are capable of 226 * handling all messages and tags. For example, some older versions of 227 * popular clients were prepared to parse just one interesting tag, 228 * \c time, and would break if multiple tags were included. Furthermore, 229 * messages that are specific to a certain capability, should not be sent 230 * to a client that has not requested the respective capability. Thus, in 231 * order to stay compatible with a variety of IRC clients, ZNC has to 232 * filter out messages and tags that the client has not explicitly 233 * requested. 234 * 235 * ### Message types 236 * 237 * The following table documents which capabilities the client is required 238 * to have requested in order to receive certain types of messages. 239 * 240 * Message type | Capability 241 * ------------ | ---------- 242 * \c ACCOUNT | \l CClient::HasAccountNotify() (<a href="http://ircv3.net/specs/extensions/account-notify-3.1.html">account-notify</a>) 243 * \c AWAY | \l CClient::HasAwayNotify() (<a href="http://ircv3.net/specs/extensions/away-notify-3.1.html">away-notify</a>) 244 * 245 * ### Message tags 246 * 247 * The following table documents currently supported message tags, and 248 * which capabilities the client is required to have requested to receive 249 * the respective message tags. 250 * 251 * Message tag | Capability 252 * ----------- | ---------- 253 * \c time | \l CClient::HasServerTime() (<a href="http://ircv3.net/specs/extensions/server-time-3.2.html">server-time</a>) 254 * \c batch | \l CClient::HasBatch() (<a href="http://ircv3.net/specs/extensions/batch-3.2.html">batch</a>) 255 * 256 * Additional tags can be added via \l CClient::SetTagSupport(). 257 * 258 * @warning Bypassing the filter may cause troubles to some older IRC 259 * clients. 260 * 261 * It is possible to bypass the filter by converting a message to a string 262 * using \l CMessage::ToString(), and passing the resulting raw line to the 263 * \l CClient::PutClientRaw(const CString& sLine): 264 * \code 265 * pClient->PutClientRaw(Message.ToString()); 266 * \endcode 267 */ 268 bool PutClient(const CMessage& Message); 269 unsigned int PutStatus(const CTable& table); 270 void PutStatus(const CString& sLine); 271 void PutStatusNotice(const CString& sLine); 272 void PutModule(const CString& sModule, const CString& sLine); 273 void PutModNotice(const CString& sModule, const CString& sLine); 274 IsCapEnabled(const CString & sCap)275 bool IsCapEnabled(const CString& sCap) const { 276 return 1 == m_ssAcceptedCaps.count(sCap); 277 } 278 IsTagEnabled(const CString & sTag)279 bool IsTagEnabled(const CString& sTag) const { 280 return 1 == m_ssSupportedTags.count(sTag); 281 } 282 /** Registers a tag as being supported or unsupported by the client. 283 * This doesn't affect tags which the client sends. 284 * @param sTag The tag to register. 285 * @param bState Whether the client supports the tag. 286 */ 287 void SetTagSupport(const CString& sTag, bool bState); 288 289 void NotifyServerDependentCaps(const SCString& ssCaps); 290 void ClearServerDependentCaps(); 291 292 void ReadLine(const CString& sData) override; 293 bool SendMotd(); 294 void HelpUser(const CString& sFilter = ""); 295 void AuthUser(); 296 void Connected() override; 297 void Timeout() override; 298 void Disconnected() override; 299 void ConnectionRefused() override; 300 void ReachedMaxBuffer() override; 301 302 void SetNick(const CString& s); SetAway(bool bAway)303 void SetAway(bool bAway) { m_bAway = bAway; } GetUser()304 CUser* GetUser() const { return m_pUser; } 305 void SetNetwork(CIRCNetwork* pNetwork, bool bDisconnect = true, 306 bool bReconnect = true); GetNetwork()307 CIRCNetwork* GetNetwork() const { return m_pNetwork; } 308 const std::vector<CClient*>& GetClients() const; 309 const CIRCSock* GetIRCSock() const; 310 CIRCSock* GetIRCSock(); 311 CString GetFullName() const; 312 313 private: 314 void HandleCap(const CMessage& Message); 315 void RespondCap(const CString& sResponse); 316 void ParsePass(const CString& sAuthLine); 317 void ParseUser(const CString& sAuthLine); 318 void ParseIdentifier(const CString& sAuthLine); 319 320 template <typename T> 321 void AddBuffer(const T& Message); 322 void EchoMessage(const CMessage& Message); 323 324 std::set<CChan*> MatchChans(const CString& sPatterns) const; 325 unsigned int AttachChans(const std::set<CChan*>& sChans); 326 unsigned int DetachChans(const std::set<CChan*>& sChans); 327 328 bool OnActionMessage(CActionMessage& Message); 329 bool OnCTCPMessage(CCTCPMessage& Message); 330 bool OnJoinMessage(CJoinMessage& Message); 331 bool OnModeMessage(CModeMessage& Message); 332 bool OnNoticeMessage(CNoticeMessage& Message); 333 bool OnPartMessage(CPartMessage& Message); 334 bool OnPingMessage(CMessage& Message); 335 bool OnPongMessage(CMessage& Message); 336 bool OnQuitMessage(CQuitMessage& Message); 337 bool OnTextMessage(CTextMessage& Message); 338 bool OnTopicMessage(CTopicMessage& Message); 339 bool OnOtherMessage(CMessage& Message); 340 341 protected: 342 bool m_bGotPass; 343 bool m_bGotNick; 344 bool m_bGotUser; 345 bool m_bInCap; 346 bool m_bCapNotify; 347 bool m_bAwayNotify; 348 bool m_bAccountNotify; 349 bool m_bExtendedJoin; 350 bool m_bNamesx; 351 bool m_bUHNames; 352 bool m_bAway; 353 bool m_bServerTime; 354 bool m_bBatch; 355 bool m_bEchoMessage; 356 bool m_bSelfMessage; 357 bool m_bPlaybackActive; 358 CUser* m_pUser; 359 CIRCNetwork* m_pNetwork; 360 CString m_sNick; 361 CString m_sPass; 362 CString m_sUser; 363 CString m_sNetwork; 364 CString m_sIdentifier; 365 std::shared_ptr<CAuthBase> m_spAuth; 366 SCString m_ssAcceptedCaps; 367 SCString m_ssSupportedTags; 368 // The capabilities supported by the ZNC core - capability names mapped 369 // to a pair which contains a bool describing whether the capability is 370 // server-dependent, and a capability value change handler. 371 std::map<CString, std::pair<bool, std::function<void(bool bVal)>>> 372 m_mCoreCaps; 373 // A subset of CIRCSock::GetAcceptedCaps(), the caps that can be listed 374 // in CAP LS and may be notified to the client with CAP NEW (cap-notify). 375 SCString m_ssServerDependentCaps; 376 377 friend class ClientTest; 378 }; 379 380 #endif // !ZNC_CLIENT_H 381