#include "ICommand.h" #include "Commands.h" #include "IRCClient/IRCMember.h" #include "IRCClient/IRCChannel.h" #include "MdiManager.h" #include "Script/Manager.h" #include "fmt/format.h" #include "ICommandPriv.h" #include #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) #define QT_SPLIT_SKIP_EMPTY_PARTS QString::SkipEmptyParts #else #define QT_SPLIT_SKIP_EMPTY_PARTS Qt::SkipEmptyParts #endif ICommand::ICommand(IRC& connection_) : mp(new ICommandPriv(*this, connection_, connection_.getStatus())) {} ICommand::~ICommand() = default; void ICommand::parse(QString text) { auto& connection = mp->connection; if (text.isEmpty()) return; if (text[0] == '/') text = text.mid(1); if (tryParseIRC(text) || tryParseInternal(text)) return; else if (ScriptManager::instance()->runCommand(text)) return; else if (!connection.isOnline()) { const auto command = text.split(' ')[0]; print_notConnectedToServer(command); } else { const int nextSpace = text.indexOf(' '); if (nextSpace == -1) connection.raw(text.toUpper().toStdString()); else { QString command = text.mid(0, nextSpace).toUpper(); QString out = QString("%1 %2") .arg(command) .arg(text.mid(nextSpace + 1)); connection.raw(out.toStdString()); } } } IRC& ICommand::getConnection() { return mp->connection; } bool ICommand::tryParseIRC(QString cmdLine) { auto& connection = mp->connection; int spaceidx = cmdLine.indexOf(' '); spaceidx = spaceidx > -1 ? spaceidx : cmdLine.length(); const QString command = cmdLine.mid(0, spaceidx).toUpper(); cmdLine.remove(0, spaceidx); using namespace Command::IRC; // For the string constants below. if (command == PASS) { CommandData argv(cmdLine, { &ICommand::prd_Message }); if (!argv[0]) print_notEnoughParameters(command); else connection.command(PASS, *argv[0]); return true; } else if (command == NICK) { CommandData argv(cmdLine, { &ICommand::prd_Message }); if (!argv[0]) print_notEnoughParameters(command); else { if (!connection.isOnline()) connection.setNickname(*argv[0]); connection.command(NICK, *argv[0]); } return true; } else if (command == OPER) { CommandData argv(cmdLine, { &ICommand::prd_AnyWord, &ICommand::prd_AnyWord }); if (!connection.isOnline()) print_notConnectedToServer(command); else if (argv.size() < 2) print_notEnoughParameters(command); else connection.command(OPER, { *argv[0], *argv[1] }); return true; } else if (command == MODE) { if (!connection.isOnline()) print_notConnectedToServer(command); else { CommandData argv(cmdLine, { &ICommand::prd_AnyWord, &ICommand::prd_Message }); connection.raw(fmt::format("{} {}", MODE, argv.joinString(' '))); } return true; } else if (command == QUIT) { CommandData argv(cmdLine, { &ICommand::prd_Message }); connection.expectDisconnect(); connection.command(QUIT, *argv[0]); return true; } else if (command == SQUIT) { CommandData argv(cmdLine, { &ICommand::prd_AnyWord, &ICommand::prd_Message }); if (!connection.isOnline()) print_notConnectedToServer(command); else if (!argv[0]) print_notEnoughParameters(command); else connection.command(SQUIT, { *argv[0] }, *argv[1]); return true; } else if (command == JOIN) { CommandData argv(cmdLine, { &ICommand::prd_AnyWord, &ICommand::prd_AnyWord }); if (!connection.isOnline()) print_notConnectedToServer(command); else if (!argv[0]) print_notEnoughParameters(command); else connection.command(JOIN, { *argv[0], argv[1].value_or("") }); return true; } else if (command == PART) { CommandData argv(cmdLine, { &ICommand::prd_AnyWord, &ICommand::prd_Message }); if (!connection.isOnline()) print_notConnectedToServer(command); else if (!argv[0]) print_notEnoughParameters(command); else connection.command(PART, { *argv[0] }, argv[1].value_or("")); return true; } else if (command == TOPIC) { CommandData argv(cmdLine, { &ICommand::prd_Switch, &ICommand::prd_AnyWord, &ICommand::prd_Message }); if (!connection.isOnline()) print_notConnectedToServer(command); else if (!argv[1]) print_notEnoughParameters(command); else { if (argv[0].value_or("") == "-c") { connection.command(TOPIC, { *argv[1] }, ""); return true; } if (argv[2]) connection.command(TOPIC, { *argv[1] }, *argv[2]); else connection.command(TOPIC, { *argv[1] }); } return true; } else if (command == NAMES) { CommandData argv(cmdLine, { &ICommand::prd_AnyWord }); if (!connection.isOnline()) print_notConnectedToServer(command); else if (!argv[0]) print_notEnoughParameters(command); else connection.command(NAMES, { *argv[0] }); return true; } else if (command == LIST) { CommandData argv(cmdLine, { &ICommand::prd_AnyWord }); if (!connection.isOnline()) print_notConnectedToServer(command); else connection.command(LIST, { argv[0].value_or("") }); return true; } else if (command == INVITE) { CommandData argv(cmdLine, { &ICommand::prd_AnyWord, &ICommand::prd_AnyWord }); if (!connection.isOnline()) print_notConnectedToServer(command); else if (!argv[0] || !argv[1]) print_notEnoughParameters(command); else connection.command(INVITE, { *argv[0], *argv[1] }); return true; } else if (command == KICK) { CommandData argv(cmdLine, { &ICommand::prd_OptionalChannel, &ICommand::prd_AnyWord, &ICommand::prd_Message }); if (!connection.isOnline()) print_notConnectedToServer(command); else if (!argv[1]) print_notEnoughParameters(command); else mp->cmd_kick(*argv[0], *argv[1], argv[2].value_or("")); return true; } else if (command == PRIVMSG) { CommandData argv(cmdLine, { &ICommand::prd_AnyWord, &ICommand::prd_Message }); if (!connection.isOnline()) print_notConnectedToServer(command); else if (!argv[0]) print_notEnoughParameters(command); else mp->cmd_privmsg(*argv[0], argv[1].value_or("")); return true; } else if (command == NOTICE) { CommandData argv(cmdLine, { &ICommand::prd_AnyWord, &ICommand::prd_Message }); if (!connection.isOnline()) print_notConnectedToServer(command); else if (!argv[0]) print_notEnoughParameters(command); else mp->cmd_notice(*argv[0], argv[1].value_or("")); return true; } else if (command == MOTD) { CommandData argv(cmdLine, { &ICommand::prd_AnyWord }); if (!connection.isOnline()) print_notConnectedToServer(command); else connection.command(MOTD, { argv[0].value_or("") }); return true; } else if (command == LUSERS) { CommandData argv(cmdLine, { &ICommand::prd_AnyWord }); if (!connection.isOnline()) print_notConnectedToServer(command); else connection.command(LUSERS, { argv[0].value_or("") }); return true; } else if (command == VERSION) { CommandData argv(cmdLine, { &ICommand::prd_AnyWord }); if (!connection.isOnline()) print_notConnectedToServer(command); else connection.command(VERSION, { argv[0].value_or("") }); return true; } else if (command == STATS) { CommandData argv(cmdLine, { &ICommand::prd_AnyWord }); if (!connection.isOnline()) print_notConnectedToServer(command); else connection.command(STATS, { argv[0].value_or("") }); return true; } else if (command == LINKS) { CommandData argv(cmdLine, { &ICommand::prd_AnyWord }); if (!connection.isOnline()) print_notConnectedToServer(command); else connection.command(LINKS, { argv[0].value_or("") }); return true; } else if (command == TIME) { CommandData argv(cmdLine, { &ICommand::prd_AnyWord }); if (!connection.isOnline()) print_notConnectedToServer(command); else connection.command(TIME, { argv[0].value_or("") }); return true; } else if (command == CONNECT) { CommandData argv(cmdLine, { &ICommand::prd_AnyWord }); if (!connection.isOnline()) print_notConnectedToServer(command); else connection.command(CONNECT, { argv[0].value_or("") }); return true; } else if (command == TRACE) { CommandData argv(cmdLine, { &ICommand::prd_Message }); if (!connection.isOnline()) print_notConnectedToServer(command); else connection.command(TRACE, { argv[0].value_or("") }); return true; } else if (command == ADMIN) { CommandData argv(cmdLine, { &ICommand::prd_AnyWord }); if (!connection.isOnline()) print_notConnectedToServer(command); else connection.command(ADMIN, { argv[0].value_or("") }); return true; } else if (command == INFO) { CommandData argv(cmdLine, { &ICommand::prd_AnyWord }); if (!connection.isOnline()) print_notConnectedToServer(command); else connection.command(INFO, { argv[0].value_or("") }); return true; } else if (command == SERVLIST) { } else if (command == SQUERY) { } else if (command == WHO) { CommandData argv(cmdLine, { &ICommand::prd_Message }); if (!connection.isOnline()) print_notConnectedToServer(command); else connection.command(WHO, { argv[0].value_or("") }); return true; } else if (command == WHOIS) { CommandData argv(cmdLine, { &ICommand::prd_AnyWord }); if (!connection.isOnline()) print_notConnectedToServer(command); else if (!argv[0]) print_notEnoughParameters(command); else connection.command(WHOIS, { *argv[0] }); return true; } else if (command == WHOWAS) { CommandData argv(cmdLine, { &ICommand::prd_AnyWord }); if (!connection.isOnline()) print_notConnectedToServer(command); else if (!argv[0]) print_notEnoughParameters(command); else connection.command(WHOWAS, { *argv[0] }); return true; } else if (command == KILL) { CommandData argv(cmdLine, { &ICommand::prd_AnyWord, &ICommand::prd_Message }); if (!connection.isOnline()) print_notConnectedToServer(command); else if (!argv[0]) { print_notEnoughParameters(command); return true; } connection.command(KILL, { *argv[0] }, argv[1].value_or("")); return true; } else if (command == PING) { } else if (command == PONG) { } else if (command == AWAY) { CommandData argv(cmdLine, { &ICommand::prd_Message }); if (!connection.isOnline()) print_notConnectedToServer(command); else if (argv[0]) connection.command(AWAY, *argv[0]); else connection.raw(AWAY); return true; } else if (command == REHASH) { } else if (command == DIE) { } else if (command == RESTART) { } else if (command == SUMMON) { } else if (command == USERS) { } else if (command == WALLOPS) { CommandData argv(cmdLine, { &ICommand::prd_Message }); if (!connection.isOnline()) print_notConnectedToServer(command); else if (!argv[0]) print_notEnoughParameters(command); else connection.command(WALLOPS, *argv[0]); return true; } else if (command == USERHOST) { } else if (command == ISON) { } return false; } bool ICommand::tryParseInternal(QString cmdLine) { auto& connection = mp->connection; auto& status = mp->status; int spaceidx = cmdLine.indexOf(' '); spaceidx = spaceidx > -1 ? spaceidx : cmdLine.length(); const QString command = cmdLine.mid(0, spaceidx).toUpper(); cmdLine.remove(0, spaceidx); using namespace Command::Internal; using namespace Command::IRC; if (command == CTCP) { CommandData argv(cmdLine, { &ICommand::prd_AnyWord, &ICommand::prd_AnyWordToUpper, &ICommand::prd_Message }); if (!connection.isOnline()) print_notConnectedToServer(command); else if (!argv[0] || !argv[1]) print_notEnoughParameters(command); else mp->cmd_ctcp(*argv[0], *argv[1], argv[2].value_or("")); return true; } else if (command == ME) { IWin* cw = status.getActiveWindow(); if (cw->getType() != IWin::Type::Channel && cw->getType() != IWin::Type::Private) { print_invalidWindowTypeForCommand(command); return true; } CommandData argv(cmdLine, { &ICommand::prd_Message }); if (!connection.isOnline()) print_notConnectedToServer(command); else if (!argv[0]) print_notEnoughParameters(command); else { const auto target = cw->getButtonText().toStdString(); mp->cmd_me(target, argv[0].value_or("")); } return true; } else if (command == ECHO) { CommandData argv(cmdLine, { &ICommand::prd_Switch, &ICommand::prd_Switch, &ICommand::prd_Message }); if (!argv[2]) { print_notEnoughParameters(command); return true; } std::string arg1, arg2; if (argv[0]) arg1 = *argv[0]; if (argv[1]) arg2 = *argv[1]; mp->cmd_echo(arg1, arg2, *argv[2]); return true; } else if (command == QUERY) { CommandData argv(cmdLine, { &ICommand::prd_AnyWord }); if (!connection.isOnline()) print_notConnectedToServer(command); else if (!argv[0]) print_notEnoughParameters(command); else status.createPrivateWindow(argv[0]->c_str()); return true; } else if (command == MUTERESP) { QStringList list = cmdLine.split(' ', QT_SPLIT_SKIP_EMPTY_PARTS); if (list.isEmpty()) { print_notEnoughParameters(command); return true; } connection.setIgnoreVerbosity(list); return true; } else if (command == UNMUTERESP) { QStringList list = cmdLine.split(' ', QT_SPLIT_SKIP_EMPTY_PARTS); if (list.isEmpty()) { print_notEnoughParameters(command); return true; } connection.unsetIgnoreVerbosity(list); return true; } else if (command == CLEAR) { auto& mdi = MdiManager::instance(); mdi.currentWindow()->clear(); return true; } return false; } void ICommand::print_notEnoughParameters(const QString& command) { const auto& mdi = MdiManager::instance(); mdi.currentStatus()->printToActive(PrintType::ProgramInfo, tr("/%1: Not enough parameters.").arg(command.toLower())); } void ICommand::print_invalidCommandSwitch(const QString& command) { const auto& mdi = MdiManager::instance(); mdi.currentStatus()->printToActive(PrintType::ProgramInfo, tr("/%1: Invalid command switch.").arg(command.toLower())); } void ICommand::print_invalidWindowTypeForCommand(const QString& command) { const auto& mdi = MdiManager::instance(); mdi.currentStatus()->printToActive(PrintType::ProgramInfo, tr("/%1: Invalid window type for that command.").arg(command.toLower())); } void ICommand::print_notConnectedToServer(const QString& command) { const auto& mdi = MdiManager::instance(); mdi.currentStatus()->printToActive(PrintType::ProgramInfo, tr("/%1: Not connected to a server.").arg(command.toLower())); } std::optional ICommand::prd_AnyWord(int& idx, const QString& line) { std::string ret; std::string stdline = line.toStdString(); for (; idx < stdline.size(); ++idx) { const char c = stdline[idx]; if (c == ' ') break; ret += c; } if (ret.empty()) return {}; return ret; } std::optional ICommand::prd_AnyWordToUpper(int& idx, const QString& line) { auto ret = prd_AnyWord(idx, line); if (ret) { std::transform(ret->begin(), ret->end(), ret->begin(), [](unsigned char c) -> unsigned char { return std::toupper(c); }); } return ret; } std::optional ICommand::prd_OptionalChannel(int& idx, const QString& line) { const auto& mdi = MdiManager::instance(); const auto& isupport = mdi.currentStatus()->getConnection().isupport(); const auto& chantypes = isupport.at("CHANTYPES"); if (chantypes.find_first_of(line.toStdString()[idx]) == std::string::npos) return std::nullopt; return prd_AnyWord(idx, line); } std::optional ICommand::prd_NotSwitch(int& idx, const QString& line) { if (line[idx] == '-' || line[idx] == '+') return std::nullopt; return prd_AnyWord(idx, line); } std::optional ICommand::prd_Switch(int& idx, const QString& line) { if (line[idx] != '-' && line[idx] != '+') return std::nullopt; return prd_AnyWord(idx, line); } std::optional ICommand::prd_Message(int& idx, const QString& line) { QString ret = line.mid(idx); if (ret.isEmpty()) return std::nullopt; return ret.toStdString(); }