/* Copyright (C) 2008-2020 The Communi Project You may use this file under the terms of BSD license as follows: Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "ircmessage.h" #include "ircmessage_p.h" #include "ircconnection.h" #include "ircconnection_p.h" #include "ircmessagecomposer_p.h" #include "ircnetwork_p.h" #include "irccommand.h" #include "irccore_p.h" #include "irc.h" #include #include #include IRC_BEGIN_NAMESPACE /*! \file ircmessage.h \brief \#include <IrcMessage> */ /*! \class IrcMessage ircmessage.h \ingroup core \ingroup message \brief The base class of all messages. IRC messages are received from an IRC server. IrcConnection translates received messages into IrcMessage instances and emits the IrcConnection::messageReceived() signal upon message received. Subclasses of IrcMessage contain specialized accessors for parameters that are specific to the particular type of message. See IrcMessage::Type for the list of supported message types. \sa IrcConnection::messageReceived(), IrcMessage::Type */ /*! \enum IrcMessage::Type This enum describes the supported message types. */ /*! \since 3.3 \var IrcMessage::Account \brief An account notify message (IrcAccountMessage). \sa \ref ircv3 */ /*! \since 3.3 \var IrcMessage::Away \brief An away message (IrcAwayMessage). */ /*! \since 3.5 \var IrcMessage::Batch \brief A batch message (IrcBatchMessage). \sa \ref ircv3 */ /*! \var IrcMessage::Capability \brief A capability message (IrcCapabilityMessage). \sa \ref ircv3 */ /*! \var IrcMessage::Error \brief An error message (IrcErrorMessage). */ /*! \since 3.4 \var IrcMessage::HostChange \brief A host change message (IrcHostChangeMessage). */ /*! \var IrcMessage::Invite \brief An invite message (IrcInviteMessage). */ /*! \var IrcMessage::Join \brief A join message (IrcJoinMessage). */ /*! \var IrcMessage::Kick \brief A kick message (IrcKickMessage). */ /*! \var IrcMessage::Mode \brief A mode message (IrcModeMessage). */ /*! \var IrcMessage::Motd \brief A message of the day (IrcMotdMessage). */ /*! \var IrcMessage::Names \brief A names message (IrcNamesMessage). */ /*! \var IrcMessage::Nick \brief A nick message (IrcNickMessage). */ /*! \var IrcMessage::Notice \brief A notice message (IrcNoticeMessage). */ /*! \var IrcMessage::Numeric \brief A numeric message (IrcNumericMessage). */ /*! \var IrcMessage::Part \brief A part message (IrcPartMessage). */ /*! \var IrcMessage::Ping \brief A ping message (IrcPingMessage). */ /*! \var IrcMessage::Pong \brief A pong message (IrcPongMessage). */ /*! \var IrcMessage::Private \brief A private message (IrcPrivateMessage). */ /*! \var IrcMessage::Quit \brief A quit message (IrcQuitMessage). */ /*! \var IrcMessage::Topic \brief A topic message (IrcTopicMessage). */ /*! \var IrcMessage::Unknown \brief An unknown message (IrcMessage). */ /*! \since 3.3 \var IrcMessage::Whois \brief A whois reply message (IrcWhoisMessage). */ /*! \since 3.3 \var IrcMessage::Whowas \brief A whowas reply message (IrcWhowasMessage). */ /*! \since 3.1 \var IrcMessage::WhoReply \brief A who reply message (IrcWhoReplyMessage). */ /*! \enum IrcMessage::Flag This enum describes the supported message flags. */ /*! \var IrcMessage::None \brief The message has no flags. */ /*! \var IrcMessage::Own \brief The message is user's own message. */ /*! \var IrcMessage::Identified \brief The message is identified. \deprecated Use IrcMessage::account instead. */ /*! \var IrcMessage::Unidentified \brief The message is unidentified. \deprecated Use IrcMessage::account instead. */ /*! \since 3.2 \var IrcMessage::Playback \brief The message is playback. */ /*! \since 3.3 \var IrcMessage::Implicit \brief The message is an implicit "reply" after joining a channel. */ extern bool irc_is_supported_encoding(const QByteArray& encoding); // ircmessagedecoder.cpp static IrcMessage* irc_create_message(const QString& command, IrcConnection* connection) { typedef std::function IrcMessageFactory; static const QHash factories = { {"ACCOUNT", [](IrcConnection* c) { return new IrcAccountMessage(c); }}, {"AWAY", [](IrcConnection* c) { return new IrcAwayMessage(c); }}, {"BATCH", [](IrcConnection* c) { return new IrcBatchMessage(c); }}, {"CAP", [](IrcConnection* c) { return new IrcCapabilityMessage(c); }}, {"ERROR", [](IrcConnection* c) { return new IrcErrorMessage(c); }}, {"CHGHOST", [](IrcConnection* c) { return new IrcHostChangeMessage(c); }}, {"INVITE", [](IrcConnection* c) { return new IrcInviteMessage(c); }}, {"JOIN", [](IrcConnection* c) { return new IrcJoinMessage(c); }}, {"KICK", [](IrcConnection* c) { return new IrcKickMessage(c); }}, {"MODE", [](IrcConnection* c) { return new IrcModeMessage(c); }}, {"NICK", [](IrcConnection* c) { return new IrcNickMessage(c); }}, {"NOTICE", [](IrcConnection* c) { return new IrcNoticeMessage(c); }}, {"PART", [](IrcConnection* c) { return new IrcPartMessage(c); }}, {"PING", [](IrcConnection* c) { return new IrcPingMessage(c); }}, {"PONG", [](IrcConnection* c) { return new IrcPongMessage(c); }}, {"PRIVMSG", [](IrcConnection* c) { return new IrcPrivateMessage(c); }}, {"QUIT", [](IrcConnection* c) { return new IrcQuitMessage(c); }}, {"TOPIC", [](IrcConnection* c) { return new IrcTopicMessage(c); }}, }; IrcMessageFactory factory = factories.value(command); if (factory) return factory(connection); bool isNumeric = false; int number = command.toInt(&isNumeric); if (isNumeric && number > 0) return new IrcNumericMessage(connection); return new IrcMessage(connection); } /*! Constructs a new IrcMessage with \a connection. */ IrcMessage::IrcMessage(IrcConnection* connection) : QObject(connection), d_ptr(new IrcMessagePrivate) { Q_D(IrcMessage); d->connection = connection; } /*! Destructs the message. */ IrcMessage::~IrcMessage() { } /*! This property holds the message connection. \par Access function: \li \ref IrcConnection* connection() const */ IrcConnection* IrcMessage::connection() const { Q_D(const IrcMessage); return d->connection; } /*! This property holds the message network. \par Access function: \li \ref IrcNetwork* network() const */ IrcNetwork* IrcMessage::network() const { Q_D(const IrcMessage); return d->connection ? d->connection->network() : nullptr; } /*! This property holds the message type. \par Access function: \li \ref IrcMessage::Type type() const */ IrcMessage::Type IrcMessage::type() const { Q_D(const IrcMessage); return d->type; } /*! \since 3.2 \property bool IrcMessage::own This property holds whether the message is user's own. This property is provided for convenience. It is equivalent of testing for the IrcMessage::Own flag. \par Access function: \li bool isOwn() const */ bool IrcMessage::isOwn() const { return flags() & Own; } /*! \since 3.5 \property bool IrcMessage::implicit This property holds whether the message is an implicit "reply" after joining a channel. This property is provided for convenience. It is equivalent of testing for the IrcMessage::Implicit flag. \par Access function: \li bool isImplicit() const */ bool IrcMessage::isImplicit() const { return flags() & Implicit; } /*! This property holds the message flags. \par Access function: \li \ref IrcMessage::Flag "IrcMessage::Flags" flags() const \li void setFlags(\ref IrcMessage::Flag "IrcMessage::Flags" flags) (\b Since 3.2) \sa testFlag(), setFlag() */ IrcMessage::Flags IrcMessage::flags() const { Q_D(const IrcMessage); if (d->flags == -1) { d->flags = IrcMessage::None; if (d->connection && !d->prefix().isEmpty() && d->nick() == d->connection->nickName()) d->flags |= IrcMessage::Own; } return IrcMessage::Flags(d->flags); } void IrcMessage::setFlags(IrcMessage::Flags flags) { Q_D(IrcMessage); d->flags = flags; } /*! \since 3.5 Returns \c true if the \a flag is on; otherwise \c false. \sa IrcMessage::flags */ bool IrcMessage::testFlag(Flag flag) const { return flags().testFlag(flag); } /*! \since 3.5 Sets whether the \a flag is \a on. \sa IrcMessage::flags */ void IrcMessage::setFlag(Flag flag, bool on) { if (on) setFlags(flags() | flag); else setFlags(flags() & ~flag); } /*! This property holds the message command. \par Access functions: \li QString command() const \li void setCommand(const QString& command) */ QString IrcMessage::command() const { Q_D(const IrcMessage); return d->command(); } void IrcMessage::setCommand(const QString& command) { Q_D(IrcMessage); d->setCommand(command); } /*! This property holds the message sender prefix. The prefix consists of \ref nick, \ref ident and \ref host as specified in RFC 1459:
    <prefix> ::= <\ref nick> [ '!' <\ref ident> ] [ '@' <\ref host> ]
    
\par Access functions: \li QString prefix() const \li void setPrefix(const QString& prefix) */ QString IrcMessage::prefix() const { Q_D(const IrcMessage); return d->prefix(); } void IrcMessage::setPrefix(const QString& prefix) { Q_D(IrcMessage); d->setPrefix(prefix); } /*! This property holds the message sender nick. Nick is part of the sender \ref prefix as specified in RFC 1459:
    <nick> [ '!' <\ref ident> ] [ '@' <\ref host> ]
    
\par Access function: \li QString nick() const */ QString IrcMessage::nick() const { Q_D(const IrcMessage); return d->nick(); } /*! This property holds the message sender ident. Ident is part of the sender \ref prefix as specified in RFC 1459:
    <\ref nick> [ '!' <ident> ] [ '@' <\ref host> ]
    
\par Access function: \li QString ident() const */ QString IrcMessage::ident() const { Q_D(const IrcMessage); return d->ident(); } /*! This property holds the message sender host. Host is part of the sender \ref prefix as specified in RFC 1459:
    <\ref nick> [ '!' <\ref ident> ] [ '@' <host> ]
    
\par Access function: \li QString host() const */ QString IrcMessage::host() const { Q_D(const IrcMessage); return d->host(); } /*! \since 3.4 This property holds the services account of the message sender. \note Only set if the \c account-tag capability is enabled and the user has identified with services. \par Access function: \li QString account() const \sa \ref ircv3 */ QString IrcMessage::account() const { Q_D(const IrcMessage); return d->tags().value(QLatin1String("account")).toString(); } /*! This property holds the message parameters. \par Access functions: \li QStringList parameters() const \li void setParameters(const QStringList& parameters) */ QStringList IrcMessage::parameters() const { Q_D(const IrcMessage); return d->params(); } void IrcMessage::setParameters(const QStringList& parameters) { Q_D(IrcMessage); d->setParams(parameters); } /*! \since 3.5 Returns the parameter at the specified \a index. */ QString IrcMessage::parameter(int index) const { Q_D(const IrcMessage); return d->param(index); } /*! \since 3.5 Sets the \a parameter at the specified \a index. */ void IrcMessage::setParameter(int index, const QString& parameter) { Q_D(IrcMessage); QStringList params = d->params(); while (index >= params.count()) params.append(QString()); params[index] = parameter; d->setParams(params); } /*! This property holds the message time stamp. \note When the \c server-time capability is enabled, the time stamp is automatically set from the \c time message tag. \par Access functions: \li QDateTime timeStamp() const \li void setTimeStamp(const QDateTime& timeStamp) \sa \ref ircv3 */ QDateTime IrcMessage::timeStamp() const { Q_D(const IrcMessage); return d->timeStamp; } void IrcMessage::setTimeStamp(const QDateTime& timeStamp) { Q_D(IrcMessage); d->timeStamp = timeStamp; } /*! This property holds the FALLBACK encoding for the message. The fallback encoding is used when the message is detected not to be valid UTF-8 and the consequent auto-detection of message encoding fails. See QTextCodec::availableCodes() for the list of supported encodings. The default value is ISO-8859-15. \par Access functions: \li QByteArray encoding() const \li void setEncoding(const QByteArray& encoding) \sa QTextCodec::availableCodecs(), QTextCodec::codecForLocale() */ QByteArray IrcMessage::encoding() const { Q_D(const IrcMessage); return d->encoding; } void IrcMessage::setEncoding(const QByteArray& encoding) { Q_D(IrcMessage); if (!irc_is_supported_encoding(encoding)) { qWarning() << "IrcMessage::setEncoding(): unsupported encoding" << encoding; return; } d->encoding = encoding; d->invalidate(); } /*! \since 3.1 This property holds the message tags. \par Access functions: \li QVariantMap tags() const \li void setTags(const QVariantMap& tags) \sa \ref ircv3 */ QVariantMap IrcMessage::tags() const { Q_D(const IrcMessage); return d->tags(); } void IrcMessage::setTags(const QVariantMap& tags) { Q_D(IrcMessage); d->setTags(tags); } /*! \since 3.5 Returns the value of the specified tag. \sa \ref ircv3 */ QVariant IrcMessage::tag(const QString& name) const { Q_D(const IrcMessage); return d->tags().value(name); } /*! \since 3.5 Sets the value of the specified tag. \sa \ref ircv3 */ void IrcMessage::setTag(const QString& name, const QVariant& value) { Q_D(IrcMessage); QVariantMap tags = d->tags(); tags.insert(name, value); d->setTags(tags); } /*! Creates a new message from \a data and \a connection. */ IrcMessage* IrcMessage::fromData(const QByteArray& data, IrcConnection* connection) { IrcMessageData md = IrcMessageData::fromData(data); IrcMessage* message = irc_create_message(md.command, connection); Q_ASSERT(message); message->d_ptr->data = md; QByteArray tag = md.tags.value("time"); if (!tag.isEmpty()) { QDateTime ts = QDateTime::fromString(QString::fromUtf8(tag), Qt::ISODate); if (ts.isValid()) message->d_ptr->timeStamp = ts.toTimeSpec(Qt::LocalTime); } return message; } /*! Creates a new message from \a prefix, \a command and \a parameters with \a connection. */ IrcMessage* IrcMessage::fromParameters(const QString& prefix, const QString& command, const QStringList& parameters, IrcConnection* connection) { IrcMessage* message = irc_create_message(command, connection); Q_ASSERT(message); message->setPrefix(prefix); message->setCommand(command); message->setParameters(parameters); return message; } /*! \since 3.5 Clones the message. */ IrcMessage* IrcMessage::clone(QObject* parent) const { Q_D(const IrcMessage); IrcMessage* msg = irc_create_message(d->command(), d->connection); if (msg) { msg->setParent(parent); IrcMessagePrivate* p = IrcMessagePrivate::get(msg); p->timeStamp = d->timeStamp; p->encoding = d->encoding; p->flags = d->flags; p->data = d->data; foreach (IrcMessage* bm, d->batch) p->batch += bm->clone(msg); p->m_nick = d->m_nick; p->m_ident = d->m_ident; p->m_host = d->m_host; p->m_prefix = d->m_prefix; p->m_command = d->m_command; p->m_params = d->m_params; p->m_tags = d->m_tags; } return msg; } /*! \property bool IrcMessage::valid This property is \c true if the message is valid; otherwise \c false. A message is considered valid if the prefix is not empty and the parameters match the message. \par Access function: \li bool isValid() const */ bool IrcMessage::isValid() const { Q_D(const IrcMessage); return d->connection && !prefix().isNull(); } /*! Returns the message as received from an IRC server. */ QByteArray IrcMessage::toData() const { Q_D(const IrcMessage); return d->content(); } /*! \since 3.3 \class IrcAwayMessage ircmessage.h \ingroup message \brief Represents an away message. */ /*! Constructs a new IrcAwayMessage with \a connection. */ IrcAwayMessage::IrcAwayMessage(IrcConnection* connection) : IrcMessage(connection) { Q_D(IrcMessage); d->type = Away; } /*! This property holds the message content. \par Access function: \li QString content() const */ QString IrcAwayMessage::content() const { Q_D(const IrcMessage); return d->param(0); } /*! \property bool IrcAwayMessage::reply This property holds whether the message is a reply. Away messages are sent in three situations: \li as a reply when sending a message (\c true), \li as a reply when setting away status (\c true), \li as an away notification when the \c away-notify capability is enabled (\c false). \par Access function: \li bool isReply() const \sa Irc::RPL_AWAY, Irc::RPL_NOWAWAY, Irc::RPL_UNAWAY, \ref ircv3 */ bool IrcAwayMessage::isReply() const { Q_D(const IrcMessage); return d->command().toInt() != 0; } /*! \property bool IrcAwayMessage::away This property holds whether the user is away or back. \par Access function: \li bool isAway() const */ bool IrcAwayMessage::isAway() const { Q_D(const IrcMessage); int rpl = d->command().toInt(); return rpl == Irc::RPL_AWAY || rpl == Irc::RPL_NOWAWAY || (d->command() == "AWAY" && !d->param(0).isEmpty()); } bool IrcAwayMessage::isValid() const { return IrcMessage::isValid(); } /*! \since 3.3 \class IrcAccountMessage ircmessage.h \ingroup message \brief Represents an account notify message. \sa \ref ircv3 */ /*! Constructs a new IrcAccountMessage with \a connection. */ IrcAccountMessage::IrcAccountMessage(IrcConnection* connection) : IrcMessage(connection) { Q_D(IrcMessage); d->type = Account; } /*! This property holds the account name. \note The account name is empty if the user has logged out. \par Access function: \li QString account() const */ QString IrcAccountMessage::account() const { Q_D(const IrcMessage); const QString p = d->param(0); if (p != QLatin1String("*")) return p; return QString(); } bool IrcAccountMessage::isValid() const { Q_D(const IrcMessage); return IrcMessage::isValid() && !d->param(0).isEmpty(); } /*! \since 3.5 \class IrcBatchMessage ircmessage.h \ingroup message \brief Represents a batch message. \sa \ref ircv3 */ /*! Constructs a new IrcBatchMessage with \a connection. */ IrcBatchMessage::IrcBatchMessage(IrcConnection* connection) : IrcMessage(connection) { Q_D(IrcMessage); d->type = Batch; } /*! This property holds the batch tag. \par Access function: \li QString tag() const */ QString IrcBatchMessage::tag() const { Q_D(const IrcMessage); return d->param(0).mid(1); } /*! This property holds the batch type. \par Access function: \li QString type() const */ QString IrcBatchMessage::batch() const { Q_D(const IrcMessage); return d->param(1); } /*! This property holds the list of batched messages. \par Access function: \li QList messages() const */ QList IrcBatchMessage::messages() const { Q_D(const IrcMessage); return d->batch; } bool IrcBatchMessage::isValid() const { return IrcMessage::isValid() && !batch().isEmpty(); } /*! \class IrcCapabilityMessage ircmessage.h \ingroup message \brief Represents a capability message. \sa \ref ircv3 */ /*! Constructs a new IrcCapabilityMessage with \a connection. */ IrcCapabilityMessage::IrcCapabilityMessage(IrcConnection* connection) : IrcMessage(connection) { Q_D(IrcMessage); d->type = Capability; } /*! This property holds the subcommand. The following capability subcommands are defined: LS, LIST, REQ, ACK, NAK, CLEAR, END, NEW, DEL. \par Access function: \li QString subCommand() const */ QString IrcCapabilityMessage::subCommand() const { Q_D(const IrcMessage); return d->param(1); } /*! This property holds the capabilities. A list of capabilities may exist for the following subcommands: LS, LIST, REQ, ACK, NAK, NEW, DEL. \par Access function: \li QStringList capabilities() const */ QStringList IrcCapabilityMessage::capabilities() const { Q_D(const IrcMessage); QStringList caps; QStringList params = d->params(); if (params.count() > 2) caps = params.last().split(QLatin1Char(' '), Qt::SkipEmptyParts); return caps; } bool IrcCapabilityMessage::isValid() const { return IrcMessage::isValid(); } /*! \class IrcErrorMessage ircmessage.h \ingroup message \brief Represents an error message. */ /*! Constructs a new IrcErrorMessage with \a connection. */ IrcErrorMessage::IrcErrorMessage(IrcConnection* connection) : IrcMessage(connection) { Q_D(IrcMessage); d->type = Error; } /*! This property holds the error. \par Access function: \li QString error() const */ QString IrcErrorMessage::error() const { Q_D(const IrcMessage); return d->param(0); } bool IrcErrorMessage::isValid() const { return IrcMessage::isValid() && !error().isEmpty(); } /*! \since 3.4 \class IrcHostChangeMessage ircmessage.h \ingroup message \brief Represents a host change message. \sa \ref ircv3 */ /*! Constructs a new IrcHostChangeMessage with \a connection. */ IrcHostChangeMessage::IrcHostChangeMessage(IrcConnection* connection) : IrcMessage(connection) { Q_D(IrcMessage); d->type = HostChange; } /*! This property holds the user name. \par Access function: \li QString user() const */ QString IrcHostChangeMessage::user() const { Q_D(const IrcMessage); return d->param(0); } /*! This property holds the new host. \par Access function: \li QString host() const */ QString IrcHostChangeMessage::host() const { Q_D(const IrcMessage); return d->param(1); } bool IrcHostChangeMessage::isValid() const { return IrcMessage::isValid() && !user().isEmpty() && !host().isEmpty(); } /*! \class IrcInviteMessage ircmessage.h \ingroup message \brief Represents an invite message. */ /*! Constructs a new IrcInviteMessage with \a connection. */ IrcInviteMessage::IrcInviteMessage(IrcConnection* connection) : IrcMessage(connection) { Q_D(IrcMessage); d->type = Invite; } /*! This property holds the user in question. \par Access function: \li QString user() const \sa \ref ircv3 */ QString IrcInviteMessage::user() const { Q_D(const IrcMessage); return d->param(0); } /*! This property holds the channel in question. \par Access function: \li QString channel() const */ QString IrcInviteMessage::channel() const { Q_D(const IrcMessage); return d->param(1); } /*! \property bool IrcInviteMessage::reply This property holds whether the message is a reply. Invite messages are sent in three situations: \li as a notification of a received invitation (\c false), \li as a reply when sending an invitation (\c true), or \li as an invite notification when the \c invite-notify capability is enabled (\c false). \par Access function: \li bool isReply() const \sa Irc::RPL_INVITING, Irc::RPL_INVITED, IrcInviteCommand, \ref ircv3 */ bool IrcInviteMessage::isReply() const { Q_D(const IrcMessage); int rpl = d->command().toInt(); return rpl == Irc::RPL_INVITING || rpl == Irc::RPL_INVITED; } bool IrcInviteMessage::isValid() const { return IrcMessage::isValid() && !user().isEmpty() && !channel().isEmpty(); } /*! \class IrcJoinMessage ircmessage.h \ingroup message \brief Represents a join message. */ /*! Constructs a new IrcJoinMessage with \a connection. */ IrcJoinMessage::IrcJoinMessage(IrcConnection* connection) : IrcMessage(connection) { Q_D(IrcMessage); d->type = Join; } /*! This property holds the channel in question. \par Access function: \li QString channel() const */ QString IrcJoinMessage::channel() const { Q_D(const IrcMessage); return d->param(0); } /*! \since 3.3 This property holds the account name of the user. \note Only set if the \c extended-join capability is enabled and the user has identified with services. \par Access function: \li QString account() const \sa \ref ircv3 */ QString IrcJoinMessage::account() const { Q_D(const IrcMessage); const QString p = d->param(1); if (p != QLatin1String("*")) return p; return QString(); } /*! \since 3.3 This property holds the real name of the user. \note Only set if the \c extended-join capability is enabled. \par Access function: \li QString realName() const \sa \ref ircv3 */ QString IrcJoinMessage::realName() const { Q_D(const IrcMessage); return d->param(2); } bool IrcJoinMessage::isValid() const { return IrcMessage::isValid() && !channel().isEmpty(); } /*! \class IrcKickMessage ircmessage.h \ingroup message \brief Represents a kick message. */ /*! Constructs a new IrcKickMessage with \a connection. */ IrcKickMessage::IrcKickMessage(IrcConnection* connection) : IrcMessage(connection) { Q_D(IrcMessage); d->type = Kick; } /*! This property holds the channel in question. \par Access function: \li QString channel() const */ QString IrcKickMessage::channel() const { Q_D(const IrcMessage); return d->param(0); } /*! This property holds the user in question. \par Access function: \li QString user() const */ QString IrcKickMessage::user() const { Q_D(const IrcMessage); return d->param(1); } /*! This property holds the optional kick reason. \par Access function: \li QString reason() const */ QString IrcKickMessage::reason() const { Q_D(const IrcMessage); return d->param(2); } bool IrcKickMessage::isValid() const { return IrcMessage::isValid() && !channel().isEmpty() && !user().isEmpty(); } /*! \class IrcModeMessage ircmessage.h \ingroup message \brief Represents a mode message. */ /*! \enum IrcModeMessage::Kind This enum describes the kind of modes. */ /*! \var IrcModeMessage::Channel \brief Channel mode */ /*! \var IrcModeMessage::User \brief User mode */ /*! Constructs a new IrcModeMessage with \a connection. */ IrcModeMessage::IrcModeMessage(IrcConnection* connection) : IrcMessage(connection) { Q_D(IrcMessage); d->type = Mode; } /*! This property holds the target channel or user in question. \par Access function: \li QString target() const */ QString IrcModeMessage::target() const { Q_D(const IrcMessage); return d->param(0); } /*! This property holds the channel or user mode. \par Access function: \li QString mode() const */ QString IrcModeMessage::mode() const { Q_D(const IrcMessage); return d->param(1); } /*! This property holds the first mode argument. \par Access function: \li QString argument() const */ QString IrcModeMessage::argument() const { Q_D(const IrcMessage); return d->param(2); } /*! \since 3.1 This property holds the all mode arguments. \par Access function: \li QStringList arguments() const */ QStringList IrcModeMessage::arguments() const { Q_D(const IrcMessage); return d->params().mid(2); } /*! \property bool IrcModeMessage::reply This property holds whether the message is a reply. Mode messages are sent when a mode changes (\c false) and when joining a channel (\c true). \par Access function: \li bool isReply() const \sa Irc::RPL_CHANNELMODEIS */ bool IrcModeMessage::isReply() const { Q_D(const IrcMessage); int rpl = d->command().toInt(); return rpl == Irc::RPL_CHANNELMODEIS; } /*! This property holds the kind of the mode. \par Access function: \li Kind kind() const */ IrcModeMessage::Kind IrcModeMessage::kind() const { const QString m = mode().remove(QLatin1Char('+')).remove(QLatin1Char('-')); if (!m.isEmpty()) { QStringList channelModes; if (const IrcNetwork* net = network()) channelModes = net->channelModes(IrcNetwork::AllTypes); if (!channelModes.isEmpty()) { for (int i = 0; i < m.length(); ++i) { if (!channelModes.contains(m.at(i))) return User; } } } return Channel; } bool IrcModeMessage::isValid() const { return IrcMessage::isValid() && !target().isEmpty() && !mode().isEmpty(); } /*! \class IrcMotdMessage ircmessage.h \ingroup message \brief Represents a message of the day. */ /*! Constructs a new IrcMotdMessage with \a connection. */ IrcMotdMessage::IrcMotdMessage(IrcConnection* connection) : IrcMessage(connection) { Q_D(IrcMessage); d->type = Motd; setCommand(QLatin1String("MOTD")); } /*! This property holds the message of the day lines. \par Access function: \li QStringList lines() const */ QStringList IrcMotdMessage::lines() const { Q_D(const IrcMessage); return d->params().mid(1); } bool IrcMotdMessage::isValid() const { Q_D(const IrcMessage); return IrcMessage::isValid() && !d->params().isEmpty(); } /*! \class IrcNamesMessage ircmessage.h \ingroup message \brief Represents a names list message. */ /*! Constructs a new IrcNamesMessage with \a connection. */ IrcNamesMessage::IrcNamesMessage(IrcConnection* connection) : IrcMessage(connection) { Q_D(IrcMessage); d->type = Names; setCommand(QLatin1String("NAMES")); } /*! This property holds the channel. \par Access function: \li QString channel() const */ QString IrcNamesMessage::channel() const { Q_D(const IrcMessage); return d->param(0); } /*! This property holds the list of names. \par Access function: \li QStringList names() const */ QStringList IrcNamesMessage::names() const { Q_D(const IrcMessage); return d->params().mid(1); } bool IrcNamesMessage::isValid() const { Q_D(const IrcMessage); return IrcMessage::isValid() && !d->params().isEmpty(); } /*! \class IrcNickMessage ircmessage.h \ingroup message \brief Represents a nick message. */ /*! Constructs a new IrcNickMessage with \a connection. */ IrcNickMessage::IrcNickMessage(IrcConnection* connection) : IrcMessage(connection) { Q_D(IrcMessage); d->type = Nick; } /*! This property holds the old nick. This property is provided for symmetry with \ref newNick and is equal to \ref nick. \par Access function: \li QString oldNick() const */ QString IrcNickMessage::oldNick() const { Q_D(const IrcMessage); return d->nick(); } /*! This property holds the new nick. \par Access function: \li QString newNick() const */ QString IrcNickMessage::newNick() const { Q_D(const IrcMessage); return d->param(0); } bool IrcNickMessage::isValid() const { return IrcMessage::isValid() && !newNick().isEmpty(); } /*! \class IrcNoticeMessage ircmessage.h \ingroup message \brief Represents a notice message. */ /*! Constructs a new IrcNoticeMessage with \a connection. */ IrcNoticeMessage::IrcNoticeMessage(IrcConnection* connection) : IrcMessage(connection) { Q_D(IrcMessage); d->type = Notice; } /*! This property holds the target channel or user in question. \par Access function: \li QString target() const */ QString IrcNoticeMessage::target() const { Q_D(const IrcMessage); if (d->connection) { const IrcNetwork* network = d->connection->network(); return IrcNetworkPrivate::removePrefix(d->param(0), network->statusPrefixes()); } return d->param(0); } /*! This property holds the message content. \par Access function: \li QString content() const */ QString IrcNoticeMessage::content() const { Q_D(const IrcMessage); QString msg = d->param(1); if (isReply()) { msg.remove(0, 1); msg.chop(1); } return msg; } /*! \since 3.4 This property holds the status prefix of the message. \par Access function: \li QString statusPrefix() const */ QString IrcNoticeMessage::statusPrefix() const { Q_D(const IrcMessage); if (d->connection) { const IrcNetwork* network = d->connection->network(); return IrcNetworkPrivate::getPrefix(d->param(0), network->statusPrefixes()); } return QString(); } /*! \property bool IrcNoticeMessage::private This property is \c true if the notice is private, or \c false if it is a channel notice. \par Access function: \li bool isPrivate() const */ bool IrcNoticeMessage::isPrivate() const { Q_D(const IrcMessage); if (d->connection) return !target().compare(d->connection->nickName(), Qt::CaseInsensitive); return false; } /*! \property bool IrcNoticeMessage::reply This property is \c true if the message is a reply; otherwise \c false. \par Access function: \li bool isReply() const */ bool IrcNoticeMessage::isReply() const { Q_D(const IrcMessage); QString msg = d->param(1); return msg.startsWith('\1') && msg.endsWith('\1'); } bool IrcNoticeMessage::isValid() const { return IrcMessage::isValid() && !target().isEmpty() && !content().isEmpty(); } /*! \class IrcNumericMessage ircmessage.h \ingroup message \brief Represents a numeric message. */ /*! Constructs a new IrcNumericMessage with \a connection. */ IrcNumericMessage::IrcNumericMessage(IrcConnection* connection) : IrcMessage(connection) { Q_D(IrcMessage); d->type = Numeric; } /*! This property holds the numeric code. \par Access function: \li int code() const */ int IrcNumericMessage::code() const { Q_D(const IrcMessage); bool ok = false; int number = d->command().toInt(&ok); return ok ? number : -1; } /*! \since 3.3 \property bool IrcNumericMessage::composed This property holds whether the message is composed. \li \c RPL_MOTDSTART, \c RPL_MOTD, and \c RPL_ENDOFMOTD are composed as IrcMotdMessage \li \c RPL_NAMREPLY and \c RPL_ENDOFNAMES are composed as IrcNamesMessage \li \c RPL_TOPIC and \c RPL_NOTOPIC are composed as IrcTopicMessage \li \c RPL_INVITING and \c RPL_INVITED are composed as IrcInviteMessage \li \c RPL_WHOREPLY is composed as IrcWhoReplyMessage \li \c RPL_CHANNELMODEIS is composed as IrcModeMessage \li \c RPL_AWAY, \c RPL_UNAWAY, \c RPL_NOWAWAY are composed as as IrcAwayMessage \par Access function: \li bool isComposed() const */ bool IrcNumericMessage::isComposed() const { return IrcMessageComposer::isComposed(code()); } bool IrcNumericMessage::isValid() const { return IrcMessage::isValid() && code() != -1; } /*! \class IrcPartMessage ircmessage.h \ingroup message \brief Represents a part message. */ /*! Constructs a new IrcPartMessage with \a connection. */ IrcPartMessage::IrcPartMessage(IrcConnection* connection) : IrcMessage(connection) { Q_D(IrcMessage); d->type = Part; } /*! This property holds the channel in question. \par Access function: \li QString channel() const */ QString IrcPartMessage::channel() const { Q_D(const IrcMessage); return d->param(0); } /*! This property holds the optional part reason. \par Access function: \li QString reason() const */ QString IrcPartMessage::reason() const { Q_D(const IrcMessage); return d->param(1); } bool IrcPartMessage::isValid() const { return IrcMessage::isValid() && !channel().isEmpty(); } /*! \class IrcPingMessage ircmessage.h \ingroup message \brief Represents a ping message. */ /*! Constructs a new IrcPingMessage with \a connection. */ IrcPingMessage::IrcPingMessage(IrcConnection* connection) : IrcMessage(connection) { Q_D(IrcMessage); d->type = Ping; } /*! This property holds the optional message argument. \par Access function: \li QString argument() const */ QString IrcPingMessage::argument() const { Q_D(const IrcMessage); return d->param(0); } bool IrcPingMessage::isValid() const { return IrcMessage::isValid(); } /*! \class IrcPongMessage ircmessage.h \ingroup message \brief Represents a pong message. */ /*! Constructs a new IrcPongMessage with \a connection. */ IrcPongMessage::IrcPongMessage(IrcConnection* connection) : IrcMessage(connection) { Q_D(IrcMessage); d->type = Pong; } /*! This property holds the optional message argument. \par Access function: \li QString argument() const */ QString IrcPongMessage::argument() const { Q_D(const IrcMessage); QStringList params = d->params(); return params.value(params.count() - 1); } bool IrcPongMessage::isValid() const { return IrcMessage::isValid(); } /*! \class IrcPrivateMessage ircmessage.h \ingroup message \brief Represents a private message. */ /*! Constructs a new IrcPrivateMessage with \a connection. */ IrcPrivateMessage::IrcPrivateMessage(IrcConnection* connection) : IrcMessage(connection) { Q_D(IrcMessage); d->type = Private; } /*! This property holds the target channel or user in question. \par Access function: \li QString target() const */ QString IrcPrivateMessage::target() const { Q_D(const IrcMessage); if (d->connection) { const IrcNetwork* network = d->connection->network(); return IrcNetworkPrivate::removePrefix(d->param(0), network->statusPrefixes()); } return d->param(0); } /*! This property holds the message content. \par Access function: \li QString content() const */ QString IrcPrivateMessage::content() const { Q_D(const IrcMessage); QString msg = d->param(1); const bool act = isAction(); const bool req = isRequest(); if (act) msg.remove(0, 8); if (req) msg.remove(0, 1); if (act || req) msg.chop(1); return msg; } /*! \since 3.4 This property holds the status prefix of the message. \par Access function: \li QString statusPrefix() const */ QString IrcPrivateMessage::statusPrefix() const { Q_D(const IrcMessage); if (d->connection) { const IrcNetwork* network = d->connection->network(); return IrcNetworkPrivate::getPrefix(d->param(0), network->statusPrefixes()); } return QString(); } /*! \property bool IrcPrivateMessage::private This property is \c true if the message is private, or \c false if it is a channel message. \par Access function: \li bool isPrivate() const */ bool IrcPrivateMessage::isPrivate() const { Q_D(const IrcMessage); if (d->connection) return !target().compare(d->connection->nickName(), Qt::CaseInsensitive); return false; } /*! \property bool IrcPrivateMessage::action This property is \c true if the message is an action; otherwise \c false. \par Access function: \li bool isAction() const */ bool IrcPrivateMessage::isAction() const { Q_D(const IrcMessage); QString msg = d->param(1); return msg.startsWith("\1ACTION ") && msg.endsWith('\1'); } /*! \property bool IrcPrivateMessage::request This property is \c true if the message is a request; otherwise \c false. \par Access function: \li bool isRequest() const */ bool IrcPrivateMessage::isRequest() const { Q_D(const IrcMessage); QString msg = d->param(1); return msg.startsWith('\1') && msg.endsWith('\1') && !isAction(); } bool IrcPrivateMessage::isValid() const { return IrcMessage::isValid() && !target().isEmpty() && !content().isEmpty(); } /*! \class IrcQuitMessage ircmessage.h \ingroup message \brief Represents a quit message. */ /*! Constructs a new IrcQuitMessage with \a connection. */ IrcQuitMessage::IrcQuitMessage(IrcConnection* connection) : IrcMessage(connection) { Q_D(IrcMessage); d->type = Quit; } /*! This property holds the optional quit reason. \par Access function: \li QString reason() const */ QString IrcQuitMessage::reason() const { Q_D(const IrcMessage); return d->param(0); } bool IrcQuitMessage::isValid() const { return IrcMessage::isValid(); } /*! \class IrcTopicMessage ircmessage.h \ingroup message \brief Represents a topic message. */ /*! Constructs a new IrcTopicMessage with \a connection. */ IrcTopicMessage::IrcTopicMessage(IrcConnection* connection) : IrcMessage(connection) { Q_D(IrcMessage); d->type = Topic; } /*! This property holds the channel in question. \par Access function: \li QString channel() const */ QString IrcTopicMessage::channel() const { Q_D(const IrcMessage); return d->param(0); } /*! This property holds the new channel topic. \par Access function: \li QString topic() const */ QString IrcTopicMessage::topic() const { Q_D(const IrcMessage); if (d->command().toInt() == Irc::RPL_NOTOPIC) return QString(); return d->param(1); } /*! \property bool IrcTopicMessage::reply This property holds whether the message is a reply. Topic messages are sent in three situations: \li as a notification of a topic change (\c false), \li as a reply when joining a channel (\c true), or \li as a reply when explicitly querying the channel topic (\c true). \par Access function: \li bool isReply() const \sa Irc::RPL_TOPIC, Irc::RPL_NOTOPIC, IrcTopicCommand */ bool IrcTopicMessage::isReply() const { Q_D(const IrcMessage); int rpl = d->command().toInt(); return rpl == Irc::RPL_TOPIC || rpl == Irc::RPL_NOTOPIC; } bool IrcTopicMessage::isValid() const { return IrcMessage::isValid() && !channel().isEmpty(); } /*! \since 3.3 \class IrcWhoisMessage ircmessage.h \ingroup message \brief Represents a reply message to a WHOIS command. */ /*! Constructs a new IrcWhoisMessage with \a connection. */ IrcWhoisMessage::IrcWhoisMessage(IrcConnection* connection) : IrcMessage(connection) { Q_D(IrcMessage); d->type = Whois; setCommand(QLatin1String("WHOIS")); } /*! This property holds the real name of the user. \par Access function: \li QString realName() const */ QString IrcWhoisMessage::realName() const { Q_D(const IrcMessage); return d->param(0); } /*! This property holds the server address user is on. \par Access function: \li QString server() const */ QString IrcWhoisMessage::server() const { Q_D(const IrcMessage); return d->param(1); } /*! This property holds info of the server the user is on. \par Access function: \li QString info() const */ QString IrcWhoisMessage::info() const { Q_D(const IrcMessage); return d->param(2); } /*! This property holds the account name of the user. \par Access function: \li QString account() const */ QString IrcWhoisMessage::account() const { Q_D(const IrcMessage); return d->param(3); } /*! This property holds the address the user is connecting from. \par Access function: \li QString address() const */ QString IrcWhoisMessage::address() const { Q_D(const IrcMessage); return d->param(4); } /*! This property holds the time since user has been online. \par Access function: \li QDateTime since() const */ QDateTime IrcWhoisMessage::since() const { Q_D(const IrcMessage); return QDateTime::fromSecsSinceEpoch(d->param(5).toInt()); } /*! This property holds the number of seconds the user has been idle. \par Access function: \li int idle() const */ int IrcWhoisMessage::idle() const { Q_D(const IrcMessage); return d->param(6).toInt(); } /*! \property bool IrcWhoisMessage::secure This property holds whether the user is using a secure connection. \par Access function: \li bool isSecure() const */ bool IrcWhoisMessage::isSecure() const { Q_D(const IrcMessage); return !d->param(7).isEmpty(); } /*! This property holds the visible list of channels of the user. \par Access function: \li QStringList channels() const */ QStringList IrcWhoisMessage::channels() const { Q_D(const IrcMessage); return d->params().value(8).split(QLatin1Char(' '), Qt::SkipEmptyParts); } /*! \since 3.5 This property holds the away reason of the user. \par Access function: \li QString awayReason() const */ QString IrcWhoisMessage::awayReason() const { Q_D(const IrcMessage); return d->param(9); } bool IrcWhoisMessage::isValid() const { Q_D(const IrcMessage); return IrcMessage::isValid() && d->params().count() == 10; } /*! \since 3.3 \class IrcWhowasMessage ircmessage.h \ingroup message \brief Represents a reply message to a WHOWAS command. */ /*! Constructs a new IrcWhowasMessage with \a connection. */ IrcWhowasMessage::IrcWhowasMessage(IrcConnection* connection) : IrcMessage(connection) { Q_D(IrcMessage); d->type = Whowas; setCommand(QLatin1String("WHOWAS")); } /*! This property holds the real name of the user. \par Access function: \li QString realName() const */ QString IrcWhowasMessage::realName() const { Q_D(const IrcMessage); return d->param(0); } /*! This property holds the server address user was on. \par Access function: \li QString server() const */ QString IrcWhowasMessage::server() const { Q_D(const IrcMessage); return d->param(1); } /*! This property holds info of the server the user was on. \par Access function: \li QString info() const */ QString IrcWhowasMessage::info() const { Q_D(const IrcMessage); return d->param(2); } /*! This property holds the account of the user. \par Access function: \li QString account() const */ QString IrcWhowasMessage::account() const { Q_D(const IrcMessage); return d->param(3); } bool IrcWhowasMessage::isValid() const { Q_D(const IrcMessage); return IrcMessage::isValid() && d->params().count() == 9; } /*! \since 3.1 \class IrcWhoReplyMessage ircmessage.h \ingroup message \brief Represents a reply message to a WHO command. */ /*! Constructs a new IrcWhoReplyMessage with \a connection. */ IrcWhoReplyMessage::IrcWhoReplyMessage(IrcConnection* connection) : IrcMessage(connection) { Q_D(IrcMessage); d->type = WhoReply; } /*! This property holds the mask. \par Access function: \li QString mask() const */ QString IrcWhoReplyMessage::mask() const { Q_D(const IrcMessage); return d->param(0); } /*! This property holds the server of the user. \par Access function: \li QString server() const */ QString IrcWhoReplyMessage::server() const { Q_D(const IrcMessage); return d->param(1); } /*! \property bool IrcWhoReplyMessage::away This property holds whether the user is away. \par Access function: \li QString isAway() const */ bool IrcWhoReplyMessage::isAway() const { Q_D(const IrcMessage); return d->param(2).contains("G"); } /*! \property bool IrcWhoReplyMessage::servOp This property holds whether the user is a server operator. \par Access function: \li QString isServOp() const */ bool IrcWhoReplyMessage::isServOp() const { Q_D(const IrcMessage); return d->param(2).contains("*"); } /*! This property holds the real name of the user. \par Access function: \li QString realName() const */ QString IrcWhoReplyMessage::realName() const { Q_D(const IrcMessage); return d->param(3); } bool IrcWhoReplyMessage::isValid() const { return IrcMessage::isValid() && !mask().isEmpty() && !nick().isEmpty(); } #ifndef QT_NO_DEBUG_STREAM QDebug operator<<(QDebug debug, IrcMessage::Type type) { const int index = IrcMessage::staticMetaObject.indexOfEnumerator("Type"); QMetaEnum enumerator = IrcMessage::staticMetaObject.enumerator(index); const char* key = enumerator.valueToKey(type); debug << (key ? key : "Unknown"); return debug; } QDebug operator<<(QDebug debug, IrcMessage::Flag flag) { const int index = IrcMessage::staticMetaObject.indexOfEnumerator("Flag"); QMetaEnum enumerator = IrcMessage::staticMetaObject.enumerator(index); const char* key = enumerator.valueToKey(flag); debug << (key ? key : "None"); return debug; } QDebug operator<<(QDebug debug, IrcMessage::Flags flags) { QStringList lst; if (flags == IrcMessage::None) lst << "None"; if (flags & IrcMessage::Own) lst << "Own"; if (flags & IrcMessage::Playback) lst << "Playback"; if (flags & IrcMessage::Implicit) lst << "Implicit"; debug.nospace() << '(' << qPrintable(lst.join("|")) << ')'; return debug; } QDebug operator<<(QDebug debug, IrcModeMessage::Kind kind) { const int index = IrcModeMessage::staticMetaObject.indexOfEnumerator("Kind"); QMetaEnum enumerator = IrcModeMessage::staticMetaObject.enumerator(index); const char* key = enumerator.valueToKey(kind); debug << (key ? key : "Unknown"); return debug; } QDebug operator<<(QDebug debug, const IrcMessage* message) { if (!message) return debug << "IrcMessage(0x0) "; debug.nospace() << message->metaObject()->className() << '(' << (void*) message; if (!message->objectName().isEmpty()) debug.nospace() << ", name=" << qPrintable(message->objectName()); debug.nospace() << ", flags=" << message->flags(); if (!message->prefix().isEmpty()) debug.nospace() << ", prefix=" << qPrintable(message->prefix()); if (!message->command().isEmpty()) debug.nospace() << ", command=" << qPrintable(message->command()); debug.nospace() << ')'; return debug.space(); } #endif // QT_NO_DEBUG_STREAM #include "moc_ircmessage.cpp" IRC_END_NAMESPACE