The InspIRCd Project
Home | Developers | Wiki | Forums | Bug Tracker | SVN | Download
Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | File Members

channels.cpp

Go to the documentation of this file.
00001 /*       +------------------------------------+
00002  *       | Inspire Internet Relay Chat Daemon |
00003  *       +------------------------------------+
00004  *
00005  *  InspIRCd: (C) 2002-2008 InspIRCd Development Team
00006  * See: http://www.inspircd.org/wiki/index.php/Credits
00007  *
00008  * This program is free but copyrighted software; see
00009  *            the file COPYING for details.
00010  *
00011  * ---------------------------------------------------
00012  */
00013 
00014 /* $Core */
00015 
00016 #include "inspircd.h"
00017 #include <cstdarg>
00018 #include "mode.h"
00019 
00020 Channel::Channel(InspIRCd* Instance, const std::string &cname, time_t ts) : ServerInstance(Instance)
00021 {
00022         chan_hash::iterator findchan = ServerInstance->chanlist->find(cname);
00023         if (findchan != Instance->chanlist->end())
00024                 throw CoreException("Cannot create duplicate channel " + cname);
00025 
00026         (*(ServerInstance->chanlist))[cname.c_str()] = this;
00027         this->name.assign(cname, 0, ServerInstance->Config->Limits.ChanMax);
00028         this->created = ts ? ts : ServerInstance->Time();
00029         this->age = this->created;
00030 
00031         maxbans = topicset = 0;
00032         modes.reset();
00033 }
00034 
00035 void Channel::SetMode(char mode,bool mode_on)
00036 {
00037         modes[mode-65] = mode_on;
00038         if (!mode_on)
00039                 this->SetModeParam(mode,"",false);
00040 }
00041 
00042 
00043 void Channel::SetModeParam(char mode,const char* parameter,bool mode_on)
00044 {
00045         CustomModeList::iterator n = custom_mode_params.find(mode);
00046 
00047         if (mode_on)
00048         {
00049                 if (n == custom_mode_params.end())
00050                         custom_mode_params[mode] = strdup(parameter);
00051         }
00052         else
00053         {
00054                 if (n != custom_mode_params.end())
00055                 {
00056                         free(n->second);
00057                         custom_mode_params.erase(n);
00058                 }
00059         }
00060 }
00061 
00062 bool Channel::IsModeSet(char mode)
00063 {
00064         return modes[mode-65];
00065 }
00066 
00067 std::string Channel::GetModeParameter(char mode)
00068 {
00069         CustomModeList::iterator n = custom_mode_params.find(mode);
00070         if (n != custom_mode_params.end())
00071                 return n->second;
00072         return "";
00073 }
00074 
00075 int Channel::SetTopic(User *u, std::string &ntopic, bool forceset)
00076 {
00077         if (u && IS_LOCAL(u))
00078         {
00079                 if(!forceset)
00080                 {
00081                         int MOD_RESULT = 0;
00082                         /* 0: check status, 1: don't, -1: disallow change silently */
00083 
00084                         FOREACH_RESULT(I_OnLocalTopicChange,OnLocalTopicChange(u,this,ntopic));
00085                 
00086                         if (MOD_RESULT == 1)
00087                                 return CMD_FAILURE;
00088                         else if (MOD_RESULT == 0)
00089                         {
00090                                 if (!this->HasUser(u))
00091                                 {
00092                                         u->WriteNumeric(442, "%s %s :You're not on that channel!",u->nick.c_str(), this->name.c_str());
00093                                         return CMD_FAILURE;
00094                                 }
00095                                 if ((this->IsModeSet('t')) && (this->GetStatus(u) < STATUS_HOP))
00096                                 {
00097                                         u->WriteNumeric(482, "%s %s :You must be at least a half-operator to change the topic on this channel", u->nick.c_str(), this->name.c_str());
00098                                         return CMD_FAILURE;
00099                                 }
00100                         }
00101                 }
00102         }
00103 
00104         this->topic.assign(ntopic, 0, ServerInstance->Config->Limits.MaxTopic);
00105         if (u)
00106         {
00107                 this->setby.assign(ServerInstance->Config->FullHostInTopic ? u->GetFullHost() : u->nick, 0, 128);
00108                 this->WriteChannel(u, "TOPIC %s :%s", this->name.c_str(), this->topic.c_str());
00109         }
00110         else
00111         {
00112                 this->setby.assign(ServerInstance->Config->ServerName);
00113                 this->WriteChannelWithServ(ServerInstance->Config->ServerName, "TOPIC %s :%s", this->name.c_str(), this->topic.c_str());
00114         }
00115 
00116         this->topicset = ServerInstance->Time();
00117 
00118         if (u && IS_LOCAL(u))
00119         {
00120                 FOREACH_MOD(I_OnPostLocalTopicChange,OnPostLocalTopicChange(u, this, this->topic));
00121         }
00122 
00123         return CMD_SUCCESS;
00124 }
00125 
00126 long Channel::GetUserCounter()
00127 {
00128         return (this->internal_userlist.size());
00129 }
00130 
00131 void Channel::AddUser(User* user)
00132 {
00133         internal_userlist[user] = user->nick;
00134 }
00135 
00136 unsigned long Channel::DelUser(User* user)
00137 {
00138         CUListIter a = internal_userlist.find(user);
00139 
00140         if (a != internal_userlist.end())
00141         {
00142                 internal_userlist.erase(a);
00143                 /* And tidy any others... */
00144                 DelOppedUser(user);
00145                 DelHalfoppedUser(user);
00146                 DelVoicedUser(user);
00147         }
00148 
00149         return internal_userlist.size();
00150 }
00151 
00152 bool Channel::HasUser(User* user)
00153 {
00154         return (internal_userlist.find(user) != internal_userlist.end());
00155 }
00156 
00157 void Channel::AddOppedUser(User* user)
00158 {
00159         internal_op_userlist[user] = user->nick;
00160 }
00161 
00162 void Channel::DelOppedUser(User* user)
00163 {
00164         CUListIter a = internal_op_userlist.find(user);
00165         if (a != internal_op_userlist.end())
00166         {
00167                 internal_op_userlist.erase(a);
00168                 return;
00169         }
00170 }
00171 
00172 void Channel::AddHalfoppedUser(User* user)
00173 {
00174         internal_halfop_userlist[user] = user->nick;
00175 }
00176 
00177 void Channel::DelHalfoppedUser(User* user)
00178 {
00179         CUListIter a = internal_halfop_userlist.find(user);
00180 
00181         if (a != internal_halfop_userlist.end())
00182         {
00183                 internal_halfop_userlist.erase(a);
00184         }
00185 }
00186 
00187 void Channel::AddVoicedUser(User* user)
00188 {
00189         internal_voice_userlist[user] = user->nick;
00190 }
00191 
00192 void Channel::DelVoicedUser(User* user)
00193 {
00194         CUListIter a = internal_voice_userlist.find(user);
00195 
00196         if (a != internal_voice_userlist.end())
00197         {
00198                 internal_voice_userlist.erase(a);
00199         }
00200 }
00201 
00202 CUList* Channel::GetUsers()
00203 {
00204         return &internal_userlist;
00205 }
00206 
00207 CUList* Channel::GetOppedUsers()
00208 {
00209         return &internal_op_userlist;
00210 }
00211 
00212 CUList* Channel::GetHalfoppedUsers()
00213 {
00214         return &internal_halfop_userlist;
00215 }
00216 
00217 CUList* Channel::GetVoicedUsers()
00218 {
00219         return &internal_voice_userlist;
00220 }
00221 
00222 void Channel::SetDefaultModes()
00223 {
00224         ServerInstance->Logs->Log("CHANNELS", DEBUG, "SetDefaultModes %s", ServerInstance->Config->DefaultModes);
00225         irc::spacesepstream list(ServerInstance->Config->DefaultModes);
00226         std::string modeseq;
00227         std::string parameter;
00228 
00229         list.GetToken(modeseq);
00230 
00231         for (std::string::iterator n = modeseq.begin(); n != modeseq.end(); ++n)
00232         {
00233                 ModeHandler* mode = ServerInstance->Modes->FindMode(*n, MODETYPE_CHANNEL);
00234                 if (mode)
00235                 {
00236                         if (mode->GetNumParams(true))
00237                                 list.GetToken(parameter);
00238                         else
00239                                 parameter.clear();
00240 
00241                         mode->OnModeChange(ServerInstance->FakeClient, ServerInstance->FakeClient, this, parameter, true);
00242                 }
00243         }
00244 }
00245 
00246 /*
00247  * add a channel to a user, creating the record for it if needed and linking
00248  * it to the user record
00249  */
00250 Channel* Channel::JoinUser(InspIRCd* Instance, User *user, const char* cn, bool override, const char* key, bool bursting, time_t TS)
00251 {
00252         if (!user || !cn)
00253                 return NULL;
00254 
00255         char cname[MAXBUF];
00256         int MOD_RESULT = 0;
00257         std::string privs;
00258         Channel *Ptr;
00259 
00260         /*
00261          * We don't restrict the number of channels that remote users or users that are override-joining may be in.
00262          * We restrict local users to MaxChans channels.
00263          * We restrict local operators to OperMaxChans channels.
00264          * This is a lot more logical than how it was formerly. -- w00t
00265          */
00266         if (IS_LOCAL(user) && !override)
00267         {
00268                 // Checking MyClass exists because we *may* get here with NULL, not 100% sure.
00269                 if (user->MyClass && user->MyClass->GetMaxChans())
00270                 {
00271                         if (user->chans.size() >= user->MyClass->GetMaxChans())
00272                         {
00273                                 user->WriteNumeric(ERR_TOOMANYCHANNELS, "%s %s :You are on too many channels",user->nick.c_str(), cn);
00274                                 return NULL;
00275                         }
00276                 }
00277                 else
00278                 {
00279                         if (IS_OPER(user))
00280                         {
00281                                 if (user->chans.size() >= Instance->Config->OperMaxChans)
00282                                 {
00283                                         user->WriteNumeric(ERR_TOOMANYCHANNELS, "%s %s :You are on too many channels",user->nick.c_str(), cn);
00284                                         return NULL;
00285                                 }
00286                         }
00287                         else
00288                         {
00289                                 if (user->chans.size() >= Instance->Config->MaxChans)
00290                                 {
00291                                         user->WriteNumeric(ERR_TOOMANYCHANNELS, "%s %s :You are on too many channels",user->nick.c_str(), cn);
00292                                         return NULL;
00293                                 }
00294                         }
00295                 }
00296         }
00297 
00298         strlcpy(cname, cn, Instance->Config->Limits.ChanMax);
00299         Ptr = Instance->FindChan(cname);
00300 
00301         if (!Ptr)
00302         {
00303                 /*
00304                  * Fix: desync bug was here, don't set @ on remote users - spanningtree handles their permissions. bug #358. -- w00t
00305                  */
00306                 if (!IS_LOCAL(user))
00307                 {
00308                         if (!TS)
00309                                 Instance->Logs->Log("CHANNEL",DEBUG,"*** BUG *** Channel::JoinUser called for REMOTE user '%s' on channel '%s' but no TS given!", user->nick.c_str(), cn);
00310                 }
00311                 else
00312                 {
00313                         privs = "@";
00314                 }
00315 
00316                 if (IS_LOCAL(user) && override == false)
00317                 {
00318                         MOD_RESULT = 0;
00319                         FOREACH_RESULT_I(Instance,I_OnUserPreJoin, OnUserPreJoin(user, NULL, cname, privs, key ? key : ""));
00320                         if (MOD_RESULT == 1)
00321                                 return NULL;
00322                 }
00323 
00324                 Ptr = new Channel(Instance, cname, TS);
00325         }
00326         else
00327         {
00328                 /* Already on the channel */
00329                 if (Ptr->HasUser(user))
00330                         return NULL;
00331 
00332                 /*
00333                  * remote users are allowed us to bypass channel modes
00334                  * and bans (used by servers)
00335                  */
00336                 if (IS_LOCAL(user) && override == false)
00337                 {
00338                         MOD_RESULT = 0;
00339                         FOREACH_RESULT_I(Instance,I_OnUserPreJoin, OnUserPreJoin(user, Ptr, cname, privs, key ? key : ""));
00340                         if (MOD_RESULT == 1)
00341                         {
00342                                 return NULL;
00343                         }
00344                         else if (MOD_RESULT == 0)
00345                         {
00346                                 std::string ckey = Ptr->GetModeParameter('k');
00347                                 bool invited = user->IsInvited(Ptr->name.c_str());
00348                                 bool can_bypass = Instance->Config->InvBypassModes && invited;
00349 
00350                                 if (!ckey.empty())
00351                                 {
00352                                         MOD_RESULT = 0;
00353                                         FOREACH_RESULT_I(Instance, I_OnCheckKey, OnCheckKey(user, Ptr, key ? key : ""));
00354                                         if (!MOD_RESULT)
00355                                         {
00356                                                 // If no key provided, or key is not the right one, and can't bypass +k (not invited or option not enabled)
00357                                                 if ((!key || ckey != key) && !can_bypass)
00358                                                 {
00359                                                         user->WriteNumeric(ERR_BADCHANNELKEY, "%s %s :Cannot join channel (Incorrect channel key)",user->nick.c_str(), Ptr->name.c_str());
00360                                                         return NULL;
00361                                                 }
00362                                         }
00363                                 }
00364 
00365                                 if (Ptr->IsModeSet('i'))
00366                                 {
00367                                         MOD_RESULT = 0;
00368                                         FOREACH_RESULT_I(Instance,I_OnCheckInvite,OnCheckInvite(user, Ptr));
00369                                         if (!MOD_RESULT)
00370                                         {
00371                                                 if (!invited)
00372                                                 {
00373                                                         user->WriteNumeric(ERR_INVITEONLYCHAN, "%s %s :Cannot join channel (Invite only)",user->nick.c_str(), Ptr->name.c_str());
00374                                                         return NULL;
00375                                                 }
00376                                         }
00377                                 }
00378 
00379                                 std::string limit = Ptr->GetModeParameter('l');
00380                                 if (!limit.empty())
00381                                 {
00382                                         MOD_RESULT = 0;
00383                                         FOREACH_RESULT_I(Instance, I_OnCheckLimit, OnCheckLimit(user, Ptr));
00384                                         if (!MOD_RESULT)
00385                                         {
00386                                                 long llimit = atol(limit.c_str());
00387                                                 if (Ptr->GetUserCounter() >= llimit && !can_bypass)
00388                                                 {
00389                                                         user->WriteNumeric(ERR_CHANNELISFULL, "%s %s :Cannot join channel (Channel is full)",user->nick.c_str(), Ptr->name.c_str());
00390                                                         return NULL;
00391                                                 }
00392                                         }
00393                                 }
00394 
00395                                 if (Ptr->bans.size())
00396                                 {
00397                                         if (Ptr->IsBanned(user) && !can_bypass)
00398                                         {
00399                                                 user->WriteNumeric(ERR_BANNEDFROMCHAN, "%s %s :Cannot join channel (You're banned)",user->nick.c_str(), Ptr->name.c_str());
00400                                                 return NULL;
00401                                         }
00402                                 }
00403 
00404                                 /*
00405                                  * If the user has invites for this channel, remove them now
00406                                  * after a successful join so they don't build up.
00407                                  */
00408                                 if (invited)
00409                                 {
00410                                         user->RemoveInvite(Ptr->name.c_str());
00411                                 }
00412                         }
00413                 }
00414         }
00415 
00416         /* As spotted by jilles, dont bother to set this on remote users */
00417         if (IS_LOCAL(user) && Ptr->GetUserCounter() == 0)
00418                 Ptr->SetDefaultModes();
00419 
00420         return Channel::ForceChan(Instance, Ptr, user, privs, bursting);
00421 }
00422 
00423 Channel* Channel::ForceChan(InspIRCd* Instance, Channel* Ptr, User* user, const std::string &privs, bool bursting)
00424 {       
00425         std::string nick = user->nick;
00426         bool silent = false;
00427 
00428         Ptr->AddUser(user);
00429 
00430         /* Just in case they have no permissions */
00431         user->chans[Ptr] = 0;
00432 
00433         for (std::string::const_iterator x = privs.begin(); x != privs.end(); x++)
00434         {
00435                 const char status = *x;
00436                 ModeHandler* mh = Instance->Modes->FindPrefix(status);
00437                 if (mh)
00438                 {
00439                         /* Set, and make sure that the mode handler knows this mode was now set */
00440                         Ptr->SetPrefix(user, status, mh->GetPrefixRank(), true);
00441                         mh->OnModeChange(Instance->FakeClient, Instance->FakeClient, Ptr, nick, true);
00442 
00443                         switch (mh->GetPrefix())
00444                         {
00445                                 /* These logic ops are SAFE IN THIS CASE because if the entry doesnt exist,
00446                                  * addressing operator[] creates it. If they do exist, it points to it.
00447                                  * At all other times where we dont want to create an item if it doesnt exist, we
00448                                  * must stick to ::find().
00449                                  */
00450                                 case '@':
00451                                         user->chans[Ptr] |= UCMODE_OP;
00452                                 break;
00453                                 case '%':
00454                                         user->chans[Ptr] |= UCMODE_HOP;
00455                                 break;
00456                                 case '+':
00457                                         user->chans[Ptr] |= UCMODE_VOICE;
00458                                 break;
00459                         }
00460                 }
00461         }
00462 
00463         FOREACH_MOD_I(Instance,I_OnUserJoin,OnUserJoin(user, Ptr, bursting, silent));
00464 
00465         if (!silent)
00466                 Ptr->WriteChannel(user,"JOIN :%s",Ptr->name.c_str());
00467 
00468         /* Theyre not the first ones in here, make sure everyone else sees the modes we gave the user */
00469         std::string ms = Instance->Modes->ModeString(user, Ptr);
00470         if ((Ptr->GetUserCounter() > 1) && (ms.length()))
00471                 Ptr->WriteAllExceptSender(user, true, 0, "MODE %s +%s", Ptr->name.c_str(), ms.c_str());
00472 
00473         /* Major improvement by Brain - we dont need to be calculating all this pointlessly for remote users */
00474         if (IS_LOCAL(user))
00475         {
00476                 if (Ptr->topicset)
00477                 {
00478                         user->WriteNumeric(RPL_TOPIC, "%s %s :%s", user->nick.c_str(), Ptr->name.c_str(), Ptr->topic.c_str());
00479                         user->WriteNumeric(RPL_TOPICTIME, "%s %s %s %lu", user->nick.c_str(), Ptr->name.c_str(), Ptr->setby.c_str(), (unsigned long)Ptr->topicset);
00480                 }
00481                 Ptr->UserList(user);
00482         }
00483         FOREACH_MOD_I(Instance,I_OnPostJoin,OnPostJoin(user, Ptr));
00484         return Ptr;
00485 }
00486 
00487 bool Channel::IsBanned(User* user)
00488 {
00489         char mask[MAXBUF];
00490         int MOD_RESULT = 0;
00491         FOREACH_RESULT(I_OnCheckBan,OnCheckBan(user, this));
00492 
00493         if (MOD_RESULT == -1)
00494                 return true;
00495         else if (MOD_RESULT == 0)
00496         {
00497                 snprintf(mask, MAXBUF, "%s!%s@%s", user->nick.c_str(), user->ident.c_str(), user->GetIPString());
00498                 for (BanList::iterator i = this->bans.begin(); i != this->bans.end(); i++)
00499                 {
00500                         if ((InspIRCd::Match(user->GetFullHost(),i->data, NULL)) || // host
00501                                 (InspIRCd::Match(user->GetFullRealHost(),i->data, NULL)) || // uncloaked host
00502                                 (InspIRCd::MatchCIDR(mask, i->data, NULL))) // ip
00503                         {
00504                                 return true;
00505                         }
00506                 }
00507         }
00508         return false;
00509 }
00510 
00511 bool Channel::IsExtBanned(const std::string &str, char type)
00512 {
00513         int MOD_RESULT = 0;
00514         FOREACH_RESULT(I_OnCheckStringExtBan, OnCheckStringExtBan(str, this, type));
00515 
00516         if (MOD_RESULT == -1)
00517                 return true;
00518         else if (MOD_RESULT == 0)
00519         {
00520                 for (BanList::iterator i = this->bans.begin(); i != this->bans.end(); i++)
00521                 {
00522                         if (i->data[0] != type || i->data[1] != ':')
00523                                 continue;
00524 
00525                         // Iterate past char and : to get to the mask without doing a data copy(!)
00526                         std::string maskptr = i->data.substr(2);
00527                         ServerInstance->Logs->Log("EXTBANS", DEBUG, "Checking %s against %s, type is %c", str.c_str(), maskptr.c_str(), type);
00528 
00529                         if (InspIRCd::Match(str, maskptr, NULL))
00530                                 return true;
00531                 }
00532         }
00533 
00534         return false;
00535 }
00536 
00537 bool Channel::IsExtBanned(User *user, char type)
00538 {
00539         int MOD_RESULT = 0;
00540         FOREACH_RESULT(I_OnCheckExtBan, OnCheckExtBan(user, this, type));
00541 
00542         if (MOD_RESULT == -1)
00543                 return true;
00544         else if (MOD_RESULT == 0)
00545         {
00546                 char mask[MAXBUF];
00547                 snprintf(mask, MAXBUF, "%s!%s@%s", user->nick.c_str(), user->ident.c_str(), user->GetIPString());
00548 
00549                 // XXX: we should probably hook cloaked hosts in here somehow too..
00550                 if (this->IsExtBanned(mask, type))
00551                         return true;
00552 
00553                 if (this->IsExtBanned(user->GetFullHost(), type))
00554                         return true;
00555 
00556                 if (this->IsExtBanned(user->GetFullRealHost(), type))
00557                         return true;
00558         }
00559 
00560         return false;
00561 }
00562 
00563 /* Channel::PartUser
00564  * remove a channel from a users record, and return the number of users left.
00565  * Therefore, if this function returns 0 the caller should delete the Channel.
00566  */
00567 long Channel::PartUser(User *user, std::string &reason)
00568 {
00569         bool silent = false;
00570 
00571         if (!user)
00572                 return this->GetUserCounter();
00573 
00574         UCListIter i = user->chans.find(this);
00575         if (i != user->chans.end())
00576         {
00577                 FOREACH_MOD(I_OnUserPart,OnUserPart(user, this, reason, silent));
00578 
00579                 if (!silent)
00580                         this->WriteChannel(user, "PART %s%s%s", this->name.c_str(), reason.empty() ? "" : " :", reason.c_str());
00581 
00582                 user->chans.erase(i);
00583                 this->RemoveAllPrefixes(user);
00584         }
00585 
00586         if (!this->DelUser(user)) /* if there are no users left on the channel... */
00587         {
00588                 chan_hash::iterator iter = ServerInstance->chanlist->find(this->name);
00589                 /* kill the record */
00590                 if (iter != ServerInstance->chanlist->end())
00591                 {
00592                         int MOD_RESULT = 0;
00593                         FOREACH_RESULT_I(ServerInstance,I_OnChannelPreDelete, OnChannelPreDelete(this));
00594                         if (MOD_RESULT == 1)
00595                                 return 1; // delete halted by module
00596                         FOREACH_MOD(I_OnChannelDelete, OnChannelDelete(this));
00597                         ServerInstance->chanlist->erase(iter);
00598                 }
00599                 return 0;
00600         }
00601 
00602         return this->GetUserCounter();
00603 }
00604 
00605 long Channel::ServerKickUser(User* user, const char* reason, bool triggerevents, const char* servername)
00606 {
00607         bool silent = false;
00608 
00609         if (!user || !reason)
00610                 return this->GetUserCounter();
00611 
00612         if (IS_LOCAL(user))
00613         {
00614                 if (!this->HasUser(user))
00615                 {
00616                         /* Not on channel */
00617                         return this->GetUserCounter();
00618                 }
00619         }
00620 
00621         if (servername == NULL || *ServerInstance->Config->HideWhoisServer)
00622                 servername = ServerInstance->Config->ServerName;
00623 
00624         if (triggerevents)
00625         {
00626                 FOREACH_MOD(I_OnUserKick,OnUserKick(NULL, user, this, reason, silent));
00627         }
00628 
00629         UCListIter i = user->chans.find(this);
00630         if (i != user->chans.end())
00631         {
00632                 if (!silent)
00633                         this->WriteChannelWithServ(servername, "KICK %s %s :%s", this->name.c_str(), user->nick.c_str(), reason);
00634 
00635                 user->chans.erase(i);
00636                 this->RemoveAllPrefixes(user);
00637         }
00638 
00639         if (!this->DelUser(user))
00640         {
00641                 chan_hash::iterator iter = ServerInstance->chanlist->find(this->name);
00642                 /* kill the record */
00643                 if (iter != ServerInstance->chanlist->end())
00644                 {
00645                         int MOD_RESULT = 0;
00646                         FOREACH_RESULT_I(ServerInstance,I_OnChannelPreDelete, OnChannelPreDelete(this));
00647                         if (MOD_RESULT == 1)
00648                                 return 1; // delete halted by module
00649                         FOREACH_MOD(I_OnChannelDelete, OnChannelDelete(this));
00650                         ServerInstance->chanlist->erase(iter);
00651                 }
00652                 return 0;
00653         }
00654 
00655         return this->GetUserCounter();
00656 }
00657 
00658 long Channel::KickUser(User *src, User *user, const char* reason)
00659 {
00660         bool silent = false;
00661 
00662         if (!src || !user || !reason)
00663                 return this->GetUserCounter();
00664 
00665         if (IS_LOCAL(src))
00666         {
00667                 if (!this->HasUser(user))
00668                 {
00669                         src->WriteNumeric(ERR_USERNOTINCHANNEL, "%s %s %s :They are not on that channel",src->nick.c_str(), user->nick.c_str(), this->name.c_str());
00670                         return this->GetUserCounter();
00671                 }
00672                 if ((ServerInstance->ULine(user->server)) && (!ServerInstance->ULine(src->server)))
00673                 {
00674                         src->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s %s :Only a u-line may kick a u-line from a channel.",src->nick.c_str(), this->name.c_str());
00675                         return this->GetUserCounter();
00676                 }
00677                 int MOD_RESULT = 0;
00678 
00679                 if (!ServerInstance->ULine(src->server))
00680                 {
00681                         MOD_RESULT = 0;
00682                         FOREACH_RESULT(I_OnUserPreKick,OnUserPreKick(src,user,this,reason));
00683                         if (MOD_RESULT == 1)
00684                                 return this->GetUserCounter();
00685                 }
00686                 /* Set to -1 by OnUserPreKick if explicit allow was set */
00687                 if (MOD_RESULT != -1)
00688                 {
00689                         FOREACH_RESULT(I_OnAccessCheck,OnAccessCheck(src,user,this,AC_KICK));
00690                         if ((MOD_RESULT == ACR_DENY) && (!ServerInstance->ULine(src->server)))
00691                                 return this->GetUserCounter();
00692 
00693                         if ((MOD_RESULT == ACR_DEFAULT) || (!ServerInstance->ULine(src->server)))
00694                         {
00695                                 int them = this->GetStatus(src);
00696                                 int us = this->GetStatus(user);
00697                                 if ((them < STATUS_HOP) || (them < us))
00698                                 {
00699                                         src->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s %s :You must be a channel %soperator",src->nick.c_str(), this->name.c_str(), them == STATUS_HOP ? "" : "half-");
00700                                         return this->GetUserCounter();
00701                                 }
00702                         }
00703                 }
00704         }
00705 
00706         FOREACH_MOD(I_OnUserKick,OnUserKick(src, user, this, reason, silent));
00707 
00708         UCListIter i = user->chans.find(this);
00709         if (i != user->chans.end())
00710         {
00711                 /* zap it from the channel list of the user */
00712                 if (!silent)
00713                         this->WriteChannel(src, "KICK %s %s :%s", this->name.c_str(), user->nick.c_str(), reason);
00714 
00715                 user->chans.erase(i);
00716                 this->RemoveAllPrefixes(user);
00717         }
00718 
00719         if (!this->DelUser(user))
00720         /* if there are no users left on the channel */
00721         {
00722                 chan_hash::iterator iter = ServerInstance->chanlist->find(this->name.c_str());
00723 
00724                 /* kill the record */
00725                 if (iter != ServerInstance->chanlist->end())
00726                 {
00727                         int MOD_RESULT = 0;
00728                         FOREACH_RESULT_I(ServerInstance,I_OnChannelPreDelete, OnChannelPreDelete(this));
00729                         if (MOD_RESULT == 1)
00730                                 return 1; // delete halted by module
00731                         FOREACH_MOD(I_OnChannelDelete, OnChannelDelete(this));
00732                         ServerInstance->chanlist->erase(iter);
00733                 }
00734                 return 0;
00735         }
00736 
00737         return this->GetUserCounter();
00738 }
00739 
00740 void Channel::WriteChannel(User* user, const char* text, ...)
00741 {
00742         char textbuffer[MAXBUF];
00743         va_list argsPtr;
00744 
00745         if (!user || !text)
00746                 return;
00747 
00748         va_start(argsPtr, text);
00749         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
00750         va_end(argsPtr);
00751 
00752         this->WriteChannel(user, std::string(textbuffer));
00753 }
00754 
00755 void Channel::WriteChannel(User* user, const std::string &text)
00756 {
00757         CUList *ulist = this->GetUsers();
00758         char tb[MAXBUF];
00759 
00760         if (!user)
00761                 return;
00762 
00763         snprintf(tb,MAXBUF,":%s %s", user->GetFullHost().c_str(), text.c_str());
00764         std::string out = tb;
00765 
00766         for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++)
00767         {
00768                 if (IS_LOCAL(i->first))
00769                         i->first->Write(out);
00770         }
00771 }
00772 
00773 void Channel::WriteChannelWithServ(const char* ServName, const char* text, ...)
00774 {
00775         char textbuffer[MAXBUF];
00776         va_list argsPtr;
00777 
00778         if (!text)
00779                 return;
00780 
00781         va_start(argsPtr, text);
00782         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
00783         va_end(argsPtr);
00784 
00785         this->WriteChannelWithServ(ServName, std::string(textbuffer));
00786 }
00787 
00788 void Channel::WriteChannelWithServ(const char* ServName, const std::string &text)
00789 {
00790         CUList *ulist = this->GetUsers();
00791         char tb[MAXBUF];
00792 
00793         snprintf(tb,MAXBUF,":%s %s", ServName ? ServName : ServerInstance->Config->ServerName, text.c_str());
00794         std::string out = tb;
00795 
00796         for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++)
00797         {
00798                 if (IS_LOCAL(i->first))
00799                         i->first->Write(out);
00800         }
00801 }
00802 
00803 /* write formatted text from a source user to all users on a channel except
00804  * for the sender (for privmsg etc) */
00805 void Channel::WriteAllExceptSender(User* user, bool serversource, char status, const char* text, ...)
00806 {
00807         char textbuffer[MAXBUF];
00808         va_list argsPtr;
00809 
00810         if (!text)
00811                 return;
00812 
00813         va_start(argsPtr, text);
00814         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
00815         va_end(argsPtr);
00816 
00817         this->WriteAllExceptSender(user, serversource, status, std::string(textbuffer));
00818 }
00819 
00820 void Channel::WriteAllExcept(User* user, bool serversource, char status, CUList &except_list, const char* text, ...)
00821 {
00822         char textbuffer[MAXBUF];
00823         va_list argsPtr;
00824 
00825         if (!text)
00826                 return;
00827 
00828         va_start(argsPtr, text);
00829         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
00830         va_end(argsPtr);
00831 
00832         this->WriteAllExcept(user, serversource, status, except_list, std::string(textbuffer));
00833 }
00834 
00835 void Channel::WriteAllExcept(User* user, bool serversource, char status, CUList &except_list, const std::string &text)
00836 {
00837         CUList *ulist = this->GetUsers();
00838         char tb[MAXBUF];
00839 
00840         snprintf(tb,MAXBUF,":%s %s", user->GetFullHost().c_str(), text.c_str());
00841         std::string out = tb;
00842 
00843         for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++)
00844         {
00845                 if ((IS_LOCAL(i->first)) && (except_list.find(i->first) == except_list.end()))
00846                 {
00847                         /* User doesnt have the status we're after */
00848                         if (status && !strchr(this->GetAllPrefixChars(i->first), status))
00849                                 continue;
00850 
00851                         if (serversource)
00852                                 i->first->WriteServ(text);
00853                         else
00854                                 i->first->Write(out);
00855                 }
00856         }
00857 }
00858 
00859 void Channel::WriteAllExceptSender(User* user, bool serversource, char status, const std::string& text)
00860 {
00861         CUList except_list;
00862         except_list[user] = user->nick;
00863         this->WriteAllExcept(user, serversource, status, except_list, std::string(text));
00864 }
00865 
00866 /*
00867  * return a count of the users on a specific channel accounting for
00868  * invisible users who won't increase the count. e.g. for /LIST
00869  */
00870 int Channel::CountInvisible()
00871 {
00872         int count = 0;
00873         CUList *ulist= this->GetUsers();
00874         for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++)
00875         {
00876                 if (!(i->first->IsModeSet('i')))
00877                         count++;
00878         }
00879 
00880         return count;
00881 }
00882 
00883 char* Channel::ChanModes(bool showkey)
00884 {
00885         static char scratch[MAXBUF];
00886         static char sparam[MAXBUF];
00887         char* offset = scratch;
00888         std::string extparam;
00889 
00890         *scratch = '\0';
00891         *sparam = '\0';
00892 
00893         /* This was still iterating up to 190, Channel::modes is only 64 elements -- Om */
00894         for(int n = 0; n < 64; n++)
00895         {
00896                 if(this->modes[n])
00897                 {
00898                         *offset++ = n + 65;
00899                         extparam.clear();
00900                         switch (n)
00901                         {
00902                                 case CM_KEY:
00903                                         // Unfortunately this must be special-cased, as we definitely don't want to always display key.
00904                                         if (showkey)
00905                                         {
00906                                                 extparam = this->GetModeParameter('k');
00907                                         }
00908                                         else
00909                                         {
00910                                                 extparam = "<key>";
00911                                         }
00912                                         break;
00913                                 case CM_NOEXTERNAL:
00914                                 case CM_TOPICLOCK:
00915                                 case CM_INVITEONLY:
00916                                 case CM_MODERATED:
00917                                 case CM_SECRET:
00918                                 case CM_PRIVATE:
00919                                         /* We know these have no parameters */
00920                                 break;
00921                                 default:
00922                                         extparam = this->GetModeParameter(n + 65);
00923                                 break;
00924                         }
00925                         if (!extparam.empty())
00926                         {
00927                                 charlcat(sparam,' ',MAXBUF);
00928                                 strlcat(sparam,extparam.c_str(),MAXBUF);
00929                         }
00930                 }
00931         }
00932 
00933         /* Null terminate scratch */
00934         *offset = '\0';
00935         strlcat(scratch,sparam,MAXBUF);
00936         return scratch;
00937 }
00938 
00939 /* compile a userlist of a channel into a string, each nick seperated by
00940  * spaces and op, voice etc status shown as @ and +, and send it to 'user'
00941  */
00942 void Channel::UserList(User *user, CUList *ulist)
00943 {
00944         char list[MAXBUF];
00945         size_t dlen, curlen;
00946         int MOD_RESULT = 0;
00947         bool call_modules = true;
00948 
00949         if (!IS_LOCAL(user))
00950                 return;
00951 
00952         FOREACH_RESULT(I_OnUserList,OnUserList(user, this, ulist));
00953         if (MOD_RESULT == 1)
00954                 call_modules = false;
00955 
00956         if (MOD_RESULT != -1)
00957         {
00958                 if ((this->IsModeSet('s')) && (!this->HasUser(user)))
00959                 {
00960                         user->WriteNumeric(ERR_NOSUCHNICK, "%s %s :No such nick/channel",user->nick.c_str(), this->name.c_str());
00961                         return;
00962                 }
00963         }
00964 
00965         dlen = curlen = snprintf(list,MAXBUF,"%s %c %s :", user->nick.c_str(), this->IsModeSet('s') ? '@' : this->IsModeSet('p') ? '*' : '=',  this->name.c_str());
00966 
00967         int numusers = 0;
00968         char* ptr = list + dlen;
00969 
00970         if (!ulist)
00971                 ulist = this->GetUsers();
00972 
00973         /* Improvement by Brain - this doesnt change in value, so why was it inside
00974          * the loop?
00975          */
00976         bool has_user = this->HasUser(user);
00977 
00978         for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++)
00979         {
00980                 if ((!has_user) && (i->first->IsModeSet('i')))
00981                 {
00982                         /*
00983                          * user is +i, and source not on the channel, does not show
00984                          * nick in NAMES list
00985