server/ban.cpp

Go to the documentation of this file.
00001 /* $Id: ban.cpp 52533 2012-01-07 02:35:17Z shadowmaster $ */
00002 /*
00003    Copyright (C) 2008 - 2012 by Pauli Nieminen <paniemin@cc.hut.fi>
00004    Part of the Battle for Wesnoth Project http://www.wesnoth.org/
00005 
00006    This program is free software; you can redistribute it and/or modify
00007    it under the terms of the GNU General Public License as published by
00008    the Free Software Foundation; either version 2 of the License, or
00009    (at your option) any later version.
00010    This program is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY.
00012 
00013    See the COPYING file for more details.
00014 */
00015 
00016 #include "config.hpp"
00017 #include "foreach.hpp"
00018 #include "log.hpp"
00019 #include "filesystem.hpp"
00020 #include "serialization/parser.hpp"
00021 #include "serialization/binary_or_text.hpp"
00022 #include "serialization/string_utils.hpp"
00023 #include "util.hpp"
00024 
00025 #include "ban.hpp"
00026 
00027 
00028 #include <boost/bind.hpp>
00029 
00030 namespace wesnothd {
00031 
00032 
00033 static lg::log_domain log_server("server");
00034 #define ERR_SERVER LOG_STREAM(err, log_server)
00035 #define LOG_SERVER LOG_STREAM(info, log_server)
00036 #define DBG_SERVER LOG_STREAM(debug, log_server)
00037 
00038     std::ostream& operator<<(std::ostream& o, const banned& n)
00039     {
00040        return o << "IP: " << n.get_ip() <<
00041                     (n.get_nick().empty() ? "" : "  nick: " + n.get_nick()) <<
00042                     "  reason: '" << n.get_reason() << "'"
00043                     "  start_time: " << n.get_human_start_time() <<
00044                     "  end_time: " << n.get_human_end_time() <<
00045                     "  issuer: " <<  n.get_who_banned();
00046     }
00047 
00048     bool banned_compare::operator()(const banned_ptr& a, const banned_ptr& b) const
00049     {
00050         return (*a) > (*b);
00051     }
00052 
00053     banned_compare_subnet::compare_fn banned_compare_subnet::active_ = &banned_compare_subnet::less;
00054 
00055     bool banned_compare_subnet::operator()(const banned_ptr& a, const banned_ptr& b) const
00056     {
00057         return (this->*(active_))(a,b);
00058     }
00059 
00060     bool banned_compare_subnet::less(const banned_ptr& a, const banned_ptr& b) const
00061     {
00062         return a->get_int_ip() < b->get_int_ip();
00063     }
00064 
00065     const std::string banned::who_banned_default_ = "system";
00066 
00067     banned_ptr banned::create_dummy(const std::string& ip)
00068     {
00069         banned_ptr dummy(new banned(ip));
00070         return dummy;
00071     }
00072 
00073     banned::banned(const std::string& ip) :
00074         ip_(0),
00075         mask_(0),
00076         ip_text_(),
00077         end_time_(0),
00078         start_time_(0),
00079         reason_(),
00080         who_banned_(who_banned_default_),
00081         group_(),
00082         nick_()
00083     {
00084         ip_mask pair = parse_ip(ip);
00085         ip_ = pair.first;
00086         mask_ = 0xFFFFFFFF;
00087     }
00088 
00089     banned::banned(const std::string& ip,
00090                    const time_t end_time,
00091                    const std::string& reason,
00092                    const std::string& who_banned,
00093                    const std::string& group,
00094                    const std::string& nick) :
00095         ip_(0),
00096         mask_(0),
00097         ip_text_(ip),
00098         end_time_(end_time),
00099         start_time_(time(0)),
00100         reason_(reason),
00101         who_banned_(who_banned),
00102         group_(group),
00103         nick_(nick)
00104     {
00105         ip_mask pair = parse_ip(ip_text_);
00106         ip_ = pair.first;
00107         mask_ = pair.second;
00108     }
00109 
00110     banned::banned(const config& cfg) :
00111         ip_(0),
00112         mask_(0),
00113         ip_text_(),
00114         end_time_(0),
00115         start_time_(0),
00116         reason_(),
00117         who_banned_(who_banned_default_),
00118         group_(),
00119         nick_()
00120     {
00121         read(cfg);
00122     }
00123 
00124     ip_mask parse_ip(const std::string& ip)
00125     {
00126         // We use bit operations to construct the integer
00127         // ip_mask is a pair: first is ip and second is mask
00128         ip_mask ret;
00129         ret.first = 0;
00130         ret.second = 0;
00131         std::vector<std::string> split_ip = utils::split(ip, '.');
00132         if (split_ip.size() > 4) throw banned::error("Malformed ip address: " + ip);
00133 
00134         unsigned int shift = 4*8; // start shifting from the highest byte
00135         unsigned int mask = 0xFF000000;
00136         const unsigned int complete_part_mask = 0xFF;
00137         std::vector<std::string>::const_iterator part = split_ip.begin();
00138         bool wildcard = false;
00139         do {
00140             shift -= 8;
00141             mask >>= 8;
00142             if (part == split_ip.end())
00143             {
00144                 if (!wildcard)
00145                     throw banned::error("Malformed ip address: '" + ip + "'");
00146                 // Adding 0 to ip and mask is nop
00147                 // we can then break out of loop
00148                 break;
00149             } else {
00150                 if (*part == "*")
00151                 {
00152                     wildcard = true;
00153                     // Adding 0 to ip and mask is nop
00154                 } else {
00155                     //wildcard = false;
00156                     unsigned int part_ip = lexical_cast_default<unsigned int>(*part, complete_part_mask + 1);
00157                     if (part_ip > complete_part_mask)
00158                         throw banned::error("Malformed ip address: '" + ip + "'");
00159                     ret.first |= (part_ip << shift);
00160                     ret.second |= (complete_part_mask << shift);
00161                 }
00162             }
00163             ++part;
00164         } while (shift);
00165         return ret;
00166     }
00167 
00168     void banned::read(const config& cfg)
00169     {
00170         {
00171             // parse ip and mask
00172             ip_text_ = cfg["ip"].str();
00173             ip_mask pair = parse_ip(ip_text_);
00174             ip_ = pair.first;
00175             mask_ = pair.second;
00176         }
00177         nick_ = cfg["nick"].str();
00178         if (cfg.has_attribute("end_time"))
00179             end_time_ = lexical_cast_default<time_t>(cfg["end_time"], 0);
00180         if (cfg.has_attribute("start_time"))
00181             start_time_ = lexical_cast_default<time_t>(cfg["start_time"], 0);
00182         reason_ = cfg["reason"].str();
00183 
00184         // only overwrite defaults if exists
00185         if (cfg.has_attribute("who_banned"))
00186             who_banned_ = cfg["who_banned"].str();
00187         if (cfg.has_attribute("group"))
00188             group_ = cfg["group"].str();
00189     }
00190 
00191     void banned::write(config& cfg) const
00192     {
00193         cfg["ip"] = get_ip();
00194         cfg["nick"] = get_nick();
00195         if (end_time_ > 0)
00196         {
00197             std::stringstream ss;
00198             ss << end_time_;
00199             cfg["end_time"] = ss.str();
00200         }
00201         if (start_time_ > 0)
00202         {
00203             std::stringstream ss;
00204             ss << start_time_;
00205             cfg["start_time"] = ss.str();
00206         }
00207 
00208         cfg["reason"] = reason_;
00209         if (who_banned_ != who_banned_default_)
00210         {
00211             cfg["who_banned"] = who_banned_;
00212         }
00213         if (!group_.empty())
00214         {
00215             cfg["group"] = group_;
00216         }
00217     }
00218 
00219     std::string banned::get_human_start_time() const
00220     {
00221         if (start_time_ == 0)
00222             return "unknown";
00223         return lg::get_timestamp(start_time_, "%H:%M:%S %d.%m.%Y");
00224     }
00225 
00226     std::string banned::get_human_end_time() const
00227     {
00228         if (end_time_ == 0)
00229         {
00230             return "permanent";
00231         }
00232         return lg::get_timestamp(end_time_, "%H:%M:%S %d.%m.%Y");
00233     }
00234 
00235     bool banned::operator>(const banned& b) const
00236     {
00237         return end_time_ > b.get_end_time();
00238     }
00239 
00240     unsigned int banned::get_mask_ip(unsigned int mask) const
00241     {
00242         return ip_ & mask & mask_;
00243     }
00244 
00245     bool banned::match_ip(const ip_mask& pair) const {
00246         return (ip_ & mask_) == (pair.first & mask_);
00247     }
00248 
00249     // Unlike match_ip this function takes both masks into account.
00250     bool banned::match_ipmask(const ip_mask& pair) const {
00251         return (ip_ & mask_ & pair.second) == (pair.first & pair.second & mask_);
00252     }
00253 
00254     void ban_manager::read()
00255     {
00256         if (filename_.empty() || !file_exists(filename_))
00257             return;
00258         LOG_SERVER << "Reading bans from " <<  filename_ << "\n";
00259         config cfg;
00260         scoped_istream ban_file = istream_file(filename_);
00261         read_gz(cfg, *ban_file);
00262 
00263         foreach (const config &b, cfg.child_range("ban"))
00264         {
00265             try {
00266                 banned_ptr new_ban(new banned(b));
00267                 assert(bans_.insert(new_ban).second);
00268 
00269                 if (new_ban->get_end_time() != 0)
00270                     time_queue_.push(new_ban);
00271             } catch (banned::error& e) {
00272                 ERR_SERVER << e.message << " while reading bans\n";
00273             }
00274         }
00275 
00276         // load deleted too
00277         if (const config &cfg_del = cfg.child("deleted"))
00278         {
00279             foreach (const config &b, cfg_del.child_range("ban"))
00280             {
00281                 try {
00282                     banned_ptr new_ban(new banned(b));
00283                     deleted_bans_.push_back(new_ban);
00284                 } catch (banned::error& e) {
00285                     ERR_SERVER << e.message << " while reading deleted bans\n";
00286                 }
00287             }
00288         }
00289 
00290 
00291     }
00292 
00293     void ban_manager::write()
00294     {
00295         if (filename_.empty() || !dirty_)
00296             return;
00297         LOG_SERVER << "Writing bans to " <<  filename_ << "\n";
00298         dirty_ = false;
00299         config cfg;
00300         for (ban_set::const_iterator itor = bans_.begin();
00301                 itor != bans_.end(); ++itor)
00302         {
00303             config& child = cfg.add_child("ban");
00304             (*itor)->write(child);
00305         }
00306         config& deleted = cfg.add_child("deleted");
00307         for (deleted_ban_list::const_iterator itor = deleted_bans_.begin();
00308                 itor != deleted_bans_.end(); ++itor)
00309         {
00310             config& child = deleted.add_child("ban");
00311             (*itor)->write(child);
00312         }
00313 
00314         scoped_ostream ban_file = ostream_file(filename_);
00315         config_writer writer(*ban_file, true);
00316         writer.write(cfg);
00317     }
00318 
00319     bool ban_manager::parse_time(const std::string& duration, time_t* time) const
00320     {
00321         if (!time) return false;
00322 
00323         if (duration.substr(0,4) == "TIME") {
00324             struct tm* loc;
00325             loc = localtime(time);
00326 
00327             size_t number = 0;
00328             for (std::string::const_iterator i = duration.begin() + 4;
00329                     i != duration.end(); ++i) {
00330                 if (is_digit(*i))
00331                 {
00332                     number = number * 10 + to_digit(*i);
00333                 }
00334                 else
00335                 {
00336                     switch(*i)
00337                     {
00338                         case 'Y':
00339                             loc->tm_year = number;
00340                             break;
00341                         case 'M':
00342                             loc->tm_mon = number;
00343                             break;
00344                         case 'D':
00345                             loc->tm_mday = number;
00346                             break;
00347                         case 'h':
00348                             loc->tm_hour = number;
00349                             break;
00350                         case 'm':
00351                             loc->tm_min = number;
00352                             break;
00353                         case 's':
00354                             loc->tm_sec = number;
00355                             break;
00356                         default:
00357                             LOG_SERVER << "Invalid time modifier given: '" << *i << "'.\n";
00358                             break;
00359                     }
00360                     number = 0;
00361                 }
00362             }
00363             *time = mktime(loc);
00364             return true;
00365         }
00366         default_ban_times::const_iterator time_itor = ban_times_.find(duration);
00367         if (utils::lowercase(duration) == "permanent" || duration == "0") {
00368             *time = 0;
00369         } else if (ban_times_.find(duration) != ban_times_.end()) {
00370             *time += time_itor->second;
00371         } else {
00372             std::string::const_iterator i = duration.begin();
00373             int number = -1;
00374             for (std::string::const_iterator d_end = duration.end(); i != d_end; ++i) {
00375                 if (is_digit(*i))
00376                 {
00377                     if (number == -1) number = 0;
00378                     number = number * 10 + to_digit(*i);
00379                 } else {
00380                     if (number == -1) number = 1;
00381                     switch(*i)
00382                     {
00383                         case 'Y':
00384                         case 'y':
00385                             if (++i != d_end && tolower(*i) == 'e'
00386                             &&  ++i != d_end && tolower(*i) == 'a'
00387                             &&  ++i != d_end && tolower(*i) == 'r'
00388                             &&  ++i != d_end && tolower(*i) == 's') {
00389                             } else --i;
00390                             *time += number * 365*24*60*60; // a year;
00391                             break;
00392                         case 'M':
00393                             if (++i != d_end && tolower(*i) == 'i') {
00394                                 if (++i != d_end && tolower(*i) == 'n'
00395                                 &&  ++i != d_end && tolower(*i) == 'u'
00396                                 &&  ++i != d_end && tolower(*i) == 't'
00397                                 &&  ++i != d_end && tolower(*i) == 'e'
00398                                 &&  ++i != d_end && tolower(*i) == 's') {
00399                                 } else --i;
00400                                 *time += number * 60;
00401                                 break;
00402                             }
00403                             --i;
00404                             if (++i != d_end && tolower(*i) == 'o'
00405                             &&  ++i != d_end && tolower(*i) == 'n'
00406                             &&  ++i != d_end && tolower(*i) == 't'
00407                             &&  ++i != d_end && tolower(*i) == 'h'
00408                             &&  ++i != d_end && tolower(*i) == 's') {
00409                             } else --i;
00410                             *time += number * 30*24*60*60; // 30 days
00411                             break;
00412                         case 'D':
00413                         case 'd':
00414                             if (++i != d_end && tolower(*i) == 'a'
00415                             &&  ++i != d_end && tolower(*i) == 'y'
00416                             &&  ++i != d_end && tolower(*i) == 's') {
00417                             } else --i;
00418                             *time += number * 24*60*60;
00419                             break;
00420                         case 'H':
00421                         case 'h':
00422                             if (++i != d_end && tolower(*i) == 'o'
00423                             &&  ++i != d_end && tolower(*i) == 'u'
00424                             &&  ++i != d_end && tolower(*i) == 'r'
00425                             &&  ++i != d_end && tolower(*i) == 's') {
00426                             } else --i;
00427                             *time += number * 60*60;
00428                             break;
00429                         case 'm':
00430                             if (++i != d_end && tolower(*i) == 'o') {
00431                                 if (++i != d_end && tolower(*i) == 'n'
00432                                 &&  ++i != d_end && tolower(*i) == 't'
00433                                 &&  ++i != d_end && tolower(*i) == 'h'
00434                                 &&  ++i != d_end && tolower(*i) == 's') {
00435                                 } else --i;
00436                                 *time += number * 30*24*60*60; // 30 days
00437                                 break;
00438                             }
00439                             --i;
00440                             if (++i != d_end && tolower(*i) == 'i'
00441                             &&  ++i != d_end && tolower(*i) == 'n'
00442                             &&  ++i != d_end && tolower(*i) == 'u'
00443                             &&  ++i != d_end && tolower(*i) == 't'
00444                             &&  ++i != d_end && tolower(*i) == 'e'
00445                             &&  ++i != d_end && tolower(*i) == 's') {
00446                             } else --i;
00447                             *time += number * 60;
00448                             break;
00449                         case 'S':
00450                         case 's':
00451                             if (++i != d_end && tolower(*i) == 'e'
00452                             &&  ++i != d_end && tolower(*i) == 'c'
00453                             &&  ++i != d_end && tolower(*i) == 'o'
00454                             &&  ++i != d_end && tolower(*i) == 'n'
00455                             &&  ++i != d_end && tolower(*i) == 'd'
00456                             &&  ++i != d_end && tolower(*i) == 's') {
00457                             } else --i;
00458                             *time += number;
00459                             break;
00460                         default:
00461                             return false;
00462                             break;
00463                     }
00464                     number = -1;
00465                 }
00466             }
00467             if (is_digit(*--i)) {
00468                     *time += number * 60; // default to minutes
00469             }
00470         }
00471         return true;
00472     }
00473 
00474     std::string ban_manager::ban(const std::string& ip,
00475                                  const time_t& end_time,
00476                                  const std::string& reason,
00477                                  const std::string& who_banned,
00478                                  const std::string& group,
00479                                  const std::string& nick)
00480     {
00481         std::ostringstream ret;
00482         try {
00483             ban_set::iterator ban;
00484             if ((ban = bans_.find(banned::create_dummy(ip))) != bans_.end())
00485             {
00486                 // Already exsiting ban for ip. We have to first remove it
00487                 ret << "Overwriting ban: " << (**ban) << "\n";
00488                 bans_.erase(ban);
00489             }
00490         } catch (banned::error& e) {
00491             ERR_SERVER << e.message << " while creating dummy ban for finding existing ban\n";
00492             return e.message;
00493         }
00494         try {
00495             banned_ptr new_ban(new banned(ip, end_time, reason,who_banned, group, nick));
00496             bans_.insert(new_ban);
00497             if (end_time != 0)
00498                 time_queue_.push(new_ban);
00499             ret << *new_ban;
00500         } catch (banned::error& e) {
00501             ERR_SERVER << e.message << " while banning\n";
00502             return e.message;
00503         }
00504         dirty_ = true;
00505         return ret.str();
00506     }
00507 
00508     void ban_manager::unban(std::ostringstream& os, const std::string& ip)
00509     {
00510         ban_set::iterator ban;
00511         try {
00512             ban = bans_.find(banned::create_dummy(ip));
00513         } catch (banned::error& e) {
00514             ERR_SERVER << e.message << "\n";
00515             os << e.message;
00516             return;
00517         }
00518 
00519         if (ban == bans_.end())
00520         {
00521             os << "There is no ban on '" << ip << "'.";
00522             return;
00523         }
00524         // keep ban entry still in memory
00525         os << "Ban on '" << **ban << "' removed.";
00526         // group bans don't get saved
00527         if ((*ban)->get_group().empty()) deleted_bans_.push_back(*ban);
00528         bans_.erase(ban);
00529         dirty_ = true;
00530 
00531     }
00532 
00533     void ban_manager::unban_group(std::ostringstream& os, const std::string& group)
00534     {
00535         ban_set temp;
00536         std::insert_iterator<ban_set> temp_inserter(temp, temp.begin());
00537         std::remove_copy_if(bans_.begin(), bans_.end(), temp_inserter, boost::bind(&banned::match_group,boost::bind(&banned_ptr::get,_1),group));
00538 
00539         os << "Removed " << (bans_.size() - temp.size()) << " bans";
00540         bans_.swap(temp);
00541         dirty_ = true;
00542     }
00543 
00544     void ban_manager::check_ban_times(time_t time_now)
00545     {
00546         while (!time_queue_.empty())
00547         {
00548             banned_ptr ban = time_queue_.top();
00549 
00550             if (ban->get_end_time() > time_now)
00551             {
00552                 // No bans going to expire
00553                 DBG_SERVER << "ban " << ban->get_ip() << " not removed. time: " << time_now << " end_time " << ban->get_end_time() << "\n";
00554                 break;
00555             }
00556 
00557             // This ban is going to expire so delete it.
00558             LOG_SERVER << "Remove a ban " << ban->get_ip() << ". time: " << time_now << " end_time " << ban->get_end_time() << "\n";
00559             std::ostringstream os;
00560             unban(os, ban->get_ip());
00561             time_queue_.pop();
00562 
00563         }
00564         // Save bans if there is any new ones
00565         write();
00566     }
00567 
00568     void ban_manager::list_deleted_bans(std::ostringstream& out, const std::string& mask) const
00569     {
00570         if (deleted_bans_.empty())
00571         {
00572             out << "No removed bans found.";
00573             return;
00574         }
00575 
00576         ip_mask pair;
00577         try {
00578             pair = parse_ip(mask);
00579         } catch (banned::error& e) {
00580             out << "parse error: " << e.message;
00581             return;
00582         }
00583 
00584         out << "DELETED BANS LIST";
00585         for (deleted_ban_list::const_iterator i = deleted_bans_.begin();
00586                 i != deleted_bans_.end();
00587                 ++i)
00588         {
00589             if ((*i)->match_ipmask(pair)) out << "\n" << (**i);
00590         }
00591 
00592     }
00593 
00594 
00595 
00596     void ban_manager::list_bans(std::ostringstream& out, const std::string& mask) const
00597     {
00598         if (bans_.empty())
00599         {
00600             out << "No bans set.";
00601             return;
00602         }
00603 
00604         ip_mask pair;
00605         try {
00606             pair = parse_ip(mask);
00607         } catch (banned::error& e) {
00608             out << "parse error: " << e.message;
00609             return;
00610         }
00611 
00612         out << "BAN LIST";
00613         std::set<std::string> groups;
00614 
00615         for (ban_set::const_iterator i = bans_.begin();
00616                 i != bans_.end(); ++i)
00617         {
00618             if ((*i)->get_group().empty())
00619             {
00620                 if ((*i)->match_ipmask(pair)) out << "\n" << (**i);
00621             } else {
00622                 groups.insert((*i)->get_group());
00623             }
00624         }
00625 
00626         // Don't list ban groups when looking for specific bans.
00627         if (!groups.empty() && mask == "*")
00628         {
00629             out << "\nban groups: ";
00630 
00631             out << *groups.begin();
00632             std::ostream& (*fn)(std::ostream&,const std::string&) = &std::operator<<;
00633             std::for_each( ++groups.begin(), groups.end(), boost::bind(fn,boost::bind(fn,boost::ref(out),std::string(", ")),_1));
00634         }
00635 
00636     }
00637 
00638 
00639     std::string ban_manager::is_ip_banned(const std::string& ip) const
00640     {
00641         ip_mask pair;
00642         try {
00643             pair = parse_ip(ip);
00644         } catch (banned::error&) {
00645             return "";
00646         }
00647         ban_set::const_iterator ban = std::find_if(bans_.begin(), bans_.end(), boost::bind(&banned::match_ip, boost::bind(&banned_ptr::get, _1), pair));
00648         if (ban == bans_.end()) return "";
00649         const std::string& nick = (*ban)->get_nick();
00650         return (*ban)->get_reason() + (nick.empty() ? "" : " (" + nick + ")");
00651     }
00652 
00653     void ban_manager::init_ban_help()
00654     {
00655         ban_help_ = "ban <mask> <time> <reason>\n"
00656                 "The time format is: %d[%s[%d[%s[...]]]] where %s is a time"
00657                 " modifier: s or S (seconds), m (minutes), h or H (hours), d"
00658                 " or D (days), M (months) or y or Y (years) and %d is a number.\n"
00659                 "Permanent bans can be set with 'permanent' or '0' as the time"
00660                 " argument.\n";
00661         default_ban_times::iterator itor = ban_times_.begin();
00662         if (itor != ban_times_.end())
00663         {
00664             ban_help_ += "You can also use " + itor->first;
00665             ++itor;
00666         }
00667         for (; itor != ban_times_.end(); ++itor)
00668         {
00669             ban_help_ += std::string(", ") + itor->first;
00670         }
00671         if (!ban_times_.empty())
00672         {
00673             ban_help_ += " for standard ban times. (not combinable)\n";
00674         }
00675         ban_help_ += "ban 127.0.0.1 2h20m flooded lobby\n"
00676                 "kban suokko 5D flooded again\n"
00677                 "kban suokko Y One year ban for constant flooding";
00678     }
00679 
00680     void ban_manager::load_config(const config& cfg)
00681     {
00682         ban_times_.clear();
00683         foreach (const config &bt, cfg.child_range("ban_time")) {
00684             time_t duration = 0;
00685             if (parse_time(bt["time"], &duration)) {
00686                 ban_times_.insert(default_ban_times::value_type(bt["name"], duration));
00687             }
00688         }
00689         init_ban_help();
00690         if (filename_ != cfg["ban_save_file"])
00691         {
00692             dirty_ = true;
00693             filename_ = cfg["ban_save_file"].str();
00694         }
00695     }
00696 
00697     ban_manager::~ban_manager()
00698     {
00699         write();
00700     }
00701 
00702     ban_manager::ban_manager()
00703         : bans_()
00704         , deleted_bans_()
00705         , time_queue_()
00706         , ban_times_()
00707         , ban_help_()
00708         , filename_()
00709         , dirty_(false)
00710     {
00711         init_ban_help();
00712     }
00713 
00714 
00715 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines

Generated by doxygen 1.7.1 on Wed May 23 2012 01:02:55 for The Battle for Wesnoth
Gna! | Forum | Wiki | CIA | devdocs