/* * InspIRCd -- Internet Relay Chat Daemon * * Copyright (C) 2018 Matt Schatz * Copyright (C) 2013-2016 Attila Molnar * Copyright (C) 2013, 2018-2019 Sadie Powell * Copyright (C) 2012 Robby * Copyright (C) 2010 Craig Edwards * Copyright (C) 2009-2010 Daniel De Graaf * * 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 std::string newline("\n"); void TreeSocket::WriteLineNoCompat(const std::string& line) { ServerInstance->Logs->Log(MODNAME, LOG_RAWIO, "S[%d] O %s", this->GetFd(), line.c_str()); this->WriteData(line); this->WriteData(newline); } void TreeSocket::WriteLine(const std::string& original_line) { if (LinkState == CONNECTED) { if (proto_version != PROTO_NEWEST) { std::string line = original_line; std::string::size_type a = line.find(' '); if (line[0] == '@') { // The line contains tags which the 1202 protocol can't handle. line.erase(0, a + 1); a = line.find(' '); } std::string::size_type b = line.find(' ', a + 1); std::string command(line, a + 1, b-a-1); // now try to find a translation entry if (proto_version < PROTO_INSPIRCD_30) { 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, 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); 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-3.0 // 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); } else if (command == "SERVER") { // :001 SERVER inspircd.test 002 [ ...] :description // A B C std::string::size_type c = line.find(' ', b + 1); if (c == std::string::npos) return; std::string::size_type d = c + 4; std::string::size_type spcolon = line.find(" :", d); if (spcolon == std::string::npos) return; line.erase(d, spcolon-d); line.insert(c, " * 0"); if (burstsent) { WriteLineNoCompat(line); // Synthesize a : BURST