diff --git a/include/znc/ZNCString.h b/include/znc/ZNCString.h index 546701b7..4942fa11 100644 --- a/include/znc/ZNCString.h +++ b/include/znc/ZNCString.h @@ -77,7 +77,8 @@ public: EHTML, ESQL, ENAMEDFMT, - EDEBUG + EDEBUG, + EMSGTAG } EEscape; static const CaseSensitivity CaseSensitive = CaseSensitivity::CaseSensitive; diff --git a/src/Utils.cpp b/src/Utils.cpp index 580a725a..a03c9c70 100644 --- a/src/Utils.cpp +++ b/src/Utils.cpp @@ -484,7 +484,9 @@ MCString CUtils::GetMessageTags(const CString& sLine) { MCString mssTags; for (VCString::const_iterator it = vsTags.begin(); it != vsTags.end(); ++it) { - mssTags[it->Token(0, false, "=", true)] = it->Token(1, true, "=", true); + CString sKey = it->Token(0, false, "=", true); + CString sValue = it->Token(1, true, "=", true); + mssTags[sKey] = sValue.Escape(CString::EMSGTAG, CString::CString::EASCII); } return mssTags; } @@ -502,7 +504,9 @@ void CUtils::SetMessageTags(CString& sLine, const MCString& mssTags) { if (!sTags.empty()) { sTags += ";"; } - sTags += it->first + "=" + it->second; + sTags += it->first; + if (!it->second.empty()) + sTags += "=" + it->second.Escape_n(CString::EMSGTAG); } sLine = "@" + sTags + " " + sLine; } diff --git a/src/ZNCString.cpp b/src/ZNCString.cpp index c6b7bfe8..e8ce010f 100644 --- a/src/ZNCString.cpp +++ b/src/ZNCString.cpp @@ -182,6 +182,8 @@ CString::EEscape CString::ToEscape(const CString& sEsc) { return ENAMEDFMT; } else if (sEsc.Equals("DEBUG")) { return EDEBUG; + } else if (sEsc.Equals("MSGTAG")) { + return EMSGTAG; } return EASCII; @@ -322,6 +324,33 @@ CString CString::Escape_n(EEscape eFrom, EEscape eTo) const { } else { ch = *p; } + + break; + case EMSGTAG: + if (*p != '\\' || iLength < (a +1)) { + ch = *p; + } else { + a++; + p++; + + if (*p == ':') { + ch = ';'; + } else if (*p == 's') { + ch = ' '; + } else if (*p == '0') { + ch = '\0'; + } else if (*p == '\\') { + ch = '\\'; + } else if (*p == 'r') { + ch = '\r'; + } else if (*p == 'n') { + ch = '\n'; + } else { + ch = *p; + } + } + + break; } switch (eTo) { @@ -380,6 +409,16 @@ CString CString::Escape_n(EEscape eFrom, EEscape eTo) const { sRet += ch; } + break; + case EMSGTAG: + if (ch == ';') { sRet += '\\'; sRet += ':'; + } else if (ch == ' ') { sRet += '\\'; sRet += 's'; + } else if (ch == '\0') { sRet += '\\'; sRet += '0'; + } else if (ch == '\\') { sRet += '\\'; sRet += '\\'; + } else if (ch == '\r') { sRet += '\\'; sRet += 'r'; + } else if (ch == '\n') { sRet += '\\'; sRet += 'n'; + } else { sRet += ch; } + break; } } diff --git a/test/StringTest.cpp b/test/StringTest.cpp index 1c09ee45..fddf9c74 100644 --- a/test/StringTest.cpp +++ b/test/StringTest.cpp @@ -38,21 +38,23 @@ protected: } void testString(const CString& in, const CString& url, - const CString& html, const CString& sql) { + const CString& html, const CString& sql, const CString& tag) { SCOPED_TRACE("String: " + in); testEncode(in, url, "URL"); testEncode(in, html, "HTML"); testEncode(in, sql, "SQL"); + testEncode(in, tag, "MSGTAG"); } }; TEST_F(EscapeTest, Test) { - // input url html sql - testString("abcdefg", "abcdefg", "abcdefg", "abcdefg"); - testString("\n\t\r", "%0A%09%0D", "\n\t\r", "\\n\\t\\r"); - testString("'\"", "%27%22", "'"", "\\'\\\""); - testString("&<>", "%26%3C%3E", "&<>", "&<>"); + // input url html sql msgtag + testString("abcdefg","abcdefg", "abcdefg", "abcdefg", "abcdefg"); + testString("\n\t\r", "%0A%09%0D", "\n\t\r", "\\n\\t\\r", "\\n\t\\r"); + testString("'\"", "%27%22", "'"", "\\'\\\"", "'\""); + testString("&<>", "%26%3C%3E", "&<>", "&<>", "&<>"); + testString(" ;", "+%3B", " ;", " ;", "\\s\\:"); } TEST(StringTest, Bool) { diff --git a/test/UtilsTest.cpp b/test/UtilsTest.cpp index 4f4197bb..9d2f30c5 100644 --- a/test/UtilsTest.cpp +++ b/test/UtilsTest.cpp @@ -37,6 +37,25 @@ TEST(IRC32, GetMessageTags) { exp["a"] = "==b=="; EXPECT_EQ(exp, CUtils::GetMessageTags("@a===b== :rest")); exp.clear(); + + exp["a"] = ""; + exp["b"] = "c"; + exp["d"] = ""; + EXPECT_EQ(exp, CUtils::GetMessageTags("@a;b=c;d :rest")); + exp.clear(); + + exp["semi-colon"] += ';'; + exp["space"] += ' '; + exp["NUL"] += '\0'; + exp["backslash"] += '\\'; + exp["CR"] += '\r'; + exp["LF"] += '\n'; + EXPECT_EQ(exp, CUtils::GetMessageTags(R"(@semi-colon=\:;space=\s;NUL=\0;backslash=\\;CR=\r;LF=\n :rest)")); + exp.clear(); + + exp["a"] = "; \\\r\n"; + EXPECT_EQ(exp, CUtils::GetMessageTags(R"(@a=\:\s\\\r\n :rest)")); + exp.clear(); } TEST(IRC32, SetMessageTags) { @@ -54,5 +73,24 @@ TEST(IRC32, SetMessageTags) { tags["c"] = "d"; CUtils::SetMessageTags(sLine, tags); EXPECT_EQ("@a=b;c=d :rest", sLine); + + tags["e"] = ""; + CUtils::SetMessageTags(sLine, tags); + EXPECT_EQ("@a=b;c=d;e :rest", sLine); + tags.clear(); + + tags["semi-colon"] += ';'; + tags["space"] += ' '; + tags["NUL"] += '\0'; + tags["backslash"] += '\\'; + tags["CR"] += '\r'; + tags["LF"] += '\n'; + CUtils::SetMessageTags(sLine, tags); + EXPECT_EQ(R"(@CR=\r;LF=\n;NUL=\0;backslash=\\;semi-colon=\:;space=\s :rest)", sLine); + tags.clear(); + + tags["a"] = "; \\\r\n"; + CUtils::SetMessageTags(sLine, tags); + EXPECT_EQ(R"(@a=\:\s\\\r\n :rest)", sLine); }