Coverage Report

Created: 2025-11-09 06:46

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/znc/include/znc/Message.h
Line
Count
Source
1
/*
2
 * Copyright (C) 2004-2025 ZNC, see the NOTICE file for details.
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *     http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16
17
#ifndef ZNC_MESSAGE_H
18
#define ZNC_MESSAGE_H
19
20
#ifdef SWIG
21
#define ZNC_MSG_DEPRECATED(msg)
22
#else
23
#define ZNC_MSG_DEPRECATED(msg) __attribute__((deprecated(msg)))
24
#endif
25
26
#include <znc/zncconfig.h>
27
#include <znc/ZNCString.h>
28
#include <znc/Nick.h>
29
#include <sys/time.h>
30
31
class CChan;
32
class CClient;
33
class CIRCNetwork;
34
35
/**
36
 * Here is a small explanation of how messages on IRC work, and how you can use
37
 * this class to get useful information from the parsed message. The output
38
 * varies greatly and this advice may not apply to every message type, but this
39
 * will hopefully help you understand how it works more accurately.
40
 *
41
 * @t=some-tag :server.network.net 366 something #channel :End of /NAMES list.
42
 * tags         nick               cmd 0         1         2
43
 *
44
 * - `tags` is the IRCv3 tags associated with the message, obtained with
45
 *   GetTag("t").  the @time tag can also be obtained with GetTime(), some
46
 *   messages have other tags with them. Tags may not always be present. Refer
47
 *   to IRCv3 for documentation on tags.
48
 * - `nick` is the sender, which can be obtained with GetNick()
49
 * - `cmd` is command, which is obtained via GetCommand()
50
 * - `0`, `1`, ... are parameters, available via GetParam(n), which removes the
51
 *   leading colon (:). If you don't want to remove the colon, use
52
 *   GetParamsColon().
53
 *
54
 * For certain events, like a PRIVMSG, convienience commands like GetChan() and
55
 * GetNick() are available, this is not true for all CMessage extensions.
56
 */
57
class CMessage {
58
  public:
59
    explicit CMessage(const CString& sMessage = "");
60
    CMessage(const CNick& Nick, const CString& sCommand,
61
             const VCString& vsParams = VCString(),
62
             const MCString& mssTags = MCString::EmptyMap);
63
64
    enum class Type {
65
        Unknown,
66
        Account,
67
        Action,
68
        Authenticate,
69
        Away,
70
        Capability,
71
        ChgHost,
72
        CTCP,
73
        Error,
74
        Invite,
75
        Join,
76
        Kick,
77
        Mode,
78
        Nick,
79
        Notice,
80
        Numeric,
81
        Part,
82
        Ping,
83
        Pong,
84
        Quit,
85
        TagMsg,
86
        Text,
87
        Topic,
88
        Wallops,
89
    };
90
    /**
91
     * Returns which type of message this is.
92
     *
93
     * This is mostly about which subclass of CMessage is intended to be used,
94
     * but does not map 1:1 to the subclasses. The number is NOT guaranteed to
95
     * be the same across ZNC versions. For practical purposes, GetCommand() may
96
     * be more useful.
97
     */
98
0
    Type GetType() const { return m_eType; }
99
100
    bool Equals(const CMessage& Other) const;
101
    void Clone(const CMessage& Other);
102
103
    // ZNC <-> IRC
104
0
    CIRCNetwork* GetNetwork() const { return m_pNetwork; }
105
0
    void SetNetwork(CIRCNetwork* pNetwork) { m_pNetwork = pNetwork; }
106
107
    // ZNC <-> CLI
108
0
    CClient* GetClient() const { return m_pClient; }
109
0
    void SetClient(CClient* pClient) { m_pClient = pClient; }
110
111
0
    CChan* GetChan() const { return m_pChan; }
112
0
    void SetChan(CChan* pChan) { m_pChan = pChan; }
113
114
0
    CNick& GetNick() { return m_Nick; }
115
0
    const CNick& GetNick() const { return m_Nick; }
116
0
    void SetNick(const CNick& Nick) { m_Nick = Nick; }
117
118
0
    const CString& GetCommand() const { return m_sCommand; }
119
    void SetCommand(const CString& sCommand);
120
121
2.14k
    const VCString& GetParams() const { return m_vsParams; }
122
123
    /**
124
     * Get a subset of the message parameters
125
     *
126
     * This allows accessing a vector of a specific range of parameters,
127
     * allowing easy inline use, such as `pChan->SetModes(Message.GetParam(2), Message.GetParamsSplit(3));`
128
     *
129
     * @param uIdx The index of the first parameter to retrieve
130
     * @param uLen How many parameters to retrieve
131
     * @return A VCString containing the retrieved parameters
132
     */
133
    VCString GetParamsSplit(unsigned int uIdx, unsigned int uLen = -1) const;
134
    void SetParams(const VCString& vsParams);
135
    void SetParams(VCString&& vsParams);
136
137
    /// @deprecated use GetParamsColon() instead.
138
    CString GetParams(unsigned int uIdx, unsigned int uLen = -1) const
139
0
        ZNC_MSG_DEPRECATED("Use GetParamsColon() instead") {
140
0
        return GetParamsColon(uIdx, uLen);
141
0
    }
142
    CString GetParamsColon(unsigned int uIdx, unsigned int uLen = -1) const;
143
144
    CString GetParam(unsigned int uIdx) const;
145
    void SetParam(unsigned int uIdx, const CString& sParam);
146
147
0
    const timeval& GetTime() const { return m_time; }
148
0
    void SetTime(const timeval& ts) { m_time = ts; }
149
150
0
    const MCString& GetTags() const { return m_mssTags; }
151
0
    MCString& GetTags() { return m_mssTags; }
152
0
    void SetTags(const MCString& mssTags) { m_mssTags = mssTags; }
153
154
    CString GetTag(const CString& sKey) const;
155
    void SetTag(const CString& sKey, const CString& sValue);
156
157
    enum FormatFlags {
158
        IncludeAll = 0x0,
159
        ExcludePrefix = 0x1,
160
        ExcludeTags = 0x2
161
    };
162
163
    CString ToString(unsigned int uFlags = IncludeAll) const;
164
    void Parse(const CString& sMessage);
165
166
// Implicit and explicit conversion to a subclass reference.
167
#ifndef SWIG
168
    template <typename M>
169
0
    M& As() & {
170
0
        static_assert(std::is_base_of<CMessage, M>{},
171
0
                      "Must be subclass of CMessage");
172
0
        static_assert(sizeof(M) == sizeof(CMessage),
173
0
                      "No data members allowed in CMessage subclasses.");
174
0
        return static_cast<M&>(*this);
175
0
    }
Unexecuted instantiation: CActionMessage& CMessage::As<CActionMessage>() &
Unexecuted instantiation: CChgHostMessage& CMessage::As<CChgHostMessage>() &
Unexecuted instantiation: CCTCPMessage& CMessage::As<CCTCPMessage>() &
Unexecuted instantiation: CInviteMessage& CMessage::As<CInviteMessage>() &
Unexecuted instantiation: CJoinMessage& CMessage::As<CJoinMessage>() &
Unexecuted instantiation: CKickMessage& CMessage::As<CKickMessage>() &
Unexecuted instantiation: CModeMessage& CMessage::As<CModeMessage>() &
Unexecuted instantiation: CNickMessage& CMessage::As<CNickMessage>() &
Unexecuted instantiation: CNoticeMessage& CMessage::As<CNoticeMessage>() &
Unexecuted instantiation: CNumericMessage& CMessage::As<CNumericMessage>() &
Unexecuted instantiation: CPartMessage& CMessage::As<CPartMessage>() &
Unexecuted instantiation: CQuitMessage& CMessage::As<CQuitMessage>() &
Unexecuted instantiation: CTargetMessage& CMessage::As<CTargetMessage>() &
Unexecuted instantiation: CTextMessage& CMessage::As<CTextMessage>() &
Unexecuted instantiation: CTopicMessage& CMessage::As<CTopicMessage>() &
Unexecuted instantiation: CAuthenticateMessage const& CMessage::As<CAuthenticateMessage const>() &
176
177
    template <typename M>
178
0
    const M& As() const& {
179
0
        static_assert(std::is_base_of<CMessage, M>{},
180
0
                      "Must be subclass of CMessage");
181
0
        static_assert(sizeof(M) == sizeof(CMessage),
182
0
                      "No data members allowed in CMessage subclasses.");
183
0
        return static_cast<const M&>(*this);
184
0
    }
185
186
    template <typename M, typename = typename std::enable_if<
187
                              std::is_base_of<CMessage, M>{}>::type>
188
0
    operator M&() & {
189
0
        return As<M>();
190
0
    }
Unexecuted instantiation: CMessage::operator CActionMessage&<CActionMessage, void>() &
Unexecuted instantiation: CMessage::operator CChgHostMessage&<CChgHostMessage, void>() &
Unexecuted instantiation: CMessage::operator CCTCPMessage&<CCTCPMessage, void>() &
Unexecuted instantiation: CMessage::operator CInviteMessage&<CInviteMessage, void>() &
Unexecuted instantiation: CMessage::operator CJoinMessage&<CJoinMessage, void>() &
Unexecuted instantiation: CMessage::operator CKickMessage&<CKickMessage, void>() &
Unexecuted instantiation: CMessage::operator CModeMessage&<CModeMessage, void>() &
Unexecuted instantiation: CMessage::operator CNickMessage&<CNickMessage, void>() &
Unexecuted instantiation: CMessage::operator CNoticeMessage&<CNoticeMessage, void>() &
Unexecuted instantiation: CMessage::operator CNumericMessage&<CNumericMessage, void>() &
Unexecuted instantiation: CMessage::operator CPartMessage&<CPartMessage, void>() &
Unexecuted instantiation: CMessage::operator CQuitMessage&<CQuitMessage, void>() &
Unexecuted instantiation: CMessage::operator CTargetMessage&<CTargetMessage, void>() &
Unexecuted instantiation: CMessage::operator CTextMessage&<CTextMessage, void>() &
Unexecuted instantiation: CMessage::operator CTopicMessage&<CTopicMessage, void>() &
Unexecuted instantiation: CMessage::operator CAuthenticateMessage const&<CAuthenticateMessage const, void>() &
191
    template <typename M, typename = typename std::enable_if<
192
                              std::is_base_of<CMessage, M>{}>::type>
193
    operator const M&() const& {
194
        return As<M>();
195
    }
196
// REGISTER_ZNC_MESSAGE allows SWIG to instantiate correct .As<> calls.
197
#define REGISTER_ZNC_MESSAGE(M)
198
#else
199
    // SWIG (as of 3.0.7) doesn't parse ref-qualifiers, and doesn't
200
    // differentiate constness.
201
    template <typename M>
202
    M& As();
203
#endif
204
205
  private:
206
    void InitTime();
207
    void InitType();
208
209
    CNick m_Nick;
210
    CString m_sCommand;
211
    VCString m_vsParams;
212
    MCString m_mssTags;
213
    timeval m_time;
214
    CIRCNetwork* m_pNetwork = nullptr;
215
    CClient* m_pClient = nullptr;
216
    CChan* m_pChan = nullptr;
217
    Type m_eType = Type::Unknown;
218
    bool m_bColon = false;
219
};
220
221
// For gtest
222
#ifdef GTEST_FAIL
223
template <typename M, typename = typename std::enable_if<
224
                          std::is_base_of<CMessage, M>{}>::type>
225
inline ::std::ostream& operator<<(::std::ostream& os, const M& msg) {
226
    return os << msg.ToString().Escape_n(CString::EDEBUG);
227
}
228
#endif
229
230
// The various CMessage subclasses are "mutable views" to the data held by
231
// CMessage.
232
// They provide convenient access to message type speficic attributes, but are
233
// not
234
// allowed to hold extra data of their own.
235
class CTargetMessage : public CMessage {
236
  public:
237
0
    CString GetTarget() const { return GetParam(0); }
238
0
    void SetTarget(const CString& sTarget) { SetParam(0, sTarget); }
239
};
240
REGISTER_ZNC_MESSAGE(CTargetMessage);
241
242
class CActionMessage : public CTargetMessage {
243
  public:
244
0
    CString GetText() const {
245
0
        return GetParam(1).TrimPrefix_n("\001ACTION ").TrimSuffix_n("\001");
246
0
    }
247
0
    void SetText(const CString& sText) {
248
0
        SetParam(1, "\001ACTION " + sText + "\001");
249
0
    }
250
};
251
REGISTER_ZNC_MESSAGE(CActionMessage);
252
253
class CAuthenticateMessage : public CMessage {
254
  public:
255
0
    CString GetText() const { return GetParam(0); }
256
0
    void SetText(const CString& sText) { SetParam(0, sText); }
257
};
258
REGISTER_ZNC_MESSAGE(CAuthenticateMessage);
259
260
class CCTCPMessage : public CTargetMessage {
261
  public:
262
0
    bool IsReply() const { return GetCommand().Equals("NOTICE"); }
263
0
    CString GetText() const {
264
0
        return GetParam(1).TrimPrefix_n("\001").TrimSuffix_n("\001");
265
0
    }
266
0
    void SetText(const CString& sText) { SetParam(1, "\001" + sText + "\001"); }
267
};
268
REGISTER_ZNC_MESSAGE(CCTCPMessage);
269
270
class CJoinMessage : public CTargetMessage {
271
  public:
272
0
    CString GetKey() const { return GetParam(1); }
273
0
    void SetKey(const CString& sKey) { SetParam(1, sKey); }
274
};
275
REGISTER_ZNC_MESSAGE(CJoinMessage);
276
277
class CModeMessage : public CTargetMessage {
278
  public:
279
    /// @deprecated Use GetModeList() and GetModeParams()
280
0
    CString GetModes() const { return GetParamsColon(1).TrimPrefix_n(":"); }
281
282
0
    CString GetModeList() const { return GetParam(1); };
283
284
0
    VCString GetModeParams() const { return GetParamsSplit(2); };
285
286
0
    bool HasModes() const { return !GetModeList().empty(); };
287
};
288
REGISTER_ZNC_MESSAGE(CModeMessage);
289
290
class CNickMessage : public CMessage {
291
  public:
292
0
    CString GetOldNick() const { return GetNick().GetNick(); }
293
0
    CString GetNewNick() const { return GetParam(0); }
294
0
    void SetNewNick(const CString& sNick) { SetParam(0, sNick); }
295
};
296
REGISTER_ZNC_MESSAGE(CNickMessage);
297
298
class CNoticeMessage : public CTargetMessage {
299
  public:
300
0
    CString GetText() const { return GetParam(1); }
301
0
    void SetText(const CString& sText) { SetParam(1, sText); }
302
};
303
REGISTER_ZNC_MESSAGE(CNoticeMessage);
304
305
class CNumericMessage : public CMessage {
306
  public:
307
0
    unsigned int GetCode() const { return GetCommand().ToUInt(); }
308
};
309
REGISTER_ZNC_MESSAGE(CNumericMessage);
310
311
class CKickMessage : public CTargetMessage {
312
  public:
313
0
    CString GetKickedNick() const { return GetParam(1); }
314
0
    void SetKickedNick(const CString& sNick) { SetParam(1, sNick); }
315
0
    CString GetReason() const { return GetParam(2); }
316
0
    void SetReason(const CString& sReason) { SetParam(2, sReason); }
317
0
    CString GetText() const { return GetReason(); }
318
0
    void SetText(const CString& sText) { SetReason(sText); }
319
};
320
REGISTER_ZNC_MESSAGE(CKickMessage);
321
322
class CInviteMessage : public CMessage {
323
  public:
324
0
    CString GetInvitedNick() const { return GetParam(0); }
325
0
    void SetInvitedNick(const CString& sNick) { SetParam(0, sNick); }
326
0
    CString GetChannel() const { return GetParam(1); }
327
0
    void SetChannel(const CString& sChannel) { SetParam(1, sChannel); }
328
};
329
REGISTER_ZNC_MESSAGE(CInviteMessage);
330
331
class CPartMessage : public CTargetMessage {
332
  public:
333
0
    CString GetReason() const { return GetParam(1); }
334
0
    void SetReason(const CString& sReason) { SetParam(1, sReason); }
335
0
    CString GetText() const { return GetReason(); }
336
0
    void SetText(const CString& sText) { SetReason(sText); }
337
};
338
REGISTER_ZNC_MESSAGE(CPartMessage);
339
340
class CQuitMessage : public CMessage {
341
  public:
342
0
    CString GetReason() const { return GetParam(0); }
343
0
    void SetReason(const CString& sReason) { SetParam(0, sReason); }
344
0
    CString GetText() const { return GetReason(); }
345
0
    void SetText(const CString& sText) { SetReason(sText); }
346
};
347
REGISTER_ZNC_MESSAGE(CQuitMessage);
348
349
class CTextMessage : public CTargetMessage {
350
  public:
351
0
    CString GetText() const { return GetParam(1); }
352
0
    void SetText(const CString& sText) { SetParam(1, sText); }
353
};
354
REGISTER_ZNC_MESSAGE(CTextMessage);
355
356
class CTopicMessage : public CTargetMessage {
357
  public:
358
0
    CString GetTopic() const { return GetParam(1); }
359
0
    void SetTopic(const CString& sTopic) { SetParam(1, sTopic); }
360
0
    CString GetText() const { return GetTopic(); }
361
0
    void SetText(const CString& sText) { SetTopic(sText); }
362
};
363
REGISTER_ZNC_MESSAGE(CTopicMessage);
364
365
class CChgHostMessage : public CMessage {
366
  public:
367
0
    CString GetNewIdent() const { return GetParam(0); }
368
0
    void SetNewIdent(const CString& sIdent) { SetParam(0, sIdent); }
369
0
    CString GetNewHost() const { return GetParam(1); }
370
0
    void SetNewHost(const CString& sHost) { SetParam(1, sHost); }
371
};
372
REGISTER_ZNC_MESSAGE(CChgHostMessage);
373
374
#endif  // !ZNC_MESSAGE_H