00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014 #include "services.h"
00015 #include "modules.h"
00016 #include "users.h"
00017 #include "account.h"
00018 #include "protocol.h"
00019 #include "servers.h"
00020 #include "channels.h"
00021 #include "bots.h"
00022 #include "config.h"
00023 #include "opertype.h"
00024 #include "nickserv.h"
00025 #include "language.h"
00026
00027 user_map UserListByNick, UserListByUID;
00028
00029 int OperCount = 0;
00030 unsigned MaxUserCount = 0;
00031 time_t MaxUserTime = 0;
00032
00033 std::list<User *> User::quitting_users;
00034
00035 User::User(const Anope::string &snick, const Anope::string &sident, const Anope::string &shost, const Anope::string &svhost, const Anope::string &sip, Server *sserver, const Anope::string &srealname, time_t ssignon, const Anope::string &smodes, const Anope::string &suid)
00036 {
00037 if (snick.empty() || sident.empty() || shost.empty())
00038 throw CoreException("Bad args passed to User::User");
00039
00040
00041 quit = false;
00042 server = NULL;
00043 invalid_pw_count = invalid_pw_time = lastmemosend = lastnickreg = lastmail = 0;
00044 on_access = false;
00045
00046 this->nick = snick;
00047 this->ident = sident;
00048 this->host = shost;
00049 this->vhost = svhost;
00050 if (!svhost.empty())
00051 this->SetCloakedHost(svhost);
00052 this->ip = sip;
00053 this->server = sserver;
00054 this->realname = srealname;
00055 this->timestamp = this->signon = ssignon;
00056 this->SetModesInternal("%s", smodes.c_str());
00057 this->uid = suid;
00058 this->super_admin = false;
00059
00060 size_t old = UserListByNick.size();
00061 UserListByNick[snick] = this;
00062 if (!suid.empty())
00063 UserListByUID[suid] = this;
00064 if (old == UserListByNick.size())
00065 Log(LOG_DEBUG) << "Duplicate user " << snick << " in user table?";
00066
00067 this->nc = NULL;
00068
00069 if (sserver)
00070 {
00071 ++sserver->users;
00072 Log(this, "connect") << (!svhost.empty() ? Anope::string("(") + svhost + ") " : "") << "(" << srealname << ") " << sip << " connected to the network (" << sserver->GetName() << ")";
00073 }
00074
00075 if (UserListByNick.size() > MaxUserCount)
00076 {
00077 MaxUserCount = UserListByNick.size();
00078 MaxUserTime = Anope::CurTime;
00079 Log(this, "maxusers") << "connected - new maximum user count: " << UserListByNick.size();
00080 }
00081
00082 bool exempt = false;
00083 if (server && server->IsULined())
00084 exempt = true;
00085 FOREACH_MOD(I_OnUserConnect, OnUserConnect(this, exempt));
00086 }
00087
00088 void User::ChangeNick(const Anope::string &newnick, time_t ts)
00089 {
00090
00091 if (newnick.empty())
00092 throw CoreException("User::ChangeNick() got a bad argument");
00093
00094 this->super_admin = false;
00095 Log(this, "nick") << "(" << this->realname << ") changed nick to " << newnick;
00096
00097 Anope::string old = this->nick;
00098 this->timestamp = ts;
00099
00100 if (this->nick.equals_ci(newnick))
00101 this->nick = newnick;
00102 else
00103 {
00104 NickAlias *old_na = NickAlias::Find(this->nick);
00105 if (old_na && (this->IsIdentified(true) || this->IsRecognized()))
00106 old_na->last_seen = Anope::CurTime;
00107
00108 UserListByNick.erase(this->nick);
00109 this->nick = newnick;
00110 UserListByNick[this->nick] = this;
00111
00112 on_access = false;
00113 NickAlias *na = NickAlias::Find(this->nick);
00114 if (na)
00115 on_access = na->nc->IsOnAccess(this);
00116
00117 if (na && na->nc == this->Account())
00118 {
00119 na->last_seen = Anope::CurTime;
00120 this->UpdateHost();
00121 }
00122 }
00123
00124 FOREACH_MOD(I_OnUserNickChange, OnUserNickChange(this, old));
00125 }
00126
00127 void User::SetDisplayedHost(const Anope::string &shost)
00128 {
00129 if (shost.empty())
00130 throw CoreException("empty host? in MY services? it seems it's more likely than I thought.");
00131
00132 this->vhost = shost;
00133
00134 Log(this, "host") << "changed vhost to " << shost;
00135
00136 this->UpdateHost();
00137 }
00138
00139 const Anope::string &User::GetDisplayedHost() const
00140 {
00141 if (!this->vhost.empty())
00142 return this->vhost;
00143 else if (this->HasMode("CLOAK") && !this->GetCloakedHost().empty())
00144 return this->GetCloakedHost();
00145 else
00146 return this->host;
00147 }
00148
00149 void User::SetCloakedHost(const Anope::string &newhost)
00150 {
00151 if (newhost.empty())
00152 throw "empty host in User::SetCloakedHost";
00153
00154 chost = newhost;
00155
00156 Log(this, "host") << "changed cloaked host to " << newhost;
00157
00158 this->UpdateHost();
00159 }
00160
00161 const Anope::string &User::GetCloakedHost() const
00162 {
00163 return chost;
00164 }
00165
00166 const Anope::string &User::GetUID() const
00167 {
00168 if (!this->uid.empty() && IRCD->RequiresID)
00169 return this->uid;
00170 else
00171 return this->nick;
00172 }
00173
00174 void User::SetVIdent(const Anope::string &sident)
00175 {
00176 this->vident = sident;
00177
00178 Log(this, "ident") << "changed vident to " << sident;
00179
00180 this->UpdateHost();
00181 }
00182
00183 const Anope::string &User::GetVIdent() const
00184 {
00185 if (!this->vident.empty())
00186 return this->vident;
00187 else
00188 return this->ident;
00189 }
00190
00191 void User::SetIdent(const Anope::string &sident)
00192 {
00193 this->ident = sident;
00194
00195 Log(this, "ident") << "changed real ident to " << sident;
00196
00197 this->UpdateHost();
00198 }
00199
00200 const Anope::string &User::GetIdent() const
00201 {
00202 return this->ident;
00203 }
00204
00205 Anope::string User::GetMask() const
00206 {
00207 return this->nick + "!" + this->ident + "@" + this->host;
00208 }
00209
00210 Anope::string User::GetDisplayedMask() const
00211 {
00212 return this->nick + "!" + this->GetVIdent() + "@" + this->GetDisplayedHost();
00213 }
00214
00215 void User::SetRealname(const Anope::string &srealname)
00216 {
00217 if (srealname.empty())
00218 throw CoreException("realname empty in SetRealname");
00219
00220 this->realname = srealname;
00221 NickAlias *na = NickAlias::Find(this->nick);
00222
00223 if (na && (this->IsIdentified(true) || this->IsRecognized()))
00224 na->last_realname = srealname;
00225
00226 Log(this, "realname") << "changed realname to " << srealname;
00227 }
00228
00229 User::~User()
00230 {
00231 if (this->server != NULL)
00232 {
00233 Log(this, "disconnect") << "(" << this->realname << ") disconnected from the network (" << this->server->GetName() << ")";
00234 --this->server->users;
00235 }
00236
00237 FOREACH_MOD(I_OnPreUserLogoff, OnPreUserLogoff(this));
00238
00239 ModeManager::StackerDel(this);
00240 this->Logout();
00241
00242 if (this->HasMode("OPER"))
00243 --OperCount;
00244
00245 while (!this->chans.empty())
00246 this->chans.front()->chan->DeleteUser(this);
00247
00248 UserListByNick.erase(this->nick);
00249 if (!this->uid.empty())
00250 UserListByUID.erase(this->uid);
00251
00252 FOREACH_MOD(I_OnPostUserLogoff, OnPostUserLogoff(this));
00253 }
00254
00255 void User::SendMessage(const BotInfo *source, const char *fmt, ...)
00256 {
00257 va_list args;
00258 char buf[BUFSIZE] = "";
00259
00260 const char *translated_message = Language::Translate(this, fmt);
00261
00262 va_start(args, fmt);
00263 vsnprintf(buf, BUFSIZE - 1, translated_message, args);
00264
00265 this->SendMessage(source, Anope::string(buf));
00266
00267 va_end(args);
00268 }
00269
00270 void User::SendMessage(const BotInfo *source, const Anope::string &msg)
00271 {
00272 const char *translated_message = Language::Translate(this, msg.c_str());
00273
00274
00275
00276
00277
00278
00279 sepstream sep(translated_message, '\n');
00280 Anope::string tok;
00281 while (sep.GetToken(tok))
00282 {
00283 if (Config->UsePrivmsg && ((!this->nc && Config->NSDefFlags.count("msg")) || (this->nc && this->nc->HasExt("MSG"))))
00284 IRCD->SendPrivmsg(source, this->GetUID(), "%s", tok.c_str());
00285 else
00286 IRCD->SendNotice(source, this->GetUID(), "%s", tok.c_str());
00287 }
00288 }
00289
00346 void User::Collide(NickAlias *na)
00347 {
00348 if (na)
00349 na->Extend("COLLIDED");
00350
00351 if (IRCD->CanSVSNick)
00352 {
00353 Anope::string guestnick;
00354
00355 int i = 0;
00356 do
00357 {
00358 guestnick = Config->NSGuestNickPrefix + stringify(static_cast<uint16_t>(rand()));
00359 } while (User::Find(guestnick) && i++ < 10);
00360
00361 if (i == 11)
00362 this->Kill(Config->NickServ, "Services nickname-enforcer kill");
00363 else
00364 {
00365 if (NickServ)
00366 this->SendMessage(NickServ, _("Your nickname is now being changed to \002%s\002"), guestnick.c_str());
00367 IRCD->SendForceNickChange(this, guestnick, Anope::CurTime);
00368 }
00369 }
00370 else
00371 this->Kill(Config->NickServ, "Services nickname-enforcer kill");
00372 }
00373
00374 void User::Identify(NickAlias *na)
00375 {
00376 if (!na)
00377 {
00378 Log() << "User::Identify() called with NULL pointer";
00379 return;
00380 }
00381
00382 if (this->nick.equals_ci(na->nick))
00383 {
00384 Anope::string last_usermask = this->GetIdent() + "@" + this->GetDisplayedHost();
00385 Anope::string last_realhost = this->GetIdent() + "@" + this->host;
00386 na->last_usermask = last_usermask;
00387 na->last_realhost = last_realhost;
00388 na->last_realname = this->realname;
00389 na->last_seen = Anope::CurTime;
00390 }
00391
00392 this->Login(na->nc);
00393 IRCD->SendLogin(this);
00394
00395 const NickAlias *this_na = NickAlias::Find(this->nick);
00396 if (!Config->NoNicknameOwnership && this_na && this_na->nc == *na->nc && na->nc->HasExt("UNCONFIRMED") == false)
00397 this->SetMode(NickServ, "REGISTERED");
00398
00399 FOREACH_MOD(I_OnNickIdentify, OnNickIdentify(this));
00400
00401 if (this->IsServicesOper())
00402 {
00403 if (!this->nc->o->ot->modes.empty())
00404 {
00405 this->SetModes(OperServ, "%s", this->nc->o->ot->modes.c_str());
00406 if (OperServ)
00407 this->SendMessage(OperServ, "Changing your usermodes to \002%s\002", this->nc->o->ot->modes.c_str());
00408 UserMode *um = ModeManager::FindUserModeByName("OPER");
00409 if (um && !this->HasMode("OPER") && this->nc->o->ot->modes.find(um->mchar) != Anope::string::npos)
00410 IRCD->SendOper(this);
00411 }
00412 if (IRCD->CanSetVHost && !this->nc->o->vhost.empty())
00413 {
00414 if (OperServ)
00415 this->SendMessage(OperServ, "Changing your vhost to \002%s\002", this->nc->o->vhost.c_str());
00416 this->SetDisplayedHost(this->nc->o->vhost);
00417 IRCD->SendVhost(this, "", this->nc->o->vhost);
00418 }
00419 }
00420 }
00421
00422
00423 void User::Login(NickCore *core)
00424 {
00425 this->Logout();
00426 this->nc = core;
00427 core->users.push_back(this);
00428
00429 this->UpdateHost();
00430
00431 if (this->server->IsSynced())
00432 Log(this, "account") << "is now identified as " << this->nc->display;
00433 }
00434
00435 void User::Logout()
00436 {
00437 if (!this->nc)
00438 return;
00439
00440 Log(this, "account") << "is no longer identified as " << this->nc->display;
00441
00442 std::list<User *>::iterator it = std::find(this->nc->users.begin(), this->nc->users.end(), this);
00443 if (it != this->nc->users.end())
00444 this->nc->users.erase(it);
00445
00446 this->nc = NULL;
00447 }
00448
00449 NickCore *User::Account() const
00450 {
00451 return this->nc;
00452 }
00453
00454 bool User::IsIdentified(bool CheckNick) const
00455 {
00456 if (CheckNick && this->nc)
00457 {
00458 NickAlias *na = NickAlias::Find(this->nc->display);
00459
00460 if (na && *na->nc == *this->nc)
00461 return true;
00462
00463 return false;
00464 }
00465
00466 return this->nc ? true : false;
00467 }
00468
00469 bool User::IsRecognized(bool CheckSecure) const
00470 {
00471 if (CheckSecure && on_access)
00472 {
00473 const NickAlias *na = NickAlias::Find(this->nick);
00474
00475 if (!na || na->nc->HasExt("SECURE"))
00476 return false;
00477 }
00478
00479 return on_access;
00480 }
00481
00482 bool User::IsServicesOper()
00483 {
00484 if (!this->nc || !this->nc->IsServicesOper())
00485
00486 return false;
00487 else if (this->nc->o->require_oper && !this->HasMode("OPER"))
00488 return false;
00489 else if (!this->nc->o->certfp.empty() && this->fingerprint != this->nc->o->certfp)
00490
00491 return false;
00492 else if (!this->nc->o->hosts.empty())
00493 {
00494 bool match = false;
00495 Anope::string match_host = this->GetIdent() + "@" + this->host;
00496 for (unsigned i = 0; i < this->nc->o->hosts.size(); ++i)
00497 if (Anope::Match(match_host, this->nc->o->hosts[i]))
00498 match = true;
00499 if (match == false)
00500 return false;
00501 }
00502
00503 EventReturn MOD_RESULT;
00504 FOREACH_RESULT(I_IsServicesOper, IsServicesOper(this));
00505 if (MOD_RESULT == EVENT_STOP)
00506 return false;
00507
00508 return true;
00509 }
00510
00511 bool User::HasCommand(const Anope::string &command)
00512 {
00513 if (this->IsServicesOper())
00514 return this->nc->o->ot->HasCommand(command);
00515 return false;
00516 }
00517
00518 bool User::HasPriv(const Anope::string &priv)
00519 {
00520 if (this->IsServicesOper())
00521 return this->nc->o->ot->HasPriv(priv);
00522 return false;
00523 }
00524
00525 void User::UpdateHost()
00526 {
00527 if (this->host.empty())
00528 return;
00529
00530 NickAlias *na = NickAlias::Find(this->nick);
00531 on_access = false;
00532 if (na)
00533 on_access = na->nc->IsOnAccess(this);
00534
00535 if (na && (this->IsIdentified(true) || this->IsRecognized()))
00536 {
00537 Anope::string last_usermask = this->GetIdent() + "@" + this->GetDisplayedHost();
00538 Anope::string last_realhost = this->GetIdent() + "@" + this->host;
00539 na->last_usermask = last_usermask;
00540 na->last_realhost = last_realhost;
00541 }
00542 }
00543
00544 bool User::HasMode(const Anope::string &mname) const
00545 {
00546 return this->modes.count(mname);
00547 }
00548
00549 void User::SetModeInternal(UserMode *um, const Anope::string ¶m)
00550 {
00551 if (!um)
00552 return;
00553
00554 this->modes[um->name] = param;
00555
00556 FOREACH_MOD(I_OnUserModeSet, OnUserModeSet(this, um->name));
00557 }
00558
00559 void User::RemoveModeInternal(UserMode *um)
00560 {
00561 if (!um)
00562 return;
00563
00564 this->modes.erase(um->name);
00565
00566 FOREACH_MOD(I_OnUserModeUnset, OnUserModeUnset(this, um->name));
00567 }
00568
00569 void User::SetMode(const BotInfo *bi, UserMode *um, const Anope::string &Param)
00570 {
00571 if (!um || HasMode(um->name))
00572 return;
00573
00574 ModeManager::StackerAdd(bi, this, um, true, Param);
00575 SetModeInternal(um, Param);
00576 }
00577
00578 void User::SetMode(const BotInfo *bi, const Anope::string &uname, const Anope::string &Param)
00579 {
00580 SetMode(bi, ModeManager::FindUserModeByName(uname), Param);
00581 }
00582
00583 void User::RemoveMode(const BotInfo *bi, UserMode *um)
00584 {
00585 if (!um || !HasMode(um->name))
00586 return;
00587
00588 ModeManager::StackerAdd(bi, this, um, false);
00589 RemoveModeInternal(um);
00590 }
00591
00592 void User::RemoveMode(const BotInfo *bi, const Anope::string &name)
00593 {
00594 RemoveMode(bi, ModeManager::FindUserModeByName(name));
00595 }
00596
00597 void User::SetModes(const BotInfo *bi, const char *umodes, ...)
00598 {
00599 char buf[BUFSIZE] = "";
00600 va_list args;
00601 Anope::string modebuf, sbuf;
00602 int add = -1;
00603 va_start(args, umodes);
00604 vsnprintf(buf, BUFSIZE - 1, umodes, args);
00605 va_end(args);
00606
00607 spacesepstream sep(buf);
00608 sep.GetToken(modebuf);
00609 for (unsigned i = 0, end = modebuf.length(); i < end; ++i)
00610 {
00611 UserMode *um;
00612
00613 switch (modebuf[i])
00614 {
00615 case '+':
00616 add = 1;
00617 continue;
00618 case '-':
00619 add = 0;
00620 continue;
00621 default:
00622 if (add == -1)
00623 continue;
00624 um = ModeManager::FindUserModeByChar(modebuf[i]);
00625 if (!um)
00626 continue;
00627 }
00628
00629 if (add)
00630 {
00631 if (um->type == MODE_PARAM && sep.GetToken(sbuf))
00632 this->SetMode(bi, um, sbuf);
00633 else
00634 this->SetMode(bi, um);
00635 }
00636 else
00637 this->RemoveMode(bi, um);
00638 }
00639 }
00640
00641 void User::SetModesInternal(const char *umodes, ...)
00642 {
00643 char buf[BUFSIZE] = "";
00644 va_list args;
00645 Anope::string modebuf, sbuf;
00646 int add = -1;
00647 va_start(args, umodes);
00648 vsnprintf(buf, BUFSIZE - 1, umodes, args);
00649 va_end(args);
00650
00651 Log(this, "mode") << "changes modes to " << buf;
00652
00653 spacesepstream sep(buf);
00654 sep.GetToken(modebuf);
00655 for (unsigned i = 0, end = modebuf.length(); i < end; ++i)
00656 {
00657 UserMode *um;
00658
00659 switch (modebuf[i])
00660 {
00661 case '+':
00662 add = 1;
00663 continue;
00664 case '-':
00665 add = 0;
00666 continue;
00667 default:
00668 if (add == -1)
00669 continue;
00670 um = ModeManager::FindUserModeByChar(modebuf[i]);
00671 if (!um)
00672 continue;
00673 }
00674
00675 if (add)
00676 {
00677 if (um->type == MODE_PARAM && sep.GetToken(sbuf))
00678 this->SetModeInternal(um, sbuf);
00679 else
00680 this->SetModeInternal(um);
00681 }
00682 else
00683 this->RemoveModeInternal(um);
00684
00685 if (um->name == "OPER")
00686 {
00687 if (add)
00688 ++OperCount;
00689 else
00690 --OperCount;
00691 }
00692 else if (um->name == "CLOAK" || um->name == "VHOST")
00693 {
00694 if (!add && !this->vhost.empty())
00695 this->vhost.clear();
00696 this->UpdateHost();
00697 }
00698 }
00699 }
00700
00701 Anope::string User::GetModes() const
00702 {
00703 Anope::string m, params;
00704
00705 typedef std::map<Anope::string, Anope::string> mode_map;
00706 for (mode_map::const_iterator it = this->modes.begin(), it_end = this->modes.end(); it != it_end; ++it)
00707 {
00708 UserMode *um = ModeManager::FindUserModeByName(it->first);
00709 if (um == NULL)
00710 continue;
00711
00712 m += um->mchar;
00713
00714 if (!it->second.empty())
00715 params += " " + it->second;
00716 }
00717
00718 return m + params;
00719 }
00720
00721 ChanUserContainer *User::FindChannel(const Channel *c) const
00722 {
00723 for (User::ChanUserList::const_iterator it = this->chans.begin(), it_end = this->chans.end(); it != it_end; ++it)
00724 if ((*it)->chan == c)
00725 return *it;
00726 return NULL;
00727 }
00728
00729 bool User::IsProtected() const
00730 {
00731 if (this->HasMode("PROTECTED") || this->HasMode("GOD"))
00732 return true;
00733
00734 return false;
00735 }
00736
00737 void User::Kill(const Anope::string &source, const Anope::string &reason)
00738 {
00739 Anope::string real_source = source.empty() ? Config->ServerName : source;
00740 Anope::string real_reason = real_source + " (" + reason + ")";
00741
00742 IRCD->SendSVSKill(BotInfo::Find(source), this, "%s", real_reason.c_str());
00743 }
00744
00745 void User::KillInternal(const Anope::string &source, const Anope::string &reason)
00746 {
00747 if (this->quit)
00748 {
00749 Log(LOG_DEBUG) << "Duplicate quit for " << this->nick;
00750 return;
00751 }
00752
00753 Log(this, "killed") << "was killed by " << source << " (Reason: " << reason << ")";
00754
00755 NickAlias *na = NickAlias::Find(this->nick);
00756 if (na && !na->nc->HasExt("SUSPENDED") && (this->IsRecognized() || this->IsIdentified(true)))
00757 {
00758 na->last_seen = Anope::CurTime;
00759 na->last_quit = reason;
00760 }
00761
00762 this->quit = true;
00763 quitting_users.push_back(this);
00764 }
00765
00766 void User::Quit(const Anope::string &reason)
00767 {
00768 if (this->quit)
00769 {
00770 Log(LOG_DEBUG) << "Duplicate quit for " << this->nick;
00771 return;
00772 }
00773
00774 this->quit = true;
00775 quitting_users.push_back(this);
00776 }
00777
00778 bool User::Quitting() const
00779 {
00780 return this->quit;
00781 }
00782
00783 Anope::string User::Mask() const
00784 {
00785 Anope::string mask;
00786 Anope::string mident = this->GetIdent();
00787 Anope::string mhost = this->GetDisplayedHost();
00788
00789 if (mident[0] == '~')
00790 mask = "*" + mident + "@";
00791 else
00792 mask = mident + "@";
00793
00794 size_t dot;
00795
00796 if (mhost.find_first_not_of("0123456789.") == Anope::string::npos && (dot = mhost.find('.')) != Anope::string::npos && (dot = mhost.find('.', dot + 1)) != Anope::string::npos && (dot = mhost.find('.', dot + 1)) != Anope::string::npos && mhost.find('.', dot + 1) == Anope::string::npos)
00797 {
00798 dot = mhost.find('.');
00799 mask += mhost.substr(0, dot) + ".*";
00800 }
00801 else
00802 {
00803 if ((dot = mhost.find('.')) != Anope::string::npos && mhost.find('.', dot + 1) != Anope::string::npos)
00804 mask += "*" + mhost.substr(dot);
00805 else
00806 mask += mhost;
00807 }
00808
00809 return mask;
00810 }
00811
00812 bool User::BadPassword()
00813 {
00814 if (!Config->BadPassLimit)
00815 return false;
00816
00817 if (Config->BadPassTimeout > 0 && this->invalid_pw_time > 0 && this->invalid_pw_time < Anope::CurTime - Config->BadPassTimeout)
00818 this->invalid_pw_count = 0;
00819 ++this->invalid_pw_count;
00820 this->invalid_pw_time = Anope::CurTime;
00821 if (this->invalid_pw_count >= Config->BadPassLimit)
00822 {
00823 this->Kill(Config->ServerName, "Too many invalid passwords");
00824 return true;
00825 }
00826
00827 return false;
00828 }
00829
00830 User* User::Find(const Anope::string &name, bool nick_only)
00831 {
00832 if (!nick_only && isdigit(name[0]) && IRCD->RequiresID)
00833 {
00834 user_map::iterator it = UserListByUID.find(name);
00835 if (it != UserListByUID.end())
00836 return it->second;
00837 }
00838 else
00839 {
00840 user_map::iterator it = UserListByNick.find(name);
00841 if (it != UserListByNick.end())
00842 return it->second;
00843 }
00844
00845 return NULL;
00846 }
00847
00848 void User::QuitUsers()
00849 {
00850 for (std::list<User *>::iterator it = quitting_users.begin(), it_end = quitting_users.end(); it != it_end; ++it)
00851 delete *it;
00852 quitting_users.clear();
00853 }
00854