Introduce a Message type (#506)

This commit is contained in:
J-P Nurmi
2015-02-17 23:46:33 +01:00
parent 51caa5c4cf
commit a06bf1c985
5 changed files with 409 additions and 3 deletions
+2 -2
View File
@@ -44,12 +44,12 @@ LIB_SRCS := ZNCString.cpp Csocket.cpp znc.cpp IRCNetwork.cpp User.cpp IRCSock.c
Client.cpp Chan.cpp Nick.cpp Server.cpp Modules.cpp MD5.cpp Buffer.cpp Utils.cpp \
FileUtils.cpp HTTPSock.cpp Template.cpp ClientCommand.cpp Socket.cpp SHA256.cpp \
WebModules.cpp Listener.cpp Config.cpp ZNCDebug.cpp Threads.cpp version.cpp Query.cpp \
SSLVerifyHost.cpp
SSLVerifyHost.cpp Message.cpp
LIB_SRCS := $(addprefix src/,$(LIB_SRCS))
BIN_SRCS := src/main.cpp
LIB_OBJS := $(patsubst %cpp,%o,$(LIB_SRCS))
BIN_OBJS := $(patsubst %cpp,%o,$(BIN_SRCS))
TESTS := StringTest ConfigTest UtilsTest ThreadTest NickTest ClientTest NetworkTest
TESTS := StringTest ConfigTest UtilsTest ThreadTest NickTest ClientTest NetworkTest MessageTest
TESTS := $(addprefix test/,$(addsuffix .o,$(TESTS)))
CLEAN := znc src/*.o test/*.o core core.* .version_extra .depend modules/.depend \
unittest $(LIBZNC)
+1 -1
View File
@@ -292,7 +292,7 @@ if test "$POLL" = "yes"; then
fi
AC_CHECK_LIB( gnugetopt, getopt_long,)
AC_CHECK_FUNCS([lstat getopt_long getpassphrase])
AC_CHECK_FUNCS([lstat getopt_long getpassphrase clock_gettime])
# ----- Check for dlopen
+90
View File
@@ -0,0 +1,90 @@
/*
* Copyright (C) 2004-2015 ZNC, see the NOTICE file for details.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _MESSAGE_H
#define _MESSAGE_H
#include <znc/zncconfig.h>
#include <znc/ZNCString.h>
#include <znc/Nick.h>
#include <sys/time.h>
class CChan;
class CClient;
class CIRCNetwork;
class CMessage {
public:
explicit CMessage(const CString& sMessage = "");
CMessage(const CNick& Nick, const CString& sCommand, const VCString& vsParams, const MCString& mssTags = MCString::EmptyMap);
// ZNC <-> IRC
CIRCNetwork* GetNetwork() const { return m_pNetwork; }
void SetNetwork(CIRCNetwork* pNetwork) { m_pNetwork = pNetwork; }
// ZNC <-> CLI
CClient* GetClient() const { return m_pClient; }
void SetClient(CClient* pClient) { m_pClient = pClient; }
CChan* GetChan() const { return m_pChan; }
void SetChan(CChan* pChan) { m_pChan = pChan; }
CNick& GetNick() { return m_Nick; }
const CNick& GetNick() const { return m_Nick; }
void SetNick(const CNick& Nick) { m_Nick = Nick; }
const CString& GetCommand() const { return m_sCommand; }
void SetCommand(const CString& sCommand) { m_sCommand = sCommand; }
const VCString& GetParams() const { return m_vsParams; }
CString GetParams(unsigned int uIdx, unsigned int uLen = -1) const;
void SetParams(const VCString& vsParams) { m_vsParams = vsParams; }
CString GetParam(unsigned int uIdx) const;
void SetParam(unsigned int uIdx, const CString& sParam);
const timeval& GetTime() const { return m_time; }
void SetTime(const timeval& ts) { m_time = ts; }
const MCString& GetTags() const { return m_mssTags; }
void SetTags(const MCString& mssTags) { m_mssTags = mssTags; }
CString GetTag(const CString& sKey) const;
void SetTag(const CString& sKey, const CString& sValue);
enum FormatFlags {
IncludeAll = 0x0,
ExcludePrefix = 0x1,
ExcludeTags = 0x2
};
CString ToString(unsigned int uFlags = IncludeAll) const;
void Parse(CString sMessage);
private:
void InitTime();
CNick m_Nick;
CString m_sCommand;
VCString m_vsParams;
MCString m_mssTags;
timeval m_time;
CIRCNetwork* m_pNetwork = nullptr;
CClient* m_pClient = nullptr;
CChan* m_pChan = nullptr;
};
#endif // !_MESSAGE_H
+177
View File
@@ -0,0 +1,177 @@
/*
* Copyright (C) 2004-2015 ZNC, see the NOTICE file for details.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <znc/Message.h>
#include <znc/Utils.h>
CMessage::CMessage(const CString& sMessage)
{
Parse(sMessage);
InitTime();
}
CMessage::CMessage(const CNick& Nick, const CString& sCommand, const VCString& vsParams, const MCString& mssTags)
: m_Nick(Nick), m_sCommand(sCommand), m_vsParams(vsParams), m_mssTags(mssTags)
{
InitTime();
}
CString CMessage::GetParams(unsigned int uIdx, unsigned int uLen) const
{
VCString vsParams;
if (uLen > m_vsParams.size() - uIdx - 1) {
uLen = m_vsParams.size() - uIdx - 1;
}
for (unsigned int i = uIdx; i <= uIdx + uLen; ++i) {
CString sParam = m_vsParams[i];
if (sParam.Contains(" ")) {
sParam = ":" + sParam;
}
vsParams.push_back(sParam);
}
return CString(" ").Join(vsParams.begin(), vsParams.end());
}
CString CMessage::GetParam(unsigned int uIdx) const
{
if (uIdx >= m_vsParams.size()) {
return "";
}
return m_vsParams[uIdx];
}
void CMessage::SetParam(unsigned int uIdx, const CString& sParam)
{
if (uIdx >= m_vsParams.size()) {
m_vsParams.resize(uIdx + 1);
}
m_vsParams[uIdx] = sParam;
}
CString CMessage::GetTag(const CString& sKey) const
{
MCString::const_iterator it = m_mssTags.find(sKey);
if (it != m_mssTags.end()) {
return it->second;
}
return "";
}
void CMessage::SetTag(const CString& sKey, const CString& sValue)
{
m_mssTags[sKey] = sValue;
}
CString CMessage::ToString(unsigned int uFlags) const
{
CString sMessage;
// <prefix>
if (!(uFlags & ExcludePrefix)) {
CString sPrefix = m_Nick.GetHostMask();
if (!sPrefix.empty()) {
sMessage += ":" + sPrefix;
}
}
// <command>
if (!m_sCommand.empty()) {
if (!sMessage.empty()) {
sMessage += " ";
}
sMessage += m_sCommand;
}
// <params>
for (const CString& sParam : m_vsParams) {
sMessage += " ";
if (sParam.Contains(" ")) {
sMessage += ":";
}
sMessage += sParam;
}
// <tags>
if (!(uFlags & ExcludeTags)) {
CUtils::SetMessageTags(sMessage, m_mssTags);
}
return sMessage;
}
void CMessage::Parse(CString sMessage)
{
// <tags>
if (sMessage.StartsWith("@")) {
m_mssTags = CUtils::GetMessageTags(sMessage);
sMessage = sMessage.Token(1, true);
}
// <message> ::= [':' <prefix> <SPACE> ] <command> <params> <crlf>
// <prefix> ::= <servername> | <nick> [ '!' <user> ] [ '@' <host> ]
// <command> ::= <letter> { <letter> } | <number> <number> <number>
// <SPACE> ::= ' ' { ' ' }
// <params> ::= <SPACE> [ ':' <trailing> | <middle> <params> ]
// <middle> ::= <Any *non-empty* sequence of octets not including SPACE
// or NUL or CR or LF, the first of which may not be ':'>
// <trailing> ::= <Any, possibly *empty*, sequence of octets not including
// NUL or CR or LF>
// <prefix>
if (sMessage.TrimLeft(":")) {
m_Nick.Parse(sMessage.Token(0));
sMessage = sMessage.Token(1, true);
}
// <command>
m_sCommand = sMessage.Token(0);
sMessage = sMessage.Token(1, true);
// <params>
m_vsParams.clear();
while (!sMessage.empty()) {
if (sMessage.TrimLeft(":")) {
m_vsParams.push_back(sMessage);
sMessage.clear();
} else {
m_vsParams.push_back(sMessage.Token(0));
sMessage = sMessage.Token(1, true);
}
}
}
void CMessage::InitTime()
{
auto it = m_mssTags.find("time");
if (it != m_mssTags.end()) {
m_time = CUtils::ParseServerTime(it->second);
return;
}
#ifdef HAVE_CLOCK_GETTIME
timespec ts;
if (clock_gettime(CLOCK_REALTIME, &ts) == 0) {
m_time.tv_sec = ts.tv_sec;
m_time.tv_usec = ts.tv_nsec / 1000;
return;
}
#endif
if (!gettimeofday(&m_time, nullptr)) {
m_time.tv_sec = time(nullptr);
m_time.tv_usec = 0;
}
}
+139
View File
@@ -0,0 +1,139 @@
/*
* Copyright (C) 2004-2015 ZNC, see the NOTICE file for details.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <gtest/gtest.h>
#include <znc/ZNCString.h>
#include <znc/Message.h>
TEST(MessageTest, SetParam) {
CMessage msg;
msg.SetParam(1, "bar");
msg.SetParam(0, "foo");
VCString params = {"foo", "bar"};
EXPECT_EQ(params, msg.GetParams());
msg.SetParam(3, "baz");
params = {"foo", "bar", "", "baz"};
EXPECT_EQ(params, msg.GetParams());
}
TEST(MessageTest, ToString) {
EXPECT_EQ("CMD", CMessage("CMD").ToString());
EXPECT_EQ("CMD p1", CMessage("CMD p1").ToString());
EXPECT_EQ("CMD p1 p2", CMessage("CMD p1 p2").ToString());
EXPECT_EQ("CMD :p p p", CMessage("CMD :p p p").ToString());
EXPECT_EQ(":irc.znc.in", CMessage(":irc.znc.in").ToString());
EXPECT_EQ(":irc.znc.in CMD", CMessage(":irc.znc.in CMD").ToString());
EXPECT_EQ(":irc.znc.in CMD p1", CMessage(":irc.znc.in CMD p1").ToString());
EXPECT_EQ(":irc.znc.in CMD p1 p2", CMessage(":irc.znc.in CMD p1 p2").ToString());
EXPECT_EQ(":irc.znc.in CMD :p p p", CMessage(":irc.znc.in CMD :p p p").ToString());
}
TEST(MessageTest, FormatFlags) {
const CString line = "@foo=bar :irc.example.com COMMAND param";
CMessage msg(line);
EXPECT_EQ(line, msg.ToString());
EXPECT_EQ(":irc.example.com COMMAND param", msg.ToString(CMessage::ExcludeTags));
EXPECT_EQ("@foo=bar COMMAND param", msg.ToString(CMessage::ExcludePrefix));
EXPECT_EQ("COMMAND param", msg.ToString(CMessage::ExcludePrefix|CMessage::ExcludeTags));
}
// The test data for MessageTest.Parse originates from https://github.com/SaberUK/ircparser
//
// IRCParser - Internet Relay Chat Message Parser
//
// Copyright (C) 2015 Peter "SaberUK" Powell <petpow@saberuk.com>
//
// Permission to use, copy, modify, and/or distribute this software for any purpose with or without
// fee is hereby granted, provided that the above copyright notice and this permission notice appear
// in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
// SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
// AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
// NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
// OF THIS SOFTWARE.
// when checking a valid message with tags and a source
TEST(MessageTest, ParseWithTags) {
const CString line = "@tag1=value1;tag2;vendor1/tag3=value2;vendor2/tag4 :irc.example.com COMMAND param1 param2 :param3 param3";
MCString tags;
tags["tag1"] = "value1";
tags["tag2"] = "";
tags["vendor1/tag3"] = "value2";
tags["vendor2/tag4"] = "";
VCString params = {"param1", "param2", "param3 param3"};
CMessage msg(line);
EXPECT_EQ(line, msg.ToString());
EXPECT_EQ(tags, msg.GetTags());
EXPECT_EQ("irc.example.com", msg.GetNick().GetNick());
EXPECT_EQ("COMMAND", msg.GetCommand());
EXPECT_EQ(params, msg.GetParams());
}
// when checking a valid message with a source but no tags
TEST(MessageTest, ParseWithoutTags) {
const CString line = ":irc.example.com COMMAND param1 param2 :param3 param3";
VCString params = {"param1", "param2", "param3 param3"};
CMessage msg(line);
EXPECT_EQ(line, msg.ToString());
EXPECT_EQ(MCString(), msg.GetTags());
EXPECT_EQ("irc.example.com", msg.GetNick().GetNick());
EXPECT_EQ("COMMAND", msg.GetCommand());
EXPECT_EQ(params, msg.GetParams());
}
// when checking a valid message with tags but no source
TEST(MessageTest, ParseWithoutSource) {
const CString line = "@tag1=value1;tag2;vendor1/tag3=value2;vendor2/tag4 COMMAND param1 param2 :param3 param3";
MCString tags;
tags["tag1"] = "value1";
tags["tag2"] = "";
tags["vendor1/tag3"] = "value2";
tags["vendor2/tag4"] = "";
VCString params = {"param1", "param2", "param3 param3"};
CMessage msg(line);
EXPECT_EQ(line, msg.ToString());
EXPECT_EQ(tags, msg.GetTags());
EXPECT_EQ("", msg.GetNick().GetNick());
EXPECT_EQ("COMMAND", msg.GetCommand());
EXPECT_EQ(params, msg.GetParams());
}
// when checking a valid message with no tags, source or parameters
TEST(MessageTest, ParseWithoutSourceAndTags) {
const CString line = "COMMAND";
CMessage msg(line);
EXPECT_EQ(line, msg.ToString());
EXPECT_EQ(MCString(), msg.GetTags());
EXPECT_EQ("", msg.GetNick().GetNick());
EXPECT_EQ("COMMAND", msg.GetCommand());
EXPECT_EQ(VCString(), msg.GetParams());
}