23 #include "ircresponseparser.h"
25 #include "irc/constants/ircresponsetype.h"
26 #include "irc/ircctcpparser.h"
27 #include "irc/ircglobal.h"
28 #include "irc/ircmessageclass.h"
29 #include "irc/ircnetworkadapter.h"
30 #include "irc/ircuserinfo.h"
32 #include "patternlist.h"
33 #include "strings.hpp"
36 #include <QRegularExpression>
37 #include <QStringList>
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 QRegularExpression prefixRegExp(
"(^:\\S+\\s)(.*)");
91 auto match = prefixRegExp.match(formattedMessage);
93 QString prefix = match.captured(1);
94 QString remainder = formattedMessage.mid(prefix.length());
96 d->prefix = Strings::triml(prefix,
":").trimmed();
99 int indexExclamation = prefix.indexOf(
'!');
100 if (indexExclamation > 0)
101 d->sender = d->prefix.left(indexExclamation);
103 d->sender = d->prefix;
105 QStringList msgParameters = remainder.split(
" ");
106 if (!msgParameters.isEmpty())
108 d->type = msgParameters.takeFirst();
109 d->params = msgParameters;
111 return parseMessage();
127 QString nickname = d->params.takeFirst();
135 d->params.takeFirst();
136 QString nickname = d->params.takeFirst();
137 QString reason = joinAndTrimColonIfNecessary(d->params);
138 emit printToNetworksCurrentChatBox(tr(
"User %1 is away: %2").arg(nickname, reason),
139 IRCMessageClass::NetworkAction);
146 d->params.takeFirst();
149 QString nickname = d->params.takeFirst();
150 QString user = d->params.takeFirst();
151 QString hostName = d->params.takeFirst();
152 QString realName = joinAndTrimColonIfNecessary(d->params);
154 emit
whoIsUser(nickname, user, hostName, realName);
169 d->params.takeFirst();
170 emit printToNetworksCurrentChatBox(d->params.join(
" "), IRCMessageClass::NetworkAction);
176 d->params.takeFirst();
177 QString nick = d->params.takeFirst();
178 int secondsIdle = d->params.takeFirst().toInt();
179 emit userIdleTime(nick, secondsIdle);
180 if (d->params.first().toInt() != 0)
182 int joinedOn = d->params.takeFirst().toInt();
183 #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0)
184 emit userNetworkJoinDateTime(nick, QDateTime::fromSecsSinceEpoch(joinedOn));
186 emit userNetworkJoinDateTime(nick, QDateTime::fromTime_t(joinedOn));
194 d->params.takeFirst();
195 QString nick = d->params.takeFirst();
196 QString channels = joinAndTrimColonIfNecessary(d->params);
197 emit printToNetworksCurrentChatBox(tr(
"%1 is on channels: %2").arg(nick, channels),
198 IRCMessageClass::NetworkAction);
204 d->params.takeFirst();
205 QString nick = d->params.takeFirst();
206 QString account = d->params.takeFirst();
207 QString message = joinAndTrimColonIfNecessary(d->params);
208 emit printToNetworksCurrentChatBox(QString(
"%1 %2 %3").arg(nick, message, account),
209 IRCMessageClass::NetworkAction);
215 d->params.takeFirst();
216 emit iSupportReceived(d->params.join(
" "));
222 d->params.takeFirst();
223 QString channel = d->params.takeFirst();
224 QString topic = joinAndTrimColonIfNecessary(d->params);
225 QString msg = tr(
"Topic: %1").arg(topic);
232 d->params.takeFirst();
233 QString channel = d->params.takeFirst();
234 QString who = d->params.takeFirst();
235 qint64 timestampSeconds = d->params.takeFirst().toLongLong();
236 QDateTime date = QDateTime::fromMSecsSinceEpoch(timestampSeconds * 1000);
237 QString msg = tr(
"Topic set by %1 on %2.").arg(who, date.toString(
"yyyy-MM-dd hh:mm:ss"));
244 d->params.takeFirst();
245 QString channel = d->params.takeFirst();
246 QString url = joinAndTrimColonIfNecessary(d->params);
247 emit
printWithClass(tr(
"URL: %1").arg(url), channel, IRCMessageClass::ChannelAction);
253 d->params.takeFirst();
254 QString channel = d->params.takeFirst();
255 QString time = joinAndTrimColonIfNecessary(d->params);
257 IRCMessageClass::ChannelAction);
270 QString channelName =
"";
271 while (!IRCGlobal::isChannelName(channelName) && !d->params.isEmpty())
273 channelName = d->params.takeFirst();
276 if (channelName.isEmpty())
278 emit parseError(tr(
"RPLNamReply: Received names list but no channel name."));
284 if (!d->params.isEmpty())
285 d->params[0] = d->params[0].remove(0, 1);
287 emit namesListReceived(channelName, d->params);
293 QString channel = d->params[1];
294 emit namesListEndReceived(channel);
303 d->params.takeFirst();
309 emit
print(
"\n----------------------", QString());
312 emit
print(joinAndTrimColonIfNecessary(d->params), QString());
318 emit
print(
"----------------------\n", QString());
329 d->params.takeFirst();
332 emit
print(joinAndTrimColonIfNecessary(d->params), QString());
342 d->params.takeFirst();
346 QString number = d->params.takeFirst();
347 emit
print(number +
" " + joinAndTrimColonIfNecessary(d->params), QString());
354 d->params.takeFirst();
357 QString nickname = d->params.takeFirst();
359 emit noSuchNickname(nickname);
366 d->params.takeFirst();
367 QString badNick = d->params.takeFirst();
368 QString msg = tr(
"Erroneous nickname: %1").arg(badNick);
369 if (d->params.join(
" ").compare(
":Erroneous nickname", Qt::CaseInsensitive) != 0)
370 msg += tr(
" (%1)").arg(joinAndTrimColonIfNecessary(d->params));
371 emit printToNetworksCurrentChatBox(msg, IRCMessageClass::Error);
378 d->params.takeFirst();
380 QString nickname = d->params.takeFirst();
382 emit nicknameInUse(nickname);
386 case IRCResponseType::ERRChannelIsFull:
387 case IRCResponseType::ERRInviteOnlyChan:
388 case IRCResponseType::ERRBannedFromChan:
389 case IRCResponseType::ERRBadChannelKey:
390 case IRCResponseType::ERRBadChannelMask:
391 case IRCResponseType::ERRNoChanModes:
395 d->params.takeFirst();
396 QString channel = d->params.takeFirst();
397 QString reason = joinAndTrimColonIfNecessary(d->params);
400 case IRCResponseType::ERRChannelIsFull:
401 case IRCResponseType::ERRInviteOnlyChan:
402 case IRCResponseType::ERRBannedFromChan:
403 case IRCResponseType::ERRBadChannelKey:
404 emit printToNetworksCurrentChatBox(tr(
"%1: %2").arg(channel, reason),
405 IRCMessageClass::Error);
408 emit
printWithClass(reason, channel, IRCMessageClass::ChannelAction);
415 case IRCResponseType::Join:
417 QString channel = d->params[0];
418 trimColonIfNecessary(channel);
420 emit userJoinsChannel(channel, d->sender, d->prefix);
424 case IRCResponseType::Kick:
426 QString channel = d->params.takeFirst();
427 QString whoIsKicked = d->params.takeFirst();
429 QString reason = joinAndTrimColonIfNecessary(d->params);
431 emit kick(channel, d->sender, whoIsKicked, reason);
435 case IRCResponseType::Kill:
437 QString victim = d->params.takeFirst();
438 QString comment = joinAndTrimColonIfNecessary(d->params);
439 emit kill(victim, comment);
443 case IRCResponseType::Mode:
445 QString channel = d->params.takeFirst();
446 QString flagsString = d->params.takeFirst();
450 if (!d->params.isEmpty())
452 emit
modeInfo(channel, d->sender, flagsString +
" " + d->params.join(
" "));
453 parseUserModeMessage(channel, flagsString, d->params);
459 case IRCResponseType::Nick:
461 QString oldNickname = d->sender;
462 QString newNickname = d->params[0];
463 trimColonIfNecessary(newNickname);
465 emit userChangesNickname(oldNickname, newNickname);
469 case IRCResponseType::Part:
471 QString farewellMessage = QString();
472 QString channel = d->params[0];
474 if (d->params.size() > 1)
476 d->params.pop_front();
478 farewellMessage = joinAndTrimColonIfNecessary(d->params);
481 emit userPartsChannel(channel, d->sender, farewellMessage);
485 case IRCResponseType::Ping:
487 QString pongToWhom = d->params[0];
489 emit sendPongMessage(pongToWhom);
493 case IRCResponseType::PrivMsg:
494 case IRCResponseType::Notice:
495 parsePrivMsgOrNotice();
498 case IRCResponseType::Quit:
500 QString farewellMessage = QString();
501 farewellMessage = joinAndTrimColonIfNecessary(d->params);
503 emit userQuitsNetwork(d->sender, farewellMessage);
507 case IRCResponseType::Topic:
509 QString channel = d->params.takeFirst();
510 QString topic = joinAndTrimColonIfNecessary(d->params);
511 QString msg = tr(
"New topic set by user %1:\n%2").arg(d->sender, topic);
520 if (responseType.numericType() > 1)
522 emit
print(joinAndTrimColonIfNecessary(d->params),
"");
531 "IRCResponseParser: Type '%1' was recognized but there has been no parse code implemented for it.\
532 (yep, it's a bug in the application!)"
540 void IRCResponseParser::parsePrivMsgOrNotice()
542 if (isPrefixIgnored())
544 QString recipient = d->params.takeFirst();
545 if (!IRCGlobal::isChannelName(recipient))
551 recipient = d->sender;
555 QString content = joinAndTrimColonIfNecessary(d->params);
560 IRCCtcpParser ctcp(d->network, d->sender, recipient, content, ctcpMsgType);
565 case IRCCtcpParser::PrintAsNormalMessage:
568 case IRCCtcpParser::DisplayInServerTab:
569 emit
printWithClass(ctcp.printable(), QString(), IRCMessageClass::Ctcp);
571 case IRCCtcpParser::DisplayThroughGlobalMessage:
572 emit printToNetworksCurrentChatBox(ctcp.printable(), IRCMessageClass::Ctcp);
574 case IRCCtcpParser::DontShow:
577 gLog << QString(
"Unhandled CTCP echo type: %1").arg(ctcp.echo());
578 assert(
false &&
"Unhandled CTCP echo type");
581 if (!ctcp.reply().isEmpty() && responseType.type() != IRCResponseType::Notice)
582 d->network->sendMessage(QString(
"/NOTICE %1 %2%3%2").arg(d->sender, QChar(0x1), ctcp.reply()));
586 if (responseType == IRCResponseType::PrivMsg)
587 emit privMsgReceived(recipient, d->sender, content);
588 else if (responseType == IRCResponseType::Notice)
589 emit
print(tr(
"[%1]: %2").arg(d->sender, content), recipient);
591 emit parseError(tr(
"Type '%1' was incorrectly parsed in PrivMsg block.").arg(d->type));
595 void IRCResponseParser::parseUserModeMessage(
const QString &channel, QString flagsString, QStringList &nicknames)
602 FlagModes flagMode = getFlagMode(flagsString[0].toLatin1());
604 if (flagMode == FlagModeError)
606 emit parseError(tr(
"MODE flags string from IRC server are incorrect: \"%1\". "
607 "Information for channel \"%2\" might not be correct anymore.")
608 .arg(flagsString, channel));
612 for (
int i = 1; i < flagsString.size(); ++i)
614 char flagChar = flagsString[i].toLatin1();
616 FlagModes tmpFlagMode = getFlagMode(flagChar);
617 if (tmpFlagMode == FlagModeError)
619 if (nicknames.empty())
622 QList<char> addedFlags;
623 QList<char> removedFlags;
625 QString name = nicknames[0];
630 addedFlags << flagChar;
634 removedFlags << flagChar;
638 emit parseError(tr(
"IRCResponseParser::parseUserModeMessage(): "
639 "wrong FlagMode. Information for channel \"%2\" might not be correct anymore."));
643 emit userModeChanged(channel, name, addedFlags, removedFlags);
645 nicknames.pop_front();
649 flagMode = tmpFlagMode;
655 QString &IRCResponseParser::trimColonIfNecessary(QString &str)
const
657 if (!str.isEmpty() && str[0] ==
':')