diff --git a/Makefile.in b/Makefile.in index f8b2560e..871f4d39 100644 --- a/Makefile.in +++ b/Makefile.in @@ -601,8 +601,8 @@ am__libcservice_la_SOURCES_DIST = mod.cservice/banMatcher.cc \ mod.cservice/SUSPENDMECommand.cc mod.cservice/TOPICCommand.cc \ mod.cservice/UNBANCommand.cc mod.cservice/UNFORCECommand.cc \ mod.cservice/UNSUSPENDCommand.cc mod.cservice/VERIFYCommand.cc \ - mod.cservice/VOICECommand.cc mod.cservice/WHITELISTCommand.cc \ - mod.cservice/MODECommand.cc + mod.cservice/VOICECommand.cc mod.cservice/WCommand.cc \ + mod.cservice/WHITELISTCommand.cc mod.cservice/MODECommand.cc @COND_MODCSERVICE_TRUE@am_libcservice_la_OBJECTS = mod.cservice/libcservice_la-banMatcher.lo \ @COND_MODCSERVICE_TRUE@ mod.cservice/libcservice_la-cservice.lo \ @COND_MODCSERVICE_TRUE@ mod.cservice/libcservice_la-cservice_crypt.lo \ @@ -670,6 +670,7 @@ am__libcservice_la_SOURCES_DIST = mod.cservice/banMatcher.cc \ @COND_MODCSERVICE_TRUE@ mod.cservice/libcservice_la-UNSUSPENDCommand.lo \ @COND_MODCSERVICE_TRUE@ mod.cservice/libcservice_la-VERIFYCommand.lo \ @COND_MODCSERVICE_TRUE@ mod.cservice/libcservice_la-VOICECommand.lo \ +@COND_MODCSERVICE_TRUE@ mod.cservice/libcservice_la-WCommand.lo \ @COND_MODCSERVICE_TRUE@ mod.cservice/libcservice_la-WHITELISTCommand.lo \ @COND_MODCSERVICE_TRUE@ mod.cservice/libcservice_la-MODECommand.lo libcservice_la_OBJECTS = $(am_libcservice_la_OBJECTS) @@ -1180,6 +1181,7 @@ am__depfiles_remade = db/$(DEPDIR)/libgnuworldDB_la-gnuworldDB.Plo \ mod.cservice/$(DEPDIR)/libcservice_la-UNSUSPENDCommand.Plo \ mod.cservice/$(DEPDIR)/libcservice_la-VERIFYCommand.Plo \ mod.cservice/$(DEPDIR)/libcservice_la-VOICECommand.Plo \ + mod.cservice/$(DEPDIR)/libcservice_la-WCommand.Plo \ mod.cservice/$(DEPDIR)/libcservice_la-WHITELISTCommand.Plo \ mod.cservice/$(DEPDIR)/libcservice_la-banMatcher.Plo \ mod.cservice/$(DEPDIR)/libcservice_la-csGline.Plo \ @@ -2060,6 +2062,7 @@ gnuworld_LDADD = libgnuworld.la libgnuworldcore.la $(LIBLTDL) @COND_MODCSERVICE_TRUE@ mod.cservice/UNSUSPENDCommand.cc \ @COND_MODCSERVICE_TRUE@ mod.cservice/VERIFYCommand.cc \ @COND_MODCSERVICE_TRUE@ mod.cservice/VOICECommand.cc \ +@COND_MODCSERVICE_TRUE@ mod.cservice/WCommand.cc \ @COND_MODCSERVICE_TRUE@ mod.cservice/WHITELISTCommand.cc \ @COND_MODCSERVICE_TRUE@ mod.cservice/MODECommand.cc @@ -2956,6 +2959,8 @@ mod.cservice/libcservice_la-VERIFYCommand.lo: \ mod.cservice/libcservice_la-VOICECommand.lo: \ mod.cservice/$(am__dirstamp) \ mod.cservice/$(DEPDIR)/$(am__dirstamp) +mod.cservice/libcservice_la-WCommand.lo: mod.cservice/$(am__dirstamp) \ + mod.cservice/$(DEPDIR)/$(am__dirstamp) mod.cservice/libcservice_la-WHITELISTCommand.lo: \ mod.cservice/$(am__dirstamp) \ mod.cservice/$(DEPDIR)/$(am__dirstamp) @@ -3665,6 +3670,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@mod.cservice/$(DEPDIR)/libcservice_la-UNSUSPENDCommand.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@mod.cservice/$(DEPDIR)/libcservice_la-VERIFYCommand.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@mod.cservice/$(DEPDIR)/libcservice_la-VOICECommand.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@mod.cservice/$(DEPDIR)/libcservice_la-WCommand.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@mod.cservice/$(DEPDIR)/libcservice_la-WHITELISTCommand.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@mod.cservice/$(DEPDIR)/libcservice_la-banMatcher.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@mod.cservice/$(DEPDIR)/libcservice_la-csGline.Plo@am__quote@ # am--include-marker @@ -5197,6 +5203,13 @@ mod.cservice/libcservice_la-VOICECommand.lo: mod.cservice/VOICECommand.cc @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcservice_la_CXXFLAGS) $(CXXFLAGS) -c -o mod.cservice/libcservice_la-VOICECommand.lo `test -f 'mod.cservice/VOICECommand.cc' || echo '$(srcdir)/'`mod.cservice/VOICECommand.cc +mod.cservice/libcservice_la-WCommand.lo: mod.cservice/WCommand.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcservice_la_CXXFLAGS) $(CXXFLAGS) -MT mod.cservice/libcservice_la-WCommand.lo -MD -MP -MF mod.cservice/$(DEPDIR)/libcservice_la-WCommand.Tpo -c -o mod.cservice/libcservice_la-WCommand.lo `test -f 'mod.cservice/WCommand.cc' || echo '$(srcdir)/'`mod.cservice/WCommand.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) mod.cservice/$(DEPDIR)/libcservice_la-WCommand.Tpo mod.cservice/$(DEPDIR)/libcservice_la-WCommand.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='mod.cservice/WCommand.cc' object='mod.cservice/libcservice_la-WCommand.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcservice_la_CXXFLAGS) $(CXXFLAGS) -c -o mod.cservice/libcservice_la-WCommand.lo `test -f 'mod.cservice/WCommand.cc' || echo '$(srcdir)/'`mod.cservice/WCommand.cc + mod.cservice/libcservice_la-WHITELISTCommand.lo: mod.cservice/WHITELISTCommand.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcservice_la_CXXFLAGS) $(CXXFLAGS) -MT mod.cservice/libcservice_la-WHITELISTCommand.lo -MD -MP -MF mod.cservice/$(DEPDIR)/libcservice_la-WHITELISTCommand.Tpo -c -o mod.cservice/libcservice_la-WHITELISTCommand.lo `test -f 'mod.cservice/WHITELISTCommand.cc' || echo '$(srcdir)/'`mod.cservice/WHITELISTCommand.cc @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) mod.cservice/$(DEPDIR)/libcservice_la-WHITELISTCommand.Tpo mod.cservice/$(DEPDIR)/libcservice_la-WHITELISTCommand.Plo @@ -6853,6 +6866,7 @@ distclean: distclean-recursive -rm -f mod.cservice/$(DEPDIR)/libcservice_la-UNSUSPENDCommand.Plo -rm -f mod.cservice/$(DEPDIR)/libcservice_la-VERIFYCommand.Plo -rm -f mod.cservice/$(DEPDIR)/libcservice_la-VOICECommand.Plo + -rm -f mod.cservice/$(DEPDIR)/libcservice_la-WCommand.Plo -rm -f mod.cservice/$(DEPDIR)/libcservice_la-WHITELISTCommand.Plo -rm -f mod.cservice/$(DEPDIR)/libcservice_la-banMatcher.Plo -rm -f mod.cservice/$(DEPDIR)/libcservice_la-csGline.Plo @@ -7230,6 +7244,7 @@ maintainer-clean: maintainer-clean-recursive -rm -f mod.cservice/$(DEPDIR)/libcservice_la-UNSUSPENDCommand.Plo -rm -f mod.cservice/$(DEPDIR)/libcservice_la-VERIFYCommand.Plo -rm -f mod.cservice/$(DEPDIR)/libcservice_la-VOICECommand.Plo + -rm -f mod.cservice/$(DEPDIR)/libcservice_la-WCommand.Plo -rm -f mod.cservice/$(DEPDIR)/libcservice_la-WHITELISTCommand.Plo -rm -f mod.cservice/$(DEPDIR)/libcservice_la-banMatcher.Plo -rm -f mod.cservice/$(DEPDIR)/libcservice_la-csGline.Plo diff --git a/bin/cservice.example.conf b/bin/cservice.example.conf index 0a73ff2b..5829c1a1 100644 --- a/bin/cservice.example.conf +++ b/bin/cservice.example.conf @@ -400,6 +400,14 @@ max_fingerprints = 10 # Timeout for a SASL authentication session (in seconds) sasl_timeout = 30 +# +# This is used if X/W (2002 edition) is linked to the network. +# This feature was brought during the 30th anniversary of CService in 2025. +# To enable this feature, define THERETURN_ENABLED in cservice_config.h +# +w_server = channels2.gnuworld.org +w_nick = W + # # Optional settings to enable Pushover notifications. # To use this, you need to create an application on Pushover @@ -420,4 +428,4 @@ pushover_enable = no prometheus_enable = no # prometheus_ip = 127.0.0.1 -# prometheus_port = 9091 \ No newline at end of file +# prometheus_port = 9091 diff --git a/libgnuworld/match.cc b/libgnuworld/match.cc index d478d1f0..9cd3ef45 100644 --- a/libgnuworld/match.cc +++ b/libgnuworld/match.cc @@ -1054,7 +1054,7 @@ int mmexec(const char* wcm, int wminlen, const char* rcm, int rminlen) { // return 1; // // /* If passed, we deal only with the hostip part, and because match() handles cidr -//addresses too +// addresses too // * our matching is complete. // */ // if (match(extractHostIP(mask), extractHostIP(address)) == 0) diff --git a/mod.ccontrol/LIMITSCommand.cc b/mod.ccontrol/LIMITSCommand.cc index d0b81318..ba863ca6 100644 --- a/mod.ccontrol/LIMITSCommand.cc +++ b/mod.ccontrol/LIMITSCommand.cc @@ -75,7 +75,7 @@ bool LIMITSCommand::Exec(iClient* theClient, const string& Message) { "SYNTAX: ADDISP [abuse email]"); // bot->Notice(theClient, "-f: forcecount - use it if you want limits to still be // enforced even if the netblocks" " of this isp are not the closest match for a - //client"); bot->Notice(theClient, "-i: inactive - deactivates glines and does not + // client"); bot->Notice(theClient, "-i: inactive - deactivates glines and does not // prevent new clients from connecting using iauth. It does reports only."); return true; } diff --git a/mod.ccontrol/ccontrol.cc b/mod.ccontrol/ccontrol.cc index f5b16a61..c64777c0 100644 --- a/mod.ccontrol/ccontrol.cc +++ b/mod.ccontrol/ccontrol.cc @@ -909,7 +909,8 @@ void ccontrol::OnServerMessage(iServer* Server, const string& Message, bool) { tmpServer->setLagTime(lagTime); tmpServer->setLastLagRecv(::time(0)); // if ((lagTime > MAX_LAG_TIME) && ((::time(0) - tmpServer->getLastLagReport()) > - // LAG_REPORT_INTERVAL)) { tmpServer->setLastLagReport(::time(0)); MsgChanLag("[lag] %s is + // LAG_REPORT_INTERVAL)) { tmpServer->setLastLagReport(::time(0)); + // MsgChanLag("[lag] %s is //%ds lagged", Server->getName().c_str(), (int) (lagTime / 1000)); // } } diff --git a/mod.cservice/Makefile.am b/mod.cservice/Makefile.am index 587e0fdc..9f30adb6 100755 --- a/mod.cservice/Makefile.am +++ b/mod.cservice/Makefile.am @@ -76,6 +76,7 @@ libcservice_la_SOURCES = \ mod.cservice/UNSUSPENDCommand.cc \ mod.cservice/VERIFYCommand.cc \ mod.cservice/VOICECommand.cc \ + mod.cservice/WCommand.cc \ mod.cservice/WHITELISTCommand.cc \ mod.cservice/MODECommand.cc diff --git a/mod.cservice/PURGECommand.cc b/mod.cservice/PURGECommand.cc index 2b46de55..0765b7c4 100644 --- a/mod.cservice/PURGECommand.cc +++ b/mod.cservice/PURGECommand.cc @@ -97,6 +97,15 @@ bool PURGECommand::Exec(iClient* theClient, const string& Message) { return false; } +#ifdef THERETURN_ENABLED + /* Don't purge a channel registered with W. */ + if (theChan->hasW()) { + bot->Notice(theClient, "This channel is registered with %s. Use /msg X W PURGE %s first", + bot->getConfwNickName().c_str(), theChan->getName().c_str()); + return false; + } +#endif + bool reop; string reason; diff --git a/mod.cservice/STATUSCommand.cc b/mod.cservice/STATUSCommand.cc index ed8d64e5..15e658c8 100644 --- a/mod.cservice/STATUSCommand.cc +++ b/mod.cservice/STATUSCommand.cc @@ -129,6 +129,19 @@ bool STATUSCommand::Exec(iClient* theClient, const string& Message) { bot->Notice(theClient, "I'm \002not\002 in this channel."); } +#ifdef THERETURN_ENABLED + if (theChan->hasW()) { + bot->Notice(theClient, "The channel is also registered with %s.", + bot->getConfwNickName().c_str()); + } + + if (!theChan->hasW() && theChan->getWTS() > 0 && admLevel > 500) { + bot->Notice(theClient, "The channel was purged with %s on %s", + bot->getConfwNickName().c_str(), + prettyTime(theChan->getWTS()).c_str()); + } +#endif + bot->Notice(theClient, "MassDeopPro: %i", theChan->getMassDeopPro()); string flagsSet; diff --git a/mod.cservice/WCommand.cc b/mod.cservice/WCommand.cc new file mode 100644 index 00000000..ce8ce203 --- /dev/null +++ b/mod.cservice/WCommand.cc @@ -0,0 +1,271 @@ +/** + * WCommand.cc + * + * This program 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; either version 2 + * of the License, or (at your option) any later version. + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + * + */ + +#include +#include + +#include "cservice.h" +#include "StringTokenizer.h" +#include "Network.h" +#include "ELog.h" +#include "responses.h" + +#ifdef THERETURN_ENABLED + +namespace gnuworld { +using std::endl; +using std::string; +using std::stringstream; + +bool WCommand::Exec(iClient* theClient, const string& Message) { + bot->incStat("COMMANDS.W"); + + /* Check if the user is authed. */ + sqlUser* theUser = bot->isAuthed(theClient, true); + if (!theUser) { + return false; + } + + /* Get admin access level. */ + unsigned int admLevel = bot->getAdminAccessLevel(theUser); + + int confLevel = bot->getConfigVar("THERETURN_INTEGRATION")->asInt(); + /* If config is 0, we stay silent. */ + if (confLevel == 0 && admLevel < 750) { + return false; + } + + /* if config is 1, we notify the user that W is currently unavailable. */ + else if (confLevel == 1 && admLevel < 1000) { + bot->Notice(theClient, "%s is currently unavailable.", bot->getConfwNickName().c_str()); + return false; + } + + StringTokenizer st(Message); + if (st.size() < 3) { + Usage(theClient); + return false; + } + + /* Check if the channel is registered with X. */ + sqlChannel* theChan = bot->getChannelRecord(st[2]); + if (!theChan || theChan->getName() == "*") { + bot->Notice(theClient, bot->getResponse(theUser, language::chan_not_reg).c_str(), + st[2].c_str()); + return false; + } + /* We don't process channels with a length of more than 45 characters. W is old, remember. */ + if (theChan->getName().length() > 45) { + bot->Notice(theClient, + "Apologise, but %s don't accept channels with more than 45 characters.", + bot->getConfwNickName().c_str()); + return false; + } + + /* Check that W is available. */ + iServer* wServer = Network->findServerName(bot->getConfwServerName()); + iClient* wClient = Network->findNick(bot->getConfwNickName()); + if (!wServer || !wClient) { + bot->Notice(theClient, "%s is unavailable. Try again later.", + bot->getConfwNickName().c_str()); + return false; + } + + Channel* tmpChan = Network->findChannel(theChan->getName()); + if (!tmpChan) { + bot->Notice(theClient, bot->getResponse(theUser, language::chan_is_empty).c_str(), + theChan->getName().c_str()); + return false; + } + + ChannelUser* wChanUser = tmpChan->findUser(wClient); + unsigned int level = bot->getEffectiveAccessLevel(theUser, theChan, true); + string Command = string_upper(st[1]); + + /* Registration of channel. */ + if (Command == "JOIN") { + /* Is the channel already registered? Order W to re-join. */ + if (theChan->hasW()) { + /* Check access level. */ + if (level < 450) { + bot->Notice(theClient, bot->getResponse(theUser, language::insuf_access).c_str(), + theChan->getName().c_str()); + return false; + } + + /* Is W on the channel? */ + if (wChanUser) { + bot->Notice(theClient, "%s is already on that channel.", + bot->getConfwNickName().c_str()); + return false; + } + + /* We send a XQ to W. The callback will handle the rest. */ + bot->logPrivAdminMessage("%s (%s) has initiated a re-join of %s to %s", + theClient->getNickName().c_str(), + theClient->getAccount().c_str(), + wClient->getNickName().c_str(), theChan->getName().c_str()); + + stringstream xQuery; + xQuery << bot->getCharYY() << " XQ " << wServer->getCharYY() + << " AnyCServiceRouting :JOIN " << theChan->getName() << " " + << theClient->getCharYYXXX() << endl; + + bot->Write(xQuery); + return true; + } else { + /* Check access level. */ + if (level < 500) { + bot->Notice(theClient, bot->getResponse(theUser, language::insuf_access).c_str(), + theChan->getName().c_str()); + return false; + } + + /* We don't accept this command through FORCE. */ + if (bot->isForced(theChan, theUser)) { + bot->Notice(theClient, "Channel registration in %s cannot be done through FORCE.", + bot->getConfwNickName().c_str()); + return false; + } + + /* We send a XQ to W. The callback will handle the rest. */ + bot->logPrivAdminMessage("%s (%s) has initiated %s-registration of %s", + theClient->getNickName().c_str(), + theClient->getAccount().c_str(), + wClient->getNickName().c_str(), theChan->getName().c_str()); + + stringstream xQuery; + xQuery << bot->getCharYY() << " XQ " << wServer->getCharYY() + << " AnyCServiceRouting :REG " << theChan->getName() << " " + << theClient->getCharYYXXX() << endl; + + bot->Write(xQuery); + return true; + } + } else if (Command == "PURGE") { + if (level < 500) { + bot->Notice(theClient, bot->getResponse(theUser, language::insuf_access).c_str(), + theChan->getName().c_str()); + return false; + } + + /* Is the channel registered? */ + if (!theChan->hasW()) { + bot->Notice(theClient, "This channel is not registered with %s", + wClient->getNickName().c_str()); + return false; + } + + /* We send a XQ to W. The callback will handle the rest. */ + bot->logPrivAdminMessage("%s (%s) has initiated %s-purge of %s", + theClient->getNickName().c_str(), theClient->getAccount().c_str(), + wClient->getNickName().c_str(), theChan->getName().c_str()); + + stringstream xQuery; + xQuery << bot->getCharYY() << " XQ " << wServer->getCharYY() + << " AnyCServiceRouting :PURGE " << theChan->getName() << " " + << theClient->getCharYYXXX() << endl; + + bot->Write(xQuery); + + return true; + } else if (Command == "PART") { + if (level < 450) { + bot->Notice(theClient, bot->getResponse(theUser, language::insuf_access).c_str(), + st[1].c_str()); + return false; + } + + /* Is the channel registered? */ + if (!theChan->hasW()) { + bot->Notice(theClient, "This channel is not registered with %s", + wClient->getNickName().c_str()); + return false; + } + + /* Is W on the channel? */ + if (!wChanUser) { + bot->Notice(theClient, bot->getResponse(theUser, language::cant_find_on_chan).c_str(), + wClient->getNickName().c_str()); + return false; + } + + /* We send a XQ to W. The callback will handle the rest. */ + bot->logPrivAdminMessage("%s (%s) has initiated %s-part of %s", + theClient->getNickName().c_str(), theClient->getAccount().c_str(), + wClient->getNickName().c_str(), theChan->getName().c_str()); + + if (theChan->getFlag(sqlChannel::F_OPLOG)) + bot->NoticeChannelOps(theChan->getName(), "%s has requested that %s parts the channel", + wClient->getNickName().c_str(), theClient->getNickName().c_str()); + + stringstream xQuery; + xQuery << bot->getCharYY() << " XQ " << wServer->getCharYY() << " AnyCServiceRouting :PART " + << theChan->getName() << " " << theClient->getCharYYXXX() << endl; + + bot->Write(xQuery); + + return true; + } else if (Command == "PURGE") { + /* Admin? */ + if (admLevel < 750) { + bot->Notice( + theClient, + bot->getResponse(theUser, language::insuf_access, + string("You have insufficient access to perform that command."))); + return false; + } + + /* Is the channel registered? */ + if (!theChan->hasW()) { + bot->Notice(theClient, "%s is not registered with %s", theChan->getName().c_str(), + wClient->getNickName().c_str()); + return false; + } + + if (st.size() > 3 && st[3] == "-f") { + theChan->setW(false); + bot->Notice(theClient, + "%s has been flagged as not registered with %s. I have NOT purged the " + "channel with %s and this will no longer be possible through X.", + theChan->getName().c_str(), wClient->getNickName().c_str(), + wClient->getNickName().c_str()); + return true; + } else { + bot->Notice(theClient, + "%s is flagged as registered with %s in my records. To manually update my " + "records, use /msg X W PURGE %s -f to force unregistration. This should " + "ONLY be used if %s is already purged with %s", + theChan->getName().c_str(), wClient->getNickName().c_str(), + theChan->getName().c_str(), theChan->getName().c_str(), + wClient->getNickName().c_str()); + return true; + } + } else { + Usage(theClient); + return false; + } + + return true; +} + +} // namespace gnuworld + +#endif // THERETURN_ENABLED diff --git a/mod.cservice/constants.h b/mod.cservice/constants.h index 05530426..67a3aa24 100644 --- a/mod.cservice/constants.h +++ b/mod.cservice/constants.h @@ -38,8 +38,8 @@ namespace sql { const std::string channel_fields = "id,name,flags,mass_deop_pro,flood_pro,url,channels.description,comment,keywords,registered_ts," "channel_ts,channel_mode,userflags,channels.last_updated,limit_offset,limit_period,limit_grace," - "limit_max,max_bans,no_take,welcome,limit_joinmax,limit_joinsecs,limit_joinperiod,limit_" - "joinmode"; + "limit_max,max_bans,no_take,welcome,limit_joinmax,limit_joinsecs,limit_joinperiod," + "limit_joinmode"; const std::string user_fields = "users.id,users.user_name,users.password,users.url,users.language_id,users.flags,users.last_" "updated_by,users.last_updated,users.signup_ts,users.email,users.maxlogins,users." diff --git a/mod.cservice/cservice.cc b/mod.cservice/cservice.cc index c236c9b4..9c00c548 100644 --- a/mod.cservice/cservice.cc +++ b/mod.cservice/cservice.cc @@ -213,6 +213,9 @@ cservice::cservice(const string& args) this, "NOTES", "send , read all, erase ", 10)); #ifdef NEW_IRCU_FEATURES RegisterCommand(new CERTCommand(this, "CERT", " [fingerprint] [note]", 10)); +#endif +#ifdef THERETURN_ENABLED + RegisterCommand(new WCommand(this, "W", "[join | part | purge] <#channel>", 10)); #endif RegisterCommand(new OPCommand(this, "OP", "<#channel> [nick] [nick] ..", 3)); RegisterCommand(new DEOPCommand(this, "DEOP", "<#channel> [nick] [nick] ..", 3)); @@ -2688,11 +2691,17 @@ struct newChanData { void cservice::updateChannels() { stringstream theQuery; - theQuery << "SELECT " << sql::channel_fields - << ",pending.manager_id as mngr_id,date_part('epoch', CURRENT_TIMESTAMP)::int as " + theQuery << "SELECT " << sql::channel_fields; +#ifdef THERETURN_ENABLED + theQuery << ",cw.status,cw.timestamp"; +#endif + theQuery << ",pending.manager_id as mngr_id,date_part('epoch', CURRENT_TIMESTAMP)::int as " "db_unixtime FROM " - << "channels LEFT JOIN pending ON channels.id = pending.channel_id " - << "WHERE channels.last_updated >= " << lastChannelRefresh + << "channels LEFT JOIN pending ON channels.id = pending.channel_id "; +#ifdef THERETURN_ENABLED + theQuery << "LEFT JOIN channels_w cw on channels.id = cw.channel_id "; +#endif + theQuery << "WHERE channels.last_updated >= " << lastChannelRefresh << " AND channels.registered_ts <> 0"; if (!SQLDb->Exec(theQuery, true)) { @@ -3848,7 +3857,7 @@ void cservice::checkNewIncomings() { unsigned int pendingTime = SupportDays * JudgeDaySeconds; // currentTime() stringstream theQuery; // theQuery << "SELECT channels.name,channels.id,manager_id,users.user_name FROM - //channels,pending,users WHERE channels.id = pending.channel_id " + // channels,pending,users WHERE channels.id = pending.channel_id " theQuery << "SELECT channels.name,channels.id,users.user_name FROM channels,pending,users " "WHERE channels.id = pending.channel_id " << "AND pending.status = 0 AND (pending.created_ts + " << pendingTime @@ -4493,6 +4502,11 @@ void cservice::OnEvent(const eventType& theEvent, void* data1, void* data2, void // Process the channel OPLIST data from mod.openchanfix doXROplist(theServer, Routing, Message); } +#ifdef THERETURN_ENABLED + if (Command == "REG" || Command == "PURGE" || Command == "PART" || Command == "JOIN") { + doXRW(theServer, Command, Message); + } +#endif // THERETURN_ENABLED break; } case EVT_ACCOUNT: { @@ -6621,7 +6635,7 @@ void cservice::checkDbConnectionStatus() { // TODO: Is this ok? SQLDb->Exec("LISTEN channels_u; LISTEN users_u; LISTEN levels_u;"); // SQLDb->ExecCommandOk("LISTEN channels_u; LISTEN users_u; - //LISTEN levels_u;"); + // LISTEN levels_u;"); logAdminMessage("Successfully reconnected to database server. Panic over ;)"); connectRetries = 0; } @@ -6630,14 +6644,19 @@ void cservice::checkDbConnectionStatus() { void cservice::preloadChannelCache() { stringstream theQuery; - theQuery << "SELECT " << sql::channel_fields << " FROM channels" - << " WHERE registered_ts <> 0" << ends; + theQuery << "SELECT " << sql::channel_fields; +#ifdef THERETURN_ENABLED + theQuery << ",cw.status,cw.timestamp"; +#endif + theQuery << " FROM channels "; +#ifdef THERETURN_ENABLED + theQuery << "LEFT JOIN channels_w cw on channels.id = cw.channel_id "; +#endif + theQuery << "WHERE registered_ts <> 0"; elog << "*** [CMaster::preloadChannelCache]: Loading all registered channel records: " << endl; - if (SQLDb->Exec(theQuery, true)) - // if( PGRES_TUPLES_OK == status ) - { + if (SQLDb->Exec(theQuery, true)) { for (unsigned int i = 0; i < SQLDb->Tuples(); i++) { /* Add this information to the channel cache. */ @@ -6651,6 +6670,8 @@ void cservice::preloadChannelCache() { sqlChannelIDCache.insert(sqlChannelIDHashType::value_type(newChan->getID(), newChan)); } // for() + } else { + LOGSQL_ERROR(SQLDb); } // if() elog << "*** [CMaster::preloadChannelCache]: Done. Loaded " << SQLDb->Tuples() @@ -8494,6 +8515,100 @@ bool cservice::doXQOplist(const string& chanName) { "AnyCServiceRouting", Message.c_str()); } +#ifdef THERETURN_ENABLED +bool cservice::doXRW(iServer* theServer, const string& Command, const string& Message) { + if (!theServer) + return false; + + if (theServer->getName() != wServerName) { + LOG(ERROR, "Received XR from invalid server ({}). Ignoring", theServer->getName()); + return false; + } + + StringTokenizer st(Message); + if (st.size() < 4) { + LOG(ERROR, "Invalid number of parameters."); + LOG(ERROR, "[W-XQ]: Invalid number of parameters: {}", Message); + return false; + } + + string Status = st[1]; + sqlChannel* theChan = getChannelRecord(st[2]); + + if (!theChan) { + elog << "cservice::doXRW: Can't find the channel!" << endl; + return false; + } + + iClient* theClient = Network->findClient(st[3]); + if (!theClient) { + LOG(ERROR, "We have lost the client?"); + return false; + } + + if (Command == "REG") { + // XR routing :REG <#channel> + if (st.size() < 4) { + LOG(ERROR, "Invalid number of parameters."); + return false; + } + + if (Status == "NO") { + LOG(ERROR, "{} rejected the registration of {}", wNickName, st[2]); + Notice(theClient, "Sorry, %s is unable to join that channel.", wNickName.c_str()); + + return false; + } else if (Status == "YES") { + logPrivAdminMessage("%s has been successfully registered with %s", + theChan->getName().c_str(), wNickName.c_str()); + + Notice(theClient, "Congratulations! The channel %s is now registered with %s", + st[2].c_str(), wNickName.c_str()); + Notice(theClient, "Use the following command to login: /msg %s@%s login %s %s", + wNickName.c_str(), wServerName.c_str(), theChan->getName().c_str(), + st[4].c_str()); + Notice(theClient, + "To get a list of commands available to you, type /msg %s showcommands %s", + wNickName.c_str(), theChan->getName().c_str()); + + theChan->setW(true); + } + } else if (Command == "PURGE") { + if (Status == "NO") { + LOG(ERROR, "{} rejected the purging of {}", wNickName, theChan->getName()); + Notice(theClient, + "Sorry, an error has occurred. Please contact a CService representative."); + return false; + } else if (Status == "YES") { + logPrivAdminMessage("%s has been successfully purged by %s.", + theChan->getName().c_str(), wNickName.c_str()); + + Notice(theClient, "Done. The channel %s has been successfully purged by %s.", + theChan->getName().c_str(), wNickName.c_str()); + + theChan->setW(false); + } + } else if (Command == "PART") { + if (Status == "NO") { + LOG(ERROR, "{} rejected to part {}", wNickName, theChan->getName()); + Notice(theClient, + "Sorry, an error has occurred. Please contact a CService representative."); + } else if (Status == "YES") { + Notice(theClient, "Done."); + } + } else if (Command == "JOIN") { + if (Status == "NO") { + LOG(ERROR, "{} rejected to join {}", wNickName, theChan->getName()); + Notice(theClient, + "Sorry, an error has occurred. Please contact a CService representative."); + } else if (Status == "YES") { + Notice(theClient, "Done."); + } + } + return true; +} +#endif // THERETURN_ENABLED + bool cservice::doXROplist(iServer* /*theServer*/, const string& Routing, const string& Message) { // AB XR Az iauth:15_d :OPLIST #empfoo LOG(TRACE, "XQ-OPLIST: Routing: {} Message: {}", Routing, Message); diff --git a/mod.cservice/cservice.h b/mod.cservice/cservice.h index 561f68f3..ca3afacb 100644 --- a/mod.cservice/cservice.h +++ b/mod.cservice/cservice.h @@ -389,6 +389,9 @@ class cservice : public xClient { bool doXQLogin(iServer*, const string&, const string&); bool doXQIsCheck(iServer*, const string&, const string&, const string&); bool doXQSASL(iServer*, const string&, const string&); +#ifdef THERETURN_ENABLED + bool doXRW(iServer*, const string&, const string&); +#endif // THERETURN_ENABLED /** * Array of sqlUser flags and the corresponding accountFlags. diff --git a/mod.cservice/cserviceCommands.h b/mod.cservice/cserviceCommands.h index a05780f3..7b35203c 100644 --- a/mod.cservice/cserviceCommands.h +++ b/mod.cservice/cserviceCommands.h @@ -143,6 +143,10 @@ DECLARE_COMMAND(ADDCOMMENT) DECLARE_COMMAND(SHUTDOWN) DECLARE_COMMAND(HELLO) +#ifdef THERETURN_ENABLED +DECLARE_COMMAND(W) +#endif // THERETURN_ENABLED + } // namespace gnuworld #endif // __CSERVICECOMMANDS_H diff --git a/mod.cservice/cservice_config.h b/mod.cservice/cservice_config.h index fee9a842..8f56f995 100644 --- a/mod.cservice/cservice_config.h +++ b/mod.cservice/cservice_config.h @@ -146,4 +146,9 @@ */ #define CRYPT_SALT_LEN 16 +/** + * Define this if you want to enable the integration with X/W (2002 edition). + */ +#define THERETURN_ENABLED + #endif // __CSERVICE_CONFIG_H diff --git a/mod.cservice/cservice_confvars.h b/mod.cservice/cservice_confvars.h index 6007dd5b..2e2f1745 100644 --- a/mod.cservice/cservice_confvars.h +++ b/mod.cservice/cservice_confvars.h @@ -34,6 +34,8 @@ CONFIG_VAR(std::string, limitJoinAllowedModes, "limitjoin_allowedmodes") \ CONFIG_VAR(std::string, welcomeNewChanMessage, "welcome_newchan_message") \ CONFIG_VAR(std::string, welcomeNewChanTopic, "welcome_newchan_topic") \ + CONFIG_VAR(std::string, wServerName, "w_server") \ + CONFIG_VAR(std::string, wNickName, "w_nick") \ CONFIG_VAR(int, updateInterval, "update_interval") \ CONFIG_VAR(int, expireInterval, "expire_interval") \ CONFIG_VAR(int, cacheInterval, "cache_interval") \ diff --git a/mod.cservice/sqlChannel.cc b/mod.cservice/sqlChannel.cc index 5fdbfdb6..7bb5c3f9 100644 --- a/mod.cservice/sqlChannel.cc +++ b/mod.cservice/sqlChannel.cc @@ -108,8 +108,12 @@ sqlChannel::sqlChannel(cservice* _bot) registered_ts(0), channel_ts(0), channel_mode(), userflags(0), last_topic(0), inChan(false), last_used(0), limit_offset(3), limit_period(20), last_limit_check(0), last_flood(0), limit_grace(2), limit_max(0), limit_joinmax(0), limit_joinsecs(0), limit_joinmode(), - limit_joinperiod(0), limit_jointimerID(0), max_bans(0), no_take(0), logger(_bot->getLogger()), - SQLDb(_bot->SQLDb) {} + limit_joinperiod(0), limit_jointimerID(0), max_bans(0), no_take(0), +#ifdef THERETURN_ENABLED + hasw(false), w_ts(0), +#endif + logger(_bot->getLogger()), SQLDb(_bot->SQLDb) { +} bool sqlChannel::loadData(const string& channelName) { /* @@ -120,11 +124,16 @@ bool sqlChannel::loadData(const string& channelName) { LOG(TRACE, "Attempting to load data for channel-name: {}", channelName); stringstream queryString; - queryString << "SELECT " - << sql::channel_fields - //<< " FROM channels WHERE registered_ts <> 0 AND lower(name) = '" - << " FROM channels WHERE lower(name) = '" - << escapeSQLChars(string_lower(channelName)) << "'" << ends; + queryString << "SELECT " << sql::channel_fields; +#ifdef THERETURN_ENABLED + queryString << ",cw.status,cw.timestamp"; +#endif + queryString << " FROM channels "; +#ifdef THERETURN_ENABLED + queryString << "LEFT JOIN channels_w cw on channels.id = cw.channel_id "; +#endif + queryString << "WHERE lower(channels.name) = '" << escapeSQLChars(string_lower(channelName)) + << "'"; if (SQLDb->Exec(queryString, true)) // if( PGRES_TUPLES_OK == status ) @@ -152,10 +161,15 @@ bool sqlChannel::loadData(int channelID) { LOG_MSG(TRACE, "Attempting to load data for channel-id: {}", channelID); stringstream queryString; - queryString << "SELECT " - << sql::channel_fields - //<< " FROM channels WHERE registered_ts <> 0 AND id = " - << " FROM channels WHERE id = " << channelID << ends; + queryString << "SELECT " << sql::channel_fields; +#ifdef THERETURN_ENABLED + queryString << ",cw.status,cw.timestamp"; +#endif + queryString << " FROM channels "; +#ifdef THERETURN_ENABLED + queryString << "LEFT JOIN channels_w cw on channels.id = cw.channel_id"; +#endif + queryString << "WHERE channels.id = " << channelID; if (SQLDb->Exec(queryString, true)) // if( PGRES_TUPLES_OK == status ) @@ -206,10 +220,36 @@ void sqlChannel::setAllMembers(int row) { limit_joinsecs = atoi(SQLDb->GetValue(row, 22)); limit_joinperiod = atoi(SQLDb->GetValue(row, 23)); limit_joinmode = SQLDb->GetValue(row, 24); +#ifdef THERETURN_ENABLED + hasw = atoi(SQLDb->GetValue(row, 25)); // This must always be the last column. + w_ts = atoi(SQLDb->GetValue(row, 26)); +#endif setAllFlood(); } +#ifdef THERETURN_ENABLED +bool sqlChannel::setW(const bool status) { + hasw = status; + + stringstream queryString; + queryString << "INSERT INTO channels_w (channel_id,status,timestamp) VALUES (" << id << "," + << status << ",date_part('epoch', CURRENT_TIMESTAMP)::int) " + << "ON CONFLICT (channel_id) DO UPDATE SET status = " << status + << ",timestamp=date_part('epoch', CURRENT_TIMESTAMP)::int " + << "RETURNING timestamp"; + + if (!SQLDb->Exec(queryString)) { + LOGSQL_ERROR(SQLDb); + return false; + } + + w_ts = atoi(SQLDb->GetValue(0, 0)); + + return true; +} +#endif // THERETURN_ENABLED + bool sqlChannel::commit() { /* * Build an SQL statement to commit the transient data in this storage class diff --git a/mod.cservice/sqlChannel.h b/mod.cservice/sqlChannel.h index ef2aa1b9..403cdddd 100644 --- a/mod.cservice/sqlChannel.h +++ b/mod.cservice/sqlChannel.h @@ -34,6 +34,7 @@ #include "dbHandle.h" #include "server.h" #include "sqlBan.h" +#include "cservice_config.h" namespace gnuworld { @@ -180,6 +181,11 @@ class sqlChannel { inline const flagType& getFlags() const { return flags; } +#ifdef THERETURN_ENABLED + inline bool hasW() const { return hasw; } + inline const time_t& getWTS() const { return w_ts; } +#endif // THERETURN_ENABLED + inline bool getFlag(const flagType& whichFlag) const { return (flags & whichFlag); } inline const unsigned short int& getMassDeopPro() const { return mass_deop_pro; } @@ -413,6 +419,9 @@ class sqlChannel { * object back to the database. */ bool commit(); +#ifdef THERETURN_ENABLED + bool setW(const bool); +#endif // THERETURN_ENABLED bool insertRecord(); void setAllMembers(int); @@ -504,6 +513,11 @@ class sqlChannel { unsigned int no_take; time_t now; +#ifdef THERETURN_ENABLED + bool hasw; + time_t w_ts; +#endif // THERETURN_ENABLED + Logger* logger; dbHandle* SQLDb; }; diff --git a/mod.cservice/sqlPendingChannel.cc b/mod.cservice/sqlPendingChannel.cc index 3804d141..eeff8479 100644 --- a/mod.cservice/sqlPendingChannel.cc +++ b/mod.cservice/sqlPendingChannel.cc @@ -59,7 +59,7 @@ sqlPendingChannel::~sqlPendingChannel() { for (trafficListType::iterator ptr = trafficList.begin(); ptr != trafficList.end(); ++ptr) { sqlPendingTraffic* toDie = ptr->second; // elog << "Autocleanup of Traffic record for #" << toDie->ip_number << - //endl; + // endl; delete (toDie); } @@ -67,7 +67,7 @@ sqlPendingChannel::~sqlPendingChannel() { ptr != uniqueSupporterList.end(); ++ptr) { sqlPendingTraffic* toDie = ptr->second; // elog << "Autocleanup of Traffic record for #" << toDie->ip_number << - //endl; + // endl; delete (toDie); } } diff --git a/mod.dronescan/dronescan.cc b/mod.dronescan/dronescan.cc index e0d53591..f495f88c 100644 --- a/mod.dronescan/dronescan.cc +++ b/mod.dronescan/dronescan.cc @@ -844,7 +844,7 @@ void dronescan::processJoinPartChannels() { // { // if (glined.size() != 0) { // log(WARN,"Aborting glines for channel %s because only %d flooding - //clients from %d addresses were found", + // clients from %d addresses were found", // itr->first.c_str(),clientcount,glined.size()); // } // }