X-Git-Url: https://git.netwichtig.de/gitweb/?a=blobdiff_plain;f=src%2Fusers.cpp;h=31244f98706b5bf28344a44c762a3f36c04a7ca2;hb=f087d825a88760d251b8f80b5d50bc98b40fa2ae;hp=645e07046a3ce674573c24f3ecae272be739b6e1;hpb=91eebe370cfde1b3f904b29bd9ea12bc2d18527f;p=user%2Fhenk%2Fcode%2Finspircd.git diff --git a/src/users.cpp b/src/users.cpp index 645e07046..31244f987 100644 --- a/src/users.cpp +++ b/src/users.cpp @@ -11,9 +11,6 @@ * --------------------------------------------------- */ -#include "configreader.h" -#include "channels.h" -#include "users.h" #include "inspircd.h" #include #include "socketengine.h" @@ -61,7 +58,7 @@ bool DoType(ServerConfig* conf, const char* tag, char** entries, ValueList &valu const char* TypeName = values[0].GetString(); const char* Classes = values[1].GetString(); - conf->opertypes[TypeName] = strdup(Classes); + conf->opertypes[TypeName] = strnewdup(Classes); return true; } @@ -70,7 +67,7 @@ bool DoClass(ServerConfig* conf, const char* tag, char** entries, ValueList &val const char* ClassName = values[0].GetString(); const char* CommandList = values[1].GetString(); - conf->operclass[ClassName] = strdup(CommandList); + conf->operclass[ClassName] = strnewdup(CommandList); return true; } @@ -164,8 +161,12 @@ UserResolver::UserResolver(InspIRCd* Instance, userrec* user, std::string to_res this->bound_fd = user->GetFd(); } -void UserResolver::OnLookupComplete(const std::string &result, unsigned int ttl, bool cached) +void UserResolver::OnLookupComplete(const std::string &result, unsigned int ttl, bool cached, int resultnum) { + /* We are only interested in the first matching result */ + if (resultnum) + return; + if ((!this->fwd) && (ServerInstance->SE->GetRef(this->bound_fd) == this->bound_user)) { this->bound_user->stored_host = result; @@ -183,14 +184,10 @@ void UserResolver::OnLookupComplete(const std::string &result, unsigned int ttl, bound_user->res_forward = new UserResolver(this->ServerInstance, this->bound_user, result, (!strncmp(ip, "0::ffff:", 8) ? DNS_QUERY_A : DNS_QUERY_AAAA), cached); } else - { /* IPV4 lookup (mixed protocol mode) */ - bound_user->res_forward = new UserResolver(this->ServerInstance, this->bound_user, result, DNS_QUERY_A, cached); - } -#else +#endif /* IPV4 lookup (ipv4 only mode) */ bound_user->res_forward = new UserResolver(this->ServerInstance, this->bound_user, result, DNS_QUERY_A, cached); -#endif this->ServerInstance->AddResolver(bound_user->res_forward, cached); } } @@ -202,7 +199,7 @@ void UserResolver::OnLookupComplete(const std::string &result, unsigned int ttl, else if ((this->fwd) && (ServerInstance->SE->GetRef(this->bound_fd) == this->bound_user)) { /* Both lookups completed */ - std::string result2 = "0::ffff:"; + std::string result2("0::ffff:"); result2.append(result); if (this->bound_user->GetIPString() == result || this->bound_user->GetIPString() == result2) { @@ -214,7 +211,7 @@ void UserResolver::OnLookupComplete(const std::string &result, unsigned int ttl, { /* Hostnames starting with : are not a good thing (tm) */ if (*(hostname.c_str()) == ':') - hostname = "0" + hostname; + hostname.insert(0, "0"); this->bound_user->WriteServ("NOTICE Auth :*** Found your hostname (%s)%s", hostname.c_str(), (cached ? " -- cached" : "")); this->bound_user->dns_done = true; @@ -312,21 +309,25 @@ const char* userrec::FormatModes() void userrec::DecrementModes() { - for (int n = 0; n < 64; n++) + ServerInstance->Log(DEBUG,"DecrementModes()"); + for (unsigned char n = 'A'; n <= 'z'; n++) { - if (modes[n]) + if (modes[n-65]) { - ModeHandler* mh = ServerInstance->Modes->FindMode(n+65, MODETYPE_USER); + ServerInstance->Log(DEBUG,"DecrementModes() found mode %c", n); + ModeHandler* mh = ServerInstance->Modes->FindMode(n, MODETYPE_USER); if (mh) + { + ServerInstance->Log(DEBUG,"Found handler %c and call ChangeCount", n); mh->ChangeCount(-1); + } } } } -userrec::userrec(InspIRCd* Instance) : ServerInstance(Instance) +userrec::userrec(InspIRCd* Instance, const std::string &uid) : ServerInstance(Instance) { - // the PROPER way to do it, AVOID bzero at *ALL* costs - *password = *nick = *ident = *host = *dhost = *fullname = *awaymsg = *oper = 0; + *password = *nick = *ident = *host = *dhost = *fullname = *awaymsg = *oper = *uuid = 0; server = (char*)Instance->FindServerNamePtr(Instance->Config->ServerName); reset_due = ServerInstance->Time(); age = ServerInstance->Time(true); @@ -334,9 +335,9 @@ userrec::userrec(InspIRCd* Instance) : ServerInstance(Instance) ChannelCount = timeout = flood = bytes_in = bytes_out = cmds_in = cmds_out = 0; muted = exempt = haspassed = dns_done = false; fd = -1; - recvq = ""; - sendq = ""; - WriteError = ""; + recvq.clear(); + sendq.clear(); + WriteError.clear(); res_forward = res_reverse = NULL; Visibility = NULL; ip = NULL; @@ -346,6 +347,42 @@ userrec::userrec(InspIRCd* Instance) : ServerInstance(Instance) memset(snomasks,0,sizeof(snomasks)); /* Invalidate cache */ operquit = cached_fullhost = cached_hostip = cached_makehost = cached_fullrealhost = NULL; + + if (uid.empty()) + strlcpy(uuid, Instance->GetUID().c_str(), UUID_LENGTH); + else + strlcpy(uuid, uid.c_str(), UUID_LENGTH); + + ServerInstance->Log(DEBUG,"New UUID for user: %s (%s)", uuid, uid.empty() ? "allocated new" : "used remote"); + + user_hash::iterator finduuid = Instance->uuidlist->find(uuid); + if (finduuid == Instance->uuidlist->end()) + (*Instance->uuidlist)[uuid] = this; + else + throw CoreException("Duplicate UUID "+std::string(uuid)+" in userrec constructor"); +} + +void userrec::RemoveCloneCounts() +{ + clonemap::iterator x = ServerInstance->local_clones.find(this->GetIPString()); + if (x != ServerInstance->local_clones.end()) + { + x->second--; + if (!x->second) + { + ServerInstance->local_clones.erase(x); + } + } + + clonemap::iterator y = ServerInstance->global_clones.find(this->GetIPString()); + if (y != ServerInstance->global_clones.end()) + { + y->second--; + if (!y->second) + { + ServerInstance->global_clones.erase(y); + } + } } userrec::~userrec() @@ -356,25 +393,7 @@ userrec::~userrec() free(operquit); if (ip) { - clonemap::iterator x = ServerInstance->local_clones.find(this->GetIPString()); - if (x != ServerInstance->local_clones.end()) - { - x->second--; - if (!x->second) - { - ServerInstance->local_clones.erase(x); - } - } - - clonemap::iterator y = ServerInstance->global_clones.find(this->GetIPString()); - if (y != ServerInstance->global_clones.end()) - { - y->second--; - if (!y->second) - { - ServerInstance->global_clones.erase(y); - } - } + this->RemoveCloneCounts(); if (this->GetProtocolFamily() == AF_INET) { @@ -387,6 +406,8 @@ userrec::~userrec() } #endif } + + ServerInstance->uuidlist->erase(uuid); } char* userrec::MakeHost() @@ -431,8 +452,8 @@ char* userrec::MakeHostIP() void userrec::CloseSocket() { - shutdown(this->fd,2); - close(this->fd); + ServerInstance->SE->Shutdown(this, 2); + ServerInstance->SE->Close(this); } char* userrec::GetFullHost() @@ -473,7 +494,11 @@ int userrec::ReadData(void* buffer, size_t size) { if (IS_LOCAL(this)) { +#ifndef WIN32 return read(this->fd, buffer, size); +#else + return recv(this->fd, (char*)buffer, size, 0); +#endif } else return 0; @@ -632,7 +657,7 @@ bool userrec::BufferIsReady() void userrec::ClearBuffer() { - recvq = ""; + recvq.clear(); } std::string userrec::GetBuffer() @@ -646,8 +671,12 @@ std::string userrec::GetBuffer() * Usually there are only one or two of these, * so its is computationally cheap to do. */ - while ((*recvq.begin() == '\r') || (*recvq.begin() == '\n')) - recvq.erase(recvq.begin()); + std::string::iterator t = recvq.begin(); + while (t != recvq.end() && (*t == '\r' || *t == '\n')) + { + recvq.erase(t); + t = recvq.begin(); + } for (std::string::iterator x = recvq.begin(); x != recvq.end(); x++) { @@ -709,12 +738,13 @@ void userrec::FlushWriteBuf() { if ((this->fd == FD_MAGIC_NUMBER) || (*this->GetWriteError())) { - sendq = ""; + sendq.clear(); } if ((sendq.length()) && (this->fd != FD_MAGIC_NUMBER)) { int old_sendq_length = sendq.length(); - int n_sent = write(this->fd, this->sendq.data(), this->sendq.length()); + int n_sent = ServerInstance->SE->Send(this, this->sendq.data(), this->sendq.length(), 0); + if (n_sent == -1) { if (errno == EAGAIN) @@ -728,7 +758,7 @@ void userrec::FlushWriteBuf() { /* Fatal error, set write error and bail */ - this->SetWriteError(strerror(errno)); + this->SetWriteError(errno ? strerror(errno) : "EOF from client"); return; } } @@ -805,16 +835,9 @@ void userrec::UnOper() // unset their oper type (what IS_OPER checks), and remove +o *this->oper = 0; this->modes[UM_OPERATOR] = 0; - - // remove them from the opers list. - for (std::vector::iterator a = ServerInstance->all_opers.begin(); a < ServerInstance->all_opers.end(); a++) - { - if (*a == this) - { - ServerInstance->all_opers.erase(a); - return; - } - } + + // remove the user from the oper list. Will remove multiple entries as a safeguard against bug #404 + ServerInstance->all_opers.remove(this); } } @@ -826,6 +849,7 @@ void userrec::UnOper() void userrec::QuitUser(InspIRCd* Instance, userrec *user, const std::string &quitreason, const char* operreason) { + user->Write("ERROR :Closing link (%s@%s) [%s]", user->ident, user->host, operreason); user->muted = true; Instance->GlobalCulls.AddItem(user, quitreason.c_str(), operreason); } @@ -845,42 +869,38 @@ void userrec::AddToWhoWas() /* add a client connection to the sockets list */ void userrec::AddClient(InspIRCd* Instance, int socket, int port, bool iscached, int socketfamily, sockaddr* ip) { - std::string tempnick = ConvToStr(socket) + "-unknown"; - user_hash::iterator iter = Instance->clientlist->find(tempnick); + /* NOTE: Calling this one parameter constructor for userrec automatically + * allocates a new UUID and places it in the hash_map. + */ + userrec* New = NULL; + try + { + New = new userrec(Instance); + } + catch (...) + { + Instance->Log(DEFAULT,"*** WTF *** Duplicated UUID! -- Crack smoking monkies have been unleashed."); + Instance->WriteOpers("*** WARNING *** Duplicate UUID allocated!"); + return; + } + + int j = 0; + + Instance->unregistered_count++; + char ipaddr[MAXBUF]; #ifdef IPV6 if (socketfamily == AF_INET6) inet_ntop(AF_INET6, &((const sockaddr_in6*)ip)->sin6_addr, ipaddr, sizeof(ipaddr)); else - inet_ntop(AF_INET, &((const sockaddr_in*)ip)->sin_addr, ipaddr, sizeof(ipaddr)); -#else - inet_ntop(AF_INET, &((const sockaddr_in*)ip)->sin_addr, ipaddr, sizeof(ipaddr)); #endif - userrec* New; - int j = 0; - - Instance->unregistered_count++; + inet_ntop(AF_INET, &((const sockaddr_in*)ip)->sin_addr, ipaddr, sizeof(ipaddr)); - /* - * fix by brain. - * as these nicknames are 'RFC impossible', we can be sure nobody is going to be - * using one as a registered connection. As they are per fd, we can also safely assume - * that we wont have collisions. Therefore, if the nick exists in the list, its only - * used by a dead socket, erase the iterator so that the new client may reclaim it. - * this was probably the cause of 'server ignores me when i hammer it with reconnects' - * issue in earlier alphas/betas - */ - if (iter != Instance->clientlist->end()) - { - userrec* goner = iter->second; - DELETE(goner); - Instance->clientlist->erase(iter); - } + (*(Instance->clientlist))[New->uuid] = New; + New->SetFd(socket); - New = new userrec(Instance); - (*(Instance->clientlist))[tempnick] = New; - New->fd = socket; - strlcpy(New->nick,tempnick.c_str(),NICKMAX-1); + /* The users default nick is their UUID */ + strlcpy(New->nick, New->uuid, NICKMAX - 1); New->server = Instance->FindServerNamePtr(Instance->Config->ServerName); /* We don't need range checking here, we KNOW 'unknown\0' will fit into the ident field. */ @@ -897,6 +917,18 @@ void userrec::AddClient(InspIRCd* Instance, int socket, int port, bool iscached, New->dhost[j] = New->host[j] = *temp; New->dhost[j] = New->host[j] = 0; + if (socket > -1) + { + if (!Instance->SE->AddFd(New)) + { + Instance->Log(DEBUG,"ERROR: Could not add new user %s!%s@%s to the socket engine!!!", New->nick, New->ident, New->host); + close(socket); + shutdown(socket,2); + delete New; + return; + } + } + Instance->AddLocalClone(New); Instance->AddGlobalClone(New); @@ -905,15 +937,18 @@ void userrec::AddClient(InspIRCd* Instance, int socket, int port, bool iscached, * See my note down there for why this is required. DO NOT REMOVE. :) -- w00t */ ConnectClass* i = New->GetClass(); - New->CheckClass(); - New->pingmax = i->GetPingTime(); - New->nping = Instance->Time() + i->GetPingTime() + Instance->Config->dns_timeout; - New->timeout = Instance->Time() + i->GetRegTimeout(); - New->flood = i->GetFlood(); - New->threshold = i->GetThreshold(); - New->sendqmax = i->GetSendqMax(); - New->recvqmax = i->GetRecvqMax(); + if (!i) + { + userrec::QuitUser(Instance, New, "Access denied by configuration"); + return; + } + + /* + * Check connect class settings and initialise settings into userrec. + * This will be done again after DNS resolution. -- w00t + */ + New->CheckClass(); Instance->local_users.push_back(New); @@ -934,11 +969,13 @@ void userrec::AddClient(InspIRCd* Instance, int socket, int port, bool iscached, * which for the time being is a physical impossibility (even the largest networks dont have more * than about 10,000 users on ONE server!) */ +#ifndef WINDOWS if ((unsigned int)socket >= MAX_DESCRIPTORS) { userrec::QuitUser(Instance, New, "Server is full"); return; } +#endif New->exempt = (Instance->XLines->matches_exception(New) != NULL); if (!New->exempt) @@ -947,21 +984,14 @@ void userrec::AddClient(InspIRCd* Instance, int socket, int port, bool iscached, if (r) { char reason[MAXBUF]; + if (*Instance->Config->MoronBanner) + New->WriteServ("NOTICE %s :*** %s", New->nick, Instance->Config->MoronBanner); snprintf(reason,MAXBUF,"Z-Lined: %s",r->reason); userrec::QuitUser(Instance, New, reason); return; } } - if (socket > -1) - { - if (!Instance->SE->AddFd(New)) - { - userrec::QuitUser(Instance, New, "Internal error handling connection"); - return; - } - } - /* NOTE: even if dns lookups are *off*, we still need to display this. * BOPM and other stuff requires it. */ @@ -989,29 +1019,16 @@ unsigned long userrec::LocalCloneCount() /* * Check class restrictions */ -void userrec::CheckClass() +void userrec::CheckClass(const std::string &explicit_class) { - ConnectClass* a = this->GetClass(); + ConnectClass* a = this->GetClass(explicit_class); if ((!a) || (a->GetType() == CC_DENY)) { userrec::QuitUser(ServerInstance, this, "Unauthorised connection"); return; } - - if ((!a->GetPass().empty()) && (!this->haspassed)) - { - userrec::QuitUser(ServerInstance, this, "Invalid password"); - return; - } - - if ((!a) || (a->GetType() == CC_DENY)) - { - userrec::QuitUser(ServerInstance, this,"Unauthorised connection"); - return; - } - - if ((a->GetMaxLocal()) && (this->LocalCloneCount() > a->GetMaxLocal())) + else if ((a->GetMaxLocal()) && (this->LocalCloneCount() > a->GetMaxLocal())) { userrec::QuitUser(ServerInstance, this, "No more connections allowed from your host via this connect class (local)"); ServerInstance->WriteOpers("*** WARNING: maximum LOCAL connections (%ld) exceeded for IP %s", a->GetMaxLocal(), this->GetIPString()); @@ -1023,6 +1040,15 @@ void userrec::CheckClass() ServerInstance->WriteOpers("*** WARNING: maximum GLOBAL connections (%ld) exceeded for IP %s", a->GetMaxGlobal(), this->GetIPString()); return; } + + this->pingmax = a->GetPingTime(); + this->nping = ServerInstance->Time() + a->GetPingTime() + ServerInstance->Config->dns_timeout; + this->timeout = ServerInstance->Time() + a->GetRegTimeout(); + this->flood = a->GetFlood(); + this->threshold = a->GetThreshold(); + this->sendqmax = a->GetSendqMax(); + this->recvqmax = a->GetRecvqMax(); + this->MaxChans = a->GetMaxChans(); } void userrec::FullConnect() @@ -1037,7 +1063,16 @@ void userrec::FullConnect() * Don't remove this! -- w00t */ this->CheckClass(); - + + /* Check the password, if one is required by the user's connect class. + * This CANNOT be in CheckClass(), because that is called prior to PASS as well! + */ + if ((!this->GetClass()->GetPass().empty()) && (!this->haspassed)) + { + userrec::QuitUser(ServerInstance, this, "Invalid password"); + return; + } + if (!this->exempt) { GLine* r = ServerInstance->XLines->matches_gline(this); @@ -1046,8 +1081,10 @@ void userrec::FullConnect() { this->muted = true; char reason[MAXBUF]; + if (*ServerInstance->Config->MoronBanner) + this->WriteServ("NOTICE %s :*** %s", this->nick, ServerInstance->Config->MoronBanner); snprintf(reason,MAXBUF,"G-Lined: %s",r->reason); - ServerInstance->GlobalCulls.AddItem(this, reason); + userrec::QuitUser(ServerInstance, this, reason); return; } @@ -1057,11 +1094,12 @@ void userrec::FullConnect() { this->muted = true; char reason[MAXBUF]; + if (*ServerInstance->Config->MoronBanner) + this->WriteServ("NOTICE %s :*** %s", this, ServerInstance->Config->MoronBanner); snprintf(reason,MAXBUF,"K-Lined: %s",n->reason); - ServerInstance->GlobalCulls.AddItem(this, reason); + userrec::QuitUser(ServerInstance, this, reason); return; } - } this->WriteServ("NOTICE Auth :Welcome to \002%s\002!",ServerInstance->Config->Network); @@ -1072,6 +1110,9 @@ void userrec::FullConnect() ServerInstance->Config->Send005(this); + this->WriteServ("042 %s %s :your unique ID", this->nick, this->uuid); + + this->ShowMOTD(); /* Now registered */ @@ -1165,10 +1206,15 @@ bool userrec::ForceNickChange(const char* newnick) if (this->registered == REG_ALL) { - const char* pars[1]; - pars[0] = newnick; - std::string cmd = "NICK"; - return (ServerInstance->Parser->CallHandler(cmd, pars, 1, this) == CMD_SUCCESS); + std::deque dummy; + command_t* nickhandler = ServerInstance->Parser->GetHandler("NICK"); + if (nickhandler) + { + nickhandler->HandleInternal(1, dummy); + bool result = (ServerInstance->Parser->CallHandler("NICK", &newnick, 1, this) == CMD_SUCCESS); + nickhandler->HandleInternal(0, dummy); + return result; + } } return false; } @@ -1336,12 +1382,12 @@ const char* userrec::GetIPString(char* buf) */ void userrec::Write(std::string text) { - if ((this->fd < 0) || (this->fd > MAX_DESCRIPTORS)) + if (!ServerInstance->SE->BoundsCheckFd(this)) return; try { - /* ServerInstance->Log(DEBUG,"C[%d] <- %s", this->GetFd(), text.c_str()); + /* ServerInstance->Log(DEBUG,"C[%d] O %s", this->GetFd(), text.c_str()); * WARNING: The above debug line is VERY loud, do NOT * enable it till we have a good way of filtering it * out of the logs (e.g. 1.2 would be good). @@ -1358,6 +1404,9 @@ void userrec::Write(std::string text) { try { + /* XXX: The lack of buffering here is NOT a bug, modules implementing this interface have to + * implement their own buffering mechanisms + */ ServerInstance->Config->GetIOHook(this->GetPort())->OnRawSocketWrite(this->fd, text.data(), text.length()); } catch (CoreException& modexcept) @@ -1600,12 +1649,13 @@ void userrec::WriteWallOps(const std::string &text) if (!IS_OPER(this) && IS_LOCAL(this)) return; - std::string wallop = "WALLOPS :" + text; + std::string wallop("WALLOPS :"); + wallop.append(text); for (std::vector::const_iterator i = ServerInstance->local_users.begin(); i != ServerInstance->local_users.end(); i++) { userrec* t = *i; - if ((IS_LOCAL(t)) && (t->modes[UM_WALLOPS])) + if (t->IsModeSet('w')) this->WriteTo(t,wallop); } } @@ -1764,7 +1814,7 @@ std::string userrec::ChannelList(userrec* source) * If the channel is NOT private/secret OR the user shares a common channel * If the user is an oper, and the option is set. */ - if ((source == this) || (IS_OPER(source) && ServerInstance->Config->OperSpyWhois) || (((!i->first->modes[CM_PRIVATE]) && (!i->first->modes[CM_SECRET])) || (i->first->HasUser(source)))) + if ((source == this) || (IS_OPER(source) && ServerInstance->Config->OperSpyWhois) || (((!i->first->IsModeSet('p')) && (!i->first->IsModeSet('s'))) || (i->first->HasUser(source)))) { list.append(i->first->GetPrefixChar(this)).append(i->first->name).append(" "); } @@ -1823,27 +1873,42 @@ void userrec::SplitChanList(userrec* dest, const std::string &cl) } } +unsigned int userrec::GetMaxChans() +{ + return this->MaxChans; +} /* looks up a users password for their connection class (/ tags) * NOTE: If the or tag specifies an ip, and this user resolves, * then their ip will be taken as 'priority' anyway, so for example, * will match joe!bloggs@localhost */ -ConnectClass* userrec::GetClass() +ConnectClass* userrec::GetClass(const std::string &explicit_name) { - for (ClassVector::iterator i = ServerInstance->Config->Classes.begin(); i != ServerInstance->Config->Classes.end(); i++) + if (!explicit_name.empty()) + { + for (ClassVector::iterator i = ServerInstance->Config->Classes.begin(); i != ServerInstance->Config->Classes.end(); i++) + { + if (explicit_name == i->GetName()) + return &(*i); + } + } + else { - if (((match(this->GetIPString(),i->GetHost().c_str(),true)) || (match(this->host,i->GetHost().c_str())))) + for (ClassVector::iterator i = ServerInstance->Config->Classes.begin(); i != ServerInstance->Config->Classes.end(); i++) { - if (i->GetPort()) + if (((match(this->GetIPString(),i->GetHost().c_str(),true)) || (match(this->host,i->GetHost().c_str())))) { - if (this->GetPort() == i->GetPort()) - return &(*i); + if (i->GetPort()) + { + if (this->GetPort() == i->GetPort()) + return &(*i); + else + continue; + } else - continue; + return &(*i); } - else - return &(*i); } } return NULL; @@ -1906,15 +1971,16 @@ void userrec::ShowRULES() { if (!ServerInstance->Config->RULES.size()) { - this->WriteServ("NOTICE %s :Rules file is missing.",this->nick); + this->WriteServ("434 %s :RULES File is missing",this->nick); return; } - this->WriteServ("NOTICE %s :%s rules",this->nick,ServerInstance->Config->ServerName); + + this->WriteServ("308 %s :- %s Server Rules -",this->nick,ServerInstance->Config->ServerName); for (file_cache::iterator i = ServerInstance->Config->RULES.begin(); i != ServerInstance->Config->RULES.end(); i++) - this->WriteServ("NOTICE %s :%s",this->nick,i->c_str()); + this->WriteServ("232 %s :- %s",this->nick,i->c_str()); - this->WriteServ("NOTICE %s :End of %s rules.",this->nick,ServerInstance->Config->ServerName); + this->WriteServ("309 %s :End of RULES command.",this->nick); } void userrec::HandleEvent(EventType et, int errornum)