From c0a5ecb40b65072a051fa29412142f9eeff9a9f6 Mon Sep 17 00:00:00 2001 From: Alexey Sokolov Date: Thu, 14 Nov 2013 08:25:58 +0400 Subject: [PATCH] Add support for character encodings Previous commit added support of it to Csocket. When encoding is specified, core will convert incoming messages to UTF-8, and outgoing messages from UTF-8. When no encoding is specified, it will do nothing to bytes, like before. This is to be changed somewhere in future, to have UTF-8 on wire by default too. When encoding's name starts with *, incoming messages will be treated as UTF-8, if it is already correct UTF-8. Otherwise, it's converted. Fix #151 Fix #366 --- configure.ac | 10 ++++++++++ include/znc/IRCNetwork.h | 3 +++ include/znc/Socket.h | 5 +++++ include/znc/User.h | 3 +++ src/Client.cpp | 1 + src/IRCNetwork.cpp | 13 +++++++++++++ src/IRCSock.cpp | 1 + src/Listener.cpp | 1 + src/User.cpp | 6 ++++++ 9 files changed, 43 insertions(+) diff --git a/configure.ac b/configure.ac index c161e596..394775b2 100644 --- a/configure.ac +++ b/configure.ac @@ -562,6 +562,15 @@ then fi AM_ICONV +PKG_CHECK_MODULES([icu], [icu-uc], [ + appendLib "$icu_LIBS" + appendCXX "$icu_CFLAGS" + HAVE_ICU=yes + AC_DEFINE([HAVE_ICU], [1], [Enable ICU library for Unicode handling]) + AC_DEFINE([U_USING_ICU_NAMESPACE], [0], [Do not clutter global namespace with ICU C++ stuff]) +], [ + HAVE_ICU=no +]) AC_CACHE_CHECK([for GNU make], [ac_cv_path_GNUMAKE], [ AC_PATH_PROGS_FEATURE_CHECK([GNUMAKE], [make gmake], [[ @@ -635,6 +644,7 @@ else echo "charset: yes" fi echo "zlib: $ZLIB" +echo "icu: $HAVE_ICU" echo "run from src: $RUNFROMSOURCE" echo echo "Now you can run \"$GNUMAKE\" to compile ZNC" diff --git a/include/znc/IRCNetwork.h b/include/znc/IRCNetwork.h index 071b9a4e..174314b6 100644 --- a/include/znc/IRCNetwork.h +++ b/include/znc/IRCNetwork.h @@ -141,12 +141,14 @@ public: const CString& GetIdent(const bool bAllowDefault = true) const; const CString& GetRealName() const; const CString& GetBindHost() const; + const CString& GetEncoding() const; void SetNick(const CString& s); void SetAltNick(const CString& s); void SetIdent(const CString& s); void SetRealName(const CString& s); void SetBindHost(const CString& s); + void SetEncoding(const CString& s); double GetFloodRate() const { return m_fFloodRate; } unsigned short int GetFloodBurst() const { return m_uFloodBurst; } @@ -167,6 +169,7 @@ protected: CString m_sIdent; CString m_sRealName; CString m_sBindHost; + CString m_sEncoding; CModules* m_pModules; diff --git a/include/znc/Socket.h b/include/znc/Socket.h index 5ca2d89b..fe3026d5 100644 --- a/include/znc/Socket.h +++ b/include/znc/Socket.h @@ -30,6 +30,11 @@ public: ~CZNCSock() {} virtual int ConvertAddress(const struct sockaddr_storage * pAddr, socklen_t iAddrLen, CS_STRING & sIP, u_short * piPort); + +#ifndef HAVE_ICU + // Don't fail to compile when ICU is not enabled + void SetEncoding(const CString&) {} +#endif }; enum EAddrType { diff --git a/include/znc/User.h b/include/znc/User.h index 583a2732..b6d2de67 100644 --- a/include/znc/User.h +++ b/include/znc/User.h @@ -120,6 +120,7 @@ public: void SetDenySetBindHost(bool b); bool SetStatusPrefix(const CString& s); void SetDefaultChanModes(const CString& s); + void SetClientEncoding(const CString& s); void SetQuitMsg(const CString& s); bool AddCTCPReply(const CString& sCTCP, const CString& sReply); bool DelCTCPReply(const CString& sCTCP); @@ -153,6 +154,7 @@ public: const CString& GetPassSalt() const; const std::set& GetAllowedHosts() const; const CString& GetTimestampFormat() const; + const CString& GetClientEncoding() const; bool GetTimestampAppend() const; bool GetTimestampPrepend() const; @@ -192,6 +194,7 @@ protected: CString m_sPassSalt; CString m_sStatusPrefix; CString m_sDefaultChanModes; + CString m_sClientEncoding; CString m_sQuitMsg; MCString m_mssCTCPReplies; diff --git a/src/Client.cpp b/src/Client.cpp index d41294a0..7a84d1de 100644 --- a/src/Client.cpp +++ b/src/Client.cpp @@ -673,6 +673,7 @@ void CClient::AcceptLogin(CUser& User) { SetTimeout(540, TMO_READ); SetSockName("USR::" + m_pUser->GetUserName()); + SetEncoding(m_pUser->GetClientEncoding()); if (!m_sNetwork.empty()) { m_pNetwork = m_pUser->FindNetwork(m_sNetwork); diff --git a/src/IRCNetwork.cpp b/src/IRCNetwork.cpp index 7333eeee..d25aea1d 100644 --- a/src/IRCNetwork.cpp +++ b/src/IRCNetwork.cpp @@ -56,6 +56,7 @@ CIRCNetwork::CIRCNetwork(CUser *pUser, const CString& sName) { m_sChanPrefixes = ""; m_bIRCAway = false; + m_sEncoding = ""; m_fFloodRate = 1; m_uFloodBurst = 4; @@ -78,6 +79,7 @@ CIRCNetwork::CIRCNetwork(CUser *pUser, const CIRCNetwork &Network) { m_sChanPrefixes = ""; m_bIRCAway = false; + m_sEncoding = ""; m_RawBuffer.SetLineCount(100, true); // This should be more than enough raws, especially since we are buffering the MOTD separately m_MotdBuffer.SetLineCount(200, true); // This should be more than enough motd lines @@ -99,6 +101,7 @@ void CIRCNetwork::Clone(const CIRCNetwork& Network, bool bCloneName) { SetIdent(Network.GetIdent()); SetRealName(Network.GetRealName()); SetBindHost(Network.GetBindHost()); + SetEncoding(Network.GetEncoding()); // Servers const vector& vServers = Network.GetServers(); @@ -259,6 +262,7 @@ bool CIRCNetwork::ParseConfig(CConfig *pConfig, CString& sError, bool bUpgrade) { "ident", &CIRCNetwork::SetIdent }, { "realname", &CIRCNetwork::SetRealName }, { "bindhost", &CIRCNetwork::SetBindHost }, + { "encoding", &CIRCNetwork::SetEncoding }, }; size_t numStringOptions = sizeof(StringOptions) / sizeof(StringOptions[0]); TOption BoolOptions[] = { @@ -405,6 +409,7 @@ CConfig CIRCNetwork::ToConfig() { config.AddKeyValuePair("IRCConnectEnabled", CString(GetIRCConnectEnabled())); config.AddKeyValuePair("FloodRate", CString(GetFloodRate())); config.AddKeyValuePair("FloodBurst", CString(GetFloodBurst())); + config.AddKeyValuePair("Encoding", m_sEncoding); // Modules CModules& Mods = GetModules(); @@ -1102,6 +1107,10 @@ const CString& CIRCNetwork::GetBindHost() const { return m_sBindHost; } +const CString& CIRCNetwork::GetEncoding() const { + return m_sEncoding; +} + void CIRCNetwork::SetNick(const CString& s) { if (m_pUser->GetNick().Equals(s)) { m_sNick = ""; @@ -1142,6 +1151,10 @@ void CIRCNetwork::SetBindHost(const CString& s) { } } +void CIRCNetwork::SetEncoding(const CString& s) { + m_sEncoding = s; +} + CString CIRCNetwork::ExpandString(const CString& sStr) const { CString sRet; return ExpandString(sStr, sRet); diff --git a/src/IRCSock.cpp b/src/IRCSock.cpp index 97cca445..ed4eb587 100644 --- a/src/IRCSock.cpp +++ b/src/IRCSock.cpp @@ -65,6 +65,7 @@ CIRCSock::CIRCSock(CIRCNetwork* pNetwork) : CZNCSock() { EnableReadLine(); m_Nick.SetIdent(m_pNetwork->GetIdent()); m_Nick.SetHost(m_pNetwork->GetBindHost()); + SetEncoding(m_pNetwork->GetEncoding()); m_uMaxNickLen = 9; m_uCapPaused = 0; diff --git a/src/Listener.cpp b/src/Listener.cpp index 064f0b61..4fddeba8 100644 --- a/src/Listener.cpp +++ b/src/Listener.cpp @@ -89,6 +89,7 @@ CIncomingConnection::CIncomingConnection(const CString& sHostname, unsigned shor // This has to be fixed up later, if desired. SetTimeout(120, 0); + SetEncoding("UTF-8"); EnableReadLine(); } diff --git a/src/User.cpp b/src/User.cpp index c3254a49..4b47a75a 100644 --- a/src/User.cpp +++ b/src/User.cpp @@ -101,6 +101,7 @@ CUser::CUser(const CString& sUserName) m_bAppendTimestamp = false; m_bPrependTimestamp = true; m_uMaxNetworks = 1; + m_sClientEncoding = ""; m_pUserTimer = new CUserTimer(this); CZNC::Get().GetManager().AddCron(m_pUserTimer); } @@ -147,6 +148,7 @@ bool CUser::ParseConfig(CConfig* pConfig, CString& sError) { { "dccvhost", &CUser::SetDCCBindHost }, { "timestampformat", &CUser::SetTimestampFormat }, { "skin", &CUser::SetSkinName }, + { "clientencoding", &CUser::SetClientEncoding }, }; size_t numStringOptions = sizeof(StringOptions) / sizeof(StringOptions[0]); TOption UIntOptions[] = { @@ -700,6 +702,7 @@ bool CUser::Clone(const CUser& User, CString& sErrorRet, bool bCloneNetworks) { SetJoinTries(User.JoinTries()); SetMaxNetworks(User.MaxNetworks()); SetMaxJoins(User.MaxJoins()); + SetClientEncoding(User.GetClientEncoding()); // Allowed Hosts m_ssAllowedHosts.clear(); @@ -896,6 +899,7 @@ CConfig CUser::ToConfig() { config.AddKeyValuePair("JoinTries", CString(m_uMaxJoinTries)); config.AddKeyValuePair("MaxNetworks", CString(m_uMaxNetworks)); config.AddKeyValuePair("MaxJoins", CString(m_uMaxJoins)); + config.AddKeyValuePair("ClientEncoding", GetClientEncoding()); // Allow Hosts if (!m_ssAllowedHosts.empty()) { @@ -1104,6 +1108,7 @@ void CUser::SetDenyLoadMod(bool b) { m_bDenyLoadMod = b; } void CUser::SetAdmin(bool b) { m_bAdmin = b; } void CUser::SetDenySetBindHost(bool b) { m_bDenySetBindHost = b; } void CUser::SetDefaultChanModes(const CString& s) { m_sDefaultChanModes = s; } +void CUser::SetClientEncoding(const CString& s) { m_sClientEncoding = s; } void CUser::SetQuitMsg(const CString& s) { m_sQuitMsg = s; } void CUser::SetAutoClearChanBuffer(bool b) { m_bAutoClearChanBuffer = b; } @@ -1175,6 +1180,7 @@ bool CUser::DenySetBindHost() const { return m_bDenySetBindHost; } bool CUser::MultiClients() const { return m_bMultiClients; } const CString& CUser::GetStatusPrefix() const { return m_sStatusPrefix; } const CString& CUser::GetDefaultChanModes() const { return m_sDefaultChanModes; } +const CString& CUser::GetClientEncoding() const { return m_sClientEncoding; } bool CUser::HasSpaceForNewNetwork() const { return GetNetworks().size() < MaxNetworks(); } CString CUser::GetQuitMsg() const { return (!m_sQuitMsg.Trim_n().empty()) ? m_sQuitMsg : CZNC::GetTag(false); }