X-Git-Url: https://git.netwichtig.de/gitweb/?a=blobdiff_plain;f=src%2Fmodules%2Fm_spanningtree%2Fcompat.cpp;h=0f55793f1d722fde5ded68d82b736c94f6c453df;hb=9028c48512367394af2f790b53658bc6a197f95e;hp=71272edec6b13e70e948e3136678bd60ec08852a;hpb=e2af2347fc035d702e45f12e772223a8d578410d;p=user%2Fhenk%2Fcode%2Finspircd.git diff --git a/src/modules/m_spanningtree/compat.cpp b/src/modules/m_spanningtree/compat.cpp index 71272edec..0f55793f1 100644 --- a/src/modules/m_spanningtree/compat.cpp +++ b/src/modules/m_spanningtree/compat.cpp @@ -1,89 +1,437 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ +/* + * InspIRCd -- Internet Relay Chat Daemon * - * InspIRCd: (C) 2002-2009 InspIRCd Development Team - * See: http://wiki.inspircd.org/Credits + * Copyright (C) 2009-2010 Daniel De Graaf * - * This program is free but copyrighted software; see - * the file COPYING for details. + * 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" #include "treesocket.h" +#include "treeserver.h" -static const char* const forge_common_1201[] = { - "m_chghost.so", - "m_chgname.so", - "m_remove.so", - "m_sajoin.so", - "m_sakick.so", - "m_sanick.so", - "m_sapart.so", - "m_saquit.so", - "m_setident.so", -}; - -void TreeSocket::CompatAddModules(std::vector& modlist) -{ - if (proto_version < 1202) - { - // you MUST have chgident loaded in order to be able to translate FIDENT - modlist.push_back("m_chgident.so"); - for(int i=0; i * sizeof(char*) < sizeof(forge_common_1201); i++) - { - if (ServerInstance->Modules->Find(forge_common_1201[i])) - modlist.push_back(forge_common_1201[i]); - } - } -} +static std::string newline("\n"); -void TreeSocket::WriteLine(std::string line) +void TreeSocket::WriteLine(const std::string& original_line) { if (LinkState == CONNECTED) { - if (line[0] != ':') + if (original_line.c_str()[0] != ':') { - ServerInstance->Logs->Log("m_spanningtree", DEFAULT, "Sending line without server prefix!"); - line = ":" + ServerInstance->Config->GetSID() + " " + line; + ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Sending line without server prefix!"); + WriteLine(":" + ServerInstance->Config->GetSID() + " " + original_line); + return; } if (proto_version != ProtocolVersion) { - std::string::size_type a = line.find(' ') + 1; - std::string::size_type b = line.find(' ', a); - std::string command = line.substr(a, b-a); + std::string line = original_line; + std::string::size_type a = line.find(' '); + std::string::size_type b = line.find(' ', a + 1); + std::string command = line.substr(a + 1, b-a-1); // now try to find a translation entry // TODO a more efficient lookup method will be needed later - if (proto_version < 1202 && command == "FIDENT") - { - ServerInstance->Logs->Log("m_spanningtree",DEBUG,"Rewriting FIDENT for 1201-protocol server"); - line = ":" + ServerInstance->Config->GetSID() + " CHGIDENT " + line.substr(1,a-2) + line.substr(b); - } - else if (proto_version < 1202 && command == "SAVE") + if (proto_version < 1205) { - ServerInstance->Logs->Log("m_spanningtree",DEBUG,"Rewriting SAVE for 1201-protocol server"); - std::string::size_type c = line.find(' ', b + 1); - std::string uid = line.substr(b, c - b); - line = ":" + ServerInstance->Config->GetSID() + " SVSNICK " + uid + line.substr(b); - } - else if (proto_version < 1202 && command == "AWAY") - { - if (b != std::string::npos) + if (command == "IJOIN") + { + // Convert + // : IJOIN [ []] + // to + // : FJOIN + [], + std::string::size_type c = line.find(' ', b + 1); + if (c == std::string::npos) + return; + + std::string::size_type d = line.find(' ', c + 1); + // Erase membership id first + line.erase(c, d-c); + if (d == std::string::npos) + { + // No TS or modes in the command + // :22DAAAAAB IJOIN #chan + const std::string channame = line.substr(b+1, c-b-1); + Channel* chan = ServerInstance->FindChan(channame); + if (!chan) + return; + + line.push_back(' '); + line.append(ConvToStr(chan->age)); + line.append(" + ,"); + } + else + { + d = line.find(' ', c + 1); + if (d == std::string::npos) + { + // TS present, no modes + // :22DAAAAAC IJOIN #chan 12345 + line.append(" + ,"); + } + else + { + // Both TS and modes are present + // :22DAAAAAC IJOIN #chan 12345 ov + std::string::size_type e = line.find(' ', d + 1); + if (e != std::string::npos) + line.erase(e); + + line.insert(d, " +"); + line.push_back(','); + } + } + + // Move the uuid to the end and replace the I with an F + line.append(line.substr(1, 9)); + line.erase(4, 6); + line[5] = 'F'; + } + else if (command == "RESYNC") + return; + else if (command == "METADATA") + { + // Drop TS for channel METADATA, translate METADATA operquit into an OPERQUIT command + // :sid METADATA #target TS extname ... + // A B C D + if (b == std::string::npos) + return; + + std::string::size_type c = line.find(' ', b + 1); + if (c == std::string::npos) + return; + + std::string::size_type d = line.find(' ', c + 1); + if (d == std::string::npos) + return; + + if (line[b + 1] == '#') + { + // We're sending channel metadata + line.erase(c, d-c); + } + else if (!line.compare(c, d-c, " operquit", 9)) + { + // ":22D METADATA 22DAAAAAX operquit :message" -> ":22DAAAAAX OPERQUIT :message" + line = ":" + line.substr(b+1, c-b) + "OPERQUIT" + line.substr(d); + } + } + else if (command == "FTOPIC") + { + // Drop channel TS for FTOPIC + // :sid FTOPIC #target TS TopicTS setter :newtopic + // A B C D E F + // :uid FTOPIC #target TS TopicTS :newtopic + // A B C D E + if (b == std::string::npos) + return; + + std::string::size_type c = line.find(' ', b + 1); + if (c == std::string::npos) + return; + + std::string::size_type d = line.find(' ', c + 1); + if (d == std::string::npos) + return; + + std::string::size_type e = line.find(' ', d + 1); + if (line[e+1] == ':') + { + line.erase(c, e-c); + line.erase(a+1, 1); + } + else + line.erase(c, d-c); + } + else if ((command == "PING") || (command == "PONG")) + { + // :22D PING 20D + if (line.length() < 13) + return; + + // Insert the source SID (and a space) between the command and the first parameter + line.insert(10, line.substr(1, 4)); + } + else if (command == "OPERTYPE") { + std::string::size_type colon = line.find(':', b); + if (colon != std::string::npos) + { + for (std::string::iterator i = line.begin()+colon; i != line.end(); ++i) + { + if (*i == ' ') + *i = '_'; + } + line.erase(colon, 1); + } + } + else if (command == "INVITE") + { + // :22D INVITE 22DAAAAAN #chan TS ExpirationTime + // A B C D E + if (b == std::string::npos) + return; + std::string::size_type c = line.find(' ', b + 1); - line.erase(b,c-b); + if (c == std::string::npos) + return; + + std::string::size_type d = line.find(' ', c + 1); + if (d == std::string::npos) + return; + + std::string::size_type e = line.find(' ', d + 1); + // If there is no expiration time then everything will be erased from 'd' + line.erase(d, e-d); + } + else if (command == "FJOIN") + { + // Strip membership ids + // :22D FJOIN #chan 1234 +f 4:3 :o,22DAAAAAB:15 o,22DAAAAAA:15 + // :22D FJOIN #chan 1234 +f 4:3 o,22DAAAAAB:15 + // :22D FJOIN #chan 1234 +Pf 4:3 : + + // If the last parameter is prefixed by a colon then it's a userlist which may have 0 or more users; + // if it isn't, then it is a single member + std::string::size_type spcolon = line.find(" :"); + if (spcolon != std::string::npos) + { + spcolon++; + // Loop while there is a ':' in the userlist, this is never true if the channel is empty + std::string::size_type pos = std::string::npos; + while ((pos = line.rfind(':', pos-1)) > spcolon) + { + // Find the next space after the ':' + std::string::size_type sp = line.find(' ', pos); + // Erase characters between the ':' and the next space after it, including the ':' but not the space; + // if there is no next space, everything will be erased between pos and the end of the line + line.erase(pos, sp-pos); + } + } + else + { + // Last parameter is a single member + std::string::size_type sp = line.rfind(' '); + std::string::size_type colon = line.find(':', sp); + line.erase(colon); + } + } + else if (command == "KICK") + { + // Strip membership id if the KICK has one + if (b == std::string::npos) + return; + + std::string::size_type c = line.find(' ', b + 1); + if (c == std::string::npos) + return; + + std::string::size_type d = line.find(' ', c + 1); + if ((d < line.size()-1) && (original_line[d+1] != ':')) + { + // There is a third parameter which doesn't begin with a colon, erase it + std::string::size_type e = line.find(' ', d + 1); + line.erase(d, e-d); + } + } + else if (command == "SINFO") + { + // :22D SINFO version :InspIRCd-2.2 + // A B C + std::string::size_type c = line.find(' ', b + 1); + if (c == std::string::npos) + return; + + // Only translating SINFO version, discard everything else + if (line.compare(b, 9, " version ", 9)) + return; + + line = line.substr(0, 5) + "VERSION" + line.substr(c); } } + ServerInstance->Logs->Log(MODNAME, LOG_RAWIO, "S[%d] O %s", this->GetFd(), line.c_str()); + this->WriteData(line); + this->WriteData(newline); + return; } } - ServerInstance->Logs->Log("m_spanningtree",DEBUG, "S[%d] O %s", this->GetFd(), line.c_str()); - line.append("\r\n"); - this->WriteData(line); + ServerInstance->Logs->Log(MODNAME, LOG_RAWIO, "S[%d] O %s", this->GetFd(), original_line.c_str()); + this->WriteData(original_line); + this->WriteData(newline); } +namespace +{ + bool InsertCurrentChannelTS(std::vector& params, unsigned int chanindex = 0, unsigned int pos = 1) + { + Channel* chan = ServerInstance->FindChan(params[chanindex]); + if (!chan) + return false; + // Insert the current TS of the channel after the pos-th parameter + params.insert(params.begin()+pos, ConvToStr(chan->age)); + return true; + } +} + +bool TreeSocket::PreProcessOldProtocolMessage(User*& who, std::string& cmd, std::vector& params) +{ + if ((cmd == "METADATA") && (params.size() >= 3) && (params[0][0] == '#')) + { + // :20D METADATA #channel extname :extdata + return InsertCurrentChannelTS(params); + } + else if ((cmd == "FTOPIC") && (params.size() >= 4)) + { + // :20D FTOPIC #channel 100 Attila :topic text + return InsertCurrentChannelTS(params); + } + else if ((cmd == "PING") || (cmd == "PONG")) + { + if (params.size() == 1) + { + // If it's a PING with 1 parameter, reply with a PONG now, if it's a PONG with 1 parameter (weird), do nothing + if (cmd[1] == 'I') + this->WriteData(":" + ServerInstance->Config->GetSID() + " PONG " + params[0] + newline); + // Don't process this message further + return false; + } + + // :20D PING 20D 22D + // :20D PONG 20D 22D + // Drop the first parameter + params.erase(params.begin()); + + // If the target is a server name, translate it to a SID + if (!InspIRCd::IsSID(params[0])) + { + TreeServer* server = Utils->FindServer(params[0]); + if (!server) + { + // We've no idea what this is, log and stop processing + ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Received a " + cmd + " with an unknown target: \"" + params[0] + "\", command dropped"); + return false; + } + + params[0] = server->GetID(); + } + } + else if ((cmd == "GLINE") || (cmd == "KLINE") || (cmd == "ELINE") || (cmd == "ZLINE") || (cmd == "QLINE")) + { + // Fix undocumented protocol usage: translate GLINE, ZLINE, etc. into ADDLINE or DELLINE + if ((params.size() != 1) && (params.size() != 3)) + return false; + + parameterlist p; + p.push_back(cmd.substr(0, 1)); + p.push_back(params[0]); + + if (params.size() == 3) + { + cmd = "ADDLINE"; + p.push_back(who->nick); + p.push_back(ConvToStr(ServerInstance->Time())); + p.push_back(ConvToStr(InspIRCd::Duration(params[1]))); + p.push_back(params[2]); + } + else + cmd = "DELLINE"; + + params.swap(p); + } + else if (cmd == "SVSMODE") + { + cmd = "MODE"; + } + else if (cmd == "OPERQUIT") + { + // Translate OPERQUIT into METADATA + if (params.empty()) + return false; + + cmd = "METADATA"; + params.insert(params.begin(), who->uuid); + params.insert(params.begin()+1, "operquit"); + who = MyRoot->ServerUser; + } + else if ((cmd == "TOPIC") && (params.size() >= 2)) + { + // :20DAAAAAC TOPIC #chan :new topic + cmd = "FTOPIC"; + if (!InsertCurrentChannelTS(params)) + return false; + + params.insert(params.begin()+2, ConvToStr(ServerInstance->Time())); + } + else if (cmd == "MODENOTICE") + { + // MODENOTICE is always supported by 2.0 but it's optional in 2.2. + params.insert(params.begin(), "*"); + params.insert(params.begin()+1, cmd); + cmd = "ENCAP"; + } + else if (cmd == "RULES") + { + return false; + } + else if (cmd == "INVITE") + { + // :20D INVITE 22DAAABBB #chan + // :20D INVITE 22DAAABBB #chan 123456789 + // Insert channel timestamp after the channel name; the 3rd parameter, if there, is the invite expiration time + return InsertCurrentChannelTS(params, 1, 2); + } + else if (cmd == "VERSION") + { + // :20D VERSION :InspIRCd-2.0 + // change to + // :20D SINFO version :InspIRCd-2.0 + cmd = "SINFO"; + params.insert(params.begin(), "version"); + } + else if (cmd == "JOIN") + { + // 2.0 allows and forwards legacy JOINs but we don't, so translate them to FJOINs before processing + if ((params.size() != 1) || (IS_SERVER(who))) + return false; // Huh? + + cmd = "FJOIN"; + Channel* chan = ServerInstance->FindChan(params[0]); + params.push_back(ConvToStr(chan ? chan->age : ServerInstance->Time())); + params.push_back("+"); + params.push_back(","); + params.back().append(who->uuid); + who = TreeServer::Get(who)->ServerUser; + } + else if ((cmd == "FMODE") && (params.size() >= 2)) + { + // Translate user mode changes with timestamp to MODE + if (params[0][0] != '#') + { + User* user = ServerInstance->FindUUID(params[0]); + if (!user) + return false; + + // Emulate the old nonsensical behavior + if (user->age < ServerCommand::ExtractTS(params[1])) + return false; + + cmd = "MODE"; + params.erase(params.begin()+1); + } + } + + return true; // Passthru +}