Quiet annoying mutex debug logging messages
[clinton/bobotpp.git] / source / Bot.C
index f372f60..ed8aa50 100644 (file)
@@ -1,6 +1,6 @@
 // Bot.C  -*- C++ -*-
 // Copyright (c) 1997, 1998 Etienne BERNARD
-// Copyright (C) 2002 Clinton Ebadi
+// Copyright (C) 2002,2003,2005 Clinton Ebadi
 
 // 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
 
 // 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.
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+// 02110-1301, USA.
 
-#include <fstream>
-#include <iomanip>
 #include <cstring>
 #include <cstdlib>
 #include <cstdio>
+
+#include <fstream>
+#include <algorithm>
+#include <iomanip>
+
+#include <dirent.h>
 #include <sys/time.h>
 #include <sys/types.h>
+#include <sys/stat.h>
 #include <unistd.h>
 
+#ifdef USESCRIPTS
+#include <libguile.h>
+#endif
+
 #include "Bot.H"
 #include "DCCConnection.H"
-#include "StringTokenizer.H"
+#include "DCCChatConnection.H"
+#include "ChannelList.H"
+#include "DCCManager.H"
+#include "DCCPerson.H"
+#include "Parser.H"
+#include "Person.H"
+#include "Server.H"
 #include "ServerConnection.H"
+#include "ServerList.H"
+#include "ShitList.H"
+#include "StringTokenizer.H"
+#include "User.H"
+#include "UserCommands.H"
+#include "UserList.H"
 #include "Utils.H"
 
+
+#ifdef USESCRIPTS
+#include "BotInterp.H"
+#include "Interp.H"
+#endif
+
+unsigned int Bot::MAX_MESSAGES = 2;
+unsigned int Bot::MAX_NICKLENGTH = 9;
+
 #define DEFAULT_NICKNAME "Bobot"
 #define DEFAULT_USERNAME "bobot"
 #define DEFAULT_IRCNAME "I'm a bobot++!"
@@ -40,6 +71,7 @@
 #define DEFAULT_HELPFILENAME "bot.help"
 #define DEFAULT_SCRIPTLOGFILENAME "script.log"
 #define DEFAULT_LOGFILENAME "bot.log"
+#define DEFAULT_LOGDIR getenv ("HOME") + String("/.bobotpp/logs/")
 #define DEFAULT_INITFILENAME "bot.init"
 #ifdef USESCRIPTS
 #define DEFAULT_AUTOEXECFILENAME "bot.autoexec"
@@ -54,24 +86,29 @@ Bot::Bot(String filename, bool debug_on)
     userHost(""),
     localIP(""),
     commandChar(DEFAULT_COMMANDCHAR),
-    configFileName(filename),
     userListFileName(DEFAULT_USERLISTFILENAME),
     shitListFileName(DEFAULT_SHITLISTFILENAME),
-    logFileName(DEFAULT_LOGFILENAME),
     helpFileName(DEFAULT_HELPFILENAME),
     initFileName(DEFAULT_INITFILENAME),
+    connected(false),
+    debug(debug_on), 
+    stop(false), 
+    sentPing(false),
+    startTime(time(NULL)), 
+    currentTime(startTime),
+    lastNickNameChange(startTime), 
+    lastChannelJoin(startTime),
+    serverConnection(0), 
+    sentUserhostID(0), 
+    receivedUserhostID(0),
+    logFileName(DEFAULT_LOGFILENAME),
+    logs_dir (DEFAULT_LOGDIR),
+    configFileName (filename)
 #ifdef USESCRIPTS
-    scriptLogFileName(DEFAULT_SCRIPTLOGFILENAME),
-    autoexecFileName(DEFAULT_AUTOEXECFILENAME),
+    ,scriptLogFileName(DEFAULT_SCRIPTLOGFILENAME),
+    autoexecFileName(DEFAULT_AUTOEXECFILENAME)
 #endif
-    connected(false),
-    debug(debug_on), stop(false), sentPing(false),
-    startTime(time(NULL)), currentTime(startTime),
-    lastNickNameChange(startTime), lastChannelJoin(startTime),
-    serverConnection(0), sentUserhostID(0), receivedUserhostID(0)
 {
-  extern userFunctionsStruct userFunctionsInit[];
-
 #ifdef HAVE_STL_CLEAR
   wantedChannels.clear();
   ignoredUserhosts.clear();
@@ -79,107 +116,87 @@ Bot::Bot(String filename, bool debug_on)
   userhostMap.clear();
 #endif
 
-  for (int i = 0; userFunctionsInit[i].name[0] != '\0'; i++) {
-    userFunctions.push_back(new
-                            userFunction(String(userFunctionsInit[i].name),
-                                         userFunctionsInit[i].function,
-                                         userFunctionsInit[i].minLevel,
-                                         userFunctionsInit[i].needsChannelName));
-  }
+  init_user_functions ();
+
+  set_log_dir (logs_dir);
+  set_log_file (logFileName);
+
 
-#if HAVE_IOSBASE
-  logFile.open(logFileName, std::ios_base::out | std::ios_base::ate 
-              | std::ios_base::app);
-#else
- logFile.open(logFileName, ios::out | ios::ate 
-             | ios::app);
-#endif
-  logLine("Starting log.");
   channelList = new ChannelList();
   serverList = new ServerList();
   readConfig();
   userList = new UserList(userListFileName);
   shitList = new ShitList(shitListFileName);
-  todoList = new TodoList();
+  dccConnections = new DCCManager ();
 
   // Let's read the alias file
-  std::ifstream initFile(initFileName);
-
-  if (initFile) {
-    String temp, alias, command;
-    std::list<userFunction *>::iterator it;
-    bool found = false;
-    userFunction *u;
-    int line = 0;
-    while (initFile >> temp, temp.length() != 0) {
-      line++;
-      StringTokenizer st(temp);
-      temp = temp.trim();
-      if (temp[0]=='#') continue;
-      if (st.countTokens(' ') != 2) {
-        std::cerr << "Error when reading alias file (" << initFileName 
-                 << ") line " << line << "...\n";
-        continue;
-      }
-      alias = st.nextToken().toUpper();
-      command = st.nextToken().toUpper();
-
-      // Does the function already exist ?
-      found = false;
-      for (it = userFunctions.begin(); it != userFunctions.end(); ++it)
-        if (alias == (*it)->name) {
-          found = true;
-          break;
-        }
-      if (found) continue;
-
-      // Check that the command exists
-      found = false;
-      for (it = userFunctions.begin(); it != userFunctions.end(); ++it)
-        if (command == (*it)->name) {
-          found = true;
-          u = *it;
-          break;
-        }
-      if (!found) continue;
-
-      userFunctions.push_back (new
-                               userFunction((char *)(const char *)alias,
-                                           u->function,
-                                           u->minLevel,
-                                           u->needsChannelName));
+  std::ifstream initFile(initFileName.c_str ());
+
+  if (initFile) 
+    {
+      // FIXME: these variables are current String instead of
+      // std::string because String>> reads an entire line. This code
+      // needs to be rewritten to use std::string and std::getline (or
+      // better yet, be removed entirely once BotConfig is in place)
+      String temp, alias, command;
+      int line = 0;
+
+      while (initFile >> temp, temp.length() != 0) 
+       {
+         StringTokenizer st(temp);
+
+         line++;
+         temp = Utils::trim_str (temp);
+
+         if (temp[0]=='#')
+           {
+             continue;
+           }
+
+         if (st.count_tokens (' ') != 2) 
+           {
+             std::cerr << "Error when reading alias file (" << initFileName 
+                       << ") line " << line << "...\n";
+             continue;
+           }
+
+         alias = Utils::to_upper (st.next_token());
+         command = Utils::to_upper (st.next_token());
+
+         // Does the function already exist ?
+         if (!userFunctions[alias])
+           {
+             if (userFunction *u = userFunctions[command])
+               userFunctions[alias] = 
+                 new
+                 userFunction(u->function,
+                              u->minLevel,
+                              u->needsChannelName);
+           }
+       }
     }
-  }
+
 
   std::srand (std::time (0)); // srand for bot-random
+
 #ifdef USESCRIPTS
-  botInterp = new BotInterp(this, scriptLogFileName);
+  botInterp = new BotInterp(this, logs_dir + scriptLogFileName);
+  Interp::Startup2 (this);
   botInterp->LoadScript(autoexecFileName);
 #endif
 }
 
 Bot::~Bot()
 {
-  // TODO: is it ok to delete iterators!?!
-
   Person *p;
   while (spyList.size() != 0) {
     p = (*spyList.begin()).second;
     spyList.erase(spyList.begin());
     delete p;
   }
-  DCCConnection *d;
-  while (dccConnections.size() != 0) {
-    d = *dccConnections.begin();
-    dccConnections.erase(dccConnections.begin());
-    delete d;
-  }
-  userFunction *u;
-  while (userFunctions.size() != 0) {
-    u = *userFunctions.begin();
-    userFunctions.erase(userFunctions.begin());
-    delete u;
-  }
+  delete dccConnections;
+  destroy_user_functions ();
+
   wantedChannel *w;
   while (wantedChannels.size() != 0) {
     w = (*wantedChannels.begin()).second;
@@ -190,7 +207,6 @@ Bot::~Bot()
   shitList->save();
   delete channelList;
   delete userList;
-  delete todoList;
   delete serverList;
   delete shitList;
   delete serverConnection;
@@ -219,7 +235,7 @@ Bot::logLine(String line)
 void
 Bot::readConfig()
 {
-  std::ifstream file(configFileName);
+  std::ifstream file(configFileName.c_str ());
   String temp;
   int line = 1;
 
@@ -232,14 +248,14 @@ Bot::readConfig()
 
     file >> temp;
 
-    if (temp.length() == 0 || temp[0] == '#') {
+    if (temp.length() == 0 || temp[(unsigned int)0] == '#') {
       line++;
       continue;
     }
     
     StringTokenizer st(temp);
-    String command = st.nextToken('=').trim().toUpper();
-    String parameters = st.nextToken('=').trim();
+    String command = Utils::to_upper (Utils::trim_str (st.next_token('=')));
+    String parameters = Utils::trim_str (st.next_token('='));
 
     if (command == "NICK" || command == "NICKNAME")
       nickName = wantedNickName = parameters;
@@ -248,7 +264,7 @@ Bot::readConfig()
     else if (command == "IRCNAME" || command == "REALNAME")
       ircName = parameters;
     else if (command == "CMDCHAR" || command == "COMMAND")
-      commandChar = parameters[0];
+      commandChar = parameters[(unsigned int)0];
     else if (command == "USERLIST")
       userListFileName = parameters;
     else if (command == "SHITLIST")
@@ -260,20 +276,43 @@ Bot::readConfig()
                  << " I will use compatibility mode, but you're really"
                  << " missing something.\n";
         StringTokenizer st2(parameters);
-        String name = st2.nextToken().toLower();
-        String key = st2.nextToken();
+        String name = Utils::to_lower (st2.next_token());
+        String key = st2.next_token();
         wantedChannels[name] = new wantedChannel("", "", key);
       } else {
         StringTokenizer st2(parameters);
-        String name = st2.nextToken(':').toLower();
-        String mode = st2.nextToken(':');
-        String keep = st2.nextToken(':');
-        String key = st2.nextToken(':');
+        String name = Utils::to_lower (st2.next_token(':'));
+        String mode = st2.next_token(':');
+        String keep = st2.next_token(':');
+        String key = st2.next_token(':');
         wantedChannels[name] = new wantedChannel(mode, keep, key);
       }
     }
     else if (command == "LOGFILE")
-      logFileName = parameters;
+      {
+       if (parameters != logFileName)
+         {
+           if (parameters[(unsigned int)0] == '/')
+             {
+               StringTokenizer log_st (parameters);
+               std::string log_dir = "/";
+
+               for (unsigned int m = log_st.count_tokens ('/');
+                    --m;
+                    m > 0)
+                 {
+                   log_dir += log_st.next_token ('/') + "/";
+                 }
+
+               std::cerr << "==" << log_dir << std::endl;
+
+               set_log_dir (log_dir);
+               set_log_file (log_st.rest ());
+             }
+           else
+             set_log_file (parameters);
+         }
+      }
 #ifdef USESCRIPTS
     else if (command == "SCRIPTLOGFILE")
       scriptLogFileName = parameters;
@@ -284,18 +323,24 @@ Bot::readConfig()
       initFileName = parameters;
     else if (command == "LOCALIP")
       localIP = parameters;
+    else if (command == "MAXNICKLENGTH")
+      MAX_NICKLENGTH = std::atoi (parameters.c_str ());
     else if (command == "SERVER") {
       if (parameters.indexOf(' ') == -1)
         serverList->addServer(new Server(parameters));
       else {
         StringTokenizer st2(parameters);
-        String name = st2.nextToken();
-        int port = std::atoi(st2.nextToken());
+        String name = st2.next_token();
+        int port = std::atoi(st2.next_token().c_str());
         serverList->addServer(new Server(name,
                                          port,
-                                         st2.nextToken()));
+                                         st2.next_token()));
       }
     }
+    else if (command == "")
+      {
+       // do nothing
+      }
     else {
       logLine(String("Syntax error in file ") + configFileName +
               ", line " + String((long)line));
@@ -314,11 +359,26 @@ Bot::run()
 {
   nextServer();
 
-  while (!stop) {
-    waitForInput();  // This is the main event loop
-    if (!serverConnection->queue->flush())
-      nextServer();
-  }
+  while (!stop) 
+    {
+      waitForInput();  // This is the main event loop
+      dccConnections->checkStale ();
+
+      if (!serverConnection->queue->flush())
+       {
+         // Disconnected
+#ifdef USESCRIPTS
+         // Run hooks/disconnect
+         this->botInterp->RunHooks 
+           (Hook::DISCONNECT, 
+            serverConnection->server->getHostName (),
+            scm_list_n 
+            (Utils::str2scm  (serverConnection->server->getHostName ()),
+             SCM_BOOL_F));
+#endif
+         nextServer();
+       }
+    }
 }
 
 void
@@ -341,9 +401,10 @@ Bot::waitForInput()
   FD_SET(sock, &rd);
 #endif
 
-  for (std::list<DCCConnection *>::iterator it = dccConnections.begin();
-       it != dccConnections.end(); ++it) {
-    int s = (*it)->getFileDescriptor();
+  DCC_MAP* dccmap = &dccConnections->dcc_map;
+  for (DCC_MAP::iterator it = dccmap->begin ();
+       it != dccmap->end(); ++it) {
+    int s = it->second->dcc->getFileDescriptor();
 #ifdef _HPUX_SOURCE
     rd |= s;
 #else
@@ -370,49 +431,36 @@ Bot::waitForInput()
       if (serverConnection->handleInput())
         nextServer();
 
-    std::list<DCCConnection *>::iterator it = dccConnections.begin();
-    std::list<DCCConnection *>::iterator it2;
-
-    while (it != dccConnections.end()) {
-      it2 = it;
-      ++it;
-#ifdef _HPUX_SOURCE
-      if (rd & (*it2)->getFileDescriptor()) {
-#else
-      if (FD_ISSET((*it2)->getFileDescriptor(), &rd)) {
-#endif
-        if ((*it2)->handleInput()) {
-          delete *it2;
-          dccConnections.erase(it2);
-        }
-      }
-    }
+    dccConnections->checkInput (rd);
   }
 
-  if (currentTime < std::time(NULL)) { // Actions that we do each second
-    currentTime = std::time(NULL);
+  if (currentTime < std::time(0)) { // Actions that we do each second
+    currentTime = std::time(0);
     for (std::map<String, unsigned int, std::less<String> >::iterator
            it = ignoredUserhosts.begin();
          it != ignoredUserhosts.end(); ++it)
       if ((*it).second > 0)
         (*it).second--;
 
-    String line;
-    while ((line = todoList->getNext()) != "") {
-      serverConnection->queue->sendChannelMode(line);
-    }
+    for (std::map<String, Channel *, std::less<String> >::iterator it = channelList->begin ();
+        it != channelList->end ();
+        ++it)
+      {
+       it->second->purge_expired_bans ();
+      }
 #ifdef USESCRIPTS
     botInterp->RunTimers(currentTime);
-#endif
 
-#ifdef USESCRIPTS
     tm *thisTime = localtime(&currentTime);
-    if (thisTime->tm_sec == 0) {
-      char s[6];
-      sprintf(s, "%2d:%2d", thisTime->tm_hour, thisTime->tm_min);
-      botInterp->RunHooks(Hook::TIMER, String(s),
-                          gh_list(Utils::string2SCM(String(s)), SCM_UNDEFINED));
-    }
+    if (thisTime->tm_sec == 0)
+      {
+       char s[6];
+       std::snprintf(s, 6, "%02d:%02d", thisTime->tm_hour, thisTime->tm_min);
+       
+       botInterp->RunHooks(Hook::TIMER, String(s),
+                           scm_list_n (Utils::str2scm (std::string (s)), 
+                                       SCM_UNDEFINED));
+      }
 #endif
 
   }
@@ -432,27 +480,19 @@ Bot::waitForInput()
          serverConnection->queue->sendJoin((*it).first, (*it).second->key);
   }
 
-  std::list<DCCConnection *>::iterator it2;
-
-  for (std::list<DCCConnection *>::iterator it = dccConnections.begin();
-       it != dccConnections.end(); ) {
-    it2 = it;
-    ++it;
-    if ((*it2)->autoRemove && currentTime >= (std::time_t)((*it2)->lastSpoken + Bot::DCC_DELAY)) {
-      delete *it2;
-      dccConnections.erase(it2);
+  if (currentTime >= (std::time_t)(serverConnection->serverLastSpoken 
+                                  + Bot::PING_TIME) && !sentPing) 
+    {
+      serverConnection->queue->sendPing("Testing connection");
+      sentPing = true;
     }
-  }
 
-  if (currentTime >= (std::time_t)(serverConnection->serverLastSpoken + Bot::PING_TIME) && !sentPing) {
-    serverConnection->queue->sendPing("Testing connection");
-    sentPing = true;
-  }
-
-  if (currentTime >= (std::time_t)(serverConnection->serverLastSpoken + Bot::TIMEOUT)) {
-    sentPing = false;
-    nextServer();
-  }
+  if (currentTime >= (std::time_t)(serverConnection->serverLastSpoken 
+                                  + Bot::TIMEOUT)) 
+    {
+      sentPing = false;
+      nextServer();
+    }
 }
 
 // We can change server if we will not lose op on a channel
@@ -467,8 +507,8 @@ Bot::canChangeServer()
        it != channelList->end(); ++it) {
     channel = (*it).first;
     c = channelList->getChannel(channel);
-    if (c->countOp == 1 &&
-        c->count > 1 && this->iAmOp(channel))
+    if (c->operator_count () == 1 &&
+        c->user_count () > 1 && this->iAmOp(channel))
       return false;
   }
   return true;
@@ -538,21 +578,35 @@ Bot::connect(int serverNumber)
 }
 
 void
-Bot::addDCC(Person * from, unsigned long address, int port)
+Bot::addDCC(Person * from, unsigned long address, int port, int type)
 {
-  DCCConnection * d = new DCCConnection(this, from->getAddress(),
-                                        address, port);
+  DCCConnection *d = 0;
+
+  if (type == CHAT)
+    {
+      d = new DCCChatConnection(this, from->getAddress (),
+                               address, port);
+    }
+  else
+    {
+      return;
+    }
 
   if (!d->connect())
-    return;
+    {
+      logLine ("DCC Connection failed from " + from->getAddress ());
+      return;
+    }
 
-  dccConnections.push_back(d);
+  logLine ("DCC CHAT accepted from" + from->getAddress ());
+  dccConnections->addConnection (d);
 }
 
 void
 Bot::rehash()
 {
-  for (std::map<String, Channel *, std::less<String> >::iterator it = channelList->begin();
+  for (std::map<String, Channel *, std::less<String> >::iterator it = 
+        channelList->begin();
        it != channelList->end(); ++it)
     serverConnection->queue->sendWho((*it).first);
 }
@@ -571,7 +625,7 @@ Bot::getUserhost(String channel, String nick)
 
 
   if (c && c->hasNick(nick))
-    return c->getUser(nick)->userhost;
+    return c->getUser(nick).userhost;
 
   unsigned long num = sentUserhostID++;
 
@@ -593,6 +647,138 @@ Bot::getUserhost(String channel, String nick)
 bool
 Bot::iAmOp(String channel)
 {
-  User * me = channelList->getChannel(channel)->getUser(nickName);
-  return (me->mode & User::OP_MODE);
+  return channelList->getChannel(channel)->getUser(nickName).mode & User::OP_MODE;
+}
+
+void
+Bot::init_user_functions ()
+{
+    // User Functions
+#define uf(f, l, b) new userFunction (f, l, b);
+  userFunctions["ACTION"] = uf (UserCommands::Action, User::USER, true);
+  userFunctions["ADDUSER"] = uf (UserCommands::AddUser, User::FRIEND, false);
+  userFunctions["ADDSERVER"] = uf (UserCommands::AddServer, User::FRIEND,
+                                   false);
+  userFunctions["ADDSHIT"] = uf (UserCommands::AddShit, User::FRIEND, false);
+  userFunctions["ALIAS"] = uf (UserCommands::Alias, User::MASTER, false);
+  userFunctions["BAN"] = uf (UserCommands::Ban, User::USER, true);
+  userFunctions["BANLIST"] = uf (UserCommands::BanList, User::USER, true);
+  userFunctions["CHANNELS"] =
+    uf (UserCommands::Channels, User::FRIEND, false);
+  userFunctions["CYCLE"] = uf (UserCommands::Cycle, User::FRIEND, true);
+  userFunctions["DCCLIST"] = uf (UserCommands::DCCList, User::FRIEND, false);
+  userFunctions["DEBAN"] = uf (UserCommands::Deban, User::USER, true);
+  userFunctions["DELSERVER"] = uf (UserCommands::DelServer, User::FRIEND,
+                                   false);
+  userFunctions["DELUSER"] = uf (UserCommands::DelUser, User::FRIEND, false);
+  userFunctions["DELSHIT"] = uf (UserCommands::DelShit, User::FRIEND, false);
+  userFunctions["DEOP"] = uf (UserCommands::Deop, User::TRUSTED_USER, true);
+  userFunctions["DIE"] = uf (UserCommands::Die, User::MASTER, false);
+  userFunctions["DO"] = uf (UserCommands::Do, User::MASTER, false);
+#ifdef USESCRIPTS
+  userFunctions["EXECUTE"] = uf (UserCommands::Execute, User::MASTER, false);
+#endif
+  userFunctions["HELP"] = uf (UserCommands::Help, User::NONE, false);
+  userFunctions["IDENT"] = uf (UserCommands::Ident, User::NONE, true);
+  userFunctions["INVITE"] = uf (UserCommands::Invite, User::USER, true);
+  userFunctions["JOIN"] = uf (UserCommands::Join, User::FRIEND, false);
+  userFunctions["KEEP"] = uf (UserCommands::Keep, User::FRIEND, true);
+  userFunctions["KICK"] = uf (UserCommands::Kick, User::USER, true);
+  userFunctions["KICKBAN"] = uf (UserCommands::KickBan, User::USER, true);
+  userFunctions["LOAD"] = uf (UserCommands::Load, User::FRIEND, false);
+#ifdef USESCRIPTS
+  userFunctions["LOADSCRIPT"] = uf (UserCommands::LoadScript, User::MASTER,
+                                    false);
+#endif
+  userFunctions["LOCK"] = uf (UserCommands::Lock, User::FRIEND, true);
+  userFunctions["MODE"] = uf (UserCommands::Mode, User::FRIEND, true);
+  userFunctions["MSG"] = uf (UserCommands::Msg, User::USER, false);
+  userFunctions["NAMES"] = uf (UserCommands::Names, User::USER, true);
+  userFunctions["NEXTSERVER"] = uf (UserCommands::NextServer, User::FRIEND,
+                                    false);
+  userFunctions["NICK"] = uf (UserCommands::Nick, User::FRIEND, false);
+  userFunctions["NSLOOKUP"] = uf (UserCommands::NsLookup, User::USER, false);
+  userFunctions["OP"] = uf (UserCommands::Op, User::TRUSTED_USER, true);
+  userFunctions["PART"] = uf (UserCommands::Part, User::FRIEND, true);
+  userFunctions["PASSWORD"] = uf (UserCommands::Password, User::USER, true);
+  userFunctions["RECONNECT"] =
+    uf (UserCommands::Reconnect, User::FRIEND, false);
+  userFunctions["RSPYMESSAGE"] =
+    uf (UserCommands::RSpyMessage, User::USER, false);
+  userFunctions["SAVE"] = uf (UserCommands::Save, User::FRIEND, false);
+  userFunctions["SAY"] = uf (UserCommands::Say, User::USER, true);
+  userFunctions["SERVER"] = uf (UserCommands::Server, User::FRIEND, false);
+  userFunctions["SERVERLIST"] =
+    uf (UserCommands::ServerList, User::FRIEND, false);
+  userFunctions["SETFLOODRATE"] =
+    uf (UserCommands::SetFloodRate, User::MASTER, false);
+  userFunctions["SETVERSION"] =
+    uf (UserCommands::SetVersion, User::MASTER, false);
+  userFunctions["SHITLIST"] =
+    uf (UserCommands::ShitList, User::FRIEND, false);
+  userFunctions["SPYLIST"] = uf (UserCommands::SpyList, User::USER, false);
+  userFunctions["SPYMESSAGE"] =
+    uf (UserCommands::SpyMessage, User::USER, false);
+  userFunctions["STATS"] = uf (UserCommands::Stats, User::FRIEND, true);
+  userFunctions["TBAN"] = uf (UserCommands::TBan, User::USER, true);
+  userFunctions["TKBAN"] = uf (UserCommands::TKBan, User::USER, true);
+  userFunctions["TOPIC"] = uf (UserCommands::Topic, User::USER, true);
+  userFunctions["UNLOCK"] = uf (UserCommands::Unlock, User::FRIEND, true);
+  userFunctions["USERLIST"] =
+    uf (UserCommands::UserList, User::FRIEND, false);
+  userFunctions["WHO"] = uf (UserCommands::Who, User::NONE, true);
+  userFunctions["WHOIS"] = uf (UserCommands::Whois, User::FRIEND, true);
+#undef uf
+}
+
+namespace 
+{
+  void erase_userf (std::pair<std::string, class userFunction*> it)
+  {
+    delete it.second;
+  }
+}
+
+void
+Bot::destroy_user_functions ()
+{
+  std::for_each (userFunctions.begin (),
+           userFunctions.end (),
+           erase_userf);
+  userFunctions.erase (userFunctions.begin (),
+                      userFunctions.end ());
+}
+
+void
+Bot::set_log_file (String name)
+{
+  logFileName = name;
+  logFile.close ();
+  logFile.clear ();
+#if HAVE_IOSBASE
+  logFile.open((logs_dir + logFileName).c_str (), std::ios_base::out | 
+              std::ios_base::ate | std::ios_base::app);
+#else
+  logFile.open((logs_dir + logFileName).c_str (), ios::out | ios::ate 
+             | ios::app);
+#endif
+
+  logLine("Starting log.");
+}
+
+void
+Bot::set_log_dir (String dir)
+{
+  logs_dir = dir;
+
+  DIR *temp = opendir (logs_dir.c_str ());
+
+  if (!temp)
+    {
+      mkdir (logs_dir.c_str (), S_IRWXU);
+    }
+  else
+    {
+      closedir (temp);
+    }
 }