| 1 | // Bot.C -*- C++ -*- |
| 2 | // Copyright (c) 1997, 1998 Etienne BERNARD |
| 3 | // Copyright (C) 2002,2003,2005 Clinton Ebadi |
| 4 | |
| 5 | // This program is free software; you can redistribute it and/or modify |
| 6 | // it under the terms of the GNU General Public License as published by |
| 7 | // the Free Software Foundation; either version 2 of the License, or |
| 8 | // any later version. |
| 9 | |
| 10 | // This program is distributed in the hope that it will be useful, |
| 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 13 | // GNU General Public License for more details. |
| 14 | |
| 15 | // You should have received a copy of the GNU General Public License |
| 16 | // along with this program; if not, write to the Free Software |
| 17 | // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
| 18 | // 02110-1301, USA. |
| 19 | |
| 20 | #include <cstring> |
| 21 | #include <cstdlib> |
| 22 | #include <cstdio> |
| 23 | |
| 24 | #include <fstream> |
| 25 | #include <algorithm> |
| 26 | #include <iomanip> |
| 27 | |
| 28 | #include <dirent.h> |
| 29 | #include <sys/time.h> |
| 30 | #include <sys/types.h> |
| 31 | #include <sys/stat.h> |
| 32 | #include <unistd.h> |
| 33 | |
| 34 | #ifdef USESCRIPTS |
| 35 | #include <libguile.h> |
| 36 | #endif |
| 37 | |
| 38 | #include "Bot.H" |
| 39 | #include "DCCConnection.H" |
| 40 | #include "DCCChatConnection.H" |
| 41 | #include "ChannelList.H" |
| 42 | #include "DCCManager.H" |
| 43 | #include "DCCPerson.H" |
| 44 | #include "Parser.H" |
| 45 | #include "Person.H" |
| 46 | #include "Server.H" |
| 47 | #include "ServerConnection.H" |
| 48 | #include "ServerList.H" |
| 49 | #include "ShitList.H" |
| 50 | #include "StringTokenizer.H" |
| 51 | #include "User.H" |
| 52 | #include "UserCommands.H" |
| 53 | #include "UserList.H" |
| 54 | #include "Utils.H" |
| 55 | |
| 56 | |
| 57 | #ifdef USESCRIPTS |
| 58 | #include "BotInterp.H" |
| 59 | #include "Interp.H" |
| 60 | #endif |
| 61 | |
| 62 | unsigned int Bot::MAX_MESSAGES = 2; |
| 63 | unsigned int Bot::MAX_NICKLENGTH = 9; |
| 64 | |
| 65 | #define DEFAULT_NICKNAME "Bobot" |
| 66 | #define DEFAULT_USERNAME "bobot" |
| 67 | #define DEFAULT_IRCNAME "I'm a bobot++!" |
| 68 | #define DEFAULT_COMMANDCHAR '!' |
| 69 | #define DEFAULT_USERLISTFILENAME "bot.users" |
| 70 | #define DEFAULT_SHITLISTFILENAME "bot.shit" |
| 71 | #define DEFAULT_HELPFILENAME "bot.help" |
| 72 | #define DEFAULT_SCRIPTLOGFILENAME "script.log" |
| 73 | #define DEFAULT_LOGFILENAME "bot.log" |
| 74 | #define DEFAULT_LOGDIR getenv ("HOME") + String("/.bobotpp/logs/") |
| 75 | #define DEFAULT_INITFILENAME "bot.init" |
| 76 | #ifdef USESCRIPTS |
| 77 | #define DEFAULT_AUTOEXECFILENAME "bot.autoexec" |
| 78 | #endif |
| 79 | |
| 80 | Bot::Bot(String filename, bool debug_on) |
| 81 | : nickName(DEFAULT_NICKNAME), |
| 82 | wantedNickName(DEFAULT_NICKNAME), |
| 83 | userName(DEFAULT_USERNAME), |
| 84 | ircName(DEFAULT_IRCNAME), |
| 85 | versionString(VERSION_STRING), |
| 86 | userHost(""), |
| 87 | localIP(""), |
| 88 | commandChar(DEFAULT_COMMANDCHAR), |
| 89 | userListFileName(DEFAULT_USERLISTFILENAME), |
| 90 | shitListFileName(DEFAULT_SHITLISTFILENAME), |
| 91 | helpFileName(DEFAULT_HELPFILENAME), |
| 92 | initFileName(DEFAULT_INITFILENAME), |
| 93 | connected(false), |
| 94 | debug(debug_on), |
| 95 | stop(false), |
| 96 | sentPing(false), |
| 97 | startTime(time(NULL)), |
| 98 | currentTime(startTime), |
| 99 | lastNickNameChange(startTime), |
| 100 | lastChannelJoin(startTime), |
| 101 | serverConnection(0), |
| 102 | sentUserhostID(0), |
| 103 | receivedUserhostID(0), |
| 104 | logFileName(DEFAULT_LOGFILENAME), |
| 105 | logs_dir (DEFAULT_LOGDIR), |
| 106 | configFileName (filename) |
| 107 | #ifdef USESCRIPTS |
| 108 | ,scriptLogFileName(DEFAULT_SCRIPTLOGFILENAME), |
| 109 | autoexecFileName(DEFAULT_AUTOEXECFILENAME) |
| 110 | #endif |
| 111 | { |
| 112 | #ifdef HAVE_STL_CLEAR |
| 113 | wantedChannels.clear(); |
| 114 | ignoredUserhosts.clear(); |
| 115 | spyList.clear(); |
| 116 | userhostMap.clear(); |
| 117 | #endif |
| 118 | |
| 119 | init_user_functions (); |
| 120 | |
| 121 | set_log_dir (logs_dir); |
| 122 | set_log_file (logFileName); |
| 123 | |
| 124 | |
| 125 | channelList = new ChannelList(); |
| 126 | serverList = new ServerList(); |
| 127 | readConfig(); |
| 128 | userList = new UserList(userListFileName); |
| 129 | shitList = new ShitList(shitListFileName); |
| 130 | dccConnections = new DCCManager (); |
| 131 | |
| 132 | // Let's read the alias file |
| 133 | std::ifstream initFile(initFileName.c_str ()); |
| 134 | |
| 135 | if (initFile) |
| 136 | { |
| 137 | // FIXME: these variables are current String instead of |
| 138 | // std::string because String>> reads an entire line. This code |
| 139 | // needs to be rewritten to use std::string and std::getline (or |
| 140 | // better yet, be removed entirely once BotConfig is in place) |
| 141 | String temp, alias, command; |
| 142 | int line = 0; |
| 143 | |
| 144 | while (initFile >> temp, temp.length() != 0) |
| 145 | { |
| 146 | StringTokenizer st(temp); |
| 147 | |
| 148 | line++; |
| 149 | temp = Utils::trim_str (temp); |
| 150 | |
| 151 | if (temp[0]=='#') |
| 152 | { |
| 153 | continue; |
| 154 | } |
| 155 | |
| 156 | if (st.count_tokens (' ') != 2) |
| 157 | { |
| 158 | std::cerr << "Error when reading alias file (" << initFileName |
| 159 | << ") line " << line << "...\n"; |
| 160 | continue; |
| 161 | } |
| 162 | |
| 163 | alias = Utils::to_upper (st.next_token()); |
| 164 | command = Utils::to_upper (st.next_token()); |
| 165 | |
| 166 | // Does the function already exist ? |
| 167 | if (!userFunctions[alias]) |
| 168 | { |
| 169 | if (userFunction *u = userFunctions[command]) |
| 170 | userFunctions[alias] = |
| 171 | new |
| 172 | userFunction(u->function, |
| 173 | u->minLevel, |
| 174 | u->needsChannelName); |
| 175 | } |
| 176 | } |
| 177 | } |
| 178 | |
| 179 | |
| 180 | std::srand (std::time (0)); // srand for bot-random |
| 181 | |
| 182 | #ifdef USESCRIPTS |
| 183 | botInterp = new BotInterp(this, logs_dir + scriptLogFileName); |
| 184 | Interp::Startup2 (this); |
| 185 | botInterp->LoadScript(autoexecFileName); |
| 186 | #endif |
| 187 | } |
| 188 | |
| 189 | Bot::~Bot() |
| 190 | { |
| 191 | Person *p; |
| 192 | while (spyList.size() != 0) { |
| 193 | p = (*spyList.begin()).second; |
| 194 | spyList.erase(spyList.begin()); |
| 195 | delete p; |
| 196 | } |
| 197 | delete dccConnections; |
| 198 | destroy_user_functions (); |
| 199 | |
| 200 | wantedChannel *w; |
| 201 | while (wantedChannels.size() != 0) { |
| 202 | w = (*wantedChannels.begin()).second; |
| 203 | wantedChannels.erase(wantedChannels.begin()); |
| 204 | delete w; |
| 205 | } |
| 206 | userList->save(); |
| 207 | shitList->save(); |
| 208 | delete channelList; |
| 209 | delete userList; |
| 210 | delete serverList; |
| 211 | delete shitList; |
| 212 | delete serverConnection; |
| 213 | logLine("Stopping log."); |
| 214 | logFile.close(); |
| 215 | } |
| 216 | |
| 217 | void |
| 218 | Bot::logLine(String line) |
| 219 | { |
| 220 | tm *d; |
| 221 | std::time_t current_time = time(0); |
| 222 | |
| 223 | d = localtime(¤t_time); |
| 224 | logFile << "[" << std::setfill('0') << std::setw(2) |
| 225 | << d->tm_mday << "/" << std::setfill('0') << std::setw(2) |
| 226 | << d->tm_mon + 1 << "/" |
| 227 | << d->tm_year + 1900 << " - " << std::setfill('0') << std::setw(2) |
| 228 | << d->tm_hour << ":" << std::setfill('0') << std::setw(2) |
| 229 | << d->tm_min << ":" << std::setfill('0') << std::setw(2) |
| 230 | << d->tm_sec << "] " |
| 231 | << line |
| 232 | << std::endl; |
| 233 | } |
| 234 | |
| 235 | void |
| 236 | Bot::readConfig() |
| 237 | { |
| 238 | std::ifstream file(configFileName.c_str ()); |
| 239 | String temp; |
| 240 | int line = 1; |
| 241 | |
| 242 | if (!file) { |
| 243 | logLine(String("I cannot find the file ") + configFileName); |
| 244 | return; |
| 245 | } |
| 246 | |
| 247 | while (!file.eof()) { |
| 248 | |
| 249 | file >> temp; |
| 250 | |
| 251 | if (temp.length() == 0 || temp[(unsigned int)0] == '#') { |
| 252 | line++; |
| 253 | continue; |
| 254 | } |
| 255 | |
| 256 | StringTokenizer st(temp); |
| 257 | String command = Utils::to_upper (Utils::trim_str (st.next_token('='))); |
| 258 | String parameters = Utils::trim_str (st.next_token('=')); |
| 259 | |
| 260 | if (command == "NICK" || command == "NICKNAME") |
| 261 | nickName = wantedNickName = parameters; |
| 262 | else if (command == "USERNAME") |
| 263 | userName = parameters; |
| 264 | else if (command == "IRCNAME" || command == "REALNAME") |
| 265 | ircName = parameters; |
| 266 | else if (command == "CMDCHAR" || command == "COMMAND") |
| 267 | commandChar = parameters[(unsigned int)0]; |
| 268 | else if (command == "USERLIST") |
| 269 | userListFileName = parameters; |
| 270 | else if (command == "SHITLIST") |
| 271 | shitListFileName = parameters; |
| 272 | else if (command == "CHANNEL") { |
| 273 | if (parameters.indexOf(':') == -1) { |
| 274 | std::cout << "Warning. The 'channel' syntax has changed." |
| 275 | << " Please see the README file for more information." |
| 276 | << " I will use compatibility mode, but you're really" |
| 277 | << " missing something.\n"; |
| 278 | StringTokenizer st2(parameters); |
| 279 | String name = Utils::to_lower (st2.next_token()); |
| 280 | String key = st2.next_token(); |
| 281 | wantedChannels[name] = new wantedChannel("", "", key); |
| 282 | } else { |
| 283 | StringTokenizer st2(parameters); |
| 284 | String name = Utils::to_lower (st2.next_token(':')); |
| 285 | String mode = st2.next_token(':'); |
| 286 | String keep = st2.next_token(':'); |
| 287 | String key = st2.next_token(':'); |
| 288 | wantedChannels[name] = new wantedChannel(mode, keep, key); |
| 289 | } |
| 290 | } |
| 291 | else if (command == "LOGFILE") |
| 292 | { |
| 293 | if (parameters != logFileName) |
| 294 | { |
| 295 | if (parameters[(unsigned int)0] == '/') |
| 296 | { |
| 297 | StringTokenizer log_st (parameters); |
| 298 | std::string log_dir = "/"; |
| 299 | |
| 300 | for (unsigned int m = log_st.count_tokens ('/'); |
| 301 | --m; |
| 302 | m > 0) |
| 303 | { |
| 304 | log_dir += log_st.next_token ('/') + "/"; |
| 305 | } |
| 306 | |
| 307 | std::cerr << "==" << log_dir << std::endl; |
| 308 | |
| 309 | set_log_dir (log_dir); |
| 310 | set_log_file (log_st.rest ()); |
| 311 | } |
| 312 | else |
| 313 | set_log_file (parameters); |
| 314 | } |
| 315 | } |
| 316 | #ifdef USESCRIPTS |
| 317 | else if (command == "SCRIPTLOGFILE") |
| 318 | scriptLogFileName = parameters; |
| 319 | else if (command == "AUTOEXECFILE") |
| 320 | autoexecFileName = parameters; |
| 321 | #endif |
| 322 | else if (command == "INITFILE") |
| 323 | initFileName = parameters; |
| 324 | else if (command == "LOCALIP") |
| 325 | localIP = parameters; |
| 326 | else if (command == "MAXNICKLENGTH") |
| 327 | MAX_NICKLENGTH = std::atoi (parameters.c_str ()); |
| 328 | else if (command == "SERVER") { |
| 329 | if (parameters.indexOf(' ') == -1) |
| 330 | serverList->addServer(new Server(parameters)); |
| 331 | else { |
| 332 | StringTokenizer st2(parameters); |
| 333 | String name = st2.next_token(); |
| 334 | int port = std::atoi(st2.next_token().c_str()); |
| 335 | serverList->addServer(new Server(name, |
| 336 | port, |
| 337 | st2.next_token())); |
| 338 | } |
| 339 | } |
| 340 | else if (command == "") |
| 341 | { |
| 342 | // do nothing |
| 343 | } |
| 344 | else { |
| 345 | logLine(String("Syntax error in file ") + configFileName + |
| 346 | ", line " + String((long)line)); |
| 347 | file.close(); |
| 348 | std::exit(1); |
| 349 | } |
| 350 | |
| 351 | line++; |
| 352 | } |
| 353 | |
| 354 | file.close(); |
| 355 | } |
| 356 | |
| 357 | void |
| 358 | Bot::run() |
| 359 | { |
| 360 | nextServer(); |
| 361 | |
| 362 | while (!stop) |
| 363 | { |
| 364 | waitForInput(); // This is the main event loop |
| 365 | dccConnections->checkStale (); |
| 366 | |
| 367 | if (!serverConnection->queue->flush()) |
| 368 | { |
| 369 | // Disconnected |
| 370 | #ifdef USESCRIPTS |
| 371 | // Run hooks/disconnect |
| 372 | this->botInterp->RunHooks |
| 373 | (Hook::DISCONNECT, |
| 374 | serverConnection->server->getHostName (), |
| 375 | scm_list_n |
| 376 | (Utils::str2scm (serverConnection->server->getHostName ()), |
| 377 | SCM_BOOL_F)); |
| 378 | #endif |
| 379 | nextServer(); |
| 380 | } |
| 381 | } |
| 382 | } |
| 383 | |
| 384 | void |
| 385 | Bot::waitForInput() |
| 386 | { |
| 387 | #ifdef _HPUX_SOURCE |
| 388 | int rd; |
| 389 | #else |
| 390 | fd_set rd; |
| 391 | #endif |
| 392 | struct timeval timer; |
| 393 | |
| 394 | int sock = serverConnection->getFileDescriptor(); |
| 395 | int maxSocketNumber = sock; |
| 396 | |
| 397 | #ifdef _HPUX_SOURCE |
| 398 | rd = sock; |
| 399 | #else |
| 400 | FD_ZERO(&rd); |
| 401 | FD_SET(sock, &rd); |
| 402 | #endif |
| 403 | |
| 404 | DCC_MAP* dccmap = &dccConnections->dcc_map; |
| 405 | for (DCC_MAP::iterator it = dccmap->begin (); |
| 406 | it != dccmap->end(); ++it) { |
| 407 | int s = it->second->dcc->getFileDescriptor(); |
| 408 | #ifdef _HPUX_SOURCE |
| 409 | rd |= s; |
| 410 | #else |
| 411 | FD_SET(s, &rd); |
| 412 | #endif |
| 413 | if (s > maxSocketNumber) |
| 414 | maxSocketNumber = s; |
| 415 | } |
| 416 | |
| 417 | timer.tv_sec = 1; |
| 418 | timer.tv_usec = 0; |
| 419 | |
| 420 | switch (select(maxSocketNumber + 1, &rd, NULL, NULL, &timer)) { |
| 421 | case 0: /* timeout */ |
| 422 | break; |
| 423 | case -1: /* error */ |
| 424 | break; |
| 425 | default: /* normal */ |
| 426 | #ifdef _HPUX_SOURCE |
| 427 | if (rd & sock) |
| 428 | #else |
| 429 | if (FD_ISSET(sock, &rd)) |
| 430 | #endif |
| 431 | if (serverConnection->handleInput()) |
| 432 | nextServer(); |
| 433 | |
| 434 | dccConnections->checkInput (rd); |
| 435 | } |
| 436 | |
| 437 | if (currentTime < std::time(0)) { // Actions that we do each second |
| 438 | currentTime = std::time(0); |
| 439 | for (std::map<String, unsigned int, std::less<String> >::iterator |
| 440 | it = ignoredUserhosts.begin(); |
| 441 | it != ignoredUserhosts.end(); ++it) |
| 442 | if ((*it).second > 0) |
| 443 | (*it).second--; |
| 444 | |
| 445 | for (std::map<String, Channel *, std::less<String> >::iterator it = channelList->begin (); |
| 446 | it != channelList->end (); |
| 447 | ++it) |
| 448 | { |
| 449 | it->second->purge_expired_bans (); |
| 450 | } |
| 451 | #ifdef USESCRIPTS |
| 452 | botInterp->RunTimers(currentTime); |
| 453 | |
| 454 | tm *thisTime = localtime(¤tTime); |
| 455 | if (thisTime->tm_sec == 0) |
| 456 | { |
| 457 | char s[6]; |
| 458 | std::snprintf(s, 6, "%02d:%02d", thisTime->tm_hour, thisTime->tm_min); |
| 459 | |
| 460 | botInterp->RunHooks(Hook::TIMER, String(s), |
| 461 | scm_list_n (Utils::str2scm (std::string (s)), |
| 462 | SCM_UNDEFINED)); |
| 463 | } |
| 464 | #endif |
| 465 | |
| 466 | } |
| 467 | |
| 468 | if (currentTime >= (time_t)(lastNickNameChange + Bot::NICK_CHANGE) && |
| 469 | nickName != wantedNickName) { |
| 470 | lastNickNameChange = currentTime; |
| 471 | serverConnection->queue->sendNick(wantedNickName); |
| 472 | } |
| 473 | |
| 474 | if (currentTime >= (std::time_t)(lastChannelJoin + Bot::CHANNEL_JOIN)) { |
| 475 | lastChannelJoin = currentTime; |
| 476 | for (std::map<String, wantedChannel *, std::less<String> >::iterator it = |
| 477 | wantedChannels.begin(); it != wantedChannels.end(); |
| 478 | ++it) |
| 479 | if (channelList->getChannel((*it).first) == 0) |
| 480 | serverConnection->queue->sendJoin((*it).first, (*it).second->key); |
| 481 | } |
| 482 | |
| 483 | if (currentTime >= (std::time_t)(serverConnection->serverLastSpoken |
| 484 | + Bot::PING_TIME) && !sentPing) |
| 485 | { |
| 486 | serverConnection->queue->sendPing("Testing connection"); |
| 487 | sentPing = true; |
| 488 | } |
| 489 | |
| 490 | if (currentTime >= (std::time_t)(serverConnection->serverLastSpoken |
| 491 | + Bot::TIMEOUT)) |
| 492 | { |
| 493 | sentPing = false; |
| 494 | nextServer(); |
| 495 | } |
| 496 | } |
| 497 | |
| 498 | // We can change server if we will not lose op on a channel |
| 499 | bool |
| 500 | Bot::canChangeServer() |
| 501 | { |
| 502 | String channel; |
| 503 | Channel *c; |
| 504 | |
| 505 | for (std::map<String, Channel *, std::less<String> >::iterator it = |
| 506 | channelList->begin(); |
| 507 | it != channelList->end(); ++it) { |
| 508 | channel = (*it).first; |
| 509 | c = channelList->getChannel(channel); |
| 510 | if (c->operator_count () == 1 && |
| 511 | c->user_count () > 1 && this->iAmOp(channel)) |
| 512 | return false; |
| 513 | } |
| 514 | return true; |
| 515 | } |
| 516 | |
| 517 | void |
| 518 | Bot::nextServer() |
| 519 | { |
| 520 | bool cont = false; |
| 521 | |
| 522 | if (channelList) |
| 523 | channelList->clear(); |
| 524 | |
| 525 | if (serverConnection) |
| 526 | userList->removeFirst(); |
| 527 | |
| 528 | delete serverConnection; |
| 529 | |
| 530 | do { |
| 531 | Server * s = serverList->nextServer(); |
| 532 | if (!s) { |
| 533 | std::cout << "No server found. Exiting..." << std::endl; |
| 534 | std::exit(1); |
| 535 | } |
| 536 | serverConnection = new ServerConnection(this, s, localIP); |
| 537 | if (!serverConnection->connect()) { |
| 538 | cont = true; |
| 539 | // We sleep 10 seconds, to avoid connection flood |
| 540 | sleep(10); |
| 541 | delete serverConnection; |
| 542 | } else { |
| 543 | cont = false; |
| 544 | } |
| 545 | } while (cont); |
| 546 | } |
| 547 | |
| 548 | void |
| 549 | Bot::reconnect() |
| 550 | { |
| 551 | if (channelList) |
| 552 | channelList->clear(); |
| 553 | |
| 554 | userList->removeFirst(); |
| 555 | |
| 556 | delete serverConnection; |
| 557 | |
| 558 | serverConnection = |
| 559 | new ServerConnection(this, serverList->currentServer(), localIP); |
| 560 | |
| 561 | serverConnection->connect(); |
| 562 | } |
| 563 | |
| 564 | void |
| 565 | Bot::connect(int serverNumber) |
| 566 | { |
| 567 | if (channelList) |
| 568 | channelList->clear(); |
| 569 | |
| 570 | userList->removeFirst(); |
| 571 | |
| 572 | delete serverConnection; |
| 573 | |
| 574 | serverConnection = |
| 575 | new ServerConnection(this, serverList->get(serverNumber), localIP); |
| 576 | |
| 577 | serverConnection->connect(); |
| 578 | } |
| 579 | |
| 580 | void |
| 581 | Bot::addDCC(Person * from, unsigned long address, int port, int type) |
| 582 | { |
| 583 | DCCConnection *d = 0; |
| 584 | |
| 585 | if (type == CHAT) |
| 586 | { |
| 587 | d = new DCCChatConnection(this, from->getAddress (), |
| 588 | address, port); |
| 589 | } |
| 590 | else |
| 591 | { |
| 592 | return; |
| 593 | } |
| 594 | |
| 595 | if (!d->connect()) |
| 596 | { |
| 597 | logLine ("DCC Connection failed from " + from->getAddress ()); |
| 598 | return; |
| 599 | } |
| 600 | |
| 601 | logLine ("DCC CHAT accepted from" + from->getAddress ()); |
| 602 | dccConnections->addConnection (d); |
| 603 | } |
| 604 | |
| 605 | void |
| 606 | Bot::rehash() |
| 607 | { |
| 608 | for (std::map<String, Channel *, std::less<String> >::iterator it = |
| 609 | channelList->begin(); |
| 610 | it != channelList->end(); ++it) |
| 611 | serverConnection->queue->sendWho((*it).first); |
| 612 | } |
| 613 | |
| 614 | String |
| 615 | Bot::getUserhost(String channel, String nick) |
| 616 | { |
| 617 | Channel *c; |
| 618 | |
| 619 | if (channel == "") |
| 620 | c = 0; |
| 621 | else |
| 622 | c = channelList->getChannel(channel); |
| 623 | |
| 624 | nick = nick.toLower(); |
| 625 | |
| 626 | |
| 627 | if (c && c->hasNick(nick)) |
| 628 | return c->getUser(nick).userhost; |
| 629 | |
| 630 | unsigned long num = sentUserhostID++; |
| 631 | |
| 632 | serverConnection->queue->sendUserhost(nick); |
| 633 | userhostMap[num] = "+"; |
| 634 | |
| 635 | while (userhostMap[num] == "+") { |
| 636 | waitForInput(); |
| 637 | serverConnection->queue->flush(); |
| 638 | } |
| 639 | |
| 640 | // We have got our answer |
| 641 | String res = userhostMap[num]; |
| 642 | userhostMap.erase(num); |
| 643 | |
| 644 | return res; |
| 645 | } |
| 646 | |
| 647 | bool |
| 648 | Bot::iAmOp(String channel) |
| 649 | { |
| 650 | return channelList->getChannel(channel)->getUser(nickName).mode & User::OP_MODE; |
| 651 | } |
| 652 | |
| 653 | void |
| 654 | Bot::init_user_functions () |
| 655 | { |
| 656 | // User Functions |
| 657 | #define uf(f, l, b) new userFunction (f, l, b); |
| 658 | userFunctions["ACTION"] = uf (UserCommands::Action, User::USER, true); |
| 659 | userFunctions["ADDUSER"] = uf (UserCommands::AddUser, User::FRIEND, false); |
| 660 | userFunctions["ADDSERVER"] = uf (UserCommands::AddServer, User::FRIEND, |
| 661 | false); |
| 662 | userFunctions["ADDSHIT"] = uf (UserCommands::AddShit, User::FRIEND, false); |
| 663 | userFunctions["ALIAS"] = uf (UserCommands::Alias, User::MASTER, false); |
| 664 | userFunctions["BAN"] = uf (UserCommands::Ban, User::USER, true); |
| 665 | userFunctions["BANLIST"] = uf (UserCommands::BanList, User::USER, true); |
| 666 | userFunctions["CHANNELS"] = |
| 667 | uf (UserCommands::Channels, User::FRIEND, false); |
| 668 | userFunctions["CYCLE"] = uf (UserCommands::Cycle, User::FRIEND, true); |
| 669 | userFunctions["DCCLIST"] = uf (UserCommands::DCCList, User::FRIEND, false); |
| 670 | userFunctions["DEBAN"] = uf (UserCommands::Deban, User::USER, true); |
| 671 | userFunctions["DELSERVER"] = uf (UserCommands::DelServer, User::FRIEND, |
| 672 | false); |
| 673 | userFunctions["DELUSER"] = uf (UserCommands::DelUser, User::FRIEND, false); |
| 674 | userFunctions["DELSHIT"] = uf (UserCommands::DelShit, User::FRIEND, false); |
| 675 | userFunctions["DEOP"] = uf (UserCommands::Deop, User::TRUSTED_USER, true); |
| 676 | userFunctions["DIE"] = uf (UserCommands::Die, User::MASTER, false); |
| 677 | userFunctions["DO"] = uf (UserCommands::Do, User::MASTER, false); |
| 678 | #ifdef USESCRIPTS |
| 679 | userFunctions["EXECUTE"] = uf (UserCommands::Execute, User::MASTER, false); |
| 680 | #endif |
| 681 | userFunctions["HELP"] = uf (UserCommands::Help, User::NONE, false); |
| 682 | userFunctions["IDENT"] = uf (UserCommands::Ident, User::NONE, true); |
| 683 | userFunctions["INVITE"] = uf (UserCommands::Invite, User::USER, true); |
| 684 | userFunctions["JOIN"] = uf (UserCommands::Join, User::FRIEND, false); |
| 685 | userFunctions["KEEP"] = uf (UserCommands::Keep, User::FRIEND, true); |
| 686 | userFunctions["KICK"] = uf (UserCommands::Kick, User::USER, true); |
| 687 | userFunctions["KICKBAN"] = uf (UserCommands::KickBan, User::USER, true); |
| 688 | userFunctions["LOAD"] = uf (UserCommands::Load, User::FRIEND, false); |
| 689 | #ifdef USESCRIPTS |
| 690 | userFunctions["LOADSCRIPT"] = uf (UserCommands::LoadScript, User::MASTER, |
| 691 | false); |
| 692 | #endif |
| 693 | userFunctions["LOCK"] = uf (UserCommands::Lock, User::FRIEND, true); |
| 694 | userFunctions["MODE"] = uf (UserCommands::Mode, User::FRIEND, true); |
| 695 | userFunctions["MSG"] = uf (UserCommands::Msg, User::USER, false); |
| 696 | userFunctions["NAMES"] = uf (UserCommands::Names, User::USER, true); |
| 697 | userFunctions["NEXTSERVER"] = uf (UserCommands::NextServer, User::FRIEND, |
| 698 | false); |
| 699 | userFunctions["NICK"] = uf (UserCommands::Nick, User::FRIEND, false); |
| 700 | userFunctions["NSLOOKUP"] = uf (UserCommands::NsLookup, User::USER, false); |
| 701 | userFunctions["OP"] = uf (UserCommands::Op, User::TRUSTED_USER, true); |
| 702 | userFunctions["PART"] = uf (UserCommands::Part, User::FRIEND, true); |
| 703 | userFunctions["PASSWORD"] = uf (UserCommands::Password, User::USER, true); |
| 704 | userFunctions["RECONNECT"] = |
| 705 | uf (UserCommands::Reconnect, User::FRIEND, false); |
| 706 | userFunctions["RSPYMESSAGE"] = |
| 707 | uf (UserCommands::RSpyMessage, User::USER, false); |
| 708 | userFunctions["SAVE"] = uf (UserCommands::Save, User::FRIEND, false); |
| 709 | userFunctions["SAY"] = uf (UserCommands::Say, User::USER, true); |
| 710 | userFunctions["SERVER"] = uf (UserCommands::Server, User::FRIEND, false); |
| 711 | userFunctions["SERVERLIST"] = |
| 712 | uf (UserCommands::ServerList, User::FRIEND, false); |
| 713 | userFunctions["SETFLOODRATE"] = |
| 714 | uf (UserCommands::SetFloodRate, User::MASTER, false); |
| 715 | userFunctions["SETVERSION"] = |
| 716 | uf (UserCommands::SetVersion, User::MASTER, false); |
| 717 | userFunctions["SHITLIST"] = |
| 718 | uf (UserCommands::ShitList, User::FRIEND, false); |
| 719 | userFunctions["SPYLIST"] = uf (UserCommands::SpyList, User::USER, false); |
| 720 | userFunctions["SPYMESSAGE"] = |
| 721 | uf (UserCommands::SpyMessage, User::USER, false); |
| 722 | userFunctions["STATS"] = uf (UserCommands::Stats, User::FRIEND, true); |
| 723 | userFunctions["TBAN"] = uf (UserCommands::TBan, User::USER, true); |
| 724 | userFunctions["TKBAN"] = uf (UserCommands::TKBan, User::USER, true); |
| 725 | userFunctions["TOPIC"] = uf (UserCommands::Topic, User::USER, true); |
| 726 | userFunctions["UNLOCK"] = uf (UserCommands::Unlock, User::FRIEND, true); |
| 727 | userFunctions["USERLIST"] = |
| 728 | uf (UserCommands::UserList, User::FRIEND, false); |
| 729 | userFunctions["WHO"] = uf (UserCommands::Who, User::NONE, true); |
| 730 | userFunctions["WHOIS"] = uf (UserCommands::Whois, User::FRIEND, true); |
| 731 | #undef uf |
| 732 | } |
| 733 | |
| 734 | namespace |
| 735 | { |
| 736 | void erase_userf (std::pair<std::string, class userFunction*> it) |
| 737 | { |
| 738 | delete it.second; |
| 739 | } |
| 740 | } |
| 741 | |
| 742 | void |
| 743 | Bot::destroy_user_functions () |
| 744 | { |
| 745 | std::for_each (userFunctions.begin (), |
| 746 | userFunctions.end (), |
| 747 | erase_userf); |
| 748 | userFunctions.erase (userFunctions.begin (), |
| 749 | userFunctions.end ()); |
| 750 | } |
| 751 | |
| 752 | void |
| 753 | Bot::set_log_file (String name) |
| 754 | { |
| 755 | logFileName = logs_dir + name; |
| 756 | logFile.close (); |
| 757 | logFile.clear (); |
| 758 | #if HAVE_IOSBASE |
| 759 | logFile.open(logFileName.c_str (), std::ios_base::out | |
| 760 | std::ios_base::ate | std::ios_base::app); |
| 761 | #else |
| 762 | logFile.open(logFileName.c_str (), ios::out | ios::ate |
| 763 | | ios::app); |
| 764 | #endif |
| 765 | |
| 766 | logLine("Starting log."); |
| 767 | } |
| 768 | |
| 769 | void |
| 770 | Bot::set_log_dir (String dir) |
| 771 | { |
| 772 | logs_dir = dir; |
| 773 | |
| 774 | DIR *temp = opendir (logs_dir.c_str ()); |
| 775 | |
| 776 | if (!temp) |
| 777 | { |
| 778 | mkdir (logs_dir.c_str (), S_IRWXU); |
| 779 | } |
| 780 | else |
| 781 | { |
| 782 | closedir (temp); |
| 783 | } |
| 784 | } |