23 #include "ircresponseparser.h"
27 #include <QStringList>
29 #include "irc/constants/ircresponsetype.h"
30 #include "irc/ircctcpparser.h"
31 #include "irc/ircglobal.h"
32 #include "irc/ircmessageclass.h"
33 #include "irc/ircnetworkadapter.h"
34 #include "irc/ircuserinfo.h"
36 #include "patternlist.h"
39 DClass<IRCResponseParser>
56 IRCResponseParser::~IRCResponseParser()
60 IRCResponseParser::FlagModes IRCResponseParser::getFlagMode(
char c)
68 return FlagModeRemove;
75 bool IRCResponseParser::isPrefixIgnored()
const
77 return d->network->ignoredUsersPatterns().isExactMatchAny(d->prefix);
80 QString IRCResponseParser::joinAndTrimColonIfNecessary(
const QStringList& strList)
const
82 QString joined = strList.join(
" ");
83 return this->trimColonIfNecessary(joined);
88 QString formattedMessage = message.trimmed();
90 QRegExp prefixRegExp(
"(^:\\S+\\s)(.*)");
91 prefixRegExp.indexIn(formattedMessage);
93 QString prefix = prefixRegExp.cap(1);
94 QString remainder = formattedMessage.mid(prefix.length());
96 d->prefix = Strings::triml(prefix,
":").trimmed();
99 int indexExclamation = prefix.indexOf(
'!');
100 if (indexExclamation > 0)
102 d->sender = d->prefix.left(indexExclamation);
106 d->sender = d->prefix;
109 QStringList msgParameters = remainder.split(
" ");
110 if (!msgParameters.isEmpty())
112 d->type = msgParameters.takeFirst();
113 d->params = msgParameters;
115 return parseMessage();
131 QString nickname = d->params.takeFirst();
139 d->params.takeFirst();
140 QString nickname = d->params.takeFirst();
141 QString reason = joinAndTrimColonIfNecessary(d->params);
142 emit printToNetworksCurrentChatBox(tr(
"User %1 is away: %2").arg(nickname, reason),
143 IRCMessageClass::NetworkAction);
150 d->params.takeFirst();
153 QString nickname = d->params.takeFirst();
154 QString user = d->params.takeFirst();
155 QString hostName = d->params.takeFirst();
156 QString realName = joinAndTrimColonIfNecessary(d->params);
158 emit
whoIsUser(nickname, user, hostName, realName);
172 d->params.takeFirst();
173 emit printToNetworksCurrentChatBox(d->params.join(
" "), IRCMessageClass::NetworkAction);
179 d->params.takeFirst();
180 QString nick = d->params.takeFirst();
181 int secondsIdle = d->params.takeFirst().toInt();
182 emit userIdleTime(nick, secondsIdle);
183 if (d->params.first().toInt() != 0)
185 int joinedOn = d->params.takeFirst().toInt();
186 emit userNetworkJoinDateTime(nick, QDateTime::fromTime_t(joinedOn));
193 d->params.takeFirst();
194 QString nick = d->params.takeFirst();
195 QString channels = joinAndTrimColonIfNecessary(d->params);
196 emit printToNetworksCurrentChatBox(tr(
"%1 is on channels: %2").arg(nick, channels),
197 IRCMessageClass::NetworkAction);
203 d->params.takeFirst();
204 QString nick = d->params.takeFirst();
205 QString account = d->params.takeFirst();
206 QString message = joinAndTrimColonIfNecessary(d->params);
207 emit printToNetworksCurrentChatBox(QString(
"%1 %2 %3").arg(nick, message, account),
208 IRCMessageClass::NetworkAction);
214 d->params.takeFirst();
215 emit iSupportReceived(d->params.join(
" "));
221 d->params.takeFirst();
222 QString channel = d->params.takeFirst();
223 QString topic = joinAndTrimColonIfNecessary(d->params);
224 QString msg = tr(
"Topic: %1").arg(topic);
231 d->params.takeFirst();
232 QString channel = d->params.takeFirst();
233 QString who = d->params.takeFirst();
234 qint64 timestampSeconds = d->params.takeFirst().toLongLong();
235 QDateTime date = QDateTime::fromMSecsSinceEpoch(timestampSeconds * 1000);
236 QString msg = tr(
"Topic set by %1 on %2.").arg(who, date.toString(
"yyyy-MM-dd hh:mm:ss"));
250 QString channelName =
"";
251 while (!IRCGlobal::isChannelName(channelName) && !d->params.isEmpty())
253 channelName = d->params.takeFirst();
256 if (channelName.isEmpty())
258 emit parseError(tr(
"RPLNamReply: Received names list but no channel name."));
264 if (!d->params.isEmpty())
266 d->params[0] = d->params[0].remove(0, 1);
269 emit namesListReceived(channelName, d->params);
275 QString channel = d->params[1];
276 emit namesListEndReceived(channel);
285 d->params.takeFirst();
291 emit
print(
"\n----------------------", QString());
294 emit
print(joinAndTrimColonIfNecessary(d->params), QString());
300 emit
print(
"----------------------\n", QString());
311 d->params.takeFirst();
314 emit
print(joinAndTrimColonIfNecessary(d->params), QString());
324 d->params.takeFirst();
328 QString number = d->params.takeFirst();
329 emit
print(number +
" " + joinAndTrimColonIfNecessary(d->params), QString());
336 d->params.takeFirst();
339 QString nickname = d->params.takeFirst();
341 emit noSuchNickname(nickname);
348 d->params.takeFirst();
349 QString badNick = d->params.takeFirst();
350 QString msg = tr(
"Erroneous nickname: %1").arg(badNick);
351 if (d->params.join(
" ").compare(
":Erroneous nickname", Qt::CaseInsensitive) != 0)
353 msg += tr(
" (%1)").arg(joinAndTrimColonIfNecessary(d->params));
355 emit printToNetworksCurrentChatBox(msg, IRCMessageClass::Error);
362 d->params.takeFirst();
364 QString nickname = d->params.takeFirst();
366 emit nicknameInUse(nickname);
373 d->params.takeFirst();
374 QString channel = d->params.takeFirst();
375 QString reason = joinAndTrimColonIfNecessary(d->params);
380 case IRCResponseType::Join:
382 QString channel = d->params[0];
383 trimColonIfNecessary(channel);
385 emit userJoinsChannel(channel, d->sender, d->prefix);
389 case IRCResponseType::Kick:
391 QString channel = d->params.takeFirst();
392 QString whoIsKicked = d->params.takeFirst();
394 QString reason = joinAndTrimColonIfNecessary(d->params);
396 emit kick(channel, d->sender, whoIsKicked, reason);
400 case IRCResponseType::Kill:
402 QString victim = d->params.takeFirst();
403 QString comment = joinAndTrimColonIfNecessary(d->params);
404 emit kill(victim, comment);
408 case IRCResponseType::Mode:
410 QString channel = d->params.takeFirst();
411 QString flagsString = d->params.takeFirst();
415 if (!d->params.isEmpty())
417 emit
modeInfo(channel, d->sender, flagsString +
" " + d->params.join(
" "));
418 parseUserModeMessage(channel, flagsString, d->params);
424 case IRCResponseType::Nick:
426 QString oldNickname = d->sender;
427 QString newNickname = d->params[0];
428 trimColonIfNecessary(newNickname);
430 emit userChangesNickname(oldNickname, newNickname);
434 case IRCResponseType::Part:
436 QString farewellMessage = QString();
437 QString channel = d->params[0];
439 if (d->params.size() > 1)
441 d->params.pop_front();
443 farewellMessage = joinAndTrimColonIfNecessary(d->params);
446 emit userPartsChannel(channel, d->sender, farewellMessage);
450 case IRCResponseType::Ping:
452 QString pongToWhom = d->params[0];
454 emit sendPongMessage(pongToWhom);
458 case IRCResponseType::PrivMsg:
459 case IRCResponseType::Notice:
460 parsePrivMsgOrNotice();
463 case IRCResponseType::Quit:
465 QString farewellMessage = QString();
466 farewellMessage = joinAndTrimColonIfNecessary(d->params);
468 emit userQuitsNetwork(d->sender, farewellMessage);
472 case IRCResponseType::Topic:
474 QString channel = d->params.takeFirst();
475 QString topic = joinAndTrimColonIfNecessary(d->params);
476 QString msg = tr(
"New topic set by user %1:\n%2").arg(d->sender, topic);
485 if (responseType.numericType() > 1)
487 emit
print(joinAndTrimColonIfNecessary(d->params),
"");
496 "IRCResponseParser: Type '%1' was recognized but there has been no parse code implemented for it.\
497 (yep, it's a bug in the application!)"
507 void IRCResponseParser::parsePrivMsgOrNotice()
509 if (isPrefixIgnored())
513 QString recipient = d->params.takeFirst();
514 if (!IRCGlobal::isChannelName(recipient))
520 recipient = d->sender;
524 QString content = joinAndTrimColonIfNecessary(d->params);
529 IRCCtcpParser ctcp(d->network, d->sender, recipient, content, ctcpMsgType);
534 case IRCCtcpParser::PrintAsNormalMessage:
537 case IRCCtcpParser::DisplayInServerTab:
538 emit
printWithClass(ctcp.printable(), QString(), IRCMessageClass::Ctcp);
540 case IRCCtcpParser::DisplayThroughGlobalMessage:
541 emit printToNetworksCurrentChatBox(ctcp.printable(), IRCMessageClass::Ctcp);
543 case IRCCtcpParser::DontShow:
546 gLog << QString(
"Unhandled CTCP echo type: %1").arg(ctcp.echo());
547 assert(
false &&
"Unhandled CTCP echo type");
550 if (!ctcp.reply().isEmpty() && responseType.type() != IRCResponseType::Notice)
552 d->network->sendMessage(QString(
"/NOTICE %1 %2%3%2").arg(d->sender, QChar(0x1), ctcp.reply()));
557 if (responseType == IRCResponseType::PrivMsg)
559 emit privMsgReceived(recipient, d->sender, content);
561 else if (responseType == IRCResponseType::Notice)
563 emit
print(QString(
"[%1]: %2").arg(d->sender, content), QString());
567 emit parseError(tr(
"Type '%1' was incorrectly parsed in PrivMsg block.").arg(d->type));
572 void IRCResponseParser::parseUserModeMessage(
const QString& channel, QString flagsString, QStringList& nicknames)
579 FlagModes flagMode = getFlagMode(flagsString[0].toAscii());
581 if (flagMode == FlagModeError)
583 emit parseError(tr(
"MODE flags string from IRC server are incorrect: \"%1\". "
584 "Information for channel \"%2\" might not be correct anymore.")
585 .arg(flagsString, channel));
589 for (
int i = 1; i < flagsString.size(); ++i)
591 char flagChar = flagsString[i].toAscii();
593 FlagModes tmpFlagMode = getFlagMode(flagChar);
594 if (tmpFlagMode == FlagModeError)
596 if (nicknames.empty())
601 QList<char> addedFlags;
602 QList<char> removedFlags;
604 QString name = nicknames[0];
609 addedFlags << flagChar;
613 removedFlags << flagChar;
617 emit parseError(tr(
"IRCResponseParser::parseUserModeMessage(): "
618 "wrong FlagMode. Information for channel \"%2\" might not be correct anymore."));
622 emit userModeChanged(channel, name, addedFlags, removedFlags);
624 nicknames.pop_front();
628 flagMode = tmpFlagMode;
634 QString& IRCResponseParser::trimColonIfNecessary(QString& str)
const
636 if (!str.isEmpty() && str[0] ==
':')
Interprets communication between the client and the IRC server.
void print(const QString &printWhat, const QString &printWhere)
Tells the network adapter to print a message.
375 - start of the message of the day
void privMsgLiteralReceived(const QString &recipient, const QString &content, const IRCMessageClass &msgClass)
Create chat window if necessary and display message 'as is' without further string gluing...
An answer is being sent through NOTICE.
void modeInfo(const QString &channel, const QString &whoSetThis, const QString &modeParams)
Carries info about MODE for display.
IRCResponseParseResult parse(const QString &message)
Parses the message received from the network.
253 - how many unknown connections
void whoIsUser(const QString &nickname, const QString &user, const QString &hostName, const QString &realName)
Emitted with response 311 RPL_WHOISUSER.
251 - how many users on how many servers
320 - it's inconclusive what this code means.
Defines types of IRC network response message.
void helloClient(const QString &nickname)
Emitted when user successfuly joins the network.
A question is being asked through PRIVMSG.
353 - names list for a channel
376 - end of the message of the day
001 - sent when client connects.
005 - all sorts of server flags.
Result info generated by the IRCResponseParser.
MsgType
Represents types defined by RFC 1459.
307 - no idea what this is, but we'll treat it the same way we treat RPLWhoIsSpecial.
255 - how many clients on how many servers,
Type unknown to this IRC client.
void printWithClass(const QString &printWhat, const QString &printWhere, const IRCMessageClass &msgClass)
Same as print(), but allows to specify message class.