Merge pull request #1000 from jpnurmi/message

Say hello to a Message type
This commit is contained in:
Alexey Sokolov
2015-08-16 13:16:46 +01:00
23 changed files with 1114 additions and 165 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
+5 -2
View File
@@ -29,7 +29,7 @@ class CClient;
class CBufLine {
public:
CBufLine() : CBufLine("") { throw 0; } // shouldn't be called, but is needed for compilation
CBufLine(const CString& sFormat, const CString& sText = "", const timeval* ts = nullptr);
CBufLine(const CString& sFormat, const CString& sText = "", const timeval* ts = nullptr, const MCString& mssTags = MCString::EmptyMap);
~CBufLine();
CString GetLine(const CClient& Client, const MCString& msParams) const;
void UpdateTime();
@@ -38,12 +38,14 @@ public:
void SetFormat(const CString& sFormat) { m_sFormat = sFormat; }
void SetText(const CString& sText) { m_sText = sText; }
void SetTime(const timeval& ts) { m_time = ts; }
void SetTags(const MCString& mssTags) { m_mssTags = mssTags; }
// !Setters
// Getters
const CString& GetFormat() const { return m_sFormat; }
const CString& GetText() const { return m_sText; }
timeval GetTime() const { return m_time; }
const MCString& GetTags() const { return m_mssTags; }
// !Getters
private:
@@ -51,6 +53,7 @@ protected:
CString m_sFormat;
CString m_sText;
timeval m_time;
MCString m_mssTags;
};
class CBuffer : private std::deque<CBufLine> {
@@ -58,7 +61,7 @@ public:
CBuffer(unsigned int uLineCount = 100);
~CBuffer();
size_type AddLine(const CString& sFormat, const CString& sText = "", const timeval* ts = nullptr);
size_type AddLine(const CString& sFormat, const CString& sText = "", const timeval* ts = nullptr, const MCString& mssTags = MCString::EmptyMap);
/// Same as AddLine, but replaces a line whose format string starts with sMatch if there is one.
size_type UpdateLine(const CString& sMatch, const CString& sFormat, const CString& sText = "");
/// Same as UpdateLine, but does nothing if this exact line already exists.
+1 -1
View File
@@ -98,7 +98,7 @@ public:
bool SetBufferCount(unsigned int u, bool bForce = false) { m_bHasBufferCountSet = true; return m_Buffer.SetLineCount(u, bForce); }
void InheritBufferCount(unsigned int u, bool bForce = false) { if (!m_bHasBufferCountSet) m_Buffer.SetLineCount(u, bForce); }
void ResetBufferCount();
size_t AddBuffer(const CString& sFormat, const CString& sText = "", const timeval* ts = nullptr) { return m_Buffer.AddLine(sFormat, sText, ts); }
size_t AddBuffer(const CString& sFormat, const CString& sText = "", const timeval* ts = nullptr, const MCString& mssTags = MCString::EmptyMap) { return m_Buffer.AddLine(sFormat, sText, ts, mssTags); }
void ClearBuffer() { m_Buffer.Clear(); }
void SendBuffer(CClient* pClient);
void SendBuffer(CClient* pClient, const CBuffer& Buffer);
+40
View File
@@ -29,6 +29,7 @@ class CUser;
class CIRCNetwork;
class CIRCSock;
class CClient;
class CMessage;
// !Forward Declarations
class CAuthBase {
@@ -174,7 +175,46 @@ public:
void SetPlaybackActive(bool bActive) { m_bPlaybackActive = bActive; }
void PutIRC(const CString& sLine);
/** Sends a raw data line to the client.
* @param sLine The line to be sent.
*
* The line is first passed \e unmodified to the \ref CModule::OnSendToClient()
* module hook. If no module halts the process, the line is then sent to the client.
*
* These lines appear in the debug output in the following syntax:
* \code [time] (user/network) ZNC -> CLI [line] \endcode
*/
void PutClient(const CString& sLine);
/** Sends a message to the client.
* @param Message The message to be sent.
* @note Only known and compatible message tags are sent.
*
* Not all IRC clients are capable of handling arbitrary sets of message
* tags. For example, some older versions of some popular clients were
* prepared to parse just one interesting tag, \c time, and would break
* if multiple tags were included. Thus, in order to stay compatible with
* a variety of IRC clients, ZNC has to filter out message tags that the
* client has not explicitly requested.
*
* The following table documents currently supported message tags, and
* which capabilities the client is required to have requested to receive
* the respective message tags.
*
* Message tag | Capability
* ----------- | ----------
* \c time | \l CClient::HasServerTime() (<a href="http://ircv3.net/specs/extensions/server-time-3.2.html">server-time</a>)
* \c batch | \l CClient::HasBatch() (<a href="http://ircv3.net/specs/extensions/batch-3.2.html">batch</a>)
*
* @warning Bypassing the filter may cause troubles to some older IRC clients.
*
* It is possible to bypass the filter by converting a message to a string
* using \l CMessage::ToString(), and passing the resulting raw line to the
* \l CClient::PutClient(const CString& sLine) overload:
* \code
* pClient->PutClient(Message.ToString());
* \endcode
*/
void PutClient(const CMessage& Message);
unsigned int PutStatus(const CTable& table);
void PutStatus(const CString& sLine);
void PutStatusNotice(const CString& sLine);
+2
View File
@@ -35,6 +35,7 @@ class CServer;
class CIRCSock;
class CIRCNetworkPingTimer;
class CIRCNetworkJoinTimer;
class CMessage;
class CIRCNetwork {
public:
@@ -90,6 +91,7 @@ public:
// !Modules
bool PutUser(const CString& sLine, CClient* pClient = nullptr, CClient* pSkipClient = nullptr);
bool PutUser(const CMessage& Message, CClient* pClient = nullptr, CClient* pSkipClient = nullptr);
bool PutStatus(const CString& sLine, CClient* pClient = nullptr, CClient* pSkipClient = nullptr);
bool PutModule(const CString& sModule, const CString& sLine, CClient* pClient = nullptr, CClient* pSkipClient = nullptr);
+9 -7
View File
@@ -28,6 +28,7 @@ class CChan;
class CUser;
class CIRCNetwork;
class CClient;
class CMessage;
// !Forward Declarations
// TODO: This class needs new name
@@ -49,13 +50,13 @@ public:
// Message Handlers
bool OnCTCPReply(CNick& Nick, CString& sMessage);
bool OnPrivCTCP(CNick& Nick, CString& sMessage);
bool OnChanCTCP(CNick& Nick, const CString& sChan, CString& sMessage);
bool OnGeneralCTCP(CNick& Nick, CString& sMessage);
bool OnPrivMsg(CNick& Nick, CString& sMessage);
bool OnChanMsg(CNick& Nick, const CString& sChan, CString& sMessage);
bool OnPrivNotice(CNick& Nick, CString& sMessage);
bool OnChanNotice(CNick& Nick, const CString& sChan, CString& sMessage);
bool OnPrivCTCP(CMessage& Message);
bool OnChanCTCP(CMessage& Message);
bool OnGeneralCTCP(CMessage& Message);
bool OnPrivMsg(CMessage& Message);
bool OnChanMsg(CMessage& Message);
bool OnPrivNotice(CMessage& Message);
bool OnChanNotice(CMessage& Message);
bool OnServerCapAvailable(const CString& sCap);
// !Message Handlers
@@ -113,6 +114,7 @@ public:
CString GetISupport(const CString& sKey, const CString& sDefault = "") const;
// !Getters
// TODO: CMessage
// This handles NAMESX and UHNAMES in a raw 353 reply
void ForwardRaw353(const CString& sLine) const;
void ForwardRaw353(const CString& sLine, CClient* pClient) const;
+193
View File
@@ -0,0 +1,193 @@
/*
* 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;
};
class CChanAction : public CMessage {
public:
CString GetText() const { return GetParam(1).TrimLeft_n("\001ACTION ").TrimRight_n("\001"); }
void SetText(const CString& sText) { SetParam(1, "\001ACTION " + sText + "\001"); }
};
class CChanCTCP : public CMessage {
public:
CString GetText() const { return GetParam(1).TrimLeft_n("\001").TrimRight_n("\001"); }
void SetText(const CString& sText) { SetParam(1, "\001" + sText + "\001"); }
};
class CChanMessage : public CMessage {
public:
CString GetText() const { return GetParam(1); }
void SetText(const CString& sText) { SetParam(1, sText); }
};
class CChanNotice : public CMessage {
public:
CString GetText() const { return GetParam(1); }
void SetText(const CString& sText) { SetParam(1, sText); }
};
class CJoinMessage : public CMessage {
public:
};
class CNickMessage : public CMessage {
public:
CString GetOldNick() const { return GetNick().GetNick(); }
CString GetNewNick() const { return GetParam(0); }
void SetNewNick(const CString& sNick) { SetParam(0, sNick); }
};
class CKickMessage : public CMessage {
public:
CString GetKickedNick() const { return GetParam(1); }
void SetKickedNick(const CString& sNick) { SetParam(1, sNick); }
CString GetReason() const { return GetParam(2); }
void SetReason(const CString& sReason) { SetParam(2, sReason); }
};
class CPartMessage : public CMessage {
public:
CString GetReason() const { return GetParam(1); }
void SetReason(const CString& sReason) { SetParam(1, sReason); }
};
class CPrivAction : public CMessage {
public:
CString GetText() const { return GetParam(1).TrimLeft_n("\001ACTION ").TrimRight_n("\001"); }
void SetText(const CString& sText) { SetParam(1, "\001ACTION " + sText + "\001"); }
};
class CPrivCTCP : public CMessage {
public:
CString GetText() const { return GetParam(1).TrimLeft_n("\001").TrimRight_n("\001"); }
void SetText(const CString& sText) { SetParam(1, "\001" + sText + "\001"); }
};
class CPrivMessage : public CMessage {
public:
CString GetText() const { return GetParam(1); }
void SetText(const CString& sText) { SetParam(1, sText); }
};
class CPrivNotice : public CMessage {
public:
CString GetText() const { return GetParam(1); }
void SetText(const CString& sText) { SetParam(1, sText); }
};
class CQuitMessage : public CMessage {
public:
CString GetReason() const { return GetParam(0); }
void SetReason(const CString& sReason) { SetParam(0, sReason); }
};
class CTopicMessage : public CMessage {
public:
CString GetTopic() const { return GetParam(1); }
void SetTopic(const CString& sTopic) { SetParam(1, sTopic); }
};
// The various CMessage subclasses are "mutable views" to the data held by CMessage.
// They provide convenient access to message type speficic attributes, but are not
// allowed to hold extra data of their own.
static_assert(sizeof(CChanAction) == sizeof(CMessage), "No data members allowed in CMessage subclasses.");
static_assert(sizeof(CChanCTCP) == sizeof(CMessage), "No data members allowed in CMessage subclasses.");
static_assert(sizeof(CChanMessage) == sizeof(CMessage), "No data members allowed in CMessage subclasses.");
static_assert(sizeof(CChanNotice) == sizeof(CMessage), "No data members allowed in CMessage subclasses.");
static_assert(sizeof(CJoinMessage) == sizeof(CMessage), "No data members allowed in CMessage subclasses.");
static_assert(sizeof(CPartMessage) == sizeof(CMessage), "No data members allowed in CMessage subclasses.");
static_assert(sizeof(CPrivAction) == sizeof(CMessage), "No data members allowed in CMessage subclasses.");
static_assert(sizeof(CPrivCTCP) == sizeof(CMessage), "No data members allowed in CMessage subclasses.");
static_assert(sizeof(CPrivMessage) == sizeof(CMessage), "No data members allowed in CMessage subclasses.");
static_assert(sizeof(CPrivNotice) == sizeof(CMessage), "No data members allowed in CMessage subclasses.");
static_assert(sizeof(CNickMessage) == sizeof(CMessage), "No data members allowed in CMessage subclasses.");
static_assert(sizeof(CKickMessage) == sizeof(CMessage), "No data members allowed in CMessage subclasses.");
static_assert(sizeof(CQuitMessage) == sizeof(CMessage), "No data members allowed in CMessage subclasses.");
static_assert(sizeof(CTopicMessage) == sizeof(CMessage), "No data members allowed in CMessage subclasses.");
#endif // !_MESSAGE_H
+33
View File
@@ -21,6 +21,7 @@
#include <znc/WebModules.h>
#include <znc/Utils.h>
#include <znc/Threads.h>
#include <znc/Message.h>
#include <znc/main.h>
#include <functional>
#include <set>
@@ -593,6 +594,7 @@ public:
* @param vChans List of channels which you and nick share.
*/
virtual void OnQuit(const CNick& Nick, const CString& sMessage, const std::vector<CChan*>& vChans);
virtual void OnQuitMessage(CQuitMessage& Message, const std::vector<CChan*>& vChans);
/** Called when a nickname change occurs. If we are changing our nick,
* sNewNick will equal m_pIRCSock->GetNick().
* @param Nick The nick which changed its nickname
@@ -600,6 +602,7 @@ public:
* @param vChans Channels which we and nick share.
*/
virtual void OnNick(const CNick& Nick, const CString& sNewNick, const std::vector<CChan*>& vChans);
virtual void OnNickMessage(CNickMessage& Message, const std::vector<CChan*>& vChans);
/** Called when a nick is kicked from a channel.
* @param OpNick The nick which generated the kick.
* @param sKickedNick The nick which was kicked.
@@ -607,6 +610,7 @@ public:
* @param sMessage The kick message.
*/
virtual void OnKick(const CNick& OpNick, const CString& sKickedNick, CChan& Channel, const CString& sMessage);
virtual void OnKickMessage(CKickMessage& Message);
/** This module hook is called just before ZNC tries to join an IRC channel.
* @param Chan The channel which is about to get joined.
* @return See CModule::EModRet.
@@ -617,12 +621,14 @@ public:
* @param Channel The channel which was joined.
*/
virtual void OnJoin(const CNick& Nick, CChan& Channel);
virtual void OnJoinMessage(CJoinMessage& Message);
/** Called when a nick parts a channel.
* @param Nick The nick who parted.
* @param Channel The channel which was parted.
* @param sMessage The part message.
*/
virtual void OnPart(const CNick& Nick, CChan& Channel, const CString& sMessage);
virtual void OnPartMessage(CPartMessage& Message);
/** Called when user is invited into a channel
* @param Nick The nick who invited you.
* @param sChan The channel the user got invited into
@@ -652,6 +658,7 @@ public:
*/
virtual EModRet OnChanBufferPlayLine2(CChan& Chan, CClient& Client, CString& sLine, const timeval& tv);
virtual EModRet OnChanBufferPlayLine(CChan& Chan, CClient& Client, CString& sLine);
virtual EModRet OnChanBufferPlayMessage(CMessage& Message);
/** Called when a line from the query buffer is played back.
* @param Client The client this line will go to.
* @param sLine The raw IRC traffic line from the buffer.
@@ -660,6 +667,7 @@ public:
*/
virtual EModRet OnPrivBufferPlayLine2(CClient& Client, CString& sLine, const timeval& tv);
virtual EModRet OnPrivBufferPlayLine(CClient& Client, CString& sLine);
virtual EModRet OnPrivBufferPlayMessage(CMessage& Message);
/** Called when a client successfully logged in to ZNC. */
virtual void OnClientLogin();
@@ -749,6 +757,7 @@ public:
* @return See CModule::EModRet.
*/
virtual EModRet OnPrivCTCP(CNick& Nick, CString& sMessage);
virtual EModRet OnPrivCTCPMessage(CPrivCTCP& Message);
/** Called when we receive a channel CTCP request <em>from IRC</em>.
* @param Nick The nick the CTCP request is from.
* @param Channel The channel to which the request was sent.
@@ -756,6 +765,7 @@ public:
* @return See CModule::EModRet.
*/
virtual EModRet OnChanCTCP(CNick& Nick, CChan& Channel, CString& sMessage);
virtual EModRet OnChanCTCPMessage(CChanCTCP& Message);
/** Called when we receive a private CTCP ACTION ("/me" in query) <em>from IRC</em>.
* This is called after CModule::OnPrivCTCP().
* @param Nick The nick the action came from.
@@ -763,6 +773,7 @@ public:
* @return See CModule::EModRet.
*/
virtual EModRet OnPrivAction(CNick& Nick, CString& sMessage);
virtual EModRet OnPrivActionMessage(CPrivAction& Message);
/** Called when we receive a channel CTCP ACTION ("/me" in a channel) <em>from IRC</em>.
* This is called after CModule::OnChanCTCP().
* @param Nick The nick the action came from.
@@ -771,12 +782,14 @@ public:
* @return See CModule::EModRet.
*/
virtual EModRet OnChanAction(CNick& Nick, CChan& Channel, CString& sMessage);
virtual EModRet OnChanActionMessage(CChanAction& Message);
/** Called when we receive a private message <em>from IRC</em>.
* @param Nick The nick which sent the message.
* @param sMessage The message.
* @return See CModule::EModRet.
*/
virtual EModRet OnPrivMsg(CNick& Nick, CString& sMessage);
virtual EModRet OnPrivMessage(CPrivMessage& Message);
/** Called when we receive a channel message <em>from IRC</em>.
* @param Nick The nick which sent the message.
* @param Channel The channel to which the message was sent.
@@ -784,12 +797,14 @@ public:
* @return See CModule::EModRet.
*/
virtual EModRet OnChanMsg(CNick& Nick, CChan& Channel, CString& sMessage);
virtual EModRet OnChanMessage(CChanMessage& Message);
/** Called when we receive a private notice.
* @param Nick The nick which sent the notice.
* @param sMessage The notice message.
* @return See CModule::EModRet.
*/
virtual EModRet OnPrivNotice(CNick& Nick, CString& sMessage);
virtual EModRet OnPrivNoticeMessage(CPrivNotice& Message);
/** Called when we receive a channel notice.
* @param Nick The nick which sent the notice.
* @param Channel The channel to which the notice was sent.
@@ -797,6 +812,7 @@ public:
* @return See CModule::EModRet.
*/
virtual EModRet OnChanNotice(CNick& Nick, CChan& Channel, CString& sMessage);
virtual EModRet OnChanNoticeMessage(CChanNotice& Message);
/** Called when we receive a channel topic change <em>from IRC</em>.
* @param Nick The nick which changed the topic.
* @param Channel The channel whose topic was changed.
@@ -804,6 +820,7 @@ public:
* @return See CModule::EModRet.
*/
virtual EModRet OnTopic(CNick& Nick, CChan& Channel, CString& sTopic);
virtual EModRet OnTopicMessage(CTopicMessage& Message);
/** Called for every CAP received via CAP LS from server.
* @param sCap capability supported by server.
@@ -1186,11 +1203,16 @@ public:
bool OnModCTCP(const CString& sMessage);
bool OnQuit(const CNick& Nick, const CString& sMessage, const std::vector<CChan*>& vChans);
bool OnQuitMessage(CQuitMessage& Message, const std::vector<CChan*>& vChans);
bool OnNick(const CNick& Nick, const CString& sNewNick, const std::vector<CChan*>& vChans);
bool OnNickMessage(CNickMessage& Message, const std::vector<CChan*>& vChans);
bool OnKick(const CNick& Nick, const CString& sOpNick, CChan& Channel, const CString& sMessage);
bool OnKickMessage(CKickMessage& Message);
bool OnJoining(CChan& Channel);
bool OnJoin(const CNick& Nick, CChan& Channel);
bool OnJoinMessage(CJoinMessage& Message);
bool OnPart(const CNick& Nick, CChan& Channel, const CString& sMessage);
bool OnPartMessage(CPartMessage& Message);
bool OnInvite(const CNick& Nick, const CString& sChan);
bool OnChanBufferStarting(CChan& Chan, CClient& Client);
@@ -1199,6 +1221,8 @@ public:
bool OnChanBufferPlayLine(CChan& Chan, CClient& Client, CString& sLine);
bool OnPrivBufferPlayLine2(CClient& Client, CString& sLine, const timeval& tv);
bool OnPrivBufferPlayLine(CClient& Client, CString& sLine);
bool OnChanBufferPlayMessage(CMessage& Message);
bool OnPrivBufferPlayMessage(CMessage& Message);
bool OnClientLogin();
bool OnClientDisconnect();
@@ -1216,14 +1240,23 @@ public:
bool OnCTCPReply(CNick& Nick, CString& sMessage);
bool OnPrivCTCP(CNick& Nick, CString& sMessage);
bool OnPrivCTCPMessage(CPrivCTCP& Message);
bool OnChanCTCP(CNick& Nick, CChan& Channel, CString& sMessage);
bool OnChanCTCPMessage(CChanCTCP& Message);
bool OnPrivAction(CNick& Nick, CString& sMessage);
bool OnPrivActionMessage(CPrivAction& Message);
bool OnChanAction(CNick& Nick, CChan& Channel, CString& sMessage);
bool OnChanActionMessage(CChanAction& Message);
bool OnPrivMsg(CNick& Nick, CString& sMessage);
bool OnPrivMessage(CPrivMessage& Message);
bool OnChanMsg(CNick& Nick, CChan& Channel, CString& sMessage);
bool OnChanMessage(CChanMessage& Message);
bool OnPrivNotice(CNick& Nick, CString& sMessage);
bool OnPrivNoticeMessage(CPrivNotice& Message);
bool OnChanNotice(CNick& Nick, CChan& Channel, CString& sMessage);
bool OnChanNoticeMessage(CChanNotice& Message);
bool OnTopic(CNick& Nick, CChan& Channel, CString& sTopic);
bool OnTopicMessage(CTopicMessage& Message);
bool OnTimerAutoJoin(CChan& Channel);
bool OnAddNetwork(CIRCNetwork& Network, CString& sErrorRet);
+1 -1
View File
@@ -38,7 +38,7 @@ public:
const CBuffer& GetBuffer() const { return m_Buffer; }
unsigned int GetBufferCount() const { return m_Buffer.GetLineCount(); }
bool SetBufferCount(unsigned int u, bool bForce = false) { return m_Buffer.SetLineCount(u, bForce); }
size_t AddBuffer(const CString& sFormat, const CString& sText = "", const timeval* ts = nullptr) { return m_Buffer.AddLine(sFormat, sText, ts); }
size_t AddBuffer(const CString& sFormat, const CString& sText = "", const timeval* ts = nullptr, const MCString& mssTags = MCString::EmptyMap) { return m_Buffer.AddLine(sFormat, sText, ts, mssTags); }
void ClearBuffer() { m_Buffer.Clear(); }
void SendBuffer(CClient* pClient);
void SendBuffer(CClient* pClient, const CBuffer& Buffer);
+1
View File
@@ -83,6 +83,7 @@ public:
static CString CTime(time_t t, const CString& sTZ);
static CString FormatTime(time_t t, const CString& sFormat, const CString& sTZ);
static CString FormatServerTime(const timeval& tv);
static timeval ParseServerTime(const CString& sTime);
static SCString GetTimezones();
static SCString GetEncodings();
+30 -14
View File
@@ -25,12 +25,12 @@ public:
virtual ~CBuffExtras() {}
void AddBuffer(CChan& Channel, const CString& sMessage) {
void AddBuffer(CChan& Channel, const CString& sMessage, const timeval* tv = nullptr, const MCString& mssTags = MCString::EmptyMap) {
// If they have AutoClearChanBuffer enabled, only add messages if no client is connected
if (Channel.AutoClearChanBuffer() && GetNetwork()->IsUserOnline())
return;
Channel.AddBuffer(":" + GetModNick() + "!" + GetModName() + "@znc.in PRIVMSG " + _NAMEDFMT(Channel.GetName()) + " :{text}", sMessage);
Channel.AddBuffer(":" + GetModNick() + "!" + GetModName() + "@znc.in PRIVMSG " + _NAMEDFMT(Channel.GetName()) + " :{text}", sMessage, tv, mssTags);
}
void OnRawMode2(const CNick* pOpNick, CChan& Channel, const CString& sModes, const CString& sArgs) override {
@@ -38,34 +38,50 @@ public:
AddBuffer(Channel, sNickMask + " set mode: " + sModes + " " + sArgs);
}
void OnKick(const CNick& OpNick, const CString& sKickedNick, CChan& Channel, const CString& sMessage) override {
AddBuffer(Channel, OpNick.GetNickMask() + " kicked " + sKickedNick + " Reason: [" + sMessage + "]");
void OnKickMessage(CKickMessage& Message) override {
const CNick& OpNick = Message.GetNick();
const CString sKickedNick = Message.GetKickedNick();
CChan& Channel = *Message.GetChan();
const CString sMessage = Message.GetReason();
AddBuffer(Channel, OpNick.GetNickMask() + " kicked " + sKickedNick + " Reason: [" + sMessage + "]", &Message.GetTime(), Message.GetTags());
}
void OnQuit(const CNick& Nick, const CString& sMessage, const vector<CChan*>& vChans) override {
void OnQuitMessage(CQuitMessage& Message, const vector<CChan*>& vChans) override {
const CNick& Nick = Message.GetNick();
const CString sMessage = Message.GetReason();
CString sMsg = Nick.GetNickMask() + " quit with message: [" + sMessage + "]";
for (CChan* pChan : vChans) {
AddBuffer(*pChan, sMsg);
AddBuffer(*pChan, sMsg, &Message.GetTime(), Message.GetTags());
}
}
void OnJoin(const CNick& Nick, CChan& Channel) override {
AddBuffer(Channel, Nick.GetNickMask() + " joined");
void OnJoinMessage(CJoinMessage& Message) override {
const CNick& Nick = Message.GetNick();
CChan& Channel = *Message.GetChan();
AddBuffer(Channel, Nick.GetNickMask() + " joined", &Message.GetTime(), Message.GetTags());
}
void OnPart(const CNick& Nick, CChan& Channel, const CString& sMessage) override {
AddBuffer(Channel, Nick.GetNickMask() + " parted with message: [" + sMessage + "]");
void OnPartMessage(CPartMessage& Message) override {
const CNick& Nick = Message.GetNick();
CChan& Channel = *Message.GetChan();
const CString sMessage = Message.GetReason();
AddBuffer(Channel, Nick.GetNickMask() + " parted with message: [" + sMessage + "]", &Message.GetTime(), Message.GetTags());
}
void OnNick(const CNick& OldNick, const CString& sNewNick, const vector<CChan*>& vChans) override {
void OnNickMessage(CNickMessage& Message, const vector<CChan*>& vChans) override {
const CNick& OldNick = Message.GetNick();
const CString sNewNick = Message.GetNewNick();
CString sMsg = OldNick.GetNickMask() + " is now known as " + sNewNick;
for (CChan* pChan : vChans) {
AddBuffer(*pChan, sMsg);
AddBuffer(*pChan, sMsg, &Message.GetTime(), Message.GetTags());
}
}
EModRet OnTopic(CNick& Nick, CChan& Channel, CString& sTopic) override {
AddBuffer(Channel, Nick.GetNickMask() + " changed the topic to: " + sTopic);
EModRet OnTopicMessage(CTopicMessage& Message) override {
const CNick& Nick = Message.GetNick();
CChan& Channel = *Message.GetChan();
const CString sTopic = Message.GetTopic();
AddBuffer(Channel, Nick.GetNickMask() + " changed the topic to: " + sTopic, &Message.GetTime(), Message.GetTags());
return CONTINUE;
}
+3 -3
View File
@@ -19,7 +19,7 @@
#include <znc/User.h>
#include <time.h>
CBufLine::CBufLine(const CString& sFormat, const CString& sText, const timeval* ts) : m_sFormat(sFormat), m_sText(sText), m_time() {
CBufLine::CBufLine(const CString& sFormat, const CString& sText, const timeval* ts, const MCString& mssTags) : m_sFormat(sFormat), m_sText(sText), m_time(), m_mssTags(mssTags) {
if (ts == nullptr)
UpdateTime();
else
@@ -54,7 +54,7 @@ CBuffer::CBuffer(unsigned int uLineCount) : m_uLineCount(uLineCount) {
CBuffer::~CBuffer() {}
CBuffer::size_type CBuffer::AddLine(const CString& sFormat, const CString& sText, const timeval* ts) {
CBuffer::size_type CBuffer::AddLine(const CString& sFormat, const CString& sText, const timeval* ts, const MCString& mssTags) {
if (!m_uLineCount) {
return 0;
}
@@ -63,7 +63,7 @@ CBuffer::size_type CBuffer::AddLine(const CString& sFormat, const CString& sText
erase(begin());
}
push_back(CBufLine(sFormat, sText, ts));
push_back(CBufLine(sFormat, sText, ts, mssTags));
return size();
}
+10 -6
View File
@@ -20,6 +20,7 @@
#include <znc/IRCNetwork.h>
#include <znc/Config.h>
#include <znc/znc.h>
#include <znc/Message.h>
using std::set;
using std::vector;
@@ -610,16 +611,19 @@ void CChan::SendBuffer(CClient* pClient, const CBuffer& Buffer) {
size_t uSize = Buffer.Size();
for (size_t uIdx = 0; uIdx < uSize; uIdx++) {
const CBufLine& BufLine = Buffer.GetBufLine(uIdx);
CString sLine = BufLine.GetLine(*pUseClient, MCString::EmptyMap);
CMessage Message(BufLine.GetLine(*pUseClient, MCString::EmptyMap));
Message.SetChan(this);
Message.SetNetwork(m_pNetwork);
Message.SetClient(pClient);
Message.SetTime(BufLine.GetTime());
Message.SetTags(BufLine.GetTags());
if (bBatch) {
MCString msBatchTags = CUtils::GetMessageTags(sLine);
msBatchTags["batch"] = sBatchName;
CUtils::SetMessageTags(sLine, msBatchTags);
Message.SetTag("batch", sBatchName);
}
bool bNotShowThisLine = false;
NETWORKMODULECALL(OnChanBufferPlayLine2(*this, *pUseClient, sLine, BufLine.GetTime()), m_pNetwork->GetUser(), m_pNetwork, nullptr, &bNotShowThisLine);
NETWORKMODULECALL(OnChanBufferPlayMessage(Message), m_pNetwork->GetUser(), m_pNetwork, nullptr, &bNotShowThisLine);
if (bNotShowThisLine) continue;
m_pNetwork->PutUser(sLine, pUseClient);
m_pNetwork->PutUser(Message, pUseClient);
}
bSkipStatusMsg = pUseClient->HasServerTime();
+37 -6
View File
@@ -96,8 +96,9 @@ void CClient::ReadLine(const CString& sData) {
DEBUG("(" << GetFullName() << ") CLI -> ZNC [" << sLine << "]");
MCString mssTags;
if (sLine.StartsWith("@")) {
// TODO support message-tags properly
mssTags = CUtils::GetMessageTags(sLine);
sLine = sLine.Token(1, true);
}
@@ -262,7 +263,7 @@ void CClient::ReadLine(const CString& sData) {
CChan* pChan = m_pNetwork->FindChan(sTarget);
if ((pChan) && (!pChan->AutoClearChanBuffer())) {
pChan->AddBuffer(":" + _NAMEDFMT(GetNickMask()) + " NOTICE " + _NAMEDFMT(sTarget) + " :{text}", sMsg);
pChan->AddBuffer(":" + _NAMEDFMT(GetNickMask()) + " NOTICE " + _NAMEDFMT(sTarget) + " :{text}", sMsg, nullptr, mssTags);
}
// Relay to the rest of the clients that may be connected to this user
@@ -313,13 +314,13 @@ void CClient::ReadLine(const CString& sData) {
CChan* pChan = m_pNetwork->FindChan(sTarget);
if (pChan && (!pChan->AutoClearChanBuffer() || !m_pNetwork->IsUserOnline())) {
pChan->AddBuffer(":" + _NAMEDFMT(GetNickMask()) + " PRIVMSG " + _NAMEDFMT(sTarget) + " :\001ACTION {text}\001", sMessage);
pChan->AddBuffer(":" + _NAMEDFMT(GetNickMask()) + " PRIVMSG " + _NAMEDFMT(sTarget) + " :\001ACTION {text}\001", sMessage, nullptr, mssTags);
}
} else {
if (!m_pUser->AutoClearQueryBuffer() || !m_pNetwork->IsUserOnline()) {
CQuery* pQuery = m_pNetwork->AddQuery(sTarget);
if (pQuery) {
pQuery->AddBuffer(":" + _NAMEDFMT(GetNickMask()) + " PRIVMSG " + _NAMEDFMT(sTarget) + " :\001ACTION {text}\001", sMessage);
pQuery->AddBuffer(":" + _NAMEDFMT(GetNickMask()) + " PRIVMSG " + _NAMEDFMT(sTarget) + " :\001ACTION {text}\001", sMessage, nullptr, mssTags);
}
}
}
@@ -368,13 +369,13 @@ void CClient::ReadLine(const CString& sData) {
CChan* pChan = m_pNetwork->FindChan(sTarget);
if ((pChan) && (!pChan->AutoClearChanBuffer() || !m_pNetwork->IsUserOnline())) {
pChan->AddBuffer(":" + _NAMEDFMT(GetNickMask()) + " PRIVMSG " + _NAMEDFMT(sTarget) + " :{text}", sMsg);
pChan->AddBuffer(":" + _NAMEDFMT(GetNickMask()) + " PRIVMSG " + _NAMEDFMT(sTarget) + " :{text}", sMsg, nullptr, mssTags);
}
} else {
if (!m_pUser->AutoClearQueryBuffer() || !m_pNetwork->IsUserOnline()) {
CQuery* pQuery = m_pNetwork->AddQuery(sTarget);
if (pQuery) {
pQuery->AddBuffer(":" + _NAMEDFMT(GetNickMask()) + " PRIVMSG " + _NAMEDFMT(sTarget) + " :{text}", sMsg);
pQuery->AddBuffer(":" + _NAMEDFMT(GetNickMask()) + " PRIVMSG " + _NAMEDFMT(sTarget) + " :{text}", sMsg, nullptr, mssTags);
}
}
}
@@ -793,6 +794,36 @@ void CClient::PutClient(const CString& sLine) {
Write(sCopy + "\r\n");
}
void CClient::PutClient(const CMessage& Message)
{
CString sLine = Message.ToString(CMessage::ExcludeTags);
// TODO: introduce a module hook that gives control over the tags that are sent
MCString mssTags;
if (HasServerTime()) {
CString sServerTime = Message.GetTag("time");
if (!sServerTime.empty()) {
mssTags["time"] = sServerTime;
} else {
mssTags["time"] = CUtils::FormatServerTime(Message.GetTime());
}
}
if (HasBatch()) {
CString sBatch = Message.GetTag("batch");
if (!sBatch.empty()) {
mssTags["batch"] = sBatch;
}
}
if (!mssTags.empty()) {
CUtils::SetMessageTags(sLine, mssTags);
}
PutClient(sLine);
}
void CClient::PutStatusNotice(const CString& sLine) {
PutModNotice("status", sLine);
}
+22 -3
View File
@@ -22,6 +22,7 @@
#include <znc/Server.h>
#include <znc/Chan.h>
#include <znc/Query.h>
#include <znc/Message.h>
#include <algorithm>
#include <memory>
@@ -642,11 +643,15 @@ void CIRCNetwork::ClientConnected(CClient *pClient) {
uSize = m_NoticeBuffer.Size();
for (uIdx = 0; uIdx < uSize; uIdx++) {
const CBufLine& BufLine = m_NoticeBuffer.GetBufLine(uIdx);
CString sLine = BufLine.GetLine(*pClient, msParams);
CMessage Message(BufLine.GetLine(*pClient, msParams));
Message.SetNetwork(this);
Message.SetClient(pClient);
Message.SetTime(BufLine.GetTime());
Message.SetTags(BufLine.GetTags());
bool bContinue = false;
NETWORKMODULECALL(OnPrivBufferPlayLine2(*pClient, sLine, BufLine.GetTime()), m_pUser, this, nullptr, &bContinue);
NETWORKMODULECALL(OnPrivBufferPlayMessage(Message), m_pUser, this, nullptr, &bContinue);
if (bContinue) continue;
pClient->PutClient(sLine);
pClient->PutClient(Message);
}
m_NoticeBuffer.Clear();
@@ -726,6 +731,20 @@ bool CIRCNetwork::PutUser(const CString& sLine, CClient* pClient, CClient* pSkip
return (pClient == nullptr);
}
bool CIRCNetwork::PutUser(const CMessage& Message, CClient* pClient, CClient* pSkipClient) {
for (CClient* pEachClient : m_vClients) {
if ((!pClient || pClient == pEachClient) && pSkipClient != pEachClient) {
pEachClient->PutClient(Message);
if (pClient) {
return true;
}
}
}
return (pClient == nullptr);
}
bool CIRCNetwork::PutStatus(const CString& sLine, CClient* pClient, CClient* pSkipClient) {
for (CClient* pEachClient : m_vClients) {
if ((!pClient || pClient == pEachClient) && pSkipClient != pEachClient) {
+119 -109
View File
@@ -20,6 +20,7 @@
#include <znc/IRCNetwork.h>
#include <znc/Server.h>
#include <znc/Query.h>
#include <znc/Message.h>
#include <time.h>
using std::set;
@@ -156,29 +157,31 @@ void CIRCSock::ReadLine(const CString& sData) {
IRCSOCKMODULECALL(OnRaw(sLine), &bReturn);
if (bReturn) return;
if (sLine.StartsWith("PING ")) {
CMessage Message(sLine);
Message.SetNetwork(m_pNetwork);
CString sCmd = Message.GetCommand();
if (sCmd.Equals("PING")) {
// Generate a reply and don't forward this to any user,
// we don't want any PING forwarded
PutIRCQuick("PONG " + sLine.substr(5));
PutIRCQuick("PONG " + Message.GetParam(0));
return;
} else if (sLine.Token(1).Equals("PONG")) {
} else if (sCmd.Equals("PONG")) {
// Block PONGs, we already responded to the pings
return;
} else if (sLine.StartsWith("ERROR ")) {
} else if (sCmd.Equals("ERROR")) {
//ERROR :Closing Link: nick[24.24.24.24] (Excess Flood)
CString sError(sLine.substr(6));
sError.TrimPrefix();
CString sError = Message.GetParam(0);
m_pNetwork->PutStatus("Error from Server [" + sError + "]");
return;
}
CString sCmd = sLine.Token(1);
if ((sCmd.length() == 3) && (isdigit(sCmd[0])) && (isdigit(sCmd[1])) && (isdigit(sCmd[2]))) {
CString sServer = sLine.Token(0).LeftChomp_n();
CString sServer = Message.GetNick().GetHostMask();
unsigned int uRaw = sCmd.ToUInt();
CString sNick = sLine.Token(2);
CString sRest = sLine.Token(3, true);
CString sNick = Message.GetParam(0);
CString sRest = Message.GetParams(1);
CString sTmp;
switch (uRaw) {
@@ -224,9 +227,9 @@ void CIRCSock::ReadLine(const CString& sData) {
m_pNetwork->UpdateExactRawBuffer(":" + _NAMEDFMT(sServer) + " " + sCmd + " {target} " + _NAMEDFMT(sRest));
break;
case 10: { // :irc.server.com 010 nick <hostname> <port> :<info>
CString sHost = sRest.Token(0);
CString sPort = sRest.Token(1);
CString sInfo = sRest.Token(2, true).TrimPrefix_n();
CString sHost = Message.GetParam(1);
CString sPort = Message.GetParam(2);
CString sInfo = Message.GetParam(3);
m_pNetwork->PutStatus("Server [" + m_pNetwork->GetCurrentServer()->GetString(false) +
"] redirects us to [" + sHost + ":" + sPort + "] with reason [" + sInfo + "]");
m_pNetwork->PutStatus("Perhaps you want to add it as a new server.");
@@ -278,7 +281,7 @@ void CIRCSock::ReadLine(const CString& sData) {
CChan* pChan = m_pNetwork->FindChan(sRest.Token(0));
if (pChan) {
unsigned long ulDate = sLine.Token(4).ToULong();
unsigned long ulDate = Message.GetParam(2).ToULong();
pChan->SetCreationDate(ulDate);
if (!pChan->IsModeKnown()) {
@@ -296,7 +299,7 @@ void CIRCSock::ReadLine(const CString& sData) {
break;
case 331: {
// :irc.server.com 331 yournick #chan :No topic is set.
CChan* pChan = m_pNetwork->FindChan(sLine.Token(3));
CChan* pChan = m_pNetwork->FindChan(Message.GetParam(1));
if (pChan) {
pChan->SetTopic("");
@@ -309,11 +312,10 @@ void CIRCSock::ReadLine(const CString& sData) {
}
case 332: {
// :irc.server.com 332 yournick #chan :This is a topic
CChan* pChan = m_pNetwork->FindChan(sLine.Token(3));
CChan* pChan = m_pNetwork->FindChan(Message.GetParam(1));
if (pChan) {
CString sTopic = sLine.Token(4, true);
sTopic.LeftChomp();
CString sTopic = Message.GetParam(2);
pChan->SetTopic(sTopic);
if (pChan->IsDetached()) {
return;
@@ -324,11 +326,11 @@ void CIRCSock::ReadLine(const CString& sData) {
}
case 333: {
// :irc.server.com 333 yournick #chan setternick 1112320796
CChan* pChan = m_pNetwork->FindChan(sLine.Token(3));
CChan* pChan = m_pNetwork->FindChan(Message.GetParam(1));
if (pChan) {
sNick = sLine.Token(4);
unsigned long ulDate = sLine.Token(5).ToULong();
sNick = Message.GetParam(2);
unsigned long ulDate = Message.GetParam(3).ToULong();
pChan->SetTopicOwner(sNick);
pChan->SetTopicDate(ulDate);
@@ -342,13 +344,10 @@ void CIRCSock::ReadLine(const CString& sData) {
}
case 352: { // WHO
// :irc.yourserver.com 352 yournick #chan ident theirhost.com irc.theirserver.com theirnick H :0 Real Name
sServer = sLine.Token(0);
sNick = sLine.Token(7);
CString sChan = sLine.Token(3);
CString sIdent = sLine.Token(4);
CString sHost = sLine.Token(5);
sServer.LeftChomp();
sNick = Message.GetParam(5);
CString sChan = Message.GetParam(1);
CString sIdent = Message.GetParam(2);
CString sHost = Message.GetParam(3);
if (sNick.Equals(GetNick())) {
m_Nick.SetIdent(sIdent);
@@ -370,7 +369,7 @@ void CIRCSock::ReadLine(const CString& sData) {
const vector<CClient*>& vClients = m_pNetwork->GetClients();
for (CClient* pClient : vClients) {
if (pClient->HasNamesx()) {
m_pNetwork->PutUser(sLine, pClient);
m_pNetwork->PutUser(Message, pClient);
} else {
// The client doesn't support multi-prefix so we need to remove
// the other prefixes.
@@ -380,6 +379,7 @@ void CIRCSock::ReadLine(const CString& sData) {
if (pos >= 2 && pos != CString::npos) {
sNewNick = sNick[0] + sNick.substr(pos);
}
// TODO: CMessage
CString sNewLine = sServer + " 352 " + sLine.Token(2) + " " +
sChan + " " + sIdent + " " + sHost + " " +
sLine.Token(6) + " " + sNewNick + " " +
@@ -505,12 +505,11 @@ void CIRCSock::ReadLine(const CString& sData) {
return;
}
} else {
CNick Nick(sLine.Token(0).TrimPrefix_n());
sCmd = sLine.Token(1);
CString sRest = sLine.Token(2, true);
CNick Nick = Message.GetNick();
if (sCmd.Equals("NICK")) {
CString sNewNick = sRest.TrimPrefix_n();
CNickMessage& NickMsg = static_cast<CNickMessage&>(Message);
CString sNewNick = NickMsg.GetNewNick();
bool bIsVisible = false;
vector<CChan*> vFoundChans;
@@ -530,22 +529,22 @@ void CIRCSock::ReadLine(const CString& sData) {
// We are changing our own nick, the clients always must see this!
bIsVisible = false;
SetNick(sNewNick);
m_pNetwork->PutUser(sLine);
m_pNetwork->PutUser(Message);
}
IRCSOCKMODULECALL(OnNick(Nick, sNewNick, vFoundChans), NOTHING);
IRCSOCKMODULECALL(OnNickMessage(NickMsg, vFoundChans), NOTHING);
if (!bIsVisible) {
return;
}
} else if (sCmd.Equals("QUIT")) {
CString sMessage = sRest.TrimPrefix_n();
CQuitMessage& QuitMsg = static_cast<CQuitMessage&>(Message);
bool bIsVisible = false;
// :nick!ident@host.com QUIT :message
if (Nick.NickEquals(GetNick())) {
m_pNetwork->PutStatus("You quit [" + sMessage + "]");
m_pNetwork->PutStatus("You quit [" + QuitMsg.GetReason() + "]");
// We don't call module hooks and we don't
// forward this quit to clients (Some clients
// disconnect if they receive such a QUIT)
@@ -565,14 +564,15 @@ void CIRCSock::ReadLine(const CString& sData) {
}
}
IRCSOCKMODULECALL(OnQuit(Nick, sMessage, vFoundChans), NOTHING);
IRCSOCKMODULECALL(OnQuitMessage(QuitMsg, vFoundChans), NOTHING);
if (!bIsVisible) {
return;
}
} else if (sCmd.Equals("JOIN")) {
CString sChan = sRest.Token(0).TrimPrefix_n();
CChan* pChan;
CJoinMessage& JoinMsg = static_cast<CJoinMessage&>(Message);
CString sChan = JoinMsg.GetParam(0);
CChan* pChan = nullptr;
if (Nick.NickEquals(GetNick())) {
m_pNetwork->AddChan(sChan, false);
@@ -588,7 +588,8 @@ void CIRCSock::ReadLine(const CString& sData) {
if (pChan) {
pChan->AddNick(Nick.GetNickMask());
IRCSOCKMODULECALL(OnJoin(Nick.GetNickMask(), *pChan), NOTHING);
JoinMsg.SetChan(pChan);
IRCSOCKMODULECALL(OnJoinMessage(JoinMsg), NOTHING);
if (pChan->IsDetached()) {
return;
@@ -610,14 +611,15 @@ void CIRCSock::ReadLine(const CString& sData) {
}
}
} else if (sCmd.Equals("PART")) {
CString sChan = sRest.Token(0).TrimPrefix_n();
CString sMsg = sRest.Token(1, true).TrimPrefix_n();
CPartMessage& PartMsg = static_cast<CPartMessage&>(Message);
CString sChan = PartMsg.GetParam(0);
CChan* pChan = m_pNetwork->FindChan(sChan);
bool bDetached = false;
if (pChan) {
pChan->RemNick(Nick.GetNick());
IRCSOCKMODULECALL(OnPart(Nick.GetNickMask(), *pChan, sMsg), NOTHING);
PartMsg.SetChan(pChan);
IRCSOCKMODULECALL(OnPartMessage(PartMsg), NOTHING);
if (pChan->IsDetached())
bDetached = true;
@@ -637,8 +639,8 @@ void CIRCSock::ReadLine(const CString& sData) {
return;
}
} else if (sCmd.Equals("MODE")) {
CString sTarget = sRest.Token(0);
CString sModes = sRest.Token(1, true).TrimPrefix_n(":");
CString sTarget = Message.GetParam(0);
CString sModes = Message.GetParams(1);
CChan* pChan = m_pNetwork->FindChan(sTarget);
if (pChan) {
@@ -670,16 +672,16 @@ void CIRCSock::ReadLine(const CString& sData) {
}
}
} else if (sCmd.Equals("KICK")) {
CKickMessage& KickMsg = static_cast<CKickMessage&>(Message);
// :opnick!ident@host.com KICK #chan nick :msg
CString sChan = sRest.Token(0);
CString sKickedNick = sRest.Token(1);
CString sMsg = sRest.Token(2, true);
sMsg.LeftChomp();
CString sChan = KickMsg.GetParam(0);
CString sKickedNick = KickMsg.GetKickedNick();
CChan* pChan = m_pNetwork->FindChan(sChan);
if (pChan) {
IRCSOCKMODULECALL(OnKick(Nick, sKickedNick, *pChan, sMsg), NOTHING);
KickMsg.SetChan(pChan);
IRCSOCKMODULECALL(OnKickMessage(KickMsg), NOTHING);
// do not remove the nick till after the OnKick call, so modules
// can do Chan.FindNick or something to get more info.
pChan->RemNick(sKickedNick);
@@ -697,9 +699,8 @@ void CIRCSock::ReadLine(const CString& sData) {
}
} else if (sCmd.Equals("NOTICE")) {
// :nick!ident@host.com NOTICE #chan :Message
CString sTarget = sRest.Token(0);
CString sMsg = sRest.Token(1, true);
sMsg.LeftChomp();
CString sTarget = Message.GetParam(0);
CString sMsg = Message.GetParam(1);
if (sMsg.WildCmp("\001*\001")) {
sMsg.LeftChomp();
@@ -715,11 +716,11 @@ void CIRCSock::ReadLine(const CString& sData) {
return;
} else {
if (sTarget.Equals(GetNick())) {
if (OnPrivNotice(Nick, sMsg)) {
if (OnPrivNotice(Message)) {
return;
}
} else {
if (OnChanNotice(Nick, sTarget, sMsg)) {
if (OnChanNotice(Message)) {
return;
}
}
@@ -733,41 +734,38 @@ void CIRCSock::ReadLine(const CString& sData) {
return;
} else if (sCmd.Equals("TOPIC")) {
CTopicMessage& TopicMsg = static_cast<CTopicMessage&>(Message);
// :nick!ident@host.com TOPIC #chan :This is a topic
CChan* pChan = m_pNetwork->FindChan(sLine.Token(2));
CChan* pChan = m_pNetwork->FindChan(TopicMsg.GetParam(0));
if (pChan) {
CString sTopic = sLine.Token(3, true);
sTopic.LeftChomp();
IRCSOCKMODULECALL(OnTopic(Nick, *pChan, sTopic), &bReturn);
TopicMsg.SetChan(pChan);
IRCSOCKMODULECALL(OnTopicMessage(TopicMsg), &bReturn);
if (bReturn) return;
pChan->SetTopicOwner(Nick.GetNick());
pChan->SetTopicDate((unsigned long) time(nullptr));
pChan->SetTopic(sTopic);
pChan->SetTopic(TopicMsg.GetTopic());
if (pChan->IsDetached()) {
return; // Don't forward this
}
sLine = ":" + Nick.GetNickMask() + " TOPIC " + pChan->GetName() + " :" + sTopic;
}
} else if (sCmd.Equals("PRIVMSG")) {
// :nick!ident@host.com PRIVMSG #chan :Message
CString sTarget = sRest.Token(0);
CString sMsg = sRest.Token(1, true).TrimPrefix_n();
CString sTarget = Message.GetParam(0);
CString sMsg = Message.GetParam(1);
if (sMsg.WildCmp("\001*\001")) {
sMsg.LeftChomp();
sMsg.RightChomp();
if (sTarget.Equals(GetNick())) {
if (OnPrivCTCP(Nick, sMsg)) {
if (OnPrivCTCP(Message)) {
return;
}
} else {
if (OnChanCTCP(Nick, sTarget, sMsg)) {
if (OnChanCTCP(Message)) {
return;
}
}
@@ -776,11 +774,11 @@ void CIRCSock::ReadLine(const CString& sData) {
return;
} else {
if (sTarget.Equals(GetNick())) {
if (OnPrivMsg(Nick, sMsg)) {
if (OnPrivMsg(Message)) {
return;
}
} else {
if (OnChanMsg(Nick, sTarget, sMsg)) {
if (OnChanMsg(Message)) {
return;
}
}
@@ -790,7 +788,7 @@ void CIRCSock::ReadLine(const CString& sData) {
}
} else if (sCmd.Equals("WALLOPS")) {
// :blub!dummy@rox-8DBEFE92 WALLOPS :this is a test
CString sMsg = sRest.Token(0, true).TrimPrefix_n();
CString sMsg = Message.GetParam(0);
if (!m_pNetwork->IsUserOnline()) {
m_pNetwork->AddNoticeBuffer(":" + _NAMEDFMT(Nick.GetNickMask()) + " WALLOPS :{text}", sMsg);
@@ -798,10 +796,10 @@ void CIRCSock::ReadLine(const CString& sData) {
} else if (sCmd.Equals("CAP")) {
// CAPs are supported only before authorization.
if (!m_bAuthed) {
// sRest.Token(0) is most likely "*". No idea why, the
// The first parameter is most likely "*". No idea why, the
// CAP spec don't mention this, but all implementations
// I've seen add this extra asterisk
CString sSubCmd = sRest.Token(1);
CString sSubCmd = Message.GetParam(1);
// If the caplist of a reply is too long, it's split
// into multiple replies. A "*" is prepended to show
@@ -815,10 +813,10 @@ void CIRCSock::ReadLine(const CString& sData) {
// to recognize past request of NAK by 100 chars
// of this reply.
CString sArgs;
if (sRest.Token(2) == "*") {
sArgs = sRest.Token(3, true).TrimPrefix_n();
if (Message.GetParam(2) == "*") {
sArgs = Message.GetParam(3);
} else {
sArgs = sRest.Token(2, true).TrimPrefix_n();
sArgs = Message.GetParam(2);
}
std::map<CString, std::function<void(bool bVal)>> mSupportedCaps = {
@@ -879,7 +877,7 @@ void CIRCSock::ReadLine(const CString& sData) {
}
}
m_pNetwork->PutUser(sLine);
m_pNetwork->PutUser(Message);
}
void CIRCSock::SendNextCap() {
@@ -917,31 +915,34 @@ bool CIRCSock::OnCTCPReply(CNick& Nick, CString& sMessage) {
return bResult;
}
bool CIRCSock::OnPrivCTCP(CNick& Nick, CString& sMessage) {
bool CIRCSock::OnPrivCTCP(CMessage& Message) {
CPrivCTCP& PrivCTCP = static_cast<CPrivCTCP&>(Message);
bool bResult = false;
IRCSOCKMODULECALL(OnPrivCTCP(Nick, sMessage), &bResult);
IRCSOCKMODULECALL(OnPrivCTCPMessage(PrivCTCP), &bResult);
if (bResult) return true;
if (sMessage.TrimPrefix("ACTION ")) {
if (PrivCTCP.GetText().StartsWith("ACTION ")) {
bResult = false;
IRCSOCKMODULECALL(OnPrivAction(Nick, sMessage), &bResult);
CPrivAction& PrivAction = static_cast<CPrivAction&>(Message);
IRCSOCKMODULECALL(OnPrivActionMessage(PrivAction), &bResult);
if (bResult) return true;
if (!m_pNetwork->IsUserOnline() || !m_pNetwork->GetUser()->AutoClearQueryBuffer()) {
const CNick& Nick = PrivAction.GetNick();
CQuery* pQuery = m_pNetwork->AddQuery(Nick.GetNick());
if (pQuery) {
pQuery->AddBuffer(":" + _NAMEDFMT(Nick.GetNickMask()) + " PRIVMSG {target} :\001ACTION {text}\001", sMessage);
pQuery->AddBuffer(":" + _NAMEDFMT(Nick.GetNickMask()) + " PRIVMSG {target} :\001ACTION {text}\001", PrivAction.GetText(), &PrivAction.GetTime(), PrivAction.GetTags());
}
}
sMessage = "ACTION " + sMessage;
}
// This handles everything which wasn't handled yet
return OnGeneralCTCP(Nick, sMessage);
return OnGeneralCTCP(Message);
}
bool CIRCSock::OnGeneralCTCP(CNick& Nick, CString& sMessage) {
bool CIRCSock::OnGeneralCTCP(CMessage& Message) {
const CNick& Nick = Message.GetNick();
const CString& sMessage = Message.GetParam(1);
const MCString& mssCTCPReplies = m_pNetwork->GetUser()->GetCTCPReplies();
CString sQuery = sMessage.Token(0).AsUpper();
MCString::const_iterator it = mssCTCPReplies.find(sQuery);
@@ -985,83 +986,92 @@ bool CIRCSock::OnGeneralCTCP(CNick& Nick, CString& sMessage) {
return false;
}
bool CIRCSock::OnPrivNotice(CNick& Nick, CString& sMessage) {
bool CIRCSock::OnPrivNotice(CMessage& Message) {
CPrivNotice& PrivNotice = static_cast<CPrivNotice&>(Message);
bool bResult = false;
IRCSOCKMODULECALL(OnPrivNotice(Nick, sMessage), &bResult);
IRCSOCKMODULECALL(OnPrivNoticeMessage(PrivNotice), &bResult);
if (bResult) return true;
if (!m_pNetwork->IsUserOnline()) {
// If the user is detached, add to the buffer
m_pNetwork->AddNoticeBuffer(":" + _NAMEDFMT(Nick.GetNickMask()) + " NOTICE {target} :{text}", sMessage);
m_pNetwork->AddNoticeBuffer(":" + _NAMEDFMT(PrivNotice.GetNick().GetNickMask()) + " NOTICE {target} :{text}", PrivNotice.GetText());
}
return false;
}
bool CIRCSock::OnPrivMsg(CNick& Nick, CString& sMessage) {
bool CIRCSock::OnPrivMsg(CMessage& Message) {
CPrivMessage& PrivMsg = static_cast<CPrivMessage&>(Message);
bool bResult = false;
IRCSOCKMODULECALL(OnPrivMsg(Nick, sMessage), &bResult);
IRCSOCKMODULECALL(OnPrivMessage(PrivMsg), &bResult);
if (bResult) return true;
if (!m_pNetwork->IsUserOnline() || !m_pNetwork->GetUser()->AutoClearQueryBuffer()) {
const CNick& Nick = PrivMsg.GetNick();
CQuery* pQuery = m_pNetwork->AddQuery(Nick.GetNick());
if (pQuery) {
pQuery->AddBuffer(":" + _NAMEDFMT(Nick.GetNickMask()) + " PRIVMSG {target} :{text}", sMessage);
pQuery->AddBuffer(":" + _NAMEDFMT(Nick.GetNickMask()) + " PRIVMSG {target} :{text}", PrivMsg.GetText(), &PrivMsg.GetTime(), PrivMsg.GetTags());
}
}
return false;
}
bool CIRCSock::OnChanCTCP(CNick& Nick, const CString& sChan, CString& sMessage) {
CChan* pChan = m_pNetwork->FindChan(sChan);
bool CIRCSock::OnChanCTCP(CMessage& Message) {
CChanCTCP& ChanCTCP = static_cast<CChanCTCP&>(Message);
CChan* pChan = m_pNetwork->FindChan(ChanCTCP.GetParam(0));
if (pChan) {
bool bResult = false;
IRCSOCKMODULECALL(OnChanCTCP(Nick, *pChan, sMessage), &bResult);
ChanCTCP.SetChan(pChan);
IRCSOCKMODULECALL(OnChanCTCPMessage(ChanCTCP), &bResult);
if (bResult) return true;
// Record a /me
if (sMessage.TrimPrefix("ACTION ")) {
if (ChanCTCP.GetText().StartsWith("ACTION ")) {
bResult = false;
IRCSOCKMODULECALL(OnChanAction(Nick, *pChan, sMessage), &bResult);
CChanAction& ChanAction = static_cast<CChanAction&>(Message);
IRCSOCKMODULECALL(OnChanActionMessage(ChanAction), &bResult);
if (bResult) return true;
if (!pChan->AutoClearChanBuffer() || !m_pNetwork->IsUserOnline() || pChan->IsDetached()) {
pChan->AddBuffer(":" + _NAMEDFMT(Nick.GetNickMask()) + " PRIVMSG " + _NAMEDFMT(sChan) + " :\001ACTION {text}\001", sMessage);
pChan->AddBuffer(":" + _NAMEDFMT(Message.GetNick().GetNickMask()) + " PRIVMSG " + _NAMEDFMT(pChan->GetName()) + " :\001ACTION {text}\001", ChanAction.GetText(), &ChanAction.GetTime(), ChanAction.GetTags());
}
sMessage = "ACTION " + sMessage;
}
}
if (OnGeneralCTCP(Nick, sMessage))
if (OnGeneralCTCP(Message))
return true;
return (pChan && pChan->IsDetached());
}
bool CIRCSock::OnChanNotice(CNick& Nick, const CString& sChan, CString& sMessage) {
CChan* pChan = m_pNetwork->FindChan(sChan);
bool CIRCSock::OnChanNotice(CMessage& Message) {
CChanNotice& ChanNotice = static_cast<CChanNotice&>(Message);
CChan* pChan = m_pNetwork->FindChan(ChanNotice.GetParam(0));
if (pChan) {
bool bResult = false;
IRCSOCKMODULECALL(OnChanNotice(Nick, *pChan, sMessage), &bResult);
ChanNotice.SetChan(pChan);
IRCSOCKMODULECALL(OnChanNoticeMessage(ChanNotice), &bResult);
if (bResult) return true;
if (!pChan->AutoClearChanBuffer() || !m_pNetwork->IsUserOnline() || pChan->IsDetached()) {
pChan->AddBuffer(":" + _NAMEDFMT(Nick.GetNickMask()) + " NOTICE " + _NAMEDFMT(sChan) + " :{text}", sMessage);
pChan->AddBuffer(":" + _NAMEDFMT(ChanNotice.GetNick().GetNickMask()) + " NOTICE " + _NAMEDFMT(pChan->GetName()) + " :{text}", ChanNotice.GetText(), &ChanNotice.GetTime(), ChanNotice.GetTags());
}
}
return ((pChan) && (pChan->IsDetached()));
}
bool CIRCSock::OnChanMsg(CNick& Nick, const CString& sChan, CString& sMessage) {
CChan* pChan = m_pNetwork->FindChan(sChan);
bool CIRCSock::OnChanMsg(CMessage& Message) {
CChanMessage& ChanMsg = static_cast<CChanMessage&>(Message);
CChan* pChan = m_pNetwork->FindChan(ChanMsg.GetParam(0));
if (pChan) {
bool bResult = false;
IRCSOCKMODULECALL(OnChanMsg(Nick, *pChan, sMessage), &bResult);
ChanMsg.SetChan(pChan);
IRCSOCKMODULECALL(OnChanMessage(ChanMsg), &bResult);
if (bResult) return true;
if (!pChan->AutoClearChanBuffer() || !m_pNetwork->IsUserOnline() || pChan->IsDetached()) {
pChan->AddBuffer(":" + _NAMEDFMT(Nick.GetNickMask()) + " PRIVMSG " + _NAMEDFMT(sChan) + " :{text}", sMessage);
pChan->AddBuffer(":" + _NAMEDFMT(ChanMsg.GetNick().GetNickMask()) + " PRIVMSG " + _NAMEDFMT(pChan->GetName()) + " :{text}", ChanMsg.GetText(), &ChanMsg.GetTime(), ChanMsg.GetTags());
}
}
+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;
}
}
+94
View File
@@ -636,11 +636,16 @@ void CModule::OnUnknownModCommand(const CString& sLine) {
}
void CModule::OnQuit(const CNick& Nick, const CString& sMessage, const vector<CChan*>& vChans) {}
void CModule::OnQuitMessage(CQuitMessage& Message, const vector<CChan*>& vChans) { OnQuit(Message.GetNick(), Message.GetReason(), vChans); }
void CModule::OnNick(const CNick& Nick, const CString& sNewNick, const vector<CChan*>& vChans) {}
void CModule::OnNickMessage(CNickMessage& Message, const vector<CChan*>& vChans) { OnNick(Message.GetNick(), Message.GetNewNick(), vChans); }
void CModule::OnKick(const CNick& Nick, const CString& sKickedNick, CChan& Channel, const CString& sMessage) {}
void CModule::OnKickMessage(CKickMessage& Message) { OnKick(Message.GetNick(), Message.GetKickedNick(), *Message.GetChan(), Message.GetReason()); }
CModule::EModRet CModule::OnJoining(CChan& Channel) { return CONTINUE; }
void CModule::OnJoin(const CNick& Nick, CChan& Channel) {}
void CModule::OnJoinMessage(CJoinMessage& Message) { OnJoin(Message.GetNick(), *Message.GetChan()); }
void CModule::OnPart(const CNick& Nick, CChan& Channel, const CString& sMessage) {}
void CModule::OnPartMessage(CPartMessage& Message) { OnPart(Message.GetNick(), *Message.GetChan(), Message.GetReason()); }
CModule::EModRet CModule::OnInvite(const CNick& Nick, const CString& sChan) { return CONTINUE; }
CModule::EModRet CModule::OnChanBufferStarting(CChan& Chan, CClient& Client) { return CONTINUE; }
@@ -655,6 +660,25 @@ CModule::EModRet CModule::OnPrivBufferPlayLine2(CClient& Client, CString& sLine,
return OnPrivBufferPlayLine(Client, sLine);
}
CModule::EModRet CModule::OnChanBufferPlayMessage(CMessage& Message) {
CString sOriginal, sModified;
sOriginal = sModified = Message.ToString(CMessage::ExcludeTags);
EModRet ret = OnChanBufferPlayLine2(*Message.GetChan(), *Message.GetClient(), sModified, Message.GetTime());
if (ret == CONTINUE && sOriginal != sModified) {
Message.Parse(sModified);
}
return ret;
}
CModule::EModRet CModule::OnPrivBufferPlayMessage(CMessage& Message) {
CString sOriginal, sModified;
sOriginal = sModified = Message.ToString(CMessage::ExcludeTags);
EModRet ret = OnPrivBufferPlayLine2(*Message.GetClient(), sModified, Message.GetTime());
if (ret == CONTINUE && sOriginal != sModified) {
Message.Parse(sModified);
}
return ret;
}
void CModule::OnClientLogin() {}
void CModule::OnClientDisconnect() {}
CModule::EModRet CModule::OnUserRaw(CString& sLine) { return CONTINUE; }
@@ -671,14 +695,68 @@ CModule::EModRet CModule::OnUserQuit(CString& sMessage) { return CONTINUE; }
CModule::EModRet CModule::OnCTCPReply(CNick& Nick, CString& sMessage) { return CONTINUE; }
CModule::EModRet CModule::OnPrivCTCP(CNick& Nick, CString& sMessage) { return CONTINUE; }
CModule::EModRet CModule::OnPrivCTCPMessage(CPrivCTCP& Message) {
CString sText = Message.GetText();
EModRet ret = OnPrivCTCP(Message.GetNick(), sText);
Message.SetText(sText);
return ret;
}
CModule::EModRet CModule::OnChanCTCP(CNick& Nick, CChan& Channel, CString& sMessage) { return CONTINUE; }
CModule::EModRet CModule::OnChanCTCPMessage(CChanCTCP& Message) {
CString sText = Message.GetText();
EModRet ret = OnChanCTCP(Message.GetNick(), *Message.GetChan(), sText);
Message.SetText(sText);
return ret;
}
CModule::EModRet CModule::OnPrivAction(CNick& Nick, CString& sMessage) { return CONTINUE; }
CModule::EModRet CModule::OnPrivActionMessage(CPrivAction& Message) {
CString sText = Message.GetText();
EModRet ret = OnPrivAction(Message.GetNick(), sText);
Message.SetText(sText);
return ret;
}
CModule::EModRet CModule::OnChanAction(CNick& Nick, CChan& Channel, CString& sMessage) { return CONTINUE; }
CModule::EModRet CModule::OnChanActionMessage(CChanAction& Message) {
CString sText = Message.GetText();
EModRet ret = OnChanAction(Message.GetNick(), *Message.GetChan(), sText);
Message.SetText(sText);
return ret;
}
CModule::EModRet CModule::OnPrivMsg(CNick& Nick, CString& sMessage) { return CONTINUE; }
CModule::EModRet CModule::OnPrivMessage(CPrivMessage& Message) {
CString sText = Message.GetText();
EModRet ret = OnPrivMsg(Message.GetNick(), sText);
Message.SetText(sText);
return ret;
}
CModule::EModRet CModule::OnChanMsg(CNick& Nick, CChan& Channel, CString& sMessage) { return CONTINUE; }
CModule::EModRet CModule::OnChanMessage(CChanMessage& Message) {
CString sText = Message.GetText();
EModRet ret = OnChanMsg(Message.GetNick(), *Message.GetChan(), sText);
Message.SetText(sText);
return ret;
}
CModule::EModRet CModule::OnPrivNotice(CNick& Nick, CString& sMessage) { return CONTINUE; }
CModule::EModRet CModule::OnPrivNoticeMessage(CPrivNotice& Message) {
CString sText = Message.GetText();
EModRet ret = OnPrivNotice(Message.GetNick(), sText);
Message.SetText(sText);
return ret;
}
CModule::EModRet CModule::OnChanNotice(CNick& Nick, CChan& Channel, CString& sMessage) { return CONTINUE; }
CModule::EModRet CModule::OnChanNoticeMessage(CChanNotice& Message) {
CString sText = Message.GetText();
EModRet ret = OnChanNotice(Message.GetNick(), *Message.GetChan(), sText);
Message.SetText(sText);
return ret;
}
CModule::EModRet CModule::OnTopic(CNick& Nick, CChan& Channel, CString& sTopic) { return CONTINUE; }
CModule::EModRet CModule::OnTopicMessage(CTopicMessage& Message) {
CString sTopic = Message.GetTopic();
EModRet ret = OnTopic(Message.GetNick(), *Message.GetChan(), sTopic);
Message.SetTopic(sTopic);
return ret;
}
CModule::EModRet CModule::OnTimerAutoJoin(CChan& Channel) { return CONTINUE; }
CModule::EModRet CModule::OnAddNetwork(CIRCNetwork& Network, CString& sErrorRet) { return CONTINUE; }
CModule::EModRet CModule::OnDeleteNetwork(CIRCNetwork& Network) { return CONTINUE; }
@@ -829,11 +907,16 @@ bool CModules::OnUserTopicRequest(CString& sChannel) { MODHALTCHK(OnUserTopicReq
bool CModules::OnUserQuit(CString& sMessage) { MODHALTCHK(OnUserQuit(sMessage)); }
bool CModules::OnQuit(const CNick& Nick, const CString& sMessage, const vector<CChan*>& vChans) { MODUNLOADCHK(OnQuit(Nick, sMessage, vChans)); return false; }
bool CModules::OnQuitMessage(CQuitMessage& Message, const vector<CChan*>& vChans) { MODUNLOADCHK(OnQuitMessage(Message, vChans)); return false; }
bool CModules::OnNick(const CNick& Nick, const CString& sNewNick, const vector<CChan*>& vChans) { MODUNLOADCHK(OnNick(Nick, sNewNick, vChans)); return false; }
bool CModules::OnNickMessage(CNickMessage& Message, const vector<CChan*>& vChans) { MODUNLOADCHK(OnNickMessage(Message, vChans)); return false; }
bool CModules::OnKick(const CNick& Nick, const CString& sKickedNick, CChan& Channel, const CString& sMessage) { MODUNLOADCHK(OnKick(Nick, sKickedNick, Channel, sMessage)); return false; }
bool CModules::OnKickMessage(CKickMessage& Message) { MODUNLOADCHK(OnKickMessage(Message)); return false; }
bool CModules::OnJoining(CChan& Channel) { MODHALTCHK(OnJoining(Channel)); }
bool CModules::OnJoin(const CNick& Nick, CChan& Channel) { MODUNLOADCHK(OnJoin(Nick, Channel)); return false; }
bool CModules::OnJoinMessage(CJoinMessage& Message) { MODUNLOADCHK(OnJoinMessage(Message)); return false; }
bool CModules::OnPart(const CNick& Nick, CChan& Channel, const CString& sMessage) { MODUNLOADCHK(OnPart(Nick, Channel, sMessage)); return false; }
bool CModules::OnPartMessage(CPartMessage& Message) { MODUNLOADCHK(OnPartMessage(Message)); return false; }
bool CModules::OnInvite(const CNick& Nick, const CString& sChan) { MODHALTCHK(OnInvite(Nick, sChan)); }
bool CModules::OnChanBufferStarting(CChan& Chan, CClient& Client) { MODHALTCHK(OnChanBufferStarting(Chan, Client)); }
bool CModules::OnChanBufferEnding(CChan& Chan, CClient& Client) { MODHALTCHK(OnChanBufferEnding(Chan, Client)); }
@@ -841,16 +924,27 @@ bool CModules::OnChanBufferPlayLine2(CChan& Chan, CClient& Client, CString& sLin
bool CModules::OnChanBufferPlayLine(CChan& Chan, CClient& Client, CString& sLine) { MODHALTCHK(OnChanBufferPlayLine(Chan, Client, sLine)); }
bool CModules::OnPrivBufferPlayLine2(CClient& Client, CString& sLine, const timeval& tv) { MODHALTCHK(OnPrivBufferPlayLine2(Client, sLine, tv)); }
bool CModules::OnPrivBufferPlayLine(CClient& Client, CString& sLine) { MODHALTCHK(OnPrivBufferPlayLine(Client, sLine)); }
bool CModules::OnChanBufferPlayMessage(CMessage& Message) { MODHALTCHK(OnChanBufferPlayMessage(Message)); }
bool CModules::OnPrivBufferPlayMessage(CMessage& Message) { MODHALTCHK(OnPrivBufferPlayMessage(Message)); }
bool CModules::OnCTCPReply(CNick& Nick, CString& sMessage) { MODHALTCHK(OnCTCPReply(Nick, sMessage)); }
bool CModules::OnPrivCTCP(CNick& Nick, CString& sMessage) { MODHALTCHK(OnPrivCTCP(Nick, sMessage)); }
bool CModules::OnPrivCTCPMessage(CPrivCTCP& Message) { MODHALTCHK(OnPrivCTCPMessage(Message)); }
bool CModules::OnChanCTCP(CNick& Nick, CChan& Channel, CString& sMessage) { MODHALTCHK(OnChanCTCP(Nick, Channel, sMessage)); }
bool CModules::OnChanCTCPMessage(CChanCTCP& Message) { MODHALTCHK(OnChanCTCPMessage(Message)); }
bool CModules::OnPrivAction(CNick& Nick, CString& sMessage) { MODHALTCHK(OnPrivAction(Nick, sMessage)); }
bool CModules::OnPrivActionMessage(CPrivAction& Message) { MODHALTCHK(OnPrivActionMessage(Message)); }
bool CModules::OnChanAction(CNick& Nick, CChan& Channel, CString& sMessage) { MODHALTCHK(OnChanAction(Nick, Channel, sMessage)); }
bool CModules::OnChanActionMessage(CChanAction& Message) { MODHALTCHK(OnChanActionMessage(Message)); }
bool CModules::OnPrivMsg(CNick& Nick, CString& sMessage) { MODHALTCHK(OnPrivMsg(Nick, sMessage)); }
bool CModules::OnPrivMessage(CPrivMessage& Message) { MODHALTCHK(OnPrivMessage(Message)); }
bool CModules::OnChanMsg(CNick& Nick, CChan& Channel, CString& sMessage) { MODHALTCHK(OnChanMsg(Nick, Channel, sMessage)); }
bool CModules::OnChanMessage(CChanMessage& Message) { MODHALTCHK(OnChanMessage(Message)); }
bool CModules::OnPrivNotice(CNick& Nick, CString& sMessage) { MODHALTCHK(OnPrivNotice(Nick, sMessage)); }
bool CModules::OnPrivNoticeMessage(CPrivNotice& Message) { MODHALTCHK(OnPrivNoticeMessage(Message)); }
bool CModules::OnChanNotice(CNick& Nick, CChan& Channel, CString& sMessage) { MODHALTCHK(OnChanNotice(Nick, Channel, sMessage)); }
bool CModules::OnChanNoticeMessage(CChanNotice& Message) { MODHALTCHK(OnChanNoticeMessage(Message)); }
bool CModules::OnTopic(CNick& Nick, CChan& Channel, CString& sTopic) { MODHALTCHK(OnTopic(Nick, Channel, sTopic)); }
bool CModules::OnTopicMessage(CTopicMessage& Message) { MODHALTCHK(OnTopicMessage(Message)); }
bool CModules::OnTimerAutoJoin(CChan& Channel) { MODHALTCHK(OnTimerAutoJoin(Channel)); }
bool CModules::OnAddNetwork(CIRCNetwork& Network, CString& sErrorRet) { MODHALTCHK(OnAddNetwork(Network, sErrorRet)); }
bool CModules::OnDeleteNetwork(CIRCNetwork& Network) { MODHALTCHK(OnDeleteNetwork(Network)); }
+10 -10
View File
@@ -17,6 +17,7 @@
#include <znc/Query.h>
#include <znc/User.h>
#include <znc/IRCNetwork.h>
#include <znc/Message.h>
using std::vector;
@@ -55,24 +56,23 @@ void CQuery::SendBuffer(CClient* pClient, const CBuffer& Buffer) {
size_t uSize = Buffer.Size();
for (size_t uIdx = 0; uIdx < uSize; uIdx++) {
const CBufLine& BufLine = Buffer.GetBufLine(uIdx);
CMessage Message(BufLine.GetLine(*pUseClient, MCString::EmptyMap));
if (!pUseClient->HasEchoMessage() && !pUseClient->HasSelfMessage()) {
CNick Sender(BufLine.GetFormat().Token(0));
if (Sender.NickEquals(pUseClient->GetNick())) {
if (Message.GetNick().NickEquals(pUseClient->GetNick())) {
continue;
}
}
CString sLine = BufLine.GetLine(*pUseClient, msParams);
Message.SetNetwork(m_pNetwork);
Message.SetClient(pUseClient);
Message.SetTime(BufLine.GetTime());
Message.SetTags(BufLine.GetTags());
if (bBatch) {
MCString msBatchTags = CUtils::GetMessageTags(sLine);
msBatchTags["batch"] = sBatchName;
CUtils::SetMessageTags(sLine, msBatchTags);
Message.SetTag("batch", sBatchName);
}
bool bContinue = false;
NETWORKMODULECALL(OnPrivBufferPlayLine2(*pUseClient, sLine, BufLine.GetTime()), m_pNetwork->GetUser(), m_pNetwork, nullptr, &bContinue);
NETWORKMODULECALL(OnPrivBufferPlayMessage(Message), m_pNetwork->GetUser(), m_pNetwork, nullptr, &bContinue);
if (bContinue) continue;
m_pNetwork->PutUser(sLine, pUseClient);
m_pNetwork->PutUser(Message, pUseClient);
}
if (bBatch) {
+13
View File
@@ -458,6 +458,19 @@ CString CUtils::FormatServerTime(const timeval& tv) {
return CString(sTime) + "." + s_msec + "Z";
}
timeval CUtils::ParseServerTime(const CString& sTime) {
struct tm stm;
memset(&stm, 0, sizeof(stm));
const char* cp = strptime(sTime.c_str(), "%Y-%m-%dT%H:%M:%S", &stm);
struct timeval tv;
tv.tv_sec = mktime(&stm);
CString s_usec(cp);
if (s_usec.TrimPrefix(".") && s_usec.TrimSuffix("Z")) {
tv.tv_usec = s_usec.ToULong() * 1000;
}
return tv;
}
namespace {
void FillTimezones(const CString& sPath, SCString& result, const CString& sPrefix) {
CDir Dir;
+282
View File
@@ -0,0 +1,282 @@
/*
* 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));
}
TEST(MessageTest, ChanAction) {
CMessage msg(":sender PRIVMSG #chan :\001ACTION text\001");
CChanAction& chan = static_cast<CChanAction&>(msg);
EXPECT_EQ("sender", chan.GetNick().GetNick());
EXPECT_EQ("PRIVMSG", chan.GetCommand());
EXPECT_EQ("text", chan.GetText());
chan.SetText("foo bar");
EXPECT_EQ("foo bar", chan.GetText());
EXPECT_EQ(":sender PRIVMSG #chan :\001ACTION foo bar\001", chan.ToString());
}
TEST(MessageTest, ChanCTCP) {
CMessage msg(":sender PRIVMSG #chan :\001text\001");
CChanCTCP& chan = static_cast<CChanCTCP&>(msg);
EXPECT_EQ("sender", chan.GetNick().GetNick());
EXPECT_EQ("PRIVMSG", chan.GetCommand());
EXPECT_EQ("text", chan.GetText());
chan.SetText("foo bar");
EXPECT_EQ("foo bar", chan.GetText());
EXPECT_EQ(":sender PRIVMSG #chan :\001foo bar\001", chan.ToString());
}
TEST(MessageTest, ChanMsg) {
CMessage msg(":sender PRIVMSG #chan :text");
CChanMessage& priv = static_cast<CChanMessage&>(msg);
EXPECT_EQ("sender", priv.GetNick().GetNick());
EXPECT_EQ("PRIVMSG", priv.GetCommand());
EXPECT_EQ("text", priv.GetText());
priv.SetText("foo bar");
EXPECT_EQ("foo bar", priv.GetText());
EXPECT_EQ(":sender PRIVMSG #chan :foo bar", priv.ToString());
}
TEST(MessageTest, Kick) {
CMessage msg(":nick KICK #chan person :reason");
CKickMessage& kick = static_cast<CKickMessage&>(msg);
EXPECT_EQ("nick", kick.GetNick().GetNick());
EXPECT_EQ("KICK", kick.GetCommand());
EXPECT_EQ("person", kick.GetKickedNick());
EXPECT_EQ("reason", kick.GetReason());
kick.SetKickedNick("noone");
EXPECT_EQ("noone", kick.GetKickedNick());
kick.SetReason("test");
EXPECT_EQ("test", kick.GetReason());
EXPECT_EQ(":nick KICK #chan noone test", kick.ToString());
}
TEST(MessageTest, Join) {
CMessage msg(":nick JOIN #chan");
EXPECT_EQ("nick", msg.GetNick().GetNick());
EXPECT_EQ("JOIN", msg.GetCommand());
EXPECT_EQ("#chan", msg.GetParam(0));
}
TEST(MessageTest, Nick) {
CMessage msg(":nick NICK person");
CNickMessage& nick = static_cast<CNickMessage&>(msg);
EXPECT_EQ("nick", nick.GetNick().GetNick());
EXPECT_EQ("NICK", nick.GetCommand());
EXPECT_EQ("nick", nick.GetOldNick());
EXPECT_EQ("person", nick.GetNewNick());
nick.SetNewNick("test");
EXPECT_EQ("test", nick.GetNewNick());
EXPECT_EQ(":nick NICK test", nick.ToString());
}
TEST(MessageTest, Part) {
CMessage msg(":nick PART #chan :reason");
CPartMessage& part = static_cast<CPartMessage&>(msg);
EXPECT_EQ("nick", part.GetNick().GetNick());
EXPECT_EQ("PART", part.GetCommand());
EXPECT_EQ("reason", part.GetReason());
part.SetReason("test");
EXPECT_EQ("test", part.GetReason());
EXPECT_EQ(":nick PART #chan test", part.ToString());
}
TEST(MessageTest, PrivAction) {
CMessage msg(":sender PRIVMSG receiver :\001ACTION text\001");
CPrivAction& priv = static_cast<CPrivAction&>(msg);
EXPECT_EQ("sender", priv.GetNick().GetNick());
EXPECT_EQ("PRIVMSG", priv.GetCommand());
EXPECT_EQ("text", priv.GetText());
priv.SetText("foo bar");
EXPECT_EQ("foo bar", priv.GetText());
EXPECT_EQ(":sender PRIVMSG receiver :\001ACTION foo bar\001", priv.ToString());
}
TEST(MessageTest, PrivCTCP) {
CMessage msg(":sender PRIVMSG receiver :\001text\001");
CPrivCTCP& priv = static_cast<CPrivCTCP&>(msg);
EXPECT_EQ("sender", priv.GetNick().GetNick());
EXPECT_EQ("PRIVMSG", priv.GetCommand());
EXPECT_EQ("text", priv.GetText());
priv.SetText("foo bar");
EXPECT_EQ("foo bar", priv.GetText());
EXPECT_EQ(":sender PRIVMSG receiver :\001foo bar\001", priv.ToString());
}
TEST(MessageTest, PrivMsg) {
CMessage msg(":sender PRIVMSG receiver :text");
CPrivMessage& priv = static_cast<CPrivMessage&>(msg);
EXPECT_EQ("sender", priv.GetNick().GetNick());
EXPECT_EQ("PRIVMSG", priv.GetCommand());
EXPECT_EQ("text", priv.GetText());
priv.SetText("foo bar");
EXPECT_EQ("foo bar", priv.GetText());
EXPECT_EQ(":sender PRIVMSG receiver :foo bar", priv.ToString());
}
TEST(MessageTest, Quit) {
CMessage msg(":nick QUIT :reason");
CQuitMessage& quit = static_cast<CQuitMessage&>(msg);
EXPECT_EQ("nick", quit.GetNick().GetNick());
EXPECT_EQ("QUIT", quit.GetCommand());
EXPECT_EQ("reason", quit.GetReason());
quit.SetReason("test");
EXPECT_EQ("test", quit.GetReason());
EXPECT_EQ(":nick QUIT test", quit.ToString());
}
TEST(MessageTest, Topic) {
CMessage msg(":nick TOPIC #chan :topic");
CTopicMessage& topic = static_cast<CTopicMessage&>(msg);
EXPECT_EQ("nick", topic.GetNick().GetNick());
EXPECT_EQ("TOPIC", topic.GetCommand());
EXPECT_EQ("topic", topic.GetTopic());
topic.SetTopic("test");
EXPECT_EQ("test", topic.GetTopic());
EXPECT_EQ(":nick TOPIC #chan test", topic.ToString());
}
// 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());
}
+29
View File
@@ -75,3 +75,32 @@ TEST(IRC32, SetMessageTags) {
EXPECT_EQ(R"(@a=\:\s\\\r\n :rest)", sLine);
}
TEST(UtilsTest, ServerTime) {
char* oldTZ = getenv("TZ");
if (oldTZ) oldTZ = strdup(oldTZ);
setenv("TZ", "UTC", 1);
tzset();
timeval tv1 = CUtils::ParseServerTime("2011-10-19T16:40:51.620Z");
CString str1 = CUtils::FormatServerTime(tv1);
EXPECT_EQ("2011-10-19T16:40:51.620Z", str1);
timeval now;
if (!gettimeofday(&now, nullptr)) {
now.tv_sec = time(nullptr);
now.tv_usec = 0;
}
CString str2 = CUtils::FormatServerTime(now);
timeval tv2 = CUtils::ParseServerTime(str2);
EXPECT_EQ(now.tv_sec, tv2.tv_sec);
EXPECT_EQ(now.tv_usec, tv2.tv_usec);
if (oldTZ) {
setenv("TZ", oldTZ, 1);
free(oldTZ);
} else {
unsetenv("TZ");
}
tzset();
}