X-Git-Url: https://git.netwichtig.de/gitweb/?a=blobdiff_plain;f=src%2Fmodules%2Fm_spanningtree%2Fcompat.cpp;h=0f55793f1d722fde5ded68d82b736c94f6c453df;hb=1d020d132ff6d05ffd865d025cf161d7637f10d8;hp=433da98470ccd4685769f2689948dd0abcc762da;hpb=2f17664ea13fe8f0983080d69886a46b58e5e266;p=user%2Fhenk%2Fcode%2Finspircd.git diff --git a/src/modules/m_spanningtree/compat.cpp b/src/modules/m_spanningtree/compat.cpp index 433da9847..0f55793f1 100644 --- a/src/modules/m_spanningtree/compat.cpp +++ b/src/modules/m_spanningtree/compat.cpp @@ -24,17 +24,19 @@ 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", LOG_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 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); @@ -45,11 +47,17 @@ void TreeSocket::WriteLine(std::string line) if (command == "IJOIN") { // Convert - // : IJOIN [ []] + // : 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 @@ -64,7 +72,7 @@ void TreeSocket::WriteLine(std::string line) } else { - std::string::size_type d = line.find(' ', c + 1); + d = line.find(' ', c + 1); if (d == std::string::npos) { // TS present, no modes @@ -93,7 +101,7 @@ void TreeSocket::WriteLine(std::string line) return; else if (command == "METADATA") { - // Drop TS for channel 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) @@ -103,21 +111,28 @@ void TreeSocket::WriteLine(std::string line) 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 - std::string::size_type d = line.find(' ', c + 1); - if (d == std::string::npos) - return; - 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 ... - // A B C D + // :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; @@ -129,7 +144,14 @@ void TreeSocket::WriteLine(std::string line) if (d == std::string::npos) return; - line.erase(c, d-c); + 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")) { @@ -140,32 +162,132 @@ void TreeSocket::WriteLine(std::string line) // 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); + 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", LOG_RAWIO, "S[%d] O %s", this->GetFd(), line.c_str()); - 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) + bool InsertCurrentChannelTS(std::vector& params, unsigned int chanindex = 0, unsigned int pos = 1) { - Channel* chan = ServerInstance->FindChan(params[0]); + Channel* chan = ServerInstance->FindChan(params[chanindex]); if (!chan) return false; - // Insert the current TS of the channel between the first and the second parameters - params.insert(params.begin()+1, ConvToStr(chan->age)); + // 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)) + if ((cmd == "METADATA") && (params.size() >= 3) && (params[0][0] == '#')) { // :20D METADATA #channel extname :extdata return InsertCurrentChannelTS(params); @@ -199,13 +321,117 @@ bool TreeSocket::PreProcessOldProtocolMessage(User*& who, std::string& cmd, std: if (!server) { // We've no idea what this is, log and stop processing - ServerInstance->Logs->Log("m_spanningtree", LOG_DEFAULT, "Received a " + cmd + " with an unknown target: \"" + params[0] + "\", command dropped"); + 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 }