diff --git a/include/znc/Client.h b/include/znc/Client.h index 3bc712d1..b97d4722 100644 --- a/include/znc/Client.h +++ b/include/znc/Client.h @@ -82,9 +82,9 @@ protected: CClient* m_pClient; }; -class CClient : public CZNCSock { +class CClient : public CIRCSocket { public: - CClient() : CZNCSock() { + CClient() : CIRCSocket() { m_pUser = NULL; m_pNetwork = NULL; m_bGotPass = false; diff --git a/include/znc/IRCSock.h b/include/znc/IRCSock.h index 8b552aef..04e97f6e 100644 --- a/include/znc/IRCSock.h +++ b/include/znc/IRCSock.h @@ -30,7 +30,8 @@ class CIRCNetwork; class CClient; // !Forward Declarations -class CIRCSock : public CZNCSock { +// TODO: This class needs new name +class CIRCSock : public CIRCSocket { public: CIRCSock(CIRCNetwork* pNetwork); virtual ~CIRCSock(); diff --git a/include/znc/Socket.h b/include/znc/Socket.h index 84b4f49e..d2d4523d 100644 --- a/include/znc/Socket.h +++ b/include/znc/Socket.h @@ -220,4 +220,37 @@ protected: CModule* m_pModule; //!< pointer to the module that this sock instance belongs to }; +/** + * @class CIRCSocket + * @brief Base IRC socket for client<->ZNC, and ZNC<->server + */ +class CIRCSocket : public CZNCSock { +public: +#ifdef HAVE_ICU + /** + * @brief Allow IRC control characters to appear even if protocol encoding explicitly disallows them. + * + * E.g. ISO-2022-JP disallows 0x0F, which in IRC means "reset format", + * so by default it gets replaced with U+FFFD ("replacement character"). + * https://code.google.com/p/chromium/issues/detail?id=277062#c3 + * + * In case if protocol encoding uses these code points for something else, the encoding takes preference, + * and they are not IRC control characters anymore. + */ + void IcuExtToUCallback( + UConverterToUnicodeArgs* toArgs, + const char* codeUnits, + int32_t length, + UConverterCallbackReason reason, + UErrorCode* err) override; + void IcuExtFromUCallback( + UConverterFromUnicodeArgs* fromArgs, + const UChar* codeUnits, + int32_t length, + UChar32 codePoint, + UConverterCallbackReason reason, + UErrorCode* err) override; +#endif +}; + #endif /* SOCKET_H */ diff --git a/src/IRCSock.cpp b/src/IRCSock.cpp index 0de72373..5a58ef40 100644 --- a/src/IRCSock.cpp +++ b/src/IRCSock.cpp @@ -54,7 +54,7 @@ bool CIRCSock::IsFloodProtected(double fRate) { return fRate > FLOOD_MINIMAL_RATE; } -CIRCSock::CIRCSock(CIRCNetwork* pNetwork) : CZNCSock() { +CIRCSock::CIRCSock(CIRCNetwork* pNetwork) : CIRCSocket() { m_pNetwork = pNetwork; m_bAuthed = false; m_bNamesx = false; diff --git a/src/Socket.cpp b/src/Socket.cpp index acd89ae8..6bf1f772 100644 --- a/src/Socket.cpp +++ b/src/Socket.cpp @@ -22,6 +22,10 @@ #include #include +#ifdef HAVE_ICU +#include +#endif + CZNCSock::CZNCSock(int timeout) : Csock(timeout) { #ifdef HAVE_LIBSSL DisableSSLCompression(); @@ -487,3 +491,54 @@ bool CSocket::Listen(unsigned short uPort, bool bSSL, unsigned int uTimeout) { CModule* CSocket::GetModule() const { return m_pModule; } /////////////////// !CSocket /////////////////// + +#ifdef HAVE_ICU +void CIRCSocket::IcuExtToUCallback( + UConverterToUnicodeArgs* toArgs, + const char* codeUnits, + int32_t length, + UConverterCallbackReason reason, + UErrorCode* err) { + // From http://www.mirc.com/colors.html + // The Control+O key combination in mIRC inserts ascii character 15, + // which turns off all previous attributes, including color, bold, underline, and italics. + // + // \x02 bold + // \x03 mIRC-compatible color + // \x04 RRGGBB color + // \x0F normal/reset (turn off bold, colors, etc.) + // \x12 reverse (weechat) + // \x16 reverse (mirc, kvirc) + // \x1D italic + // \x1F underline + // Also see http://www.visualirc.net/tech-attrs.php + // + // Keep in sync with CUser::AddTimestamp and CIRCSocket::IcuExtFromUCallback + static const std::set scAllowedChars = {'\x02', '\x03', '\x04', '\x0F', '\x12', '\x16', '\x1D', '\x1F'}; + if (reason == UCNV_ILLEGAL && length == 1 && scAllowedChars.count(*codeUnits)) { + *err = U_ZERO_ERROR; + UChar c = *codeUnits; + ucnv_cbToUWriteUChars(toArgs, &c, 1, 0, err); + return; + } + Csock::IcuExtToUCallback(toArgs, codeUnits, length, reason, err); +} + +void CIRCSocket::IcuExtFromUCallback( + UConverterFromUnicodeArgs* fromArgs, + const UChar* codeUnits, + int32_t length, + UChar32 codePoint, + UConverterCallbackReason reason, + UErrorCode* err) { + // See comment in CIRCSocket::IcuExtToUCallback + static const std::set scAllowedChars = {0x02, 0x03, 0x04, 0x0F, 0x12, 0x16, 0x1D, 0x1F}; + if (reason == UCNV_ILLEGAL && scAllowedChars.count(codePoint)) { + *err = U_ZERO_ERROR; + char c = codePoint; + ucnv_cbFromUWriteBytes(fromArgs, &c, 1, 0, err); + return; + } + Csock::IcuExtFromUCallback(fromArgs, codeUnits, length, codePoint, reason, err); +} +#endif diff --git a/src/User.cpp b/src/User.cpp index e89b4b71..1cf5e31d 100644 --- a/src/User.cpp +++ b/src/User.cpp @@ -577,6 +577,8 @@ CString CUser::AddTimestamp(time_t tm, const CString& sStr) const { // \x1D italic // \x1F underline // Also see http://www.visualirc.net/tech-attrs.php + // + // Keep in sync with CIRCSocket::IcuExt__UCallback if (CString::npos != sRet.find_first_of("\x02\x03\x04\x0F\x12\x16\x1D\x1F")) { sRet += "\x0F"; }