diff options
| author | Adam <Adam@Anope.org> | 2010-05-25 00:57:25 -0500 |
|---|---|---|
| committer | Adam <Adam@anope.org> | 2010-06-18 21:04:07 -0400 |
| commit | 4a2b9ebcf38d6c0a2860b966421b3af125438488 (patch) | |
| tree | e4af4e59fd29352138db0fb0ff614d50233850b8 /src/channels.cpp | |
| parent | 2fba686904e6f78ebab35df171c5757afeebf05d (diff) | |
Renamed all of source files from .c to .cpp
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]); |
