/* * InspIRCd -- Internet Relay Chat Daemon * * Copyright (C) 2021, 2024 Sadie Powell * * This file is part of InspIRCd. InspIRCd is free software: you can * redistribute it and/or modify it under the terms of the GNU General Public * License as published by the Free Software Foundation, version 2. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "inspircd.h" #include "main.h" namespace { size_t NextToken(const std::string& line, size_t start) { if (start == std::string::npos || start + 1 > line.length()) return std::string::npos; return line.find(' ', start + 1); } } bool TreeSocket::PreProcessNewProtocolMessage(User*& who, std::string& cmd, CommandBase::Params& params) { if (proto_version == PROTO_INSPIRCD_4) { if (insp::casemapped_equals(cmd, "ENCAP")) { if (params.size() < 2) return false; // Malformed. // : ENCAP [...]; CommandBase::Params newparams(params.begin() + 2, params.end()); if (!PreProcessNewProtocolMessage(who, params[1], newparams)) return false; // Malformed. params.erase(params.begin() + 2, params.end()); params.insert(params.end(), newparams.begin(), newparams.end()); } else if (insp::casemapped_equals(cmd, "FAIL") || insp::casemapped_equals(cmd, "WARN") || insp::casemapped_equals(cmd, "NOTE")) { // [...] : if (params.size() < 5) return false; // Malformed. // InspIRCd v5 introduced networking of standard replies. For v4 and earlier fall back // to a notice from the server. cmd = "NOTICE"; const auto* server = Utils->FindServerID(params[0]); who = server ? server->ServerUser : this->MyRoot->ServerUser; const auto has_command = params[2] != "*"; CommandBase::Params newparams; newparams.push_back(params[1]); newparams.push_back(FMT::format("*** {}{}{}", has_command ? params[2] : "", has_command ? ": " : "", params.back() )); std::swap(params, newparams); } else if (insp::casemapped_equals(cmd, "FJOIN")) { // : FJOIN :[],:/ [],:/ // ^^^^^^^^^ New in 1207 if (params.size() < 4) return false; // Malformed size_t pos = 0; while (pos != std::string::npos) { auto next = NextToken(params[3], pos); auto slash = params[3].find('/', pos); if (slash != std::string::npos) params[3].erase(slash, next - slash); pos = next; } } else if (insp::casemapped_equals(cmd, "IJOIN")) { if (params.size() < 3) return false; // Malformed. // : IJOIN [ ] // ^^^^^^^^ New in 1207 params.erase(params.begin() + 2); } else if (insp::casemapped_equals(cmd, "SETHOST") || insp::casemapped_equals(cmd, "SETIDENT") || insp::casemapped_equals(cmd, "SETNAME")) { // CHG* was merged with SET* in v5. Rewrite to the old commands. cmd.replace(0, 3, "CHG"); } else if (insp::casemapped_equals(cmd, "SETIDLE")) return false; // SETIDLE can't be networked in v4. } return true; } bool TreeSocket::PreProcessOldProtocolMessage(User*& who, std::string& cmd, CommandBase::Params& params) { if (insp::casemapped_equals(cmd, "CHGHOST") || insp::casemapped_equals(cmd, "CHGIDENT") || insp::casemapped_equals(cmd, "CHGNAME")) { if (params.size() < 2) return false; // Malformed. // CHG* and SET* were merged in v5. cmd.replace(0, 3, "SET"); } else if (insp::casemapped_equals(cmd, "ENCAP")) { if (params.size() < 2) return false; // Malformed. // : ENCAP [...]; CommandBase::Params newparams(params.begin() + 2, params.end()); if (!PreProcessOldProtocolMessage(who, params[1], newparams)) return false; // Malformed. params.erase(params.begin() + 2, params.end()); params.insert(params.end(), newparams.begin(), newparams.end()); } else if (insp::casemapped_equals(cmd, "IJOIN")) { if (params.size() < 3) return false; // Malformed. // : IJOIN [ ] // ^^^^^^^^ New in 1207 params.insert(params.begin() + 2, "0"); } return true; }