1 /* BotServ core functions 2 * 3 * (C) 2003-2020 Anope Team 4 * Contact us at team@anope.org 5 * 6 * Please read COPYING and README for further details. 7 * 8 * Based on the original code of Epona by Lara. 9 * Based on the original code of Services by Andy Church. 10 */ 11 12 #include "module.h" 13 14 class CommandBSBot : public Command 15 { 16 private: DoAdd(CommandSource & source,const std::vector<Anope::string> & params)17 void DoAdd(CommandSource &source, const std::vector<Anope::string> ¶ms) 18 { 19 const Anope::string &nick = params[1]; 20 const Anope::string &user = params[2]; 21 const Anope::string &host = params[3]; 22 const Anope::string &real = params[4]; 23 24 if (BotInfo::Find(nick, true)) 25 { 26 source.Reply(_("Bot \002%s\002 already exists."), nick.c_str()); 27 return; 28 } 29 30 Configuration::Block *networkinfo = Config->GetBlock("networkinfo"); 31 32 if (nick.length() > networkinfo->Get<unsigned>("nicklen")) 33 { 34 source.Reply(_("Bot nicks may only be %d characters long."), networkinfo->Get<unsigned>("nicklen")); 35 return; 36 } 37 38 if (user.length() > networkinfo->Get<unsigned>("userlen")) 39 { 40 source.Reply(_("Bot idents may only be %d characters long."), networkinfo->Get<unsigned>("userlen")); 41 return; 42 } 43 44 if (host.length() > networkinfo->Get<unsigned>("hostlen")) 45 { 46 source.Reply(_("Bot hosts may only be %d characters long."), networkinfo->Get<unsigned>("hostlen")); 47 return; 48 } 49 50 if (!IRCD->IsNickValid(nick)) 51 { 52 source.Reply(_("Bot nicks may only contain valid nick characters.")); 53 return; 54 } 55 56 if (!IRCD->IsIdentValid(user)) 57 { 58 source.Reply(_("Bot idents may only contain valid ident characters.")); 59 return; 60 } 61 62 if (!IRCD->IsHostValid(host)) 63 { 64 source.Reply(_("Bot hosts may only contain valid host characters.")); 65 return; 66 } 67 68 /* We check whether the nick is registered, and inform the user 69 * if so. You need to drop the nick manually before you can use 70 * it as a bot nick from now on -GD 71 */ 72 if (NickAlias::Find(nick)) 73 { 74 source.Reply(NICK_ALREADY_REGISTERED, nick.c_str()); 75 return; 76 } 77 78 User *u = User::Find(nick, true); 79 if (u) 80 { 81 source.Reply(_("Nick \002%s\002 is currently in use."), u->nick.c_str()); 82 return; 83 } 84 85 BotInfo *bi = new BotInfo(nick, user, host, real); 86 87 Log(LOG_ADMIN, source, this) << "ADD " << bi->GetMask() << " " << bi->realname; 88 89 source.Reply(_("%s!%s@%s (%s) added to the bot list."), bi->nick.c_str(), bi->GetIdent().c_str(), bi->host.c_str(), bi->realname.c_str()); 90 91 FOREACH_MOD(OnBotCreate, (bi)); 92 return; 93 } 94 DoChange(CommandSource & source,const std::vector<Anope::string> & params)95 void DoChange(CommandSource &source, const std::vector<Anope::string> ¶ms) 96 { 97 const Anope::string &oldnick = params[1]; 98 const Anope::string &nick = params.size() > 2 ? params[2] : ""; 99 const Anope::string &user = params.size() > 3 ? params[3] : ""; 100 const Anope::string &host = params.size() > 4 ? params[4] : ""; 101 const Anope::string &real = params.size() > 5 ? params[5] : ""; 102 103 if (oldnick.empty() || nick.empty()) 104 { 105 this->OnSyntaxError(source, "CHANGE"); 106 return; 107 } 108 109 BotInfo *bi = BotInfo::Find(oldnick, true); 110 if (!bi) 111 { 112 source.Reply(BOT_DOES_NOT_EXIST, oldnick.c_str()); 113 return; 114 } 115 116 if (bi->conf) 117 { 118 source.Reply(_("Bot %s is not changeable."), bi->nick.c_str()); 119 return; 120 } 121 122 Configuration::Block *networkinfo = Config->GetBlock("networkinfo"); 123 124 if (nick.length() > networkinfo->Get<unsigned>("nicklen")) 125 { 126 source.Reply(_("Bot nicks may only be %d characters long."), networkinfo->Get<unsigned>("nicklen")); 127 return; 128 } 129 130 if (user.length() > networkinfo->Get<unsigned>("userlen")) 131 { 132 source.Reply(_("Bot idents may only be %d characters long."), networkinfo->Get<unsigned>("userlen")); 133 return; 134 } 135 136 if (host.length() > networkinfo->Get<unsigned>("hostlen")) 137 { 138 source.Reply(_("Bot hosts may only be %d characters long."), networkinfo->Get<unsigned>("hostlen")); 139 return; 140 } 141 142 /* Checks whether there *are* changes. 143 * Case sensitive because we may want to change just the case. 144 * And we must finally check that the nick is not already 145 * taken by another bot. 146 */ 147 if (nick.equals_cs(bi->nick) && (!user.empty() ? user.equals_cs(bi->GetIdent()) : 1) && (!host.empty() ? host.equals_cs(bi->host) : 1) && (!real.empty() ? real.equals_cs(bi->realname) : 1)) 148 { 149 source.Reply(_("The old information is the same as the new information specified.")); 150 return; 151 } 152 153 if (!IRCD->IsNickValid(nick)) 154 { 155 source.Reply(_("Bot nicks may only contain valid nick characters.")); 156 return; 157 } 158 159 if (!user.empty() && !IRCD->IsIdentValid(user)) 160 { 161 source.Reply(_("Bot idents may only contain valid ident characters.")); 162 return; 163 } 164 165 if (!host.empty() && !IRCD->IsHostValid(host)) 166 { 167 source.Reply(_("Bot hosts may only contain valid host characters.")); 168 return; 169 } 170 171 if (!nick.equals_ci(bi->nick)) 172 { 173 if (BotInfo::Find(nick, true)) 174 { 175 source.Reply(_("Bot \002%s\002 already exists."), nick.c_str()); 176 return; 177 } 178 179 if (User::Find(nick, true)) 180 { 181 source.Reply(_("Nick \002%s\002 is currently in use."), nick.c_str()); 182 return; 183 } 184 } 185 186 if (!nick.equals_ci(bi->nick)) 187 { 188 /* We check whether the nick is registered, and inform the user 189 * if so. You need to drop the nick manually before you can use 190 * it as a bot nick from now on -GD 191 */ 192 if (NickAlias::Find(nick)) 193 { 194 source.Reply(NICK_ALREADY_REGISTERED, nick.c_str()); 195 return; 196 } 197 198 /* The new nick is really different, so we remove the Q line for the old nick. */ 199 XLine x_del(bi->nick); 200 IRCD->SendSQLineDel(&x_del); 201 202 /* Add a Q line for the new nick */ 203 XLine x(nick, "Reserved for services"); 204 IRCD->SendSQLine(NULL, &x); 205 } 206 207 if (!user.empty()) 208 { 209 IRCD->SendQuit(bi, "Quit: Be right back"); 210 bi->introduced = false; 211 } 212 else 213 IRCD->SendNickChange(bi, nick); 214 215 if (!nick.equals_cs(bi->nick)) 216 bi->SetNewNick(nick); 217 218 if (!user.empty() && !user.equals_cs(bi->GetIdent())) 219 bi->SetIdent(user); 220 if (!host.empty() && !host.equals_cs(bi->host)) 221 bi->host = host; 222 if (!real.empty() && !real.equals_cs(bi->realname)) 223 bi->realname = real; 224 225 if (!user.empty()) 226 bi->OnKill(); 227 228 source.Reply(_("Bot \002%s\002 has been changed to %s!%s@%s (%s)."), oldnick.c_str(), bi->nick.c_str(), bi->GetIdent().c_str(), bi->host.c_str(), bi->realname.c_str()); 229 Log(LOG_ADMIN, source, this) << "CHANGE " << oldnick << " to " << bi->GetMask() << " " << bi->realname; 230 231 FOREACH_MOD(OnBotChange, (bi)); 232 return; 233 } 234 DoDel(CommandSource & source,const std::vector<Anope::string> & params)235 void DoDel(CommandSource &source, const std::vector<Anope::string> ¶ms) 236 { 237 const Anope::string &nick = params[1]; 238 239 if (nick.empty()) 240 { 241 this->OnSyntaxError(source, "DEL"); 242 return; 243 } 244 245 BotInfo *bi = BotInfo::Find(nick, true); 246 if (!bi) 247 { 248 source.Reply(BOT_DOES_NOT_EXIST, nick.c_str()); 249 return; 250 } 251 252 if (bi->conf) 253 { 254 source.Reply(_("Bot %s is not deletable."), bi->nick.c_str()); 255 return; 256 } 257 258 FOREACH_MOD(OnBotDelete, (bi)); 259 260 Log(LOG_ADMIN, source, this) << "DEL " << bi->nick; 261 262 source.Reply(_("Bot \002%s\002 has been deleted."), nick.c_str()); 263 delete bi; 264 return; 265 } 266 public: CommandBSBot(Module * creator)267 CommandBSBot(Module *creator) : Command(creator, "botserv/bot", 1, 6) 268 { 269 this->SetDesc(_("Maintains network bot list")); 270 this->SetSyntax(_("\002ADD \037nick\037 \037user\037 \037host\037 \037real\037\002")); 271 this->SetSyntax(_("\002CHANGE \037oldnick\037 \037newnick\037 [\037user\037 [\037host\037 [\037real\037]]]\002")); 272 this->SetSyntax(_("\002DEL \037nick\037\002")); 273 } 274 Execute(CommandSource & source,const std::vector<Anope::string> & params)275 void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) anope_override 276 { 277 const Anope::string &cmd = params[0]; 278 279 if (Anope::ReadOnly) 280 { 281 source.Reply(_("Sorry, bot modification is temporarily disabled.")); 282 return; 283 } 284 285 if (cmd.equals_ci("ADD")) 286 { 287 // ADD nick user host real - 5 288 if (!source.HasCommand("botserv/bot/add")) 289 { 290 source.Reply(ACCESS_DENIED); 291 return; 292 } 293 294 if (params.size() < 5) 295 { 296 this->OnSyntaxError(source, "ADD"); 297 return; 298 } 299 300 std::vector<Anope::string> tempparams = params; 301 // ADD takes less params than CHANGE, so we need to take 6 if given and append it with a space to 5. 302 if (tempparams.size() >= 6) 303 tempparams[4] = tempparams[4] + " " + tempparams[5]; 304 305 return this->DoAdd(source, tempparams); 306 } 307 else if (cmd.equals_ci("CHANGE")) 308 { 309 // CHANGE oldn newn user host real - 6 310 // but only oldn and newn are required 311 if (!source.HasCommand("botserv/bot/change")) 312 { 313 source.Reply(ACCESS_DENIED); 314 return; 315 } 316 317 if (params.size() < 3) 318 { 319 this->OnSyntaxError(source, "CHANGE"); 320 return; 321 } 322 323 return this->DoChange(source, params); 324 } 325 else if (cmd.equals_ci("DEL")) 326 { 327 // DEL nick 328 if (!source.HasCommand("botserv/bot/del")) 329 { 330 source.Reply(ACCESS_DENIED); 331 return; 332 } 333 334 if (params.size() < 1) 335 { 336 this->OnSyntaxError(source, "DEL"); 337 return; 338 } 339 340 return this->DoDel(source, params); 341 } 342 else 343 this->OnSyntaxError(source, ""); 344 345 return; 346 } 347 OnHelp(CommandSource & source,const Anope::string & subcommand)348 bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override 349 { 350 this->SendSyntax(source); 351 source.Reply(" "); 352 source.Reply(_("Allows Services Operators to create, modify, and delete\n" 353 "bots that users will be able to use on their own\n" 354 "channels.\n" 355 " \n" 356 "\002BOT ADD\002 adds a bot with the given nickname, username,\n" 357 "hostname and realname. Since no integrity checks are done\n" 358 "for these settings, be really careful.\n" 359 " \n" 360 "\002BOT CHANGE\002 allows you to change the nickname, username, hostname\n" 361 "or realname of a bot without deleting it (and\n" 362 "all the data associated with it).\n" 363 " \n" 364 "\002BOT DEL\002 removes the given bot from the bot list.\n" 365 " \n" 366 "\002Note\002: You cannot create a bot with a nick that is\n" 367 "currently registered. If an unregistered user is currently\n" 368 "using the nick, they will be killed.")); 369 return true; 370 } 371 }; 372 373 class BSBot : public Module 374 { 375 CommandBSBot commandbsbot; 376 377 public: BSBot(const Anope::string & modname,const Anope::string & creator)378 BSBot(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR), 379 commandbsbot(this) 380 { 381 382 } 383 }; 384 385 MODULE_INIT(BSBot) 386