diff options
Diffstat (limited to 'src/channels.cpp')
-rw-r--r-- | src/channels.cpp | 1929 |
1 files changed, 1929 insertions, 0 deletions
diff --git a/src/channels.cpp b/src/channels.cpp new file mode 100644 index 000000000..5a439726f --- /dev/null +++ b/src/channels.cpp @@ -0,0 +1,1929 @@ +/* Channel-handling routines. + * + * (C) 2003-2010 Anope Team + * Contact us at team@anope.org + * + * Please read COPYING and README for further details. + * + * Based on the original code of Epona by Lara. + * Based on the original code of Services by Andy Church. + * + * + */ + +#include "services.h" +#include "language.h" +#include "modules.h" + +channel_map ChannelList; + +/** Default constructor + * @param name The channel name + * @param ts The time the channel was created + */ +Channel::Channel(const std::string &name, time_t ts) +{ + if (name.empty()) + throw CoreException("A channel without a name ?"); + + this->name = name; + + ChannelList[this->name.c_str()] = this; + + this->creation_time = ts; + this->topic = NULL; + this->bans = this->excepts = this->invites = NULL; + this->bd = NULL; + this->server_modetime = this->chanserv_modetime = 0; + this->server_modecount = this->chanserv_modecount = this->bouncy_modes = this->topic_sync = this->topic_time = 0; + + this->ci = cs_findchan(this->name); + if (this->ci) + this->ci->c = this; + + FOREACH_MOD(I_OnChannelCreate, OnChannelCreate(this)); +} + +/** Default destructor + */ +Channel::~Channel() +{ + BanData *bd, *next; + + FOREACH_MOD(I_OnChannelDelete, OnChannelDelete(this)); + + Alog(LOG_DEBUG) << "Deleting channel " << this->name; + + for (bd = this->bd; bd; bd = next) + { + if (bd->mask) + delete [] bd->mask; + next = bd->next; + delete bd; + } + + if (this->ci) + this->ci->c = NULL; + + if (this->topic) + delete [] this->topic; + + if (this->bans && this->bans->count) + { + while (this->bans->entries) + entry_delete(this->bans, this->bans->entries); + } + + if (ModeManager::FindChannelModeByName(CMODE_EXCEPT)) + { + if (this->excepts && this->excepts->count) + { + while (this->excepts->entries) + entry_delete(this->excepts, this->excepts->entries); + } + } + + if (ModeManager::FindChannelModeByName(CMODE_INVITEOVERRIDE)) + { + if (this->invites && this->invites->count) + { + while (this->invites->entries) + entry_delete(this->invites, this->invites->entries); + } + } + + ChannelList.erase(this->name.c_str()); +} + +void Channel::Sync() +{ + if (this->ci) + { + check_modes(this); + stick_all(this->ci); + } + + if (Me && Me->IsSynced() && !this->topic_sync) + restore_topic(name.c_str()); +} + +void Channel::JoinUser(User *user) +{ + Alog(LOG_DEBUG) << user->nick << " joins " << this->name; + + Flags<ChannelModeName> *Status = new Flags<ChannelModeName>; + ChannelContainer *cc = new ChannelContainer(this); + cc->Status = Status; + user->chans.push_back(cc); + + UserContainer *uc = new UserContainer(user); + uc->Status = Status; + this->users.push_back(uc); + + if (get_ignore(user->nick.c_str()) == NULL) + { + if (this->ci && (check_access(user, this->ci, CA_MEMO)) && (this->ci->memos.memos.size() > 0)) + { + if (this->ci->memos.memos.size() == 1) + notice_lang(Config.s_MemoServ, user, MEMO_X_ONE_NOTICE, this->ci->memos.memos.size(), this->ci->name.c_str()); + else + notice_lang(Config.s_MemoServ, user, MEMO_X_MANY_NOTICE, this->ci->memos.memos.size(), this->ci->name.c_str()); + } + /* Added channelname to entrymsg - 30.03.2004, Certus */ + /* Also, don't send the entrymsg when bursting -GD */ + if (this->ci && this->ci->entry_message && user->server->IsSynced()) + user->SendMessage(whosends(this->ci)->nick, "[%s] %s", this->name.c_str(), this->ci->entry_message); + } + + /** + * We let the bot join even if it was an ignored user, as if we don't, + * and the ignored user doesnt just leave, the bot will never + * make it into the channel, leaving the channel botless even for + * legit users - Rob + * But don't join the bot if the channel is persistant - Adam + * But join persistant channels when syncing with our uplink- DP + **/ + if (Config.s_BotServ && this->ci && this->ci->bi && (!Me->IsSynced() || !this->ci->HasFlag(CI_PERSIST))) + { + if (this->users.size() == Config.BSMinUsers) + bot_join(this->ci); + } + if (Config.s_BotServ && this->ci && this->ci->bi) + { + if (this->users.size() >= Config.BSMinUsers && (this->ci->botflags.HasFlag(BS_GREET)) + && user->Account() && user->Account()->greet && check_access(user, this->ci, CA_GREET)) + { + /* Only display the greet if the main uplink we're connected + * to has synced, or we'll get greet-floods when the net + * recovers from a netsplit. -GD + */ + if (user->server->IsSynced()) + { + ircdproto->SendPrivmsg(this->ci->bi, this->name.c_str(), "[%s] %s", user->Account()->display, user->Account()->greet); + this->ci->bi->lastmsg = time(NULL); + } + } + } +} + +/** Remove a user internally from the channel + * @param u The user + */ +void Channel::DeleteUser(User *user) +{ + if (this->ci) + update_cs_lastseen(user, this->ci); + + CUserList::iterator cit; + for (cit = this->users.begin(); (*cit)->user != user && cit != this->users.end(); ++cit); + if (cit == this->users.end()) + { + Alog(LOG_DEBUG) << "Channel::DeleteUser() tried to delete nonexistant user " << user->nick << " from channel " << this->name; + return; + } + + delete (*cit)->Status; + delete *cit; + this->users.erase(cit); + + UChannelList::iterator uit; + for (uit = user->chans.begin(); (*uit)->chan != this && uit != user->chans.end(); ++uit); + if (uit == user->chans.end()) + { + Alog(LOG_DEBUG) << "Channel::DeleteUser() tried to delete nonexistant channel " << this->name << " from " << user->nick << "'s channel list"; + return; + } + + delete *uit; + user->chans.erase(uit); + + /* Channel is persistant, it shouldn't be deleted and the service bot should stay */ + if (this->HasFlag(CH_PERSIST) || (this->ci && this->ci->HasFlag(CI_PERSIST))) + return; + + /* Channel is syncing from a netburst, don't destroy it as more users are probably wanting to join immediatly + * We also don't part the bot here either, if necessary we will part it after the sync + */ + if (this->HasFlag(CH_SYNCING)) + return; + + /* Additionally, do not delete this channel if ChanServ/a BotServ bot is inhabiting it */ + if (this->ci && this->ci->HasFlag(CI_INHABIT)) + return; + + if (Config.s_BotServ && this->ci && this->ci->bi && this->users.size() <= Config.BSMinUsers - 1) + ircdproto->SendPart(this->ci->bi, this, NULL); + + if (this->users.empty()) + delete this; +} + +/** Check if the user is on the channel + * @param u The user + * @return A user container if found, else NULL + */ +UserContainer *Channel::FindUser(User *u) +{ + for (CUserList::iterator it = this->users.begin(); it != this->users.end(); ++it) + if ((*it)->user == u) + return *it; + return NULL; +} + +/** Check if a user has a status on a channel + * @param u The user + * @param cms The status mode, or NULL to represent no status + * @return true or false + */ +bool Channel::HasUserStatus(User *u, ChannelModeStatus *cms) +{ + if (!u || (cms && cms->Type != MODE_STATUS)) + throw CoreException("Channel::HasUserStatus got bad mode"); + + /* Usually its more efficient to search the users channels than the channels users */ + ChannelContainer *cc = u->FindChannel(this); + if (cc) + { + if (cms) + return cc->Status->HasFlag(cms->Name); + else + return !cc->Status->FlagCount(); + } + + return false; +} + +/** Check if a user has a status on a channel + * Use the overloaded function for ChannelModeStatus* to check for no status + * @param u The user + * @param Name The Mode name, eg CMODE_OP, CMODE_VOICE + * @return true or false + */ +bool Channel::HasUserStatus(User *u, ChannelModeName Name) +{ + return HasUserStatus(u, dynamic_cast<ChannelModeStatus *>(ModeManager::FindChannelModeByName(Name))); +} + +/** + * See if a channel has a mode + * @param Name The mode name + * @return true or false + */ +bool Channel::HasMode(ChannelModeName Name) +{ + return modes.HasFlag(Name); +} + +/** Set a mode internally on a channel, this is not sent out to the IRCd + * @param cm The mode + * @param param The param + * @param EnforeMLock true if mlocks should be enforced, false to override mlock + */ +void Channel::SetModeInternal(ChannelMode *cm, const std::string ¶m, bool EnforceMLock) +{ + if (!cm) + return; + + EventReturn MOD_RESULT; + FOREACH_RESULT(I_OnChannelModeSet, OnChannelModeSet(this, cm->Name, param)); + + /* Setting v/h/o/a/q etc */ + if (cm->Type == MODE_STATUS) + { + if (param.empty()) + { + Alog() << "Channel::SetModeInternal() mode " << cm->ModeChar << " with no parameter for channel " << this->name; + return; + } + + /* We don't track bots */ + if (findbot(param)) + return; + + User *u = finduser(param); + if (!u) + { + Alog(LOG_DEBUG) << "MODE " << this->name << " +" << cm->ModeChar << " for nonexistant user " << param; + return; + } + + Alog(LOG_DEBUG) << "Setting +" << cm->ModeChar << " on " << this->name << " for " << u->nick; + + /* Set the status on the user */ + ChannelContainer *cc = u->FindChannel(this); + if (cc) + { + cc->Status->SetFlag(cm->Name); + } + + /* Enforce secureops, etc */ + chan_set_correct_modes(u, this, 0); + return; + } + /* Setting b/e/I etc */ + else if (cm->Type == MODE_LIST) + { + if (param.empty()) + { + Alog() << "Channel::SetModeInternal() mode " << cm->ModeChar << " with no parameter for channel " << this->name; + return; + } + + ChannelModeList *cml = dynamic_cast<ChannelModeList *>(cm); + cml->AddMask(this, param.c_str()); + return; + } + + modes.SetFlag(cm->Name); + + if (!param.empty()) + { + if (cm->Type != MODE_PARAM) + { + Alog() << "Channel::SetModeInternal() mode " << cm->ModeChar << " for " << this->name << " with a paramater, but its not a param mode"; + return; + } + + /* They could be resetting the mode to change its params */ + std::map<ChannelModeName, std::string>::iterator it = Params.find(cm->Name); + if (it != Params.end()) + { + Params.erase(it); + } + + Params.insert(std::make_pair(cm->Name, param)); + } + + /* Channel mode +P or so was set, mark this channel as persistant */ + if (cm->Name == CMODE_PERM) + { + this->SetFlag(CH_PERSIST); + if (ci) + ci->SetFlag(CI_PERSIST); + } + + /* Check if we should enforce mlock */ + if (!EnforceMLock || MOD_RESULT == EVENT_STOP) + return; + + /* Non registered channels can not be +r */ + if (!ci && HasMode(CMODE_REGISTERED)) + { + RemoveMode(NULL, CMODE_REGISTERED); + } + + /* Non registered channel has no mlock */ + if (!ci) + { + return; + } + + /* If this channel has this mode locked negative */ + if (ci->HasMLock(cm->Name, false)) + { + /* Remove the mode */ + if (cm->Type == MODE_PARAM) + { + std::string cparam; + GetParam(cm->Name, cparam); + RemoveMode(NULL, cm, cparam); + } + else if (cm->Type == MODE_REGULAR) + RemoveMode(NULL, cm); + } + /* If this is a param mode and its mlocked +, check to ensure someone didn't reset it with the wrong param */ + else if (cm->Type == MODE_PARAM && ci->HasMLock(cm->Name, true)) + { + ChannelModeParam *cmp = dynamic_cast<ChannelModeParam *>(cm); + std::string cparam, ciparam; + /* Get the param currently set on this channel */ + GetParam(cmp->Name, cparam); + /* Get the param set in mlock */ + ci->GetParam(cmp->Name, ciparam); + + /* We have the wrong param set */ + if (cparam.empty() || ciparam.empty() || cparam != ciparam) + { + /* Reset the mode with the correct param */ + SetMode(NULL, cm, ciparam); + } + } +} + +/** Remove a mode internally on a channel, this is not sent out to the IRCd + * @param cm The mode + * @param param The param + * @param EnforceMLock true if mlocks should be enforced, false to override mlock + */ +void Channel::RemoveModeInternal(ChannelMode *cm, const std::string ¶m, bool EnforceMLock) +{ + if (!cm) + return; + + EventReturn MOD_RESULT; + FOREACH_RESULT(I_OnChannelModeUnset, OnChannelModeUnset(this, cm->Name, param)); + + /* Setting v/h/o/a/q etc */ + if (cm->Type == MODE_STATUS) + { + if (param.empty()) + { + Alog() << "Channel::RemoveModeInternal() mode " << cm->ModeChar << " with no parameter for channel " << this->name; + return; + } + + /* Reset modes on bots if we're supposed to */ + BotInfo *bi = findbot(param); + if (bi) + { + if (std::find(BotModes.begin(), BotModes.end(), cm) != BotModes.end()) + { + this->SetMode(bi, cm, bi->nick); + } + /* We don't track bots */ + return; + } + + User *u = finduser(param); + if (!u) + { + Alog() << "Channel::RemoveModeInternal() MODE " << this->name << "-" << cm->ModeChar << " for nonexistant user " << param; + return; + } + + Alog(LOG_DEBUG) << "Setting -" << cm->ModeChar << " on " << this->name << " for " << u->nick; + + /* Remove the status on the user */ + ChannelContainer *cc = u->FindChannel(this); + if (cc) + { + cc->Status->UnsetFlag(cm->Name); + } + + return; + } + /* Setting b/e/I etc */ + else if (cm->Type == MODE_LIST) + { + if (param.empty()) + { + Alog() << "Channel::RemoveModeInternal() mode " << cm->ModeChar << " with no parameter for channel " << this->name; + return; + } + + ChannelModeList *cml = dynamic_cast<ChannelModeList *>(cm); + cml->DelMask(this, param.c_str()); + return; + } + + modes.UnsetFlag(cm->Name); + + if (cm->Type == MODE_PARAM) + { + std::map<ChannelModeName, std::string>::iterator it = Params.find(cm->Name); + if (it != Params.end()) + { + Params.erase(it); + } + } + + if (cm->Name == CMODE_PERM) + { + this->UnsetFlag(CH_PERSIST); + + if (ci) + { + ci->UnsetFlag(CI_PERSIST); + if (Config.s_BotServ && ci->bi && users.size() == Config.BSMinUsers - 1) + ircdproto->SendPart(ci->bi, this, NULL); + } + } + + /* We set -P in an empty channel, delete the channel */ + if (cm->Name == CMODE_PERM && users.empty()) + { + delete this; + return; + } + + /* Check for mlock */ + + /* Non registered channel, no mlock */ + if (!ci || !EnforceMLock || MOD_RESULT == EVENT_STOP) + return; + + /* This channel has this the mode locked on */ + if (ci->HasMLock(cm->Name, true)) + { + if (cm->Type == MODE_REGULAR) + { + /* Set the mode */ + SetMode(NULL, cm); + } + /* This is a param mode */ + else if (cm->Type == MODE_PARAM) + { + std::string cparam; + /* Get the param stored in mlock for this mode */ + if (ci->GetParam(cm->Name, cparam)) + SetMode(NULL, cm, cparam); + } + } +} + +/** Set a mode on a channel + * @param bi The client setting the modes + * @param cm The mode + * @param param Optional param arg for the mode + * @param EnforceMLock true if mlocks should be enforced, false to override mlock + */ +void Channel::SetMode(BotInfo *bi, ChannelMode *cm, const std::string ¶m, bool EnforceMLock) +{ + if (!cm) + return; + /* Don't set modes already set */ + if (cm->Type == MODE_REGULAR && HasMode(cm->Name)) + return; + else if (cm->Type == MODE_PARAM && HasMode(cm->Name)) + { + std::string cparam; + if (GetParam(cm->Name, cparam)) + { + if (cparam == param) + { + return; + } + } + } + else if (cm->Type == MODE_STATUS) + { + User *u = finduser(param); + if (u && HasUserStatus(u, dynamic_cast<ChannelModeStatus *>(cm))) + return; + } + else if (cm->Type == MODE_LIST) + { + // XXX this needs rewritten + } + + ModeManager::StackerAdd(bi, this, cm, true, param); + SetModeInternal(cm, param, EnforceMLock); +} + +/** + * Set a mode on a channel + * @param bi The client setting the modes + * @param Name The mode name + * @param param Optional param arg for the mode + * @param EnforceMLock true if mlocks should be enforced, false to override mlock + */ +void Channel::SetMode(BotInfo *bi, ChannelModeName Name, const std::string ¶m, bool EnforceMLock) +{ + SetMode(bi, ModeManager::FindChannelModeByName(Name), param, EnforceMLock); +} + +/** + * Set a mode on a channel + * @param bi The client setting the modes + * @param Mode The mode + * @param param Optional param arg for the mode + * @param EnforceMLock true if mlocks should be enforced, false to override mlock + */ +void Channel::SetMode(BotInfo *bi, char Mode, const std::string ¶m, bool EnforceMLock) +{ + SetMode(bi, ModeManager::FindChannelModeByChar(Mode), param, EnforceMLock); +} + +/** Remove a mode from a channel + * @param bi The client setting the modes + * @param cm The mode + * @param param Optional param arg for the mode + * @param EnforceMLock true if mlocks should be enforced, false to override mlock + */ +void Channel::RemoveMode(BotInfo *bi, ChannelMode *cm, const std::string ¶m, bool EnforceMLock) +{ + if (!cm) + return; + /* Don't unset modes that arent set */ + if ((cm->Type == MODE_REGULAR || cm->Type == MODE_PARAM) && !HasMode(cm->Name)) + return; + /* Don't unset status that aren't set */ + else if (cm->Type == MODE_STATUS) + { + User *u = finduser(param); + if (u && !HasUserStatus(u, dynamic_cast<ChannelModeStatus *>(cm))) + return; + } + else if (cm->Type == MODE_LIST) + { + // XXX this needs to be rewritten sometime + } + + /* If this mode needs no param when being unset, empty the param */ + bool SendParam = true; + if (cm->Type == MODE_PARAM) + { + ChannelModeParam *cmp = dynamic_cast<ChannelModeParam *>(cm); + if (cmp->MinusNoArg) + SendParam = false; + } + + ModeManager::StackerAdd(bi, this, cm, false, param); + RemoveModeInternal(cm, SendParam ? param : "", EnforceMLock); +} + +/** + * Remove a mode from a channel + * @param bi The client setting the modes + * @param Name The mode name + * @param param Optional param arg for the mode + * @param EnforceMLock true if mlocks should be enforced, false to override mlock + */ +void Channel::RemoveMode(BotInfo *bi, ChannelModeName Name, const std::string ¶m, bool EnforceMLock) +{ + RemoveMode(bi, ModeManager::FindChannelModeByName(Name), param, EnforceMLock); +} + +/** + * Remove a mode from a channel + * @param bi The client setting the modes + * @param Mode The mode + * @param param Optional param arg for the mode + * @param EnforceMLock true if mlocks should be enforced, false to override mlock + */ +void Channel::RemoveMode(BotInfo *bi, char Mode, const std::string ¶m, bool EnforceMLock) +{ + RemoveMode(bi, ModeManager::FindChannelModeByChar(Mode), param, EnforceMLock); +} + +/** Get a param from the channel + * @param Name The mode + * @param Target a string to put the param into + * @return true on success + */ +const bool Channel::GetParam(ChannelModeName Name, std::string &Target) +{ + std::map<ChannelModeName, std::string>::iterator it = Params.find(Name); + + Target.clear(); + + if (it != Params.end()) + { + Target = it->second; + return true; + } + + return false; +} + +/** Check if a mode is set and has a param + * @param Name The mode + */ +const bool Channel::HasParam(ChannelModeName Name) +{ + std::map<ChannelModeName, std::string>::iterator it = Params.find(Name); + + if (it != Params.end()) + { + return true; + } + + return false; +} + +/*************************************************************************/ + +/** Clear all the modes from the channel + * @param bi The client setting the modes + */ +void Channel::ClearModes(BotInfo *bi) +{ + ChannelMode *cm; + + for (size_t n = CMODE_BEGIN + 1; n != CMODE_END; ++n) + { + cm = ModeManager::FindChannelModeByName(static_cast<ChannelModeName>(n)); + + if (cm && this->HasMode(cm->Name)) + { + if (cm->Type == MODE_REGULAR) + { + this->RemoveMode(NULL, cm); + } + else if (cm->Type == MODE_PARAM) + { + std::string param; + this->GetParam(cm->Name, param); + this->RemoveMode(NULL, cm, param); + } + } + } + + modes.ClearFlags(); +} + +/** Clear all the bans from the channel + * @param bi The client setting the modes + */ +void Channel::ClearBans(BotInfo *bi) +{ + Entry *entry, *nexte; + ChannelModeList *cml; + + cml = dynamic_cast<ChannelModeList *>(ModeManager::FindChannelModeByName(CMODE_BAN)); + + if (cml && this->bans && this->bans->count) + { + for (entry = this->bans->entries; entry; entry = nexte) + { + nexte = entry->next; + + this->RemoveMode(bi, CMODE_BAN, entry->mask); + } + } +} + +/** Clear all the excepts from the channel + * @param bi The client setting the modes + */ +void Channel::ClearExcepts(BotInfo *bi) +{ + Entry *entry, *nexte; + ChannelModeList *cml; + + cml = dynamic_cast<ChannelModeList *>(ModeManager::FindChannelModeByName(CMODE_EXCEPT)); + + if (cml && this->excepts && this->excepts->count) + { + for (entry = this->excepts->entries; entry; entry = nexte) + { + nexte = entry->next; + + this->RemoveMode(bi, CMODE_EXCEPT, entry->mask); + } + } +} + +/** Clear all the invites from the channel + * @param bi The client setting the modes + */ +void Channel::ClearInvites(BotInfo *bi) +{ + Entry *entry, *nexte; + ChannelModeList *cml; + + cml = dynamic_cast<ChannelModeList *>(ModeManager::FindChannelModeByName(CMODE_INVITEOVERRIDE)); + + if (cml && this->invites && this->invites->count) + { + for (entry = this->invites->entries; entry; entry = nexte) + { + nexte = entry->next; + + this->RemoveMode(bi, CMODE_INVITEOVERRIDE, entry->mask); + } + } +} + +/** Set a string of modes on the channel + * @param bi The client setting the modes + * @param EnforceMLock Should mlock be enforced on this mode change + * @param cmodes The modes to set + */ +void Channel::SetModes(BotInfo *bi, bool EnforceMLock, const char *cmodes, ...) +{ + char buf[BUFSIZE] = ""; + va_list args; + std::string modebuf, sbuf; + int add = -1; + va_start(args, cmodes); + vsnprintf(buf, BUFSIZE - 1, cmodes, args); + va_end(args); + + spacesepstream sep(buf); + sep.GetToken(modebuf); + for (unsigned i = 0; i < modebuf.size(); ++i) + { + ChannelMode *cm; + + switch (modebuf[i]) + { + case '+': + add = 1; + continue; + case '-': + add = 0; + continue; + default: + if (add == -1) + continue; + cm = ModeManager::FindChannelModeByChar(modebuf[i]); + if (!cm) + continue; + } + + if (add) + { + if (cm->Type != MODE_REGULAR && sep.GetToken(sbuf)) + this->SetMode(bi, cm, sbuf, EnforceMLock); + else + this->SetMode(bi, cm, "", EnforceMLock); + } + else if (add == 0) + { + if (cm->Type != MODE_REGULAR && sep.GetToken(sbuf)) + this->RemoveMode(bi, cm, sbuf, EnforceMLock); + else + this->RemoveMode(bi, cm, "", EnforceMLock); + } + } +} + +/*************************************************************************/ + +/** Set modes internally on the channel + * @param c The channel + * @param ac Number of args + * @param av args + */ +void ChanSetInternalModes(Channel *c, int ac, const char **av) +{ + if (!ac) + return; + + int k = 0, j = 0, add = -1; + for (unsigned int i = 0; i < strlen(av[0]); ++i) + { + ChannelMode *cm; + + switch (av[0][i]) + { + case '+': + add = 1; + continue; + case '-': + add = 0; + continue; + default: + if (add == -1) + continue; + cm = ModeManager::FindChannelModeByChar(av[0][i]); + if (!cm) + continue; + } + + if (cm->Type == MODE_REGULAR) + { + if (add) + c->SetModeInternal(cm); + else + c->RemoveModeInternal(cm); + continue; + } + else if (cm->Type == MODE_PARAM) + { + ChannelModeParam *cmp = dynamic_cast<ChannelModeParam *>(cm); + + if (!add && cmp->MinusNoArg) + { + c->RemoveModeInternal(cm); + ++k; + continue; + } + } + if (++j < ac) + { + if (add) + c->SetModeInternal(cm, av[j]); + else + c->RemoveModeInternal(cm, av[j]); + } + else + { + Alog() << "warning: ChanSetInternalModes() recieved more modes requiring params than params, modes: " << merge_args(ac, av) << ", ac: " << ac << ", j: " << j; + } + } + + if (j + k + 1 < ac) + { + Alog() << "warning: ChanSetInternalModes() recieved more params than modes requiring them, modes: " << merge_args(ac, av) << ", ac: " << ac << ", j: " << j << " k: " << k; + } +} + +/** Kick a user from a channel internally + * @param source The sender of the kick + * @param nick The nick being kicked + * @param reason The reason for the kick + */ +void Channel::KickInternal(const std::string &source, const std::string &nick, const std::string &reason) +{ + /* If it is the bot that is being kicked, we make it rejoin the + * channel and stop immediately. + * --lara + */ + if (Config.s_BotServ && this->ci && findbot(nick)) + { + bot_join(this->ci); + return; + } + + User *user = finduser(nick); + if (!user) + { + Alog(LOG_DEBUG) << "Channel::KickInternal got a nonexistent user " << nick << " on " << this->name << ": " << reason; + return; + } + + Alog(LOG_DEBUG) << "Channel::KickInternal kicking " << user->nick << " from " << this->name; + + if (user->FindChannel(this)) + { + FOREACH_MOD(I_OnUserKicked, OnUserKicked(this, user, source, reason)); + this->DeleteUser(user); + } + else + Alog(LOG_DEBUG) << "Channel::KickInternal got kick for user " << user->nick << " who isn't on channel " << this->name << " ?"; +} + +/** Kick a user from the channel + * @param bi The sender, can be NULL for the service bot for this channel + * @param u The user being kicked + * @param reason The reason for the kick + * @return true if the kick was scucessful, false if a module blocked the kick + */ +bool Channel::Kick(BotInfo *bi, User *u, const char *reason, ...) +{ + va_list args; + char buf[BUFSIZE] = ""; + va_start(args, reason); + vsnprintf(buf, BUFSIZE - 1, reason, args); + va_end(args); + + /* May not kick ulines */ + if (u->server->IsULined()) + return false; + + /* Do not kick protected clients */ + if (u->IsProtected()) + return false; + + EventReturn MOD_RESULT; + FOREACH_RESULT(I_OnBotKick, OnBotKick(bi, this, u, buf)); + if (MOD_RESULT == EVENT_STOP) + return false; + ircdproto->SendKick(bi ? bi : whosends(this->ci), this, u, "%s", buf); + this->KickInternal(bi ? bi->nick : whosends(this->ci)->nick, u->nick, buf); + return true; +} + +/* Returns a fully featured binary modes string. If complete is 0, the + * eventual parameters won't be added to the string. + */ + +char *chan_get_modes(Channel * chan, int complete, int plus) +{ + static char res[BUFSIZE]; + char params[BUFSIZE]; + char *end = res, *value, *pend = params, *pend2 = params; + std::string param; + + if (chan->HasModes()) + { + for (std::list<Mode *>::iterator it = ModeManager::Modes.begin(); it != ModeManager::Modes.end(); ++it) + { + if ((*it)->Class != MC_CHANNEL) + continue; + + ChannelMode *cm = dynamic_cast<ChannelMode *>(*it); + + if (chan->HasMode(cm->Name)) + { + *end++ = cm->ModeChar; + + if (complete) + { + if (cm->Type == MODE_PARAM) + { + ChannelModeParam *cmp = dynamic_cast<ChannelModeParam *>(cm); + + if (plus || !cmp->MinusNoArg) + { + chan->GetParam(cmp->Name, param); + + if (!param.empty()) + { + value = const_cast<char *>(param.c_str()); + + *pend++ = ' '; + + while (*value) + *pend++ = *value++; + } + } + } + } + } + } + + while (*pend2) + *end++ = *pend2++; + } + + *end = 0; + + return res; +} + +/*************************************************************************/ + +Channel *findchan(const char *chan) +{ + return findchan(ci::string(chan)); +} + +Channel *findchan(const std::string &chan) +{ + return findchan(ci::string(chan.c_str())); +} + +Channel *findchan(const ci::string &chan) +{ + channel_map::const_iterator it = ChannelList.find(chan); + + if (it != ChannelList.end()) + return it->second; + return NULL; +} + +/*************************************************************************/ + +/* Return statistics. Pointers are assumed to be valid. */ + +void get_channel_stats(long *nrec, long *memuse) +{ + long count = 0, mem = 0; + BanData *bd; + std::string buf; + + for (channel_map::const_iterator cit = ChannelList.begin(); cit != ChannelList.end(); ++cit) + { + Channel *chan = cit->second; + + count++; + mem += sizeof(*chan); + if (chan->topic) + mem += strlen(chan->topic) + 1; + if (chan->GetParam(CMODE_KEY, buf)) + mem += buf.length() + 1; + if (chan->GetParam(CMODE_FLOOD, buf)) + mem += buf.length() + 1; + if (chan->GetParam(CMODE_REDIRECT, buf)) + mem += buf.length() + 1; + mem += get_memuse(chan->bans); + if (ModeManager::FindChannelModeByName(CMODE_EXCEPT)) + mem += get_memuse(chan->excepts); + if (ModeManager::FindChannelModeByName(CMODE_INVITEOVERRIDE)) + mem += get_memuse(chan->invites); + for (CUserList::iterator it = chan->users.begin(); it != chan->users.end(); ++it) + { + mem += sizeof(*it); + mem += sizeof((*it)->ud); + if ((*it)->ud.lastline) + mem += strlen((*it)->ud.lastline) + 1; + } + for (bd = chan->bd; bd; bd = bd->next) { + if (bd->mask) + mem += strlen(bd->mask) + 1; + mem += sizeof(*bd); + } + } + *nrec = count; + *memuse = mem; +} + +/*************************************************************************/ + +/* Is the given nick on the given channel? + This function supports links. */ + +User *nc_on_chan(Channel * c, NickCore * nc) +{ + if (!c || !nc) + return NULL; + + for (CUserList::iterator it = c->users.begin(); it != c->users.end(); ++it) + { + UserContainer *uc = *it; + + if (uc->user->Account() == nc) + return uc->user; + } + return NULL; +} + +/*************************************************************************/ +/*************************** Message Handling ****************************/ +/*************************************************************************/ + +/* Handle a JOIN command. + * av[0] = channels to join + */ + +void do_join(const char *source, int ac, const char **av) +{ + User *user; + Channel *chan; + time_t ctime = time(NULL); + + user = finduser(source); + if (!user) + { + Alog(LOG_DEBUG) << "JOIN from nonexistent user " << source << ": " << merge_args(ac, av); + return; + } + + commasepstream sep(av[0]); + ci::string buf; + while (sep.GetToken(buf)) + { + if (buf[0] == '0') + { + for (UChannelList::iterator it = user->chans.begin(); it != user->chans.end();) + { + ChannelContainer *cc = *it++; + + std::string channame = cc->chan->name; + FOREACH_MOD(I_OnPrePartChannel, OnPrePartChannel(user, cc->chan)); + cc->chan->DeleteUser(user); + FOREACH_MOD(I_OnPartChannel, OnPartChannel(user, findchan(channame.c_str()), channame, "")); + } + user->chans.clear(); + continue; + } + + chan = findchan(buf.c_str()); + + /* Channel doesn't exist, create it */ + if (!chan) + { + chan = new Channel(av[0], ctime); + } + + /* Join came with a TS */ + if (ac == 2) + { + time_t ts = atol(av[1]); + + /* Their time is older, we lose */ + if (chan->creation_time > ts) + { + Alog(LOG_DEBUG) << "recieved a new TS for JOIN: " << ts; + + if (chan->ci) + { + /* Cycle the bot to fix ts */ + if (chan->ci->bi) + { + ircdproto->SendPart(chan->ci->bi, chan, "TS reop"); + bot_join(chan->ci); + } + /* Be sure to set mlock again, else we could be -r etc.. */ + check_modes(chan); + } + } + } + + EventReturn MOD_RESULT; + FOREACH_RESULT(I_OnPreJoinChannel, OnPreJoinChannel(user, chan)); + + /* Join the user to the channel */ + chan->JoinUser(user); + /* Set the propre modes on the user */ + chan_set_correct_modes(user, chan, 1); + + /* Modules may want to allow this user in the channel, check. + * If not, CheckKick will kick/ban them, don't call OnJoinChannel after this as the user will have + * been destroyed + */ + if (MOD_RESULT != EVENT_STOP && chan && chan->ci && chan->ci->CheckKick(user)) + continue; + + FOREACH_MOD(I_OnJoinChannel, OnJoinChannel(user, chan)); + } +} + +/*************************************************************************/ + +/** Handle a KICK command. + * @param source The source of the kick + * @param ac number of args + * @param av The channel, nick(s) being kicked, and reason + */ +void do_kick(const std::string &source, int ac, const char **av) +{ + Channel *c = findchan(av[0]); + if (!c) + { + Alog(LOG_DEBUG) << "Recieved kick for nonexistant channel " << av[0]; + return; + } + + std::string buf; + commasepstream sep(av[1]); + while (sep.GetToken(buf)) + { + c->KickInternal(source, buf, av[2]); + } +} + +/*************************************************************************/ + +/* Handle a PART command. + * av[0] = channels to leave + * av[1] = reason (optional) + */ + +void do_part(const char *source, int ac, const char **av) +{ + User *user = finduser(source); + if (!user) + { + Alog(LOG_DEBUG) << "PART from nonexistent user " << source << ": " << merge_args(ac, av); + return; + } + + commasepstream sep(av[0]); + ci::string buf; + while (sep.GetToken(buf)) + { + Channel *c = findchan(buf.c_str()); + + if (!c) + { + Alog(LOG_DEBUG) << "Recieved PART from " << user->nick << " for nonexistant channel " << buf; + } + + Alog(LOG_DEBUG) << source << " leaves " << buf; + + if (user->FindChannel(c)) + { + FOREACH_MOD(I_OnPrePartChannel, OnPrePartChannel(user, c)); + std::string ChannelName = c->name; + c->DeleteUser(user); + FOREACH_MOD(I_OnPartChannel, OnPartChannel(user, findchan(ChannelName.c_str()), ChannelName, av[1] ? av[1] : "")); + } + else + Alog(LOG_DEBUG) << "Recieved PART from " << user->nick << " for " << c->name << ", but " << user->nick << " isn't in " << c->name << "?"; + } +} + +/*************************************************************************/ + +/** Process a MODE command from the server, and set the modes on the user/channel + * it was sent for + * @param source The source of the command + * @param ac Number of args in array.. + * @param av Array of args + */ +void do_cmode(const char *source, int ac, const char **av) +{ + Channel *c; + ChannelInfo *ci; + unsigned int i; + const char *t; + + if (Capab.HasFlag(CAPAB_TSMODE) || UseTSMODE) + { + for (i = 0; i < strlen(av[1]); i++) + if (!isdigit(av[1][i])) + break; + if (av[1][i] == '\0') + { + t = av[0]; + av[0] = av[1]; + av[1] = t; + ac--; + av++; + } + else + Alog() << "TSMODE enabled but MODE has no valid TS"; + } + + /* :42XAAAAAO TMODE 1106409026 #ircops +b *!*@*.aol.com */ + if (ircd->ts6) + { + if (isdigit(av[0][0])) + { + ac--; + av++; + } + } + + c = findchan(av[0]); + if (!c) + { + if (debug) + { + ci = cs_findchan(av[0]); + if (!ci || ci->HasFlag(CI_FORBIDDEN)) + Alog(LOG_DEBUG) << "MODE " << merge_args(ac - 1, av + 1) << " for nonexistant channel " << av[0]; + } + return; + } + + if (strchr(source, '.') && !av[1][strcspn(av[1], "bovahq")]) + { + if (time(NULL) != c->server_modetime) + { + c->server_modecount = 0; + c->server_modetime = time(NULL); + } + c->server_modecount++; + } + + ac--; + av++; + ChanSetInternalModes(c, ac, av); +} + +/*************************************************************************/ + +/* Handle a TOPIC command. */ + +void do_topic(const char *source, int ac, const char **av) +{ + Channel *c = findchan(av[0]); + ChannelInfo *ci; + int ts; + time_t topic_time; + char *topicsetter; + + if (ircd->sjb64) { + ts = base64dects(av[2]); + Alog(LOG_DEBUG) << "encoded TOPIC TS " << av[2] << " converted to " << ts; + } else { + ts = strtoul(av[2], NULL, 10); + } + + topic_time = ts; + + if (!c) + { + Alog(LOG_DEBUG) << "TOPIC " << merge_args(ac - 1, av + 1) << " for nonexistent channel " << av[0]; + return; + } + + /* We can be sure that the topic will be in sync here -GD */ + c->topic_sync = 1; + + ci = c->ci; + + /* For Unreal, cut off the ! and any futher part of the topic setter. + * This way, nick!ident@host setters will only show the nick. -GD + */ + topicsetter = myStrGetToken(av[1], '!', 0); + + /* If the current topic we have matches the last known topic for this + * channel exactly, there's no need to update anything and we can as + * well just return silently without updating anything. -GD + */ + if ((ac > 3) && *av[3] && ci && ci->last_topic + && (strcmp(av[3], ci->last_topic) == 0) + && (strcmp(topicsetter, ci->last_topic_setter.c_str()) == 0)) { + delete [] topicsetter; + return; + } + + if (check_topiclock(c, topic_time)) { + delete [] topicsetter; + return; + } + + if (c->topic) { + delete [] c->topic; + c->topic = NULL; + } + if (ac > 3 && *av[3]) { + c->topic = sstrdup(av[3]); + } + + c->topic_setter = topicsetter; + c->topic_time = topic_time; + delete [] topicsetter; + + record_topic(av[0]); + + FOREACH_MOD(I_OnTopicUpdated, OnTopicUpdated(c, av[0])); +} + +/*************************************************************************/ +/**************************** Internal Calls *****************************/ +/*************************************************************************/ + +/** + * Set the correct modes, or remove the ones granted without permission, + * for the specified user on ths specified channel. This doesn't give + * modes to ignored users, but does remove them if needed. + * @param user The user to give/remove modes to/from + * @param c The channel to give/remove modes on + * @param give_modes Set to 1 to give modes, 0 to not give modes + * @return void + **/ +void chan_set_correct_modes(User * user, Channel * c, int give_modes) +{ + ChannelInfo *ci; + ChannelMode *owner, *admin, *op, *halfop, *voice; + + owner = ModeManager::FindChannelModeByName(CMODE_OWNER); + admin = ModeManager::FindChannelModeByName(CMODE_PROTECT); + op = ModeManager::FindChannelModeByName(CMODE_OP); + halfop = ModeManager::FindChannelModeByName(CMODE_HALFOP); + voice = ModeManager::FindChannelModeByName(CMODE_VOICE); + + if (!c || !(ci = c->ci)) + return; + + if ((ci->HasFlag(CI_FORBIDDEN)) || (*(c->name.c_str()) == '+')) + return; + + Alog(LOG_DEBUG) << "Setting correct user modes for " << user->nick << " on " << c->name << " (" << (give_modes ? "" : "not ") << "giving modes)"; + + if (give_modes && !get_ignore(user->nick.c_str()) && (!user->Account() || user->Account()->HasFlag(NI_AUTOOP))) + { + if (owner && check_access(user, ci, CA_AUTOOWNER)) + c->SetMode(NULL, CMODE_OWNER, user->nick); + else if (admin && check_access(user, ci, CA_AUTOPROTECT)) + c->SetMode(NULL, CMODE_PROTECT, user->nick); + + if (op && check_access(user, ci, CA_AUTOOP)) + c->SetMode(NULL, CMODE_OP, user->nick); + else if (halfop && check_access(user, ci, CA_AUTOHALFOP)) + c->SetMode(NULL, CMODE_HALFOP, user->nick); + else if (voice && check_access(user, ci, CA_AUTOVOICE)) + c->SetMode(NULL, CMODE_VOICE, user->nick); + } + /* If this channel has secureops or the user matches autodeop or the channel is syncing and this is the first user and they are not ulined, check to remove modes */ + if ((ci->HasFlag(CI_SECUREOPS) || check_access(user, ci, CA_AUTODEOP) || (c->HasFlag(CH_SYNCING) && c->users.size() == 1)) && !user->server->IsULined()) + { + if (owner && c->HasUserStatus(user, CMODE_OWNER) && !check_access(user, ci, CA_FOUNDER)) + c->RemoveMode(NULL, CMODE_OWNER, user->nick); + + if (admin && c->HasUserStatus(user, CMODE_PROTECT) && !check_access(user, ci, CA_AUTOPROTECT) && !check_access(user, ci, CA_PROTECTME)) + c->RemoveMode(NULL, CMODE_PROTECT, user->nick); + + if (op && c->HasUserStatus(user, CMODE_OP) && !check_access(user, ci, CA_AUTOOP) && !check_access(user, ci, CA_OPDEOPME)) + c->RemoveMode(NULL, CMODE_OP, user->nick); + + if (halfop && c->HasUserStatus(user, CMODE_HALFOP) && !check_access(user, ci, CA_AUTOHALFOP) && !check_access(user, ci, CA_HALFOPME)) + c->RemoveMode(NULL, CMODE_HALFOP, user->nick); + } +} + +/*************************************************************************/ + +/** Set modes on every channel + * This overrides mlock on channels + * @param bi The bot to send the modes from + * @param modes The modes + */ +void MassChannelModes(BotInfo *bi, const std::string &modes) +{ + for (channel_map::const_iterator it = ChannelList.begin(); it != ChannelList.end(); ++it) + { + Channel *c = it->second; + + if (c->bouncy_modes) + return; + c->SetModes(bi, false, modes.c_str()); + } +} + +/*************************************************************************/ + +void restore_unsynced_topics() +{ + for (channel_map::const_iterator it = ChannelList.begin(); it != ChannelList.end(); ++it) + { + Channel *c = it->second; + + if (!(c->topic_sync)) + restore_topic(c->name.c_str()); + } +} + +/*************************************************************************/ + +/** + * This handles creating a new Entry. + * This function destroys and free's the given mask as a side effect. + * @param mask Host/IP/CIDR mask to convert to an entry + * @return Entry struct for the given mask, NULL if creation failed + */ +Entry *entry_create(char *mask) +{ + Entry *entry; + char *nick = NULL, *user, *host, *cidrhost; + uint32 ip, cidr; + + entry = new Entry; + entry->SetFlag(ENTRYTYPE_NONE); + entry->prev = NULL; + entry->next = NULL; + entry->nick = NULL; + entry->user = NULL; + entry->host = NULL; + entry->mask = sstrdup(mask); + + host = strchr(mask, '@'); + if (host) { + *host++ = '\0'; + /* If the user is purely a wildcard, ignore it */ + if (str_is_pure_wildcard(mask)) + user = NULL; + else { + + /* There might be a nick too */ + user = strchr(mask, '!'); + if (user) { + *user++ = '\0'; + /* If the nick is purely a wildcard, ignore it */ + if (str_is_pure_wildcard(mask)) + nick = NULL; + else + nick = mask; + } else { + nick = NULL; + user = mask; + } + } + } else { + /* It is possibly an extended ban/invite mask, but we do + * not support these at this point.. ~ Viper */ + /* If there's no user in the mask, assume a pure wildcard */ + user = NULL; + host = mask; + } + + if (nick) { + entry->nick = sstrdup(nick); + /* Check if we have a wildcard user */ + if (str_is_wildcard(nick)) + entry->SetFlag(ENTRYTYPE_NICK_WILD); + else + entry->SetFlag(ENTRYTYPE_NICK); + } + + if (user) { + entry->user = sstrdup(user); + /* Check if we have a wildcard user */ + if (str_is_wildcard(user)) + entry->SetFlag(ENTRYTYPE_USER_WILD); + else + entry->SetFlag(ENTRYTYPE_USER); + } + + /* Only check the host if it's not a pure wildcard */ + if (*host && !str_is_pure_wildcard(host)) { + if (ircd->cidrchanbei && str_is_cidr(host, &ip, &cidr, &cidrhost)) { + entry->cidr_ip = ip; + entry->cidr_mask = cidr; + entry->SetFlag(ENTRYTYPE_CIDR4); + host = cidrhost; + } else if (ircd->cidrchanbei && strchr(host, '/')) { + /* Most IRCd's don't enforce sane bans therefore it is not + * so unlikely we will encounter this. + * Currently we only support strict CIDR without taking into + * account quirks of every single ircd (nef) that ignore everything + * after the first /cidr. To add this, sanitaze before sending to + * str_is_cidr() as this expects a standard cidr. + * Add it to the internal list (so it is included in for example clear) + * but do not use if during matching.. ~ Viper */ + entry->ClearFlags(); + entry->SetFlag(ENTRYTYPE_NONE); + } else { + entry->host = sstrdup(host); + if (str_is_wildcard(host)) + entry->SetFlag(ENTRYTYPE_HOST_WILD); + else + entry->SetFlag(ENTRYTYPE_HOST); + } + } + delete [] mask; + + return entry; +} + + +/** + * Create an entry and add it at the beginning of given list. + * @param list The List the mask should be added to + * @param mask The mask to parse and add to the list + * @return Pointer to newly added entry. NULL if it fails. + */ +Entry *entry_add(EList * list, const char *mask) +{ + Entry *e; + char *hostmask; + + hostmask = sstrdup(mask); + e = entry_create(hostmask); + + if (!e) + return NULL; + + e->next = list->entries; + e->prev = NULL; + + if (list->entries) + list->entries->prev = e; + list->entries = e; + list->count++; + + return e; +} + + +/** + * Delete the given entry from a given list. + * @param list Linked list from which entry needs to be removed. + * @param e The entry to be deleted, must be member of list. + */ +void entry_delete(EList * list, Entry * e) +{ + if (!list || !e) + return; + + if (e->next) + e->next->prev = e->prev; + if (e->prev) + e->prev->next = e->next; + + if (list->entries == e) + list->entries = e->next; + + if (e->nick) + delete [] e->nick; + if (e->user) + delete [] e->user; + if (e->host) + delete [] e->host; + delete [] e->mask; + delete e; + + list->count--; +} + + +/** + * Create and initialize a new entrylist + * @return Pointer to the created EList object + **/ +EList *list_create() +{ + EList *list; + + list = new EList; + list->entries = NULL; + list->count = 0; + + return list; +} + + +/** + * Match the given Entry to the given user/host and optional IP addy + * @param e Entry struct to match against + * @param nick Nick to match against + * @param user User to match against + * @param host Host to match against + * @param ip IP to match against, set to 0 to not match this + * @return 1 for a match, 0 for no match + */ +int entry_match(Entry *e, const ci::string &nick, const ci::string &user, const ci::string &host, uint32 ip) +{ + /* If we don't get an entry, or it s an invalid one, no match ~ Viper */ + if (!e || !e->FlagCount()) + return 0; + + ci::string ci_nick(nick.c_str()), ci_user(user.c_str()), ci_host(host.c_str()); + if (ircd->cidrchanbei && e->HasFlag(ENTRYTYPE_CIDR4) && (!ip || (ip && (ip & e->cidr_mask) != e->cidr_ip))) + return 0; + if (e->HasFlag(ENTRYTYPE_NICK) && (nick.empty() || nick != e->nick)) + return 0; + if (e->HasFlag(ENTRYTYPE_USER) && (user.empty() || user != e->user)) + return 0; + if (e->HasFlag(ENTRYTYPE_HOST) && (host.empty() || host != e->host)) + return 0; + if (e->HasFlag(ENTRYTYPE_NICK_WILD) && !Anope::Match(nick, e->nick)) + return 0; + if (e->HasFlag(ENTRYTYPE_USER_WILD) && !Anope::Match(user, e->user)) + return 0; + if (e->HasFlag(ENTRYTYPE_HOST_WILD) && !Anope::Match(host, e->host)) + return 0; + + return 1; +} + +/** + * Match the given Entry to the given hostmask and optional IP addy. + * @param e Entry struct to match against + * @param mask Hostmask to match against + * @param ip IP to match against, set to 0 to not match this + * @return 1 for a match, 0 for no match + */ +int entry_match_mask(Entry * e, const char *mask, uint32 ip) +{ + char *hostmask, *nick, *user, *host; + int res; + + hostmask = sstrdup(mask); + + host = strchr(hostmask, '@'); + if (host) { + *host++ = '\0'; + user = strchr(hostmask, '!'); + if (user) { + *user++ = '\0'; + nick = hostmask; + } else { + nick = NULL; + user = hostmask; + } + } else { + nick = NULL; + user = NULL; + host = hostmask; + } + + res = entry_match(e, nick ? nick : "", user ? user : "", host ? host : "", ip); + + /* Free the destroyed mask. */ + delete [] hostmask; + + return res; +} + +/** + * Match a nick, user, host, and ip to a list entry + * @param e List that should be matched against + * @param nick The nick to match + * @param user The user to match + * @param host The host to match + * @param ip The ip to match + * @return Returns the first matching entry, if none, NULL is returned. + */ +Entry *elist_match(EList * list, const char *nick, const char *user, const char *host, + uint32 ip) +{ + Entry *e; + + if (!list || !list->entries) + return NULL; + + for (e = list->entries; e; e = e->next) { + if (entry_match(e, nick ? nick : "", user ? user : "", host ? host : "", ip)) + return e; + } + + /* We matched none */ + return NULL; +} + +/** + * Match a mask and ip to a list. + * @param list EntryList that should be matched against + * @param mask The nick!user@host mask to match + * @param ip The ip to match + * @return Returns the first matching entry, if none, NULL is returned. + */ +Entry *elist_match_mask(EList * list, const char *mask, uint32 ip) +{ + char *hostmask, *nick, *user, *host; + Entry *res; + + if (!list || !list->entries || !mask) + return NULL; + + hostmask = sstrdup(mask); + + host = strchr(hostmask, '@'); + if (host) { + *host++ = '\0'; + user = strchr(hostmask, '!'); + if (user) { + *user++ = '\0'; + nick = hostmask; + } else { + nick = NULL; + user = hostmask; + } + } else { + nick = NULL; + user = NULL; + host = hostmask; + } + + res = elist_match(list, nick, user, host, ip); + + /* Free the destroyed mask. */ + delete [] hostmask; + + return res; +} + +/** + * Check if a user matches an entry on a list. + * @param list EntryList that should be matched against + * @param user The user to match against the entries + * @return Returns the first matching entry, if none, NULL is returned. + */ +Entry *elist_match_user(EList * list, User * u) +{ + Entry *res; + char *host; + uint32 ip = 0; + + if (!list || !list->entries || !u) + return NULL; + + if (u->hostip == NULL) { + host = host_resolve(u->host); + /* we store the just resolved hostname so we don't + * need to do this again */ + if (host) { + u->hostip = sstrdup(host); + } + } else { + host = sstrdup(u->hostip); + } + + /* Convert the host to an IP.. */ + if (host) + ip = str_is_ip(host); + + /* Match what we ve got against the lists.. */ + res = elist_match(list, u->nick.c_str(), u->GetIdent().c_str(), u->host, ip); + if (!res) + res = elist_match(list, u->nick.c_str(), u->GetIdent().c_str(), u->GetDisplayedHost().c_str(), ip); + if (!res && !u->GetCloakedHost().empty() && u->GetCloakedHost() != u->GetDisplayedHost()) + res = elist_match(list, u->nick.c_str(), u->GetIdent().c_str(), u->GetCloakedHost().c_str(), ip); + + if (host) + delete [] host; + + return res; +} + +/** + * Find a entry identical to the given mask.. + * @param list EntryList that should be matched against + * @param mask The *!*@* mask to match + * @return Returns the first matching entry, if none, NULL is returned. + */ +Entry *elist_find_mask(EList * list, const char *mask) +{ + Entry *e; + + if (!list || !list->entries || !mask) + return NULL; + + for (e = list->entries; e; e = e->next) { + if (!stricmp(e->mask, mask)) + return e; + } + + return NULL; +} + +/** + * Gets the total memory use of an entrylit. + * @param list The list we should estimate the mem use of. + * @return Returns the memory useage of the given list. + */ +long get_memuse(EList * list) +{ + Entry *e; + long mem = 0; + + if (!list) + return 0; + + mem += sizeof(EList *); + mem += sizeof(Entry *) * list->count; + if (list->entries) { + for (e = list->entries; e; e = e->next) { + if (e->nick) + mem += strlen(e->nick) + 1; + if (e->user) + mem += strlen(e->user) + 1; + if (e->host) + mem += strlen(e->host) + 1; + if (e->mask) + mem += strlen(e->mask) + 1; + } + } + + return mem; +} + +/*************************************************************************/ |