summaryrefslogtreecommitdiffstats
path: root/libcommuni/src/core/irccommand.cpp
blob: d7e9b3099ce8a3fa9d1e9117442b43ce2524eeec (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
/*
  Copyright (C) 2008-2014 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 "irccommand.h"
#include "ircmessage.h"
#include <QTextCodec>
#include <QMetaEnum>
#include <QDebug>

IRC_BEGIN_NAMESPACE

/*!
    \file irccommand.h
    \brief \#include &lt;IrcCommand&gt;
 */

/*!
    \class IrcCommand irccommand.h <IrcCommand>
    \ingroup core
    \brief Provides the most common commands.

    The IrcCommand class supports the most common IRC commands out of the box,
    and can be extended for custom commands as well. See IrcCommand::Type for
    the list of built-in command types. IRC commands, as in IrcCommand instances,
    are sent to the IRC server via IrcConnection::sendCommand().

    \section creating-commands Creating commands

    It is recommended to create IrcCommand instances via static
    IrcCommand::createXxx() methods.

    \warning IrcCommand instances must be allocated on the heap, since
    IrcConnection::sendCommand() takes ownership of the command and deletes
    it once it has been sent.

    \section custom-commands Custom commands

    A "custom command" here refers to command types not listed in IrcCommand::Type,
    the list of built-in command types. There are two ways to send custom commands:
    \li by passing the string representation of a command directly to
    IrcConnection::sendRaw() or IrcConnection::sendData(), or
    \li by subclassing IrcCommand and reimplementing
    IrcCommand::toString(), which eventually creates the string representation
    of the command.

    Example implementation of a custom command:
    \code
    class IrcServerCommand : public IrcCommand
    {
        Q_OBJECT
    public:
        explicit IrcServerCommand(QObject* parent = 0) : IrcCommand(parent)
        {
        }

        // provided for convenience, to ensure correct parameter order
        static IrcCommand* create(const QString& serverName, int hopCount, const QString& info)
        {
            IrcCommand* command = new IrcServerCommand;
            command->setParameters(QStringList() << serverName << QString::number(hopCount) << info);
            return command;
        }

        // reimplemented from IrcCommand::toString()
        virtual toString() const
        {
            // SERVER <servername> <hopcount> <info>
            return QString("SERVER %1 %2 %3").arg(params.value(0), params.value(1), params.value(2));
        }
    };
    \endcode

    \sa IrcConnection::sendCommand(), IrcConnection::sendRaw(), IrcCommand::Type
 */

/*!
    \enum IrcCommand::Type
    This enum describes the built-in command types.
 */

/*!
    \var IrcCommand::Admin
    \brief An admin command (ADMIN) is used to query server admin info.
 */

/*!
    \var IrcCommand::Away
    \brief An away command (AWAY) is used to set the away status.
 */

/*!
    \var IrcCommand::Capability
    \brief A capability command (CAP) is used to manage connection capabilities.
 */

/*!
    \var IrcCommand::CtcpAction
    \brief A CTCP action command is used to send an action message to channels and users.
 */

/*!
    \var IrcCommand::CtcpReply
    \brief A CTCP reply command is used to send a reply to a request.
 */

/*!
    \var IrcCommand::CtcpRequest
    \brief A CTCP request command is used to send a request.
 */

/*!
    \var IrcCommand::Custom
    \brief A custom command
 */

/*!
    \var IrcCommand::Info
    \brief An info command (INFO) is used to query server info.
 */

/*!
    \var IrcCommand::Invite
    \brief An invite command (INVITE) is used to invite users to a channel.
 */

/*!
    \var IrcCommand::Join
    \brief A join command (JOIN) is used to start listening a specific channel.
 */

/*!
    \var IrcCommand::Kick
    \brief A kick command (KICK) is used to forcibly remove a user from a channel.
 */

/*!
    \var IrcCommand::Knock
    \brief A knock command (KNOCK) is used to request channel invitation.
 */

/*!
    \var IrcCommand::List
    \brief A list command (LIST) is used to list channels and their topics.
 */

/*!
    \var IrcCommand::Message
    \brief A message command (PRIVMSG) is used to send private messages to channels and users.
 */

/*!
    \var IrcCommand::Mode
    \brief A mode command (MODE) is used to change the mode of users and channels.
 */

/*!
    \var IrcCommand::Motd
    \brief A message of the day command (MOTD) is used to query the message of the day.
 */

/*!
    \var IrcCommand::Names
    \brief A names command (NAMES) is used to list all nicknames on a channel.
 */

/*!
    \var IrcCommand::Nick
    \brief A nick command (NICK) is used to give user a nickname or change the previous one.
 */

/*!
    \var IrcCommand::Notice
    \brief A notice command (NOTICE) is used to send notice messages to channels and users.
 */

/*!
    \var IrcCommand::Part
    \brief A part command (PART) causes the client to be removed from the channel.
 */

/*!
    \var IrcCommand::Quit
    \brief A quit command (QUIT) is used to end a client connection.
 */

/*!
    \var IrcCommand::Quote
    \brief A quote command is used to send a raw message to the server.
 */

/*!
    \var IrcCommand::Stats
    \brief A stats command (STATS) is used to query server statistics.
 */

/*!
    \var IrcCommand::Time
    \brief A time command (TIME) is used to query local server time.
 */

/*!
    \var IrcCommand::Topic
    \brief A topic command (TOPIC) is used to change or view the topic of a channel.
 */

/*!
    \var IrcCommand::Trace
    \brief A trace command (TRACE) is used to trace the connection path to a target.
 */

/*!
    \var IrcCommand::Users
    \brief A users command (USERS) is used to query server users.
 */

/*!
    \var IrcCommand::Version
    \brief A version command (VERSION) is used to query user or server version.
 */

/*!
    \var IrcCommand::Who
    \brief A who command (WHO) is used to generate a query which returns a list of matching users.
 */

/*!
    \var IrcCommand::Whois
    \brief A whois command (WHOIS) is used to query information about a particular user.
 */

/*!
    \var IrcCommand::Whowas
    \brief A whowas command (WHOWAS) is used to query information about a user that no longer exists.
 */

#ifndef IRC_DOXYGEN
class IrcCommandPrivate
{
public:
    IrcCommandPrivate() : encoding("UTF-8") { }

    QString params(int index) const;

    IrcCommand::Type type;
    QStringList parameters;
    QByteArray encoding;

    static IrcCommand* createCommand(IrcCommand::Type type, const QStringList& parameters);
};

QString IrcCommandPrivate::params(int index) const
{
    return QStringList(parameters.mid(index)).join(QLatin1String(" "));
}

IrcCommand* IrcCommandPrivate::createCommand(IrcCommand::Type type, const QStringList& parameters)
{
    IrcCommand* command = new IrcCommand;
    command->setType(type);
    command->setParameters(parameters);
    return command;
}
#endif // IRC_DOXYGEN

/*!
    Constructs a new IrcCommand with \a parent.
 */
IrcCommand::IrcCommand(QObject* parent) : QObject(parent), d_ptr(new IrcCommandPrivate)
{
    Q_D(IrcCommand);
    d->type = Custom;
}

/*!
    Destructs the IRC command.
 */
IrcCommand::~IrcCommand()
{
}

/*!
    This property holds the command type.

    \par Access functions:
    \li IrcCommand::Type <b>type</b>() const
    \li void <b>setType</b>(IrcCommand::Type type)
 */
IrcCommand::Type IrcCommand::type() const
{
    Q_D(const IrcCommand);
    return d->type;
}

void IrcCommand::setType(Type type)
{
    Q_D(IrcCommand);
    d->type = type;
}

/*!
    This property holds the command parameters.

    \par Access functions:
    \li QStringList <b>parameters</b>() const
    \li void <b>setParameters</b>(const QStringList& parameters)
 */
QStringList IrcCommand::parameters() const
{
    Q_D(const IrcCommand);
    return d->parameters;
}

void IrcCommand::setParameters(const QStringList& parameters)
{
    Q_D(IrcCommand);
    d->parameters = parameters;
}

/*!
    This property holds the encoding that is used when
    sending the command via IrcConnection::sendCommand().

    See QTextCodec::availableCodes() for the list of
    supported encodings. The default value is \c "UTF-8".

    \par Access functions:
    \li QByteArray <b>encoding</b>() const
    \li void <b>setEncoding</b>(const QByteArray& encoding)

    \sa QTextCodec::availableCodecs()
 */
QByteArray IrcCommand::encoding() const
{
    Q_D(const IrcCommand);
    return d->encoding;
}

void IrcCommand::setEncoding(const QByteArray& encoding)
{
    Q_D(IrcCommand);
    extern bool irc_is_supported_encoding(const QByteArray& encoding); // ircmessagedecoder.cpp
    if (!irc_is_supported_encoding(encoding)) {
        qWarning() << "IrcCommand::setEncoding(): unsupported encoding" << encoding;
        return;
    }
    d->encoding = encoding;
}

/*!
    Returns the command as a string.

    Reimplement for custom commands.
    \sa IrcCommand::Custom
 */
QString IrcCommand::toString() const
{
    Q_D(const IrcCommand);
    const QString p0 = d->parameters.value(0);
    const QString p1 = d->parameters.value(1);
    const QString p2 = d->parameters.value(2);

    switch (d->type) {
        case Admin:         return QString("ADMIN %1").arg(p0); // server
        case Away:          return QString("AWAY :%1").arg(d->params(0)); // reason
        case Capability:    return QString("CAP %1 :%2").arg(p0, d->params(1)); // subcmd, caps
        case CtcpAction:    return QString("PRIVMSG %1 :\1ACTION %2\1").arg(p0, d->params(1)); // target, msg
        case CtcpRequest:   return QString("PRIVMSG %1 :\1%2\1").arg(p0, d->params(1)); // target, msg
        case CtcpReply:     return QString("NOTICE %1 :\1%2\1").arg(p0, d->params(1)); // target, msg
        case Info:          return QString("INFO %1").arg(p0); // server
        case Invite:        return QString("INVITE %1 %2").arg(p0, p1); // user, chan
        case Join:          return p1.isNull() ? QString("JOIN %1").arg(p0) : QString("JOIN %1 %2").arg(p0, p1); // chan, key
        case Kick:          return p2.isNull() ? QString("KICK %1 %2").arg(p0, p1) : QString("KICK %1 %2 :%3").arg(p0, p1, d->params(2)); // chan, user, reason
        case Knock:         return QString("KNOCK %1 %2").arg(p0, p1); // chan, msg
        case List:          return p1.isNull() ? QString("LIST %1").arg(p0) : QString("LIST %1 %2").arg(p0, p1); // chan, server
        case Message:       return QString("PRIVMSG %1 :%2").arg(p0, d->params(1)); // target, msg
        case Mode:          return QString("MODE ") + d->parameters.join(" "); // target, mode, arg
        case Motd:          return QString("MOTD %1").arg(p0); // server
        case Names:         return QString("NAMES %1").arg(p0); // chan
        case Nick:          return QString("NICK %1").arg(p0); // nick
        case Notice:        return QString("NOTICE %1 :%2").arg(p0, d->params(1)); // target, msg
        case Part:          return p1.isNull() ? QString("PART %1").arg(p0) : QString("PART %1 :%2").arg(p0, d->params(1)); // chan, reason
        case Ping:          return QString("PING %1").arg(p0); // argument
        case Pong:          return QString("PONG %1").arg(p0); // argument
        case Quit:          return QString("QUIT :%1").arg(d->params(0)); // reason
        case Quote:         return d->parameters.join(" ");
        case Stats:         return QString("STATS %1 %2").arg(p0, p1); // query, server
        case Time:          return QString("TIME %1").arg(p0); // server
        case Topic:         return p1.isNull() ? QString("TOPIC %1").arg(p0) : QString("TOPIC %1 :%2").arg(p0, d->params(1)); // chan, topic
        case Trace:         return QString("TRACE %1").arg(p0); // target
        case Users:         return QString("USERS %1").arg(p0); // server
        case Version:       return p0.isNull() ? QString("VERSION") : QString("PRIVMSG %1 :\1VERSION\1").arg(p0); // user
        case Who:           return QString("WHO %1").arg(p0); // user
        case Whois:         return QString("WHOIS %1 %1").arg(p0); // user
        case Whowas:        return QString("WHOWAS %1 %1").arg(p0); // user

        case Custom:        qWarning("Reimplement IrcCommand::toString() for IrcCommand::Custom");
        default:            return QString();
    }
}

/*!
    Creates a new message from this command for \a prefix and \a connection.

    Notice that IRC servers do not echo sent message commands back to the client.
    This function is particularly useful for converting sent message commands as
    messages for presentation purposes.

    \code
    if (command->type() == IrcCommand::Message) {
        IrcMessage* message = command->toMessage(connection->nickName(), connection);
        receiveMessage(message);
        message->deleteLater();
    }
    \endcode
 */
IrcMessage* IrcCommand::toMessage(const QString& prefix, IrcConnection* connection) const
{
    return IrcMessage::fromData(":" + prefix.toUtf8() + " " + toString().toUtf8(), connection);
}

/*!
    Creates a new ADMIN command with type IrcCommand::Admin and optional parameter \a server.

    This command shows admin info for the specified \a server,
    or the current server if not specified.
 */
IrcCommand* IrcCommand::createAdmin(const QString& server)
{
    return IrcCommandPrivate::createCommand(Admin, QStringList() << server);
}

/*!
    Creates a new AWAY command with type IrcCommand::Away and optional parameter \a reason.

    Provides the server with \a reason to automatically send in reply to a private
    message directed at the user. If \a reason is omitted, the away status is removed.
 */
IrcCommand* IrcCommand::createAway(const QString& reason)
{
    return IrcCommandPrivate::createCommand(Away, QStringList() << reason);
}

/*!
    Creates a new capability command with type IrcCommand::Capability and parameters \a subCommand and a \a capability.

    Available subcommands are: LS, LIST, REQ, ACK, NAK, CLEAR and END.
 */
IrcCommand* IrcCommand::createCapability(const QString& subCommand, const QString& capability)
{
    return createCapability(subCommand, QStringList() << capability);
}

/*!
    Creates a new capability command with type IrcCommand::Capability and parameters \a subCommand and optional \a capabilities.

    Available subcommands are: LS, LIST, REQ, ACK, NAK, CLEAR and END.
 */
IrcCommand* IrcCommand::createCapability(const QString& subCommand, const QStringList& capabilities)
{
    return IrcCommandPrivate::createCommand(Capability, QStringList() << subCommand << capabilities.join(QLatin1String(" ")));
}

/*!
    Creates a new CTCP action command with type IrcCommand::CtcpAction and parameters \a target and \a action.
 */
IrcCommand* IrcCommand::createCtcpAction(const QString& target, const QString& action)
{
    return IrcCommandPrivate::createCommand(CtcpAction, QStringList() << target << action);
}

/*!
    Creates a new CTCP reply command with type IrcCommand::CtcpReply and parameters \a target and \a reply.
 */
IrcCommand* IrcCommand::createCtcpReply(const QString& target, const QString& reply)
{
    return IrcCommandPrivate::createCommand(CtcpReply, QStringList() << target << reply);
}

/*!
    Creates a new CTCP request command with type IrcCommand::CtcpRequest and parameters \a target and \a request.
 */
IrcCommand* IrcCommand::createCtcpRequest(const QString& target, const QString& request)
{
    return IrcCommandPrivate::createCommand(CtcpRequest, QStringList() << target << request);
}

/*!
    Creates a new INFO command with type IrcCommand::Info and optional parameter \a server.

    This command shows info for the specified \a server,
    or the current server if not specified.
 */
IrcCommand* IrcCommand::createInfo(const QString& server)
{
    return IrcCommandPrivate::createCommand(Info, QStringList() << server);
}

/*!
    Creates a new INVITE command with type IrcCommand::Invite and parameters \a user and \a channel.

    This command invites \a user to the \a channel. The channel does not have to exist, but
    if it does, only members of the channel are allowed to invite other clients. if the
    channel mode +i (invite-only) is set, only channel operators may invite other clients.
 */
IrcCommand* IrcCommand::createInvite(const QString& user, const QString& channel)
{
    return IrcCommandPrivate::createCommand(Invite, QStringList() << user << channel);
}

/*!
    Creates a new JOIN command with type IrcCommand::Join and parameters \a channel and optional \a key.

    This command joins the \a channel using \a key if specified.
    If the channel does not exist, it will be created.
 */
IrcCommand* IrcCommand::createJoin(const QString& channel, const QString& key)
{
    return IrcCommandPrivate::createCommand(Join, QStringList() << channel << key);
}

/*!
    This overload is provided for convenience.
 */
IrcCommand* IrcCommand::createJoin(const QStringList& channels, const QStringList& keys)
{
    return IrcCommandPrivate::createCommand(Join, QStringList() << channels.join(",") << keys.join(","));
}

/*!
    Creates a new KICK command with type IrcCommand::Kick and parameters \a channel, \a user and optional \a reason.

    This command forcibly removes \a user from \a channel,
    and may only be issued by channel operators.
 */
IrcCommand* IrcCommand::createKick(const QString& channel, const QString& user, const QString& reason)
{
    return IrcCommandPrivate::createCommand(Kick, QStringList() << channel << user << reason);
}

/*!
    Creates a new KNOCK command with type IrcCommand::Knock and parameters \a channel and optional \a message.

    This command sends an invitation request to a \a channel with an optional \a message.

    \note The command is not formally defined by an RFC, but is supported by most major IRC daemons.
    Support is indicated in a RPL_ISUPPORT reply (numeric 005) with the KNOCK keyword.
 */
IrcCommand* IrcCommand::createKnock(const QString& channel, const QString& message)
{
    return IrcCommandPrivate::createCommand(Knock, QStringList() << channel << message);
}

/*!
    Creates a new LIST command with type IrcCommand::List and optional parameters \a channels and \a server.

    This command lists all channels on the server. If \a channels are given, it will list the channel topics.
    If \a server is given, the command will be forwarded to \a server for evaluation.
 */
IrcCommand* IrcCommand::createList(const QStringList& channels, const QString& server)
{
    return IrcCommandPrivate::createCommand(List, QStringList() << channels.join(",") << server);
}

/*!
    Creates a new PRIVMSG command with type IrcCommand::Message and parameters \a target and \a message.

    This command sends \a message to \a target, which is usually a user or channel.
 */
IrcCommand* IrcCommand::createMessage(const QString& target, const QString& message)
{
    return IrcCommandPrivate::createCommand(Message, QStringList() << target << message);
}

/*!
    Creates a new MODE command with type IrcCommand::Mode and parameters \a target and optional \a mode and \a arg.

    This command is used to set both user and channel modes.
 */
IrcCommand* IrcCommand::createMode(const QString& target, const QString& mode, const QString& arg)
{
    return IrcCommandPrivate::createCommand(Mode, QStringList() << target << mode << arg);
}

/*!
    Creates a new MOTD command with type IrcCommand::Motd and optional parameter \a server.

    This command shows the message of the day on the specified \a server,
    or the current server if not specified.
 */
IrcCommand* IrcCommand::createMotd(const QString& server)
{
    return IrcCommandPrivate::createCommand(Motd, QStringList() << server);
}

/*!
    Creates a new NAMES command with type IrcCommand::Names and parameter \a channel.

    This command lists all users on the \a channel, optionally limiting to the given \a server.

    If \a channel is omitted, all users are shown, grouped by channel name with
    all users who are not on a channel being shown as part of channel "*".
    If \a server is specified, the command is sent to \a server for evaluation.
*/
IrcCommand* IrcCommand::createNames(const QString& channel, const QString& server)
{
    return IrcCommandPrivate::createCommand(Names, QStringList() << channel << server);
}

/*!
    This overload is provided for convenience.
 */
IrcCommand* IrcCommand::createNames(const QStringList& channels, const QString& server)
{
    return IrcCommandPrivate::createCommand(Names, QStringList() << channels.join(",") << server);
}

/*!
    Creates a new NICK command with type IrcCommand::Nick and parameter \a nick.

    This command allows a client to change their IRC nickname.
 */
IrcCommand* IrcCommand::createNick(const QString& nick)
{
    return IrcCommandPrivate::createCommand(Nick, QStringList() << nick);
}

/*!
    Creates a new NOTICE command with type IrcCommand::Notice and parameters \a target and \a message.

    This command sends \a notice to \a target, which is usually a user or channel.

    \note The command works similarly to PRIVMSG, except automatic replies must never be sent in reply to NOTICE messages.
 */
IrcCommand* IrcCommand::createNotice(const QString& target, const QString& message)
{
    return IrcCommandPrivate::createCommand(Notice, QStringList() << target << message);
}

/*!
    Creates a new PART command with type IrcCommand::Part and parameters \a channel and optional \a reason.

    This command causes the client to leave the specified channel.
 */
IrcCommand* IrcCommand::createPart(const QString& channel, const QString& reason)
{
    return IrcCommandPrivate::createCommand(Part, QStringList() << channel << reason);
}

/*!
    This overload is provided for convenience.
 */
IrcCommand* IrcCommand::createPart(const QStringList& channels, const QString& reason)
{
    return IrcCommandPrivate::createCommand(Part, QStringList() << channels.join(",") << reason);
}

/*!
    Creates a new PING command with type IrcCommand::Ping and \a argument.
 */
IrcCommand* IrcCommand::createPing(const QString& argument)
{
    return IrcCommandPrivate::createCommand(Ping, QStringList() << argument);
}

/*!
    Creates a new PONG command with type IrcCommand::Pong and \a argument.
 */
IrcCommand* IrcCommand::createPong(const QString& argument)
{
    return IrcCommandPrivate::createCommand(Pong, QStringList() << argument);
}

/*!
    Creates a new QUIT command with type IrcCommand::Quit and optional parameter \a reason.
 */
IrcCommand* IrcCommand::createQuit(const QString& reason)
{
    return IrcCommandPrivate::createCommand(Quit, QStringList() << reason);
}

/*!
    Creates a new QUOTE command with type IrcCommand::Quote and \a raw.
 */
IrcCommand* IrcCommand::createQuote(const QString& raw)
{
    return IrcCommandPrivate::createCommand(Quote, QStringList() << raw);
}

/*!
    Creates a new QUOTE command with type IrcCommand::Quote and \a parameters.
 */
IrcCommand* IrcCommand::createQuote(const QStringList& parameters)
{
    return IrcCommandPrivate::createCommand(Quote, parameters);
}

/*!
    Creates a new STATS command with type IrcCommand::Stats and parameters \a query and optional \a server.

    This command queries statistics about the specified \a server,
    or the current server if not specified.
 */
IrcCommand* IrcCommand::createStats(const QString& query, const QString& server)
{
    return IrcCommandPrivate::createCommand(Stats, QStringList() << query << server);
}

/*!
    Creates a new TIME command with type IrcCommand::Time and optional parameter \a server.

    This command queries local time of the specified \a server,
    or the current server if not specified.
 */
IrcCommand* IrcCommand::createTime(const QString& server)
{
    return IrcCommandPrivate::createCommand(Time, QStringList() << server);
}

/*!
    Creates a new TOPIC command with type IrcCommand::Topic and parameters \a channel and optional \a topic.

    This command allows the client to query or set the channel topic on \a channel.
    If \a topic is given, it sets the channel topic to \a topic.
    If channel mode +t is set, only a channel operator may set the topic.
 */
IrcCommand* IrcCommand::createTopic(const QString& channel, const QString& topic)
{
    return IrcCommandPrivate::createCommand(Topic, QStringList() << channel << topic);
}

/*!
    Creates a new TRACE command with type IrcCommand::Trace and optional parameter \a target.

    This command traces the connection path across the IRC network
    to the current server or to a specific \a target (server or client)
    in a similar method to traceroute.
 */
IrcCommand* IrcCommand::createTrace(const QString& target)
{
    return IrcCommandPrivate::createCommand(Trace, QStringList() << target);
}

/*!
    Creates a new USERS command with type IrcCommand::Users and optional parameter \a server.

    This command queries the users of the specified \a server,
    or the current server if not specified.
 */
IrcCommand* IrcCommand::createUsers(const QString& server)
{
    return IrcCommandPrivate::createCommand(Users, QStringList() << server);
}

/*!
    Creates a new command with type IrcCommand::Version and optional parameter \a user.

    This command queries the version of the specified \a user's client (CTCP REQUEST VERSION),
    or the current server (VERSION) if not specified.
 */
IrcCommand* IrcCommand::createVersion(const QString& user)
{
    return IrcCommandPrivate::createCommand(Version, QStringList() << user);
}

/*!
    Creates a new WHO command with type IrcCommand::Who and parameters \a mask and optional \a operators.

    This command returns a list of users who match \a mask,
    optionally matching only IRC \a operators.
 */
IrcCommand* IrcCommand::createWho(const QString& mask, bool operators)
{
    return IrcCommandPrivate::createCommand(Who, QStringList() << mask << (operators ? "o" : ""));
}

/*!
    Creates a new WHOIS command with type IrcCommand::Whois and parameter \a user.

    This command returns information about \a user.
 */
IrcCommand* IrcCommand::createWhois(const QString& user)
{
    return IrcCommandPrivate::createCommand(Whois, QStringList() << user);
}

/*!
    Creates a new WHOWAS command with type IrcCommand::Whowas and parameters \a user and optional \a count.

    This command returns information about a \a user that is no longer online
    (due to client disconnection, or nickname changes). If given, the server
    will return information from the last \a count times the nickname has been used.
 */
IrcCommand* IrcCommand::createWhowas(const QString& user, int count)
{
    return IrcCommandPrivate::createCommand(Whowas, QStringList() << user << QString::number(count));
}

#ifndef QT_NO_DEBUG_STREAM
QDebug operator<<(QDebug debug, IrcCommand::Type type)
{
    const int index = IrcCommand::staticMetaObject.indexOfEnumerator("Type");
    QMetaEnum enumerator = IrcCommand::staticMetaObject.enumerator(index);
    const char* key = enumerator.valueToKey(type);
    debug << (key ? key : "Unknown");
    return debug;
}

QDebug operator<<(QDebug debug, const IrcCommand* command)
{
    if (!command)
        return debug << "IrcCommand(0x0) ";
    debug.nospace() << command->metaObject()->className() << '(' << (void*) command;
    if (!command->objectName().isEmpty())
        debug.nospace() << ", name=" << qPrintable(command->objectName());
    debug.nospace() << ", type=" << command->type();
    QString str = command->toString();
    if (!str.isEmpty())
        debug.nospace() << ", " << str.left(20);
    debug.nospace() << ')';
    return debug.space();
}
#endif // QT_NO_DEBUG_STREAM

#include "moc_irccommand.cpp"

IRC_END_NAMESPACE