-/* +------------------------------------+\r * | Inspire Internet Relay Chat Daemon |\r * +------------------------------------+\r *\r * InspIRCd: (C) 2002-2007 InspIRCd Development Team\r * See: http://www.inspircd.org/wiki/index.php/Credits\r *\r * This program is free but copyrighted software; see\r * the file COPYING for details.\r *\r * ---------------------------------------------------\r */\r\r#include "inspircd.h"\r#include "configreader.h"\r#include "channels.h"\r#include "users.h"\r#include <stdarg.h>\r#include "socketengine.h"\r#include "wildcard.h"\r#include "xline.h"\r#include "commands/cmd_whowas.h"\r\rstatic unsigned long already_sent[MAX_DESCRIPTORS] = {0};\r\r/* XXX: Used for speeding up WriteCommon operations */\runsigned long uniq_id = 0;\r\rbool InitTypes(ServerConfig* conf, const char* tag)\r{\r if (conf->opertypes.size())\r {\r for (opertype_t::iterator n = conf->opertypes.begin(); n != conf->opertypes.end(); n++)\r {\r if (n->second)\r delete[] n->second;\r }\r }\r\r conf->opertypes.clear();\r return true;\r}\r\rbool InitClasses(ServerConfig* conf, const char* tag)\r{\r if (conf->operclass.size())\r {\r for (operclass_t::iterator n = conf->operclass.begin(); n != conf->operclass.end(); n++)\r {\r if (n->second)\r delete[] n->second;\r }\r }\r\r conf->operclass.clear();\r return true;\r}\r\rbool DoType(ServerConfig* conf, const char* tag, char** entries, ValueList &values, int* types)\r{\r const char* TypeName = values[0].GetString();\r const char* Classes = values[1].GetString();\r\r conf->opertypes[TypeName] = strnewdup(Classes);\r return true;\r}\r\rbool DoClass(ServerConfig* conf, const char* tag, char** entries, ValueList &values, int* types)\r{\r const char* ClassName = values[0].GetString();\r const char* CommandList = values[1].GetString();\r\r conf->operclass[ClassName] = strnewdup(CommandList);\r return true;\r}\r\rbool DoneClassesAndTypes(ServerConfig* conf, const char* tag)\r{\r return true;\r}\r\rstd::string userrec::ProcessNoticeMasks(const char *sm)\r{\r bool adding = true, oldadding = false;\r const char *c = sm;\r std::string output;\r\r while (c && *c)\r {\r switch (*c)\r {\r case '+':\r adding = true;\r break;\r case '-':\r adding = false;\r break;\r case '*':\r for (unsigned char d = 'A'; d <= 'z'; d++)\r {\r if (ServerInstance->SNO->IsEnabled(d))\r {\r if ((!IsNoticeMaskSet(d) && adding) || (IsNoticeMaskSet(d) && !adding))\r {\r if ((oldadding != adding) || (!output.length()))\r output += (adding ? '+' : '-');\r\r this->SetNoticeMask(d, adding);\r\r output += d;\r }\r }\r oldadding = adding;\r }\r break;\r default:\r if ((*c >= 'A') && (*c <= 'z') && (ServerInstance->SNO->IsEnabled(*c)))\r {\r if ((!IsNoticeMaskSet(*c) && adding) || (IsNoticeMaskSet(*c) && !adding))\r {\r if ((oldadding != adding) || (!output.length()))\r output += (adding ? '+' : '-');\r\r this->SetNoticeMask(*c, adding);\r\r output += *c;\r }\r }\r oldadding = adding;\r break;\r }\r\r *c++;\r }\r\r return output;\r}\r\rvoid userrec::StartDNSLookup()\r{\r try\r {\r bool cached;\r const char* ip = this->GetIPString();\r\r /* Special case for 4in6 (Have i mentioned i HATE 4in6?) */\r if (!strncmp(ip, "0::ffff:", 8))\r res_reverse = new UserResolver(this->ServerInstance, this, ip + 8, DNS_QUERY_PTR4, cached);\r else\r res_reverse = new UserResolver(this->ServerInstance, this, ip, this->GetProtocolFamily() == AF_INET ? DNS_QUERY_PTR4 : DNS_QUERY_PTR6, cached);\r\r this->ServerInstance->AddResolver(res_reverse, cached);\r }\r catch (CoreException& e)\r {\r ServerInstance->Log(DEBUG,"Error in resolver: %s",e.GetReason());\r }\r}\r\rUserResolver::UserResolver(InspIRCd* Instance, userrec* user, std::string to_resolve, QueryType qt, bool &cache) :\r Resolver(Instance, to_resolve, qt, cache), bound_user(user)\r{\r this->fwd = (qt == DNS_QUERY_A || qt == DNS_QUERY_AAAA);\r this->bound_fd = user->GetFd();\r}\r\rvoid UserResolver::OnLookupComplete(const std::string &result, unsigned int ttl, bool cached)\r{\r if ((!this->fwd) && (ServerInstance->SE->GetRef(this->bound_fd) == this->bound_user))\r {\r this->bound_user->stored_host = result;\r try\r {\r /* Check we didnt time out */\r if (this->bound_user->registered != REG_ALL)\r {\r bool cached;\r#ifdef IPV6\r if (this->bound_user->GetProtocolFamily() == AF_INET6)\r {\r /* IPV6 forward lookup (with possibility of 4in6) */\r const char* ip = this->bound_user->GetIPString();\r bound_user->res_forward = new UserResolver(this->ServerInstance, this->bound_user, result, (!strncmp(ip, "0::ffff:", 8) ? DNS_QUERY_A : DNS_QUERY_AAAA), cached);\r }\r else\r /* IPV4 lookup (mixed protocol mode) */\r#endif\r /* IPV4 lookup (ipv4 only mode) */\r bound_user->res_forward = new UserResolver(this->ServerInstance, this->bound_user, result, DNS_QUERY_A, cached);\r this->ServerInstance->AddResolver(bound_user->res_forward, cached);\r }\r }\r catch (CoreException& e)\r {\r ServerInstance->Log(DEBUG,"Error in resolver: %s",e.GetReason());\r }\r }\r else if ((this->fwd) && (ServerInstance->SE->GetRef(this->bound_fd) == this->bound_user))\r {\r /* Both lookups completed */\r std::string result2("0::ffff:");\r result2.append(result);\r if (this->bound_user->GetIPString() == result || this->bound_user->GetIPString() == result2)\r {\r std::string hostname = this->bound_user->stored_host;\r if (hostname.length() < 65)\r {\r /* Check we didnt time out */\r if ((this->bound_user->registered != REG_ALL) && (!this->bound_user->dns_done))\r {\r /* Hostnames starting with : are not a good thing (tm) */\r if (*(hostname.c_str()) == ':')\r hostname.insert(0, "0");\r\r this->bound_user->WriteServ("NOTICE Auth :*** Found your hostname (%s)%s", hostname.c_str(), (cached ? " -- cached" : ""));\r this->bound_user->dns_done = true;\r strlcpy(this->bound_user->dhost, hostname.c_str(),64);\r strlcpy(this->bound_user->host, hostname.c_str(),64);\r /* Invalidate cache */\r this->bound_user->InvalidateCache();\r }\r }\r else\r {\r if (!this->bound_user->dns_done)\r {\r this->bound_user->WriteServ("NOTICE Auth :*** Your hostname is longer than the maximum of 64 characters, using your IP address (%s) instead.", this->bound_user->GetIPString());\r this->bound_user->dns_done = true;\r }\r }\r }\r else\r {\r if (!this->bound_user->dns_done)\r {\r this->bound_user->WriteServ("NOTICE Auth :*** Your hostname does not match up with your IP address. Sorry, using your IP address (%s) instead.", this->bound_user->GetIPString());\r this->bound_user->dns_done = true;\r }\r }\r }\r}\r\rvoid UserResolver::OnError(ResolverError e, const std::string &errormessage)\r{\r if (ServerInstance->SE->GetRef(this->bound_fd) == this->bound_user)\r {\r /* Since dns timeout is implemented outside of the resolver, this was a race condition that could result in this message being sent *after*\r * the user was fully connected. This check fixes that issue - Special */\r if (!this->bound_user->dns_done)\r {\r /* Error message here */\r this->bound_user->WriteServ("NOTICE Auth :*** Could not resolve your hostname: %s; using your IP address (%s) instead.", errormessage.c_str(), this->bound_user->GetIPString());\r this->bound_user->dns_done = true;\r }\r }\r}\r\r\rbool userrec::IsNoticeMaskSet(unsigned char sm)\r{\r return (snomasks[sm-65]);\r}\r\rvoid userrec::SetNoticeMask(unsigned char sm, bool value)\r{\r snomasks[sm-65] = value;\r}\r\rconst char* userrec::FormatNoticeMasks()\r{\r static char data[MAXBUF];\r int offset = 0;\r\r for (int n = 0; n < 64; n++)\r {\r if (snomasks[n])\r data[offset++] = n+65;\r }\r\r data[offset] = 0;\r return data;\r}\r\r\r\rbool userrec::IsModeSet(unsigned char m)\r{\r return (modes[m-65]);\r}\r\rvoid userrec::SetMode(unsigned char m, bool value)\r{\r modes[m-65] = value;\r}\r\rconst char* userrec::FormatModes()\r{\r static char data[MAXBUF];\r int offset = 0;\r for (int n = 0; n < 64; n++)\r {\r if (modes[n])\r data[offset++] = n+65;\r }\r data[offset] = 0;\r return data;\r}\r\rvoid userrec::DecrementModes()\r{\r for (int n = 0; n < 64; n++)\r {\r if (modes[n])\r {\r ModeHandler* mh = ServerInstance->Modes->FindMode(n+65, MODETYPE_USER);\r if (mh)\r mh->ChangeCount(-1);\r }\r }\r}\r\ruserrec::userrec(InspIRCd* Instance) : ServerInstance(Instance)\r{\r // the PROPER way to do it, AVOID bzero at *ALL* costs\r *password = *nick = *ident = *host = *dhost = *fullname = *awaymsg = *oper = 0;\r server = (char*)Instance->FindServerNamePtr(Instance->Config->ServerName);\r reset_due = ServerInstance->Time();\r age = ServerInstance->Time(true);\r lines_in = lastping = signon = idle_lastmsg = nping = registered = 0;\r ChannelCount = timeout = flood = bytes_in = bytes_out = cmds_in = cmds_out = 0;\r muted = exempt = haspassed = dns_done = false;\r fd = -1;\r recvq.clear();\r sendq.clear();\r WriteError.clear();\r res_forward = res_reverse = NULL;\r Visibility = NULL;\r ip = NULL;\r chans.clear();\r invites.clear();\r memset(modes,0,sizeof(modes));\r memset(snomasks,0,sizeof(snomasks));\r /* Invalidate cache */\r operquit = cached_fullhost = cached_hostip = cached_makehost = cached_fullrealhost = NULL;\r}\r\rvoid userrec::RemoveCloneCounts()\r{\r clonemap::iterator x = ServerInstance->local_clones.find(this->GetIPString());\r if (x != ServerInstance->local_clones.end())\r {\r x->second--;\r if (!x->second)\r {\r ServerInstance->local_clones.erase(x);\r }\r }\r \r clonemap::iterator y = ServerInstance->global_clones.find(this->GetIPString());\r if (y != ServerInstance->global_clones.end())\r {\r y->second--;\r if (!y->second)\r {\r ServerInstance->global_clones.erase(y);\r }\r }\r}\r\ruserrec::~userrec()\r{\r this->InvalidateCache();\r this->DecrementModes();\r if (operquit)\r free(operquit);\r if (ip)\r {\r this->RemoveCloneCounts();\r\r if (this->GetProtocolFamily() == AF_INET)\r {\r delete (sockaddr_in*)ip;\r }\r#ifdef SUPPORT_IP6LINKS\r else\r {\r delete (sockaddr_in6*)ip;\r }\r#endif\r }\r}\r\rchar* userrec::MakeHost()\r{\r if (this->cached_makehost)\r return this->cached_makehost;\r\r char nhost[MAXBUF];\r /* This is much faster than snprintf */\r char* t = nhost;\r for(char* n = ident; *n; n++)\r *t++ = *n;\r *t++ = '@';\r for(char* n = host; *n; n++)\r *t++ = *n;\r *t = 0;\r\r this->cached_makehost = strdup(nhost);\r\r return this->cached_makehost;\r}\r\rchar* userrec::MakeHostIP()\r{\r if (this->cached_hostip)\r return this->cached_hostip;\r\r char ihost[MAXBUF];\r /* This is much faster than snprintf */\r char* t = ihost;\r for(char* n = ident; *n; n++)\r *t++ = *n;\r *t++ = '@';\r for(const char* n = this->GetIPString(); *n; n++)\r *t++ = *n;\r *t = 0;\r\r this->cached_hostip = strdup(ihost);\r\r return this->cached_hostip;\r}\r\rvoid userrec::CloseSocket()\r{\r shutdown(this->fd,2);\r close(this->fd);\r}\r\rchar* userrec::GetFullHost()\r{\r if (this->cached_fullhost)\r return this->cached_fullhost;\r\r char result[MAXBUF];\r char* t = result;\r for(char* n = nick; *n; n++)\r *t++ = *n;\r *t++ = '!';\r for(char* n = ident; *n; n++)\r *t++ = *n;\r *t++ = '@';\r for(char* n = dhost; *n; n++)\r *t++ = *n;\r *t = 0;\r\r this->cached_fullhost = strdup(result);\r\r return this->cached_fullhost;\r}\r\rchar* userrec::MakeWildHost()\r{\r static char nresult[MAXBUF];\r char* t = nresult;\r *t++ = '*'; *t++ = '!';\r *t++ = '*'; *t++ = '@';\r for(char* n = dhost; *n; n++)\r *t++ = *n;\r *t = 0;\r return nresult;\r}\r\rint userrec::ReadData(void* buffer, size_t size)\r{\r if (IS_LOCAL(this))\r {\r#ifndef WIN32\r return read(this->fd, buffer, size);\r#else\r return recv(this->fd, (char*)buffer, size, 0);\r#endif\r }\r else\r return 0;\r}\r\r\rchar* userrec::GetFullRealHost()\r{\r if (this->cached_fullrealhost)\r return this->cached_fullrealhost;\r\r char fresult[MAXBUF];\r char* t = fresult;\r for(char* n = nick; *n; n++)\r *t++ = *n;\r *t++ = '!';\r for(char* n = ident; *n; n++)\r *t++ = *n;\r *t++ = '@';\r for(char* n = host; *n; n++)\r *t++ = *n;\r *t = 0;\r\r this->cached_fullrealhost = strdup(fresult);\r\r return this->cached_fullrealhost;\r}\r\rbool userrec::IsInvited(const irc::string &channel)\r{\r for (InvitedList::iterator i = invites.begin(); i != invites.end(); i++)\r {\r if (channel == *i)\r {\r return true;\r }\r }\r return false;\r}\r\rInvitedList* userrec::GetInviteList()\r{\r return &invites;\r}\r\rvoid userrec::InviteTo(const irc::string &channel)\r{\r invites.push_back(channel);\r}\r\rvoid userrec::RemoveInvite(const irc::string &channel)\r{\r for (InvitedList::iterator i = invites.begin(); i != invites.end(); i++)\r {\r if (channel == *i)\r {\r invites.erase(i);\r return;\r }\r }\r}\r\rbool userrec::HasPermission(const std::string &command)\r{\r char* mycmd;\r char* savept;\r char* savept2;\r\r /*\r * users on remote servers can completely bypass all permissions based checks.\r * This prevents desyncs when one server has different type/class tags to another.\r * That having been said, this does open things up to the possibility of source changes\r * allowing remote kills, etc - but if they have access to the src, they most likely have\r * access to the conf - so it's an end to a means either way.\r */\r if (!IS_LOCAL(this))\r return true;\r\r // are they even an oper at all?\r if (IS_OPER(this))\r {\r opertype_t::iterator iter_opertype = ServerInstance->Config->opertypes.find(this->oper);\r if (iter_opertype != ServerInstance->Config->opertypes.end())\r {\r char* Classes = strdup(iter_opertype->second);\r char* myclass = strtok_r(Classes," ",&savept);\r while (myclass)\r {\r operclass_t::iterator iter_operclass = ServerInstance->Config->operclass.find(myclass);\r if (iter_operclass != ServerInstance->Config->operclass.end())\r {\r char* CommandList = strdup(iter_operclass->second);\r mycmd = strtok_r(CommandList," ",&savept2);\r while (mycmd)\r {\r if ((!strcasecmp(mycmd,command.c_str())) || (*mycmd == '*'))\r {\r free(Classes);\r free(CommandList);\r return true;\r }\r mycmd = strtok_r(NULL," ",&savept2);\r }\r free(CommandList);\r }\r myclass = strtok_r(NULL," ",&savept);\r }\r free(Classes);\r }\r }\r return false;\r}\r\r/** NOTE: We cannot pass a const reference to this method.\r * The string is changed by the workings of the method,\r * so that if we pass const ref, we end up copying it to\r * something we can change anyway. Makes sense to just let\r * the compiler do that copy for us.\r */\rbool userrec::AddBuffer(std::string a)\r{\r try\r {\r std::string::size_type i = a.rfind('\r');\r\r while (i != std::string::npos)\r {\r a.erase(i, 1);\r i = a.rfind('\r');\r }\r\r if (a.length())\r recvq.append(a);\r\r if (recvq.length() > (unsigned)this->recvqmax)\r {\r this->SetWriteError("RecvQ exceeded");\r ServerInstance->WriteOpers("*** User %s RecvQ of %d exceeds connect class maximum of %d",this->nick,recvq.length(),this->recvqmax);\r return false;\r }\r\r return true;\r }\r\r catch (...)\r {\r ServerInstance->Log(DEBUG,"Exception in userrec::AddBuffer()");\r return false;\r }\r}\r\rbool userrec::BufferIsReady()\r{\r return (recvq.find('\n') != std::string::npos);\r}\r\rvoid userrec::ClearBuffer()\r{\r recvq.clear();\r}\r\rstd::string userrec::GetBuffer()\r{\r try\r {\r if (!recvq.length())\r return "";\r\r /* Strip any leading \r or \n off the string.\r * Usually there are only one or two of these,\r * so its is computationally cheap to do.\r */\r std::string::iterator t = recvq.begin();\r while (t != recvq.end() && (*t == '\r' || *t == '\n'))\r {\r recvq.erase(t);\r t = recvq.begin();\r }\r\r for (std::string::iterator x = recvq.begin(); x != recvq.end(); x++)\r {\r /* Find the first complete line, return it as the\r * result, and leave the recvq as whats left\r */\r if (*x == '\n')\r {\r std::string ret = std::string(recvq.begin(), x);\r recvq.erase(recvq.begin(), x + 1);\r return ret;\r }\r }\r return "";\r }\r\r catch (...)\r {\r ServerInstance->Log(DEBUG,"Exception in userrec::GetBuffer()");\r return "";\r }\r}\r\rvoid userrec::AddWriteBuf(const std::string &data)\r{\r if (*this->GetWriteError())\r return;\r\r if (sendq.length() + data.length() > (unsigned)this->sendqmax)\r {\r /*\r * Fix by brain - Set the error text BEFORE calling writeopers, because\r * if we dont it'll recursively call here over and over again trying\r * to repeatedly add the text to the sendq!\r */\r this->SetWriteError("SendQ exceeded");\r ServerInstance->WriteOpers("*** User %s SendQ of %d exceeds connect class maximum of %d",this->nick,sendq.length() + data.length(),this->sendqmax);\r return;\r }\r\r try\r {\r if (data.length() > MAXBUF - 2) /* MAXBUF has a value of 514, to account for line terminators */\r sendq.append(data.substr(0,MAXBUF - 4)).append("\r\n"); /* MAXBUF-4 = 510 */\r else\r sendq.append(data);\r }\r catch (...)\r {\r this->SetWriteError("SendQ exceeded");\r ServerInstance->WriteOpers("*** User %s SendQ got an exception",this->nick);\r }\r}\r\r// send AS MUCH OF THE USERS SENDQ as we are able to (might not be all of it)\rvoid userrec::FlushWriteBuf()\r{\r try\r {\r if ((this->fd == FD_MAGIC_NUMBER) || (*this->GetWriteError()))\r {\r sendq.clear();\r }\r if ((sendq.length()) && (this->fd != FD_MAGIC_NUMBER))\r {\r int old_sendq_length = sendq.length();\r#ifndef WIN32\r int n_sent = write(this->fd, this->sendq.data(), this->sendq.length());\r#else\r int n_sent = send(this->fd, (const char*)this->sendq.data(), this->sendq.length(), 0);\r#endif\r if (n_sent == -1)\r {\r if (errno == EAGAIN)\r {\r /* The socket buffer is full. This isnt fatal,\r * try again later.\r */\r this->ServerInstance->SE->WantWrite(this);\r }\r else\r {\r /* Fatal error, set write error and bail\r */\r this->SetWriteError(strerror(errno));\r return;\r }\r }\r else\r {\r /* advance the queue */\r if (n_sent)\r this->sendq = this->sendq.substr(n_sent);\r /* update the user's stats counters */\r this->bytes_out += n_sent;\r this->cmds_out++;\r if (n_sent != old_sendq_length)\r this->ServerInstance->SE->WantWrite(this);\r }\r }\r }\r\r catch (...)\r {\r ServerInstance->Log(DEBUG,"Exception in userrec::FlushWriteBuf()");\r }\r\r if (this->sendq.empty())\r {\r FOREACH_MOD(I_OnBufferFlushed,OnBufferFlushed(this));\r }\r}\r\rvoid userrec::SetWriteError(const std::string &error)\r{\r try\r {\r // don't try to set the error twice, its already set take the first string.\r if (this->WriteError.empty())\r this->WriteError = error;\r }\r\r catch (...)\r {\r ServerInstance->Log(DEBUG,"Exception in userrec::SetWriteError()");\r }\r}\r\rconst char* userrec::GetWriteError()\r{\r return this->WriteError.c_str();\r}\r\rvoid userrec::Oper(const std::string &opertype)\r{\r try\r {\r this->modes[UM_OPERATOR] = 1;\r this->WriteServ("MODE %s :+o", this->nick);\r FOREACH_MOD(I_OnOper, OnOper(this, opertype));\r ServerInstance->Log(DEFAULT,"OPER: %s!%s@%s opered as type: %s", this->nick, this->ident, this->host, opertype.c_str());\r strlcpy(this->oper, opertype.c_str(), NICKMAX - 1);\r ServerInstance->all_opers.push_back(this);\r FOREACH_MOD(I_OnPostOper,OnPostOper(this, opertype));\r }\r\r catch (...)\r {\r ServerInstance->Log(DEBUG,"Exception in userrec::Oper()");\r }\r}\r\rvoid userrec::UnOper()\r{\r try\r {\r if (IS_OPER(this))\r {\r // unset their oper type (what IS_OPER checks), and remove +o\r *this->oper = 0;\r this->modes[UM_OPERATOR] = 0;\r\r // remove them from the opers list.\r for (std::vector<userrec*>::iterator a = ServerInstance->all_opers.begin(); a < ServerInstance->all_opers.end(); a++)\r {\r if (*a == this)\r {\r ServerInstance->all_opers.erase(a);\r return;\r }\r }\r }\r }\r\r catch (...)\r {\r ServerInstance->Log(DEBUG,"Exception in userrec::UnOper()");\r }\r}\r\rvoid userrec::QuitUser(InspIRCd* Instance, userrec *user, const std::string &quitreason, const char* operreason)\r{\r user->muted = true;\r Instance->GlobalCulls.AddItem(user, quitreason.c_str(), operreason);\r}\r\r/* adds or updates an entry in the whowas list */\rvoid userrec::AddToWhoWas()\r{\r command_t* whowas_command = ServerInstance->Parser->GetHandler("WHOWAS");\r if (whowas_command)\r {\r std::deque<classbase*> params;\r params.push_back(this);\r whowas_command->HandleInternal(WHOWAS_ADD, params);\r }\r}\r\r/* add a client connection to the sockets list */\rvoid userrec::AddClient(InspIRCd* Instance, int socket, int port, bool iscached, int socketfamily, sockaddr* ip)\r{\r std::string tempnick = ConvToStr(socket) + "-unknown";\r user_hash::iterator iter = Instance->clientlist->find(tempnick);\r char ipaddr[MAXBUF];\r#ifdef IPV6\r if (socketfamily == AF_INET6)\r inet_ntop(AF_INET6, &((const sockaddr_in6*)ip)->sin6_addr, ipaddr, sizeof(ipaddr));\r else\r#endif\r inet_ntop(AF_INET, &((const sockaddr_in*)ip)->sin_addr, ipaddr, sizeof(ipaddr));\r userrec* New;\r int j = 0;\r\r Instance->unregistered_count++;\r\r /*\r * fix by brain.\r * as these nicknames are 'RFC impossible', we can be sure nobody is going to be\r * using one as a registered connection. As they are per fd, we can also safely assume\r * that we wont have collisions. Therefore, if the nick exists in the list, its only\r * used by a dead socket, erase the iterator so that the new client may reclaim it.\r * this was probably the cause of 'server ignores me when i hammer it with reconnects'\r * issue in earlier alphas/betas\r */\r if (iter != Instance->clientlist->end())\r {\r userrec* goner = iter->second;\r DELETE(goner);\r Instance->clientlist->erase(iter);\r }\r\r New = new userrec(Instance);\r (*(Instance->clientlist))[tempnick] = New;\r New->fd = socket;\r strlcpy(New->nick,tempnick.c_str(),NICKMAX-1);\r\r New->server = Instance->FindServerNamePtr(Instance->Config->ServerName);\r /* We don't need range checking here, we KNOW 'unknown\0' will fit into the ident field. */\r strcpy(New->ident, "unknown");\r\r New->registered = REG_NONE;\r New->signon = Instance->Time() + Instance->Config->dns_timeout;\r New->lastping = 1;\r\r New->SetSockAddr(socketfamily, ipaddr, port);\r\r /* Smarter than your average bear^H^H^H^Hset of strlcpys. */\r for (const char* temp = New->GetIPString(); *temp && j < 64; temp++, j++)\r New->dhost[j] = New->host[j] = *temp;\r New->dhost[j] = New->host[j] = 0;\r\r Instance->AddLocalClone(New);\r Instance->AddGlobalClone(New);\r\r /*\r * First class check. We do this again in FullConnect after DNS is done, and NICK/USER is recieved.\r * See my note down there for why this is required. DO NOT REMOVE. :) -- w00t\r */\r ConnectClass* i = New->GetClass();\r\r if (!i)\r {\r userrec::QuitUser(Instance, New, "Access denied by configuration");\r return;\r }\r\r New->CheckClass();\r\r New->pingmax = i->GetPingTime();\r New->nping = Instance->Time() + i->GetPingTime() + Instance->Config->dns_timeout;\r New->timeout = Instance->Time() + i->GetRegTimeout();\r New->flood = i->GetFlood();\r New->threshold = i->GetThreshold();\r New->sendqmax = i->GetSendqMax();\r New->recvqmax = i->GetRecvqMax();\r\r Instance->local_users.push_back(New);\r\r if ((Instance->local_users.size() > Instance->Config->SoftLimit) || (Instance->local_users.size() >= MAXCLIENTS))\r {\r Instance->WriteOpers("*** Warning: softlimit value has been reached: %d clients", Instance->Config->SoftLimit);\r userrec::QuitUser(Instance, New,"No more connections allowed");\r return;\r }\r\r /*\r * XXX -\r * this is done as a safety check to keep the file descriptors within range of fd_ref_table.\r * its a pretty big but for the moment valid assumption:\r * file descriptors are handed out starting at 0, and are recycled as theyre freed.\r * therefore if there is ever an fd over 65535, 65536 clients must be connected to the\r * irc server at once (or the irc server otherwise initiating this many connections, files etc)\r * which for the time being is a physical impossibility (even the largest networks dont have more\r * than about 10,000 users on ONE server!)\r */\r#ifndef WINDOWS\r if ((unsigned int)socket >= MAX_DESCRIPTORS)\r {\r userrec::QuitUser(Instance, New, "Server is full");\r return;\r }\r#endif\r\r New->exempt = (Instance->XLines->matches_exception(New) != NULL);\r if (!New->exempt)\r {\r ZLine* r = Instance->XLines->matches_zline(ipaddr);\r if (r)\r {\r char reason[MAXBUF];\r if (*Instance->Config->MoronBanner)\r New->WriteServ("NOTICE %s :*** %s", New->nick, Instance->Config->MoronBanner);\r snprintf(reason,MAXBUF,"Z-Lined: %s",r->reason);\r userrec::QuitUser(Instance, New, reason);\r return;\r }\r }\r\r if (socket > -1)\r {\r if (!Instance->SE->AddFd(New))\r {\r userrec::QuitUser(Instance, New, "Internal error handling connection");\r return;\r }\r }\r\r /* NOTE: even if dns lookups are *off*, we still need to display this.\r * BOPM and other stuff requires it.\r */\r New->WriteServ("NOTICE Auth :*** Looking up your hostname...");\r}\r\runsigned long userrec::GlobalCloneCount()\r{\r clonemap::iterator x = ServerInstance->global_clones.find(this->GetIPString());\r if (x != ServerInstance->global_clones.end())\r return x->second;\r else\r return 0;\r}\r\runsigned long userrec::LocalCloneCount()\r{\r clonemap::iterator x = ServerInstance->local_clones.find(this->GetIPString());\r if (x != ServerInstance->local_clones.end())\r return x->second;\r else\r return 0;\r}\r\r/*\r * Check class restrictions\r */\rvoid userrec::CheckClass()\r{\r ConnectClass* a = this->GetClass();\r\r if ((!a) || (a->GetType() == CC_DENY))\r {\r userrec::QuitUser(ServerInstance, this, "Unauthorised connection");\r return;\r }\r else if ((a->GetMaxLocal()) && (this->LocalCloneCount() > a->GetMaxLocal()))\r {\r userrec::QuitUser(ServerInstance, this, "No more connections allowed from your host via this connect class (local)");\r ServerInstance->WriteOpers("*** WARNING: maximum LOCAL connections (%ld) exceeded for IP %s", a->GetMaxLocal(), this->GetIPString());\r return;\r }\r else if ((a->GetMaxGlobal()) && (this->GlobalCloneCount() > a->GetMaxGlobal()))\r {\r userrec::QuitUser(ServerInstance, this, "No more connections allowed from your host via this connect class (global)");\r ServerInstance->WriteOpers("*** WARNING: maximum GLOBAL connections (%ld) exceeded for IP %s", a->GetMaxGlobal(), this->GetIPString());\r return;\r }\r}\r\rvoid userrec::FullConnect()\r{\r ServerInstance->stats->statsConnects++;\r this->idle_lastmsg = ServerInstance->Time();\r\r /*\r * You may be thinking "wtf, we checked this in userrec::AddClient!" - and yes, we did, BUT.\r * At the time AddClient is called, we don't have a resolved host, by here we probably do - which\r * may put the user into a totally seperate class with different restrictions! so we *must* check again.\r * Don't remove this! -- w00t\r */\r this->CheckClass();\r \r /* Check the password, if one is required by the user's connect class.\r * This CANNOT be in CheckClass(), because that is called prior to PASS as well!\r */\r if ((!this->GetClass()->GetPass().empty()) && (!this->haspassed))\r {\r userrec::QuitUser(ServerInstance, this, "Invalid password");\r return;\r }\r \r if (!this->exempt)\r {\r GLine* r = ServerInstance->XLines->matches_gline(this);\r\r if (r)\r {\r this->muted = true;\r char reason[MAXBUF];\r if (*ServerInstance->Config->MoronBanner)\r this->WriteServ("NOTICE %s :*** %s", this->nick, ServerInstance->Config->MoronBanner);\r snprintf(reason,MAXBUF,"G-Lined: %s",r->reason);\r ServerInstance->GlobalCulls.AddItem(this, reason);\r return;\r }\r\r KLine* n = ServerInstance->XLines->matches_kline(this);\r\r if (n)\r {\r this->muted = true;\r char reason[MAXBUF];\r if (*ServerInstance->Config->MoronBanner)\r this->WriteServ("NOTICE %s :*** %s", this, ServerInstance->Config->MoronBanner);\r snprintf(reason,MAXBUF,"K-Lined: %s",n->reason);\r ServerInstance->GlobalCulls.AddItem(this, reason);\r return;\r }\r }\r\r this->WriteServ("NOTICE Auth :Welcome to \002%s\002!",ServerInstance->Config->Network);\r this->WriteServ("001 %s :Welcome to the %s IRC Network %s!%s@%s",this->nick, ServerInstance->Config->Network, this->nick, this->ident, this->host);\r this->WriteServ("002 %s :Your host is %s, running version %s",this->nick,ServerInstance->Config->ServerName,VERSION);\r this->WriteServ("003 %s :This server was created %s %s", this->nick, __TIME__, __DATE__);\r this->WriteServ("004 %s %s %s %s %s %s", this->nick, ServerInstance->Config->ServerName, VERSION, ServerInstance->Modes->UserModeList().c_str(), ServerInstance->Modes->ChannelModeList().c_str(), ServerInstance->Modes->ParaModeList().c_str());\r\r ServerInstance->Config->Send005(this);\r\r this->ShowMOTD();\r\r /* Now registered */\r if (ServerInstance->unregistered_count)\r ServerInstance->unregistered_count--;\r\r /* Trigger LUSERS output, give modules a chance too */\r int MOD_RESULT = 0;\r FOREACH_RESULT(I_OnPreCommand, OnPreCommand("LUSERS", NULL, 0, this, true, "LUSERS"));\r if (!MOD_RESULT)\r ServerInstance->CallCommandHandler("LUSERS", NULL, 0, this);\r\r /*\r * fix 3 by brain, move registered = 7 below these so that spurious modes and host\r * changes dont go out onto the network and produce 'fake direction'.\r */\r FOREACH_MOD(I_OnUserConnect,OnUserConnect(this));\r\r this->registered = REG_ALL;\r\r FOREACH_MOD(I_OnPostConnect,OnPostConnect(this));\r\r ServerInstance->SNO->WriteToSnoMask('c',"Client connecting on port %d: %s!%s@%s [%s] [%s]", this->GetPort(), this->nick, this->ident, this->host, this->GetIPString(), this->fullname);\r}\r\r/** userrec::UpdateNick()\r * re-allocates a nick in the user_hash after they change nicknames,\r * returns a pointer to the new user as it may have moved\r */\ruserrec* userrec::UpdateNickHash(const char* New)\r{\r try\r {\r //user_hash::iterator newnick;\r user_hash::iterator oldnick = ServerInstance->clientlist->find(this->nick);\r\r if (!strcasecmp(this->nick,New))\r return oldnick->second;\r\r if (oldnick == ServerInstance->clientlist->end())\r return NULL; /* doesnt exist */\r\r userrec* olduser = oldnick->second;\r (*(ServerInstance->clientlist))[New] = olduser;\r ServerInstance->clientlist->erase(oldnick);\r return olduser;\r }\r\r catch (...)\r {\r ServerInstance->Log(DEBUG,"Exception in userrec::UpdateNickHash()");\r return NULL;\r }\r}\r\rvoid userrec::InvalidateCache()\r{\r /* Invalidate cache */\r if (cached_fullhost)\r free(cached_fullhost);\r if (cached_hostip)\r free(cached_hostip);\r if (cached_makehost)\r free(cached_makehost);\r if (cached_fullrealhost)\r free(cached_fullrealhost);\r cached_fullhost = cached_hostip = cached_makehost = cached_fullrealhost = NULL;\r}\r\rbool userrec::ForceNickChange(const char* newnick)\r{\r try\r {\r int MOD_RESULT = 0;\r\r this->InvalidateCache();\r\r FOREACH_RESULT(I_OnUserPreNick,OnUserPreNick(this, newnick));\r\r if (MOD_RESULT)\r {\r ServerInstance->stats->statsCollisions++;\r return false;\r }\r\r if (ServerInstance->XLines->matches_qline(newnick))\r {\r ServerInstance->stats->statsCollisions++;\r return false;\r }\r\r if (this->registered == REG_ALL)\r {\r return (ServerInstance->Parser->CallHandler("NICK", &newnick, 1, this) == CMD_SUCCESS);\r }\r return false;\r }\r\r catch (...)\r {\r ServerInstance->Log(DEBUG,"Exception in userrec::ForceNickChange()");\r return false;\r }\r}\r\rvoid userrec::SetSockAddr(int protocol_family, const char* ip, int port)\r{\r switch (protocol_family)\r {\r#ifdef SUPPORT_IP6LINKS\r case AF_INET6:\r {\r sockaddr_in6* sin = new sockaddr_in6;\r sin->sin6_family = AF_INET6;\r sin->sin6_port = port;\r inet_pton(AF_INET6, ip, &sin->sin6_addr);\r this->ip = (sockaddr*)sin;\r }\r break;\r#endif\r case AF_INET:\r {\r sockaddr_in* sin = new sockaddr_in;\r sin->sin_family = AF_INET;\r sin->sin_port = port;\r inet_pton(AF_INET, ip, &sin->sin_addr);\r this->ip = (sockaddr*)sin;\r }\r break;\r default:\r ServerInstance->Log(DEBUG,"Ut oh, I dont know protocol %d to be set on '%s'!", protocol_family, this->nick);\r break;\r }\r}\r\rint userrec::GetPort()\r{\r if (this->ip == NULL)\r return 0;\r\r switch (this->GetProtocolFamily())\r {\r#ifdef SUPPORT_IP6LINKS\r case AF_INET6:\r {\r sockaddr_in6* sin = (sockaddr_in6*)this->ip;\r return sin->sin6_port;\r }\r break;\r#endif\r case AF_INET:\r {\r sockaddr_in* sin = (sockaddr_in*)this->ip;\r return sin->sin_port;\r }\r break;\r default:\r break;\r }\r return 0;\r}\r\rint userrec::GetProtocolFamily()\r{\r if (this->ip == NULL)\r return 0;\r\r sockaddr_in* sin = (sockaddr_in*)this->ip;\r return sin->sin_family;\r}\r\rconst char* userrec::GetIPString()\r{\r static char buf[1024];\r\r if (this->ip == NULL)\r return "";\r\r switch (this->GetProtocolFamily())\r {\r#ifdef SUPPORT_IP6LINKS\r case AF_INET6:\r {\r static char temp[1024];\r\r sockaddr_in6* sin = (sockaddr_in6*)this->ip;\r inet_ntop(sin->sin6_family, &sin->sin6_addr, buf, sizeof(buf));\r /* IP addresses starting with a : on irc are a Bad Thing (tm) */\r if (*buf == ':')\r {\r strlcpy(&temp[1], buf, sizeof(temp) - 1);\r *temp = '0';\r return temp;\r }\r return buf;\r }\r break;\r#endif\r case AF_INET:\r {\r sockaddr_in* sin = (sockaddr_in*)this->ip;\r inet_ntop(sin->sin_family, &sin->sin_addr, buf, sizeof(buf));\r return buf;\r }\r break;\r default:\r break;\r }\r return "";\r}\r\rconst char* userrec::GetIPString(char* buf)\r{\r if (this->ip == NULL)\r {\r *buf = 0;\r return buf;\r }\r\r switch (this->GetProtocolFamily())\r {\r#ifdef SUPPORT_IP6LINKS\r case AF_INET6:\r {\r static char temp[1024];\r\r sockaddr_in6* sin = (sockaddr_in6*)this->ip;\r inet_ntop(sin->sin6_family, &sin->sin6_addr, buf, sizeof(buf));\r /* IP addresses starting with a : on irc are a Bad Thing (tm) */\r if (*buf == ':')\r {\r strlcpy(&temp[1], buf, sizeof(temp) - 1);\r *temp = '0';\r strlcpy(buf, temp, sizeof(temp));\r }\r return buf;\r }\r break;\r#endif\r case AF_INET:\r {\r sockaddr_in* sin = (sockaddr_in*)this->ip;\r inet_ntop(sin->sin_family, &sin->sin_addr, buf, sizeof(buf));\r return buf;\r }\r break;\r\r default:\r break;\r }\r return "";\r}\r\r/** NOTE: We cannot pass a const reference to this method.\r * The string is changed by the workings of the method,\r * so that if we pass const ref, we end up copying it to\r * something we can change anyway. Makes sense to just let\r * the compiler do that copy for us.\r */\rvoid userrec::Write(std::string text)\r{\r#ifdef WINDOWS\r if ((this->fd < 0) || (this->m_internalFd > MAX_DESCRIPTORS))\r#else\r if ((this->fd < 0) || (this->fd > MAX_DESCRIPTORS))\r#endif\r return;\r\r try\r {\r /* ServerInstance->Log(DEBUG,"C[%d] <- %s", this->GetFd(), text.c_str());\r * WARNING: The above debug line is VERY loud, do NOT\r * enable it till we have a good way of filtering it\r * out of the logs (e.g. 1.2 would be good).\r */\r text.append("\r\n");\r }\r catch (...)\r {\r ServerInstance->Log(DEBUG,"Exception in userrec::Write() std::string::append");\r return;\r }\r\r if (ServerInstance->Config->GetIOHook(this->GetPort()))\r {\r try\r {\r /* XXX: The lack of buffering here is NOT a bug, modules implementing this interface have to\r * implement their own buffering mechanisms\r */\r ServerInstance->Config->GetIOHook(this->GetPort())->OnRawSocketWrite(this->fd, text.data(), text.length());\r }\r catch (CoreException& modexcept)\r {\r ServerInstance->Log(DEBUG, "%s threw an exception: %s", modexcept.GetSource(), modexcept.GetReason());\r }\r }\r else\r {\r this->AddWriteBuf(text);\r }\r ServerInstance->stats->statsSent += text.length();\r this->ServerInstance->SE->WantWrite(this);\r}\r\r/** Write()\r */\rvoid userrec::Write(const char *text, ...)\r{\r va_list argsPtr;\r char textbuffer[MAXBUF];\r\r va_start(argsPtr, text);\r vsnprintf(textbuffer, MAXBUF, text, argsPtr);\r va_end(argsPtr);\r\r this->Write(std::string(textbuffer));\r}\r\rvoid userrec::WriteServ(const std::string& text)\r{\r char textbuffer[MAXBUF];\r\r snprintf(textbuffer,MAXBUF,":%s %s",ServerInstance->Config->ServerName,text.c_str());\r this->Write(std::string(textbuffer));\r}\r\r/** WriteServ()\r * Same as Write(), except `text' is prefixed with `:server.name '.\r */\rvoid userrec::WriteServ(const char* text, ...)\r{\r va_list argsPtr;\r char textbuffer[MAXBUF];\r\r va_start(argsPtr, text);\r vsnprintf(textbuffer, MAXBUF, text, argsPtr);\r va_end(argsPtr);\r\r this->WriteServ(std::string(textbuffer));\r}\r\r\rvoid userrec::WriteFrom(userrec *user, const std::string &text)\r{\r char tb[MAXBUF];\r\r snprintf(tb,MAXBUF,":%s %s",user->GetFullHost(),text.c_str());\r\r this->Write(std::string(tb));\r}\r\r\r/* write text from an originating user to originating user */\r\rvoid userrec::WriteFrom(userrec *user, const char* text, ...)\r{\r va_list argsPtr;\r char textbuffer[MAXBUF];\r\r va_start(argsPtr, text);\r vsnprintf(textbuffer, MAXBUF, text, argsPtr);\r va_end(argsPtr);\r\r this->WriteFrom(user, std::string(textbuffer));\r}\r\r\r/* write text to an destination user from a source user (e.g. user privmsg) */\r\rvoid userrec::WriteTo(userrec *dest, const char *data, ...)\r{\r char textbuffer[MAXBUF];\r va_list argsPtr;\r\r va_start(argsPtr, data);\r vsnprintf(textbuffer, MAXBUF, data, argsPtr);\r va_end(argsPtr);\r\r this->WriteTo(dest, std::string(textbuffer));\r}\r\rvoid userrec::WriteTo(userrec *dest, const std::string &data)\r{\r dest->WriteFrom(this, data);\r}\r\r\rvoid userrec::WriteCommon(const char* text, ...)\r{\r char textbuffer[MAXBUF];\r va_list argsPtr;\r\r if (this->registered != REG_ALL)\r return;\r\r va_start(argsPtr, text);\r vsnprintf(textbuffer, MAXBUF, text, argsPtr);\r va_end(argsPtr);\r\r this->WriteCommon(std::string(textbuffer));\r}\r\rvoid userrec::WriteCommon(const std::string &text)\r{\r try\r {\r bool sent_to_at_least_one = false;\r char tb[MAXBUF];\r\r if (this->registered != REG_ALL)\r return;\r\r uniq_id++;\r\r /* We dont want to be doing this n times, just once */\r snprintf(tb,MAXBUF,":%s %s",this->GetFullHost(),text.c_str());\r std::string out = tb;\r\r for (UCListIter v = this->chans.begin(); v != this->chans.end(); v++)\r {\r CUList* ulist = v->first->GetUsers();\r for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++)\r {\r if ((IS_LOCAL(i->first)) && (already_sent[i->first->fd] != uniq_id))\r {\r already_sent[i->first->fd] = uniq_id;\r i->first->Write(out);\r sent_to_at_least_one = true;\r }\r }\r }\r\r /*\r * if the user was not in any channels, no users will receive the text. Make sure the user\r * receives their OWN message for WriteCommon\r */\r if (!sent_to_at_least_one)\r {\r this->Write(std::string(tb));\r }\r }\r\r catch (...)\r {\r ServerInstance->Log(DEBUG,"Exception in userrec::WriteCommon()");\r }\r}\r\r\r/* write a formatted string to all users who share at least one common\r * channel, NOT including the source user e.g. for use in QUIT\r */\r\rvoid userrec::WriteCommonExcept(const char* text, ...)\r{\r char textbuffer[MAXBUF];\r va_list argsPtr;\r\r va_start(argsPtr, text);\r vsnprintf(textbuffer, MAXBUF, text, argsPtr);\r va_end(argsPtr);\r\r this->WriteCommonExcept(std::string(textbuffer));\r}\r\rvoid userrec::WriteCommonQuit(const std::string &normal_text, const std::string &oper_text)\r{\r char tb1[MAXBUF];\r char tb2[MAXBUF];\r\r if (this->registered != REG_ALL)\r return;\r\r uniq_id++;\r snprintf(tb1,MAXBUF,":%s QUIT :%s",this->GetFullHost(),normal_text.c_str());\r snprintf(tb2,MAXBUF,":%s QUIT :%s",this->GetFullHost(),oper_text.c_str());\r std::string out1 = tb1;\r std::string out2 = tb2;\r\r for (UCListIter v = this->chans.begin(); v != this->chans.end(); v++)\r {\r CUList *ulist = v->first->GetUsers();\r for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++)\r {\r if (this != i->first)\r {\r if ((IS_LOCAL(i->first)) && (already_sent[i->first->fd] != uniq_id))\r {\r already_sent[i->first->fd] = uniq_id;\r i->first->Write(IS_OPER(i->first) ? out2 : out1);\r }\r }\r }\r }\r}\r\rvoid userrec::WriteCommonExcept(const std::string &text)\r{\r char tb1[MAXBUF];\r std::string out1;\r\r if (this->registered != REG_ALL)\r return;\r\r uniq_id++;\r snprintf(tb1,MAXBUF,":%s %s",this->GetFullHost(),text.c_str());\r out1 = tb1;\r\r for (UCListIter v = this->chans.begin(); v != this->chans.end(); v++)\r {\r CUList *ulist = v->first->GetUsers();\r for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++)\r {\r if (this != i->first)\r {\r if ((IS_LOCAL(i->first)) && (already_sent[i->first->fd] != uniq_id))\r {\r already_sent[i->first->fd] = uniq_id;\r i->first->Write(out1);\r }\r }\r }\r }\r\r}\r\rvoid userrec::WriteWallOps(const std::string &text)\r{\r if (!IS_OPER(this) && IS_LOCAL(this))\r return;\r\r std::string wallop("WALLOPS :");\r wallop.append(text);\r\r for (std::vector<userrec*>::const_iterator i = ServerInstance->local_users.begin(); i != ServerInstance->local_users.end(); i++)\r {\r userrec* t = *i;\r if (t->modes[UM_WALLOPS])\r this->WriteTo(t,wallop);\r }\r}\r\rvoid userrec::WriteWallOps(const char* text, ...)\r{\r char textbuffer[MAXBUF];\r va_list argsPtr;\r\r va_start(argsPtr, text);\r vsnprintf(textbuffer, MAXBUF, text, argsPtr);\r va_end(argsPtr);\r\r this->WriteWallOps(std::string(textbuffer));\r}\r\r/* return 0 or 1 depending if users u and u2 share one or more common channels\r * (used by QUIT, NICK etc which arent channel specific notices)\r *\r * The old algorithm in 1.0 for this was relatively inefficient, iterating over\r * the first users channels then the second users channels within the outer loop,\r * therefore it was a maximum of x*y iterations (upon returning 0 and checking\r * all possible iterations). However this new function instead checks against the\r * channel's userlist in the inner loop which is a std::map<userrec*,userrec*>\r * and saves us time as we already know what pointer value we are after.\r * Don't quote me on the maths as i am not a mathematician or computer scientist,\r * but i believe this algorithm is now x+(log y) maximum iterations instead.\r */\rbool userrec::SharesChannelWith(userrec *other)\r{\r if ((!other) || (this->registered != REG_ALL) || (other->registered != REG_ALL))\r return false;\r\r /* Outer loop */\r for (UCListIter i = this->chans.begin(); i != this->chans.end(); i++)\r {\r /* Eliminate the inner loop (which used to be ~equal in size to the outer loop)\r * by replacing it with a map::find which *should* be more efficient\r */\r if (i->first->HasUser(other))\r return true;\r }\r return false;\r}\r\rbool userrec::ChangeName(const char* gecos)\r{\r if (!strcmp(gecos, this->fullname))\r return true;\r\r if (IS_LOCAL(this))\r {\r int MOD_RESULT = 0;\r FOREACH_RESULT(I_OnChangeLocalUserGECOS,OnChangeLocalUserGECOS(this,gecos));\r if (MOD_RESULT)\r return false;\r FOREACH_MOD(I_OnChangeName,OnChangeName(this,gecos));\r }\r strlcpy(this->fullname,gecos,MAXGECOS+1);\r\r return true;\r}\r\rbool userrec::ChangeDisplayedHost(const char* host)\r{\r if (!strcmp(host, this->dhost))\r return true;\r\r if (IS_LOCAL(this))\r {\r int MOD_RESULT = 0;\r FOREACH_RESULT(I_OnChangeLocalUserHost,OnChangeLocalUserHost(this,host));\r if (MOD_RESULT)\r return false;\r FOREACH_MOD(I_OnChangeHost,OnChangeHost(this,host));\r }\r if (this->ServerInstance->Config->CycleHosts)\r this->WriteCommonExcept("QUIT :Changing hosts");\r\r /* Fix by Om: userrec::dhost is 65 long, this was truncating some long hosts */\r strlcpy(this->dhost,host,64);\r\r this->InvalidateCache();\r\r if (this->ServerInstance->Config->CycleHosts)\r {\r for (UCListIter i = this->chans.begin(); i != this->chans.end(); i++)\r {\r i->first->WriteAllExceptSender(this, false, 0, "JOIN %s", i->first->name);\r std::string n = this->ServerInstance->Modes->ModeString(this, i->first);\r if (n.length() > 0)\r i->first->WriteAllExceptSender(this, true, 0, "MODE %s +%s", i->first->name, n.c_str());\r }\r }\r\r if (IS_LOCAL(this))\r this->WriteServ("396 %s %s :is now your displayed host",this->nick,this->dhost);\r\r return true;\r}\r\rbool userrec::ChangeIdent(const char* newident)\r{\r if (!strcmp(newident, this->ident))\r return true;\r\r if (this->ServerInstance->Config->CycleHosts)\r this->WriteCommonExcept("%s","QUIT :Changing ident");\r\r strlcpy(this->ident, newident, IDENTMAX+2);\r\r this->InvalidateCache();\r\r if (this->ServerInstance->Config->CycleHosts)\r {\r for (UCListIter i = this->chans.begin(); i != this->chans.end(); i++)\r {\r i->first->WriteAllExceptSender(this, false, 0, "JOIN %s", i->first->name);\r std::string n = this->ServerInstance->Modes->ModeString(this, i->first);\r if (n.length() > 0)\r i->first->WriteAllExceptSender(this, true, 0, "MODE %s +%s", i->first->name, n.c_str());\r }\r }\r\r return true;\r}\r\rvoid userrec::SendAll(const char* command, char* text, ...)\r{\r char textbuffer[MAXBUF];\r char formatbuffer[MAXBUF];\r va_list argsPtr;\r\r va_start(argsPtr, text);\r vsnprintf(textbuffer, MAXBUF, text, argsPtr);\r va_end(argsPtr);\r\r snprintf(formatbuffer,MAXBUF,":%s %s $* :%s", this->GetFullHost(), command, textbuffer);\r std::string fmt = formatbuffer;\r\r for (std::vector<userrec*>::const_iterator i = ServerInstance->local_users.begin(); i != ServerInstance->local_users.end(); i++)\r {\r (*i)->Write(fmt);\r }\r}\r\r\rstd::string userrec::ChannelList(userrec* source)\r{\r try\r {\r std::string list;\r for (UCListIter i = this->chans.begin(); i != this->chans.end(); i++)\r {\r /* If the target is the same as the sender, let them see all their channels.\r * If the channel is NOT private/secret OR the user shares a common channel\r * If the user is an oper, and the <options:operspywhois> option is set.\r */\r if ((source == this) || (IS_OPER(source) && ServerInstance->Config->OperSpyWhois) || (((!i->first->modes[CM_PRIVATE]) && (!i->first->modes[CM_SECRET])) || (i->first->HasUser(source))))\r {\r list.append(i->first->GetPrefixChar(this)).append(i->first->name).append(" ");\r }\r }\r return list;\r }\r catch (...)\r {\r ServerInstance->Log(DEBUG,"Exception in userrec::ChannelList()");\r return "";\r }\r}\r\rvoid userrec::SplitChanList(userrec* dest, const std::string &cl)\r{\r std::string line;\r std::ostringstream prefix;\r std::string::size_type start, pos, length;\r\r try\r {\r prefix << this->nick << " " << dest->nick << " :";\r line = prefix.str();\r int namelen = strlen(ServerInstance->Config->ServerName) + 6;\r\r for (start = 0; (pos = cl.find(' ', start)) != std::string::npos; start = pos+1)\r {\r length = (pos == std::string::npos) ? cl.length() : pos;\r\r if (line.length() + namelen + length - start > 510)\r {\r ServerInstance->SendWhoisLine(this, dest, 319, "%s", line.c_str());\r line = prefix.str();\r }\r\r if(pos == std::string::npos)\r {\r line.append(cl.substr(start, length - start));\r break;\r }\r else\r {\r line.append(cl.substr(start, length - start + 1));\r }\r }\r\r if (line.length())\r {\r ServerInstance->SendWhoisLine(this, dest, 319, "%s", line.c_str());\r }\r }\r\r catch (...)\r {\r ServerInstance->Log(DEBUG,"Exception in userrec::SplitChanList()");\r }\r}\r\r\r/* looks up a users password for their connection class (<ALLOW>/<DENY> tags)\r * NOTE: If the <ALLOW> or <DENY> tag specifies an ip, and this user resolves,\r * then their ip will be taken as 'priority' anyway, so for example,\r * <connect allow="127.0.0.1"> will match joe!bloggs@localhost\r */\rConnectClass* userrec::GetClass()\r{\r for (ClassVector::iterator i = ServerInstance->Config->Classes.begin(); i != ServerInstance->Config->Classes.end(); i++)\r {\r if (((match(this->GetIPString(),i->GetHost().c_str(),true)) || (match(this->host,i->GetHost().c_str()))))\r {\r if (i->GetPort())\r {\r if (this->GetPort() == i->GetPort())\r return &(*i);\r else\r continue;\r }\r else\r return &(*i);\r }\r }\r return NULL;\r}\r\rvoid userrec::PurgeEmptyChannels()\r{\r std::vector<chanrec*> to_delete;\r\r // firstly decrement the count on each channel\r for (UCListIter f = this->chans.begin(); f != this->chans.end(); f++)\r {\r f->first->RemoveAllPrefixes(this);\r if (f->first->DelUser(this) == 0)\r {\r /* No users left in here, mark it for deletion */\r try\r {\r to_delete.push_back(f->first);\r }\r catch (...)\r {\r ServerInstance->Log(DEBUG,"Exception in userrec::PurgeEmptyChannels to_delete.push_back()");\r }\r }\r }\r\r for (std::vector<chanrec*>::iterator n = to_delete.begin(); n != to_delete.end(); n++)\r {\r chanrec* thischan = *n;\r chan_hash::iterator i2 = ServerInstance->chanlist->find(thischan->name);\r if (i2 != ServerInstance->chanlist->end())\r {\r FOREACH_MOD(I_OnChannelDelete,OnChannelDelete(i2->second));\r DELETE(i2->second);\r ServerInstance->chanlist->erase(i2);\r this->chans.erase(*n);\r }\r }\r\r this->UnOper();\r}\r\rvoid userrec::ShowMOTD()\r{\r if (!ServerInstance->Config->MOTD.size())\r {\r this->WriteServ("422 %s :Message of the day file is missing.",this->nick);\r return;\r }\r this->WriteServ("375 %s :%s message of the day", this->nick, ServerInstance->Config->ServerName);\r\r for (file_cache::iterator i = ServerInstance->Config->MOTD.begin(); i != ServerInstance->Config->MOTD.end(); i++)\r this->WriteServ("372 %s :- %s",this->nick,i->c_str());\r\r this->WriteServ("376 %s :End of message of the day.", this->nick);\r}\r\rvoid userrec::ShowRULES()\r{\r if (!ServerInstance->Config->RULES.size())\r {\r this->WriteServ("NOTICE %s :Rules file is missing.",this->nick);\r return;\r }\r this->WriteServ("NOTICE %s :%s rules",this->nick,ServerInstance->Config->ServerName);\r\r for (file_cache::iterator i = ServerInstance->Config->RULES.begin(); i != ServerInstance->Config->RULES.end(); i++)\r this->WriteServ("NOTICE %s :%s",this->nick,i->c_str());\r\r this->WriteServ("NOTICE %s :End of %s rules.",this->nick,ServerInstance->Config->ServerName);\r}\r\rvoid userrec::HandleEvent(EventType et, int errornum)\r{\r /* WARNING: May delete this user! */\r int thisfd = this->GetFd();\r\r try\r {\r switch (et)\r {\r case EVENT_READ:\r ServerInstance->ProcessUser(this);\r break;\r case EVENT_WRITE:\r this->FlushWriteBuf();\r break;\r case EVENT_ERROR:\r /** This should be safe, but dont DARE do anything after it -- Brain */\r this->SetWriteError(errornum ? strerror(errornum) : "EOF from client");\r break;\r }\r }\r catch (...)\r {\r ServerInstance->Log(DEBUG,"Exception in userrec::HandleEvent intercepted");\r }\r\r /* If the user has raised an error whilst being processed, quit them now we're safe to */\r if ((ServerInstance->SE->GetRef(thisfd) == this))\r {\r if (!WriteError.empty())\r {\r userrec::QuitUser(ServerInstance, this, GetWriteError());\r }\r }\r}\r\rvoid userrec::SetOperQuit(const std::string &oquit)\r{\r if (operquit)\r return;\r\r operquit = strdup(oquit.c_str());\r}\r\rconst char* userrec::GetOperQuit()\r{\r return operquit ? operquit : "";\r}\r\rVisData::VisData()\r{\r}\r\rVisData::~VisData()\r{\r}\r\rbool VisData::VisibleTo(userrec* user)\r{\r return true;\r}\r\r
\ No newline at end of file
+/* +------------------------------------+
+ * | Inspire Internet Relay Chat Daemon |
+ * +------------------------------------+
+ *
+ * InspIRCd: (C) 2002-2008 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+/* $Core: libIRCDusers */
+
+#include "inspircd.h"
+#include <stdarg.h>
+#include "socketengine.h"
+#include "wildcard.h"
+#include "xline.h"
+#include "bancache.h"
+#include "commands/cmd_whowas.h"
+
+static unsigned long already_sent[MAX_DESCRIPTORS] = {0};
+
+/* XXX: Used for speeding up WriteCommon operations */
+unsigned long uniq_id = 0;
+
+std::string User::ProcessNoticeMasks(const char *sm)
+{
+ bool adding = true, oldadding = false;
+ const char *c = sm;
+ std::string output;
+
+ while (c && *c)
+ {
+ switch (*c)
+ {
+ case '+':
+ adding = true;
+ break;
+ case '-':
+ adding = false;
+ break;
+ case '*':
+ for (unsigned char d = 'A'; d <= 'z'; d++)
+ {
+ if (ServerInstance->SNO->IsEnabled(d))
+ {
+ if ((!IsNoticeMaskSet(d) && adding) || (IsNoticeMaskSet(d) && !adding))
+ {
+ if ((oldadding != adding) || (!output.length()))
+ output += (adding ? '+' : '-');
+
+ this->SetNoticeMask(d, adding);
+
+ output += d;
+ }
+ }
+ oldadding = adding;
+ }
+ break;
+ default:
+ if ((*c >= 'A') && (*c <= 'z') && (ServerInstance->SNO->IsEnabled(*c)))
+ {
+ if ((!IsNoticeMaskSet(*c) && adding) || (IsNoticeMaskSet(*c) && !adding))
+ {
+ if ((oldadding != adding) || (!output.length()))
+ output += (adding ? '+' : '-');
+
+ this->SetNoticeMask(*c, adding);
+
+ output += *c;
+ }
+ }
+ oldadding = adding;
+ break;
+ }
+
+ *c++;
+ }
+
+ return output;
+}
+
+void User::StartDNSLookup()
+{
+ try
+ {
+ bool cached;
+ const char* ip = this->GetIPString();
+
+ /* Special case for 4in6 (Have i mentioned i HATE 4in6?) */
+ if (!strncmp(ip, "0::ffff:", 8))
+ res_reverse = new UserResolver(this->ServerInstance, this, ip + 8, DNS_QUERY_PTR4, cached);
+ else
+ res_reverse = new UserResolver(this->ServerInstance, this, ip, this->GetProtocolFamily() == AF_INET ? DNS_QUERY_PTR4 : DNS_QUERY_PTR6, cached);
+
+ this->ServerInstance->AddResolver(res_reverse, cached);
+ }
+ catch (CoreException& e)
+ {
+ ServerInstance->Log(DEBUG,"Error in resolver: %s",e.GetReason());
+ }
+}
+
+bool User::IsNoticeMaskSet(unsigned char sm)
+{
+ return (snomasks[sm-65]);
+}
+
+void User::SetNoticeMask(unsigned char sm, bool value)
+{
+ snomasks[sm-65] = value;
+}
+
+const char* User::FormatNoticeMasks()
+{
+ static char data[MAXBUF];
+ int offset = 0;
+
+ for (int n = 0; n < 64; n++)
+ {
+ if (snomasks[n])
+ data[offset++] = n+65;
+ }
+
+ data[offset] = 0;
+ return data;
+}
+
+
+
+bool User::IsModeSet(unsigned char m)
+{
+ return (modes[m-65]);
+}
+
+void User::SetMode(unsigned char m, bool value)
+{
+ modes[m-65] = value;
+}
+
+const char* User::FormatModes()
+{
+ static char data[MAXBUF];
+ int offset = 0;
+ for (int n = 0; n < 64; n++)
+ {
+ if (modes[n])
+ data[offset++] = n+65;
+ }
+ data[offset] = 0;
+ return data;
+}
+
+void User::DecrementModes()
+{
+ ServerInstance->Log(DEBUG,"DecrementModes()");
+ for (unsigned char n = 'A'; n <= 'z'; n++)
+ {
+ if (modes[n-65])
+ {
+ 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);
+ }
+ }
+ }
+}
+
+User::User(InspIRCd* Instance, const std::string &uid) : ServerInstance(Instance)
+{
+ *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);
+ Penalty = 0;
+ lines_in = lastping = signon = idle_lastmsg = nping = registered = 0;
+ ChannelCount = timeout = bytes_in = bytes_out = cmds_in = cmds_out = 0;
+ OverPenalty = ExemptFromPenalty = quitting = exempt = haspassed = dns_done = false;
+ fd = -1;
+ recvq.clear();
+ sendq.clear();
+ WriteError.clear();
+ res_forward = res_reverse = NULL;
+ Visibility = NULL;
+ ip = NULL;
+ MyClass = NULL;
+ AllowedOperCommands = NULL;
+ chans.clear();
+ invites.clear();
+ memset(modes,0,sizeof(modes));
+ memset(snomasks,0,sizeof(snomasks));
+ /* Invalidate cache */
+ 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 User constructor");
+}
+
+User::~User()
+{
+ /* NULL for remote users :) */
+ if (this->MyClass)
+ {
+ this->MyClass->RefCount--;
+ ServerInstance->Log(DEBUG, "User destructor -- connect refcount now: %u", this->MyClass->RefCount);
+ }
+ if (this->AllowedOperCommands)
+ {
+ delete AllowedOperCommands;
+ AllowedOperCommands = NULL;
+ }
+
+ this->InvalidateCache();
+ this->DecrementModes();
+
+ if (ip)
+ {
+ ServerInstance->Users->RemoveCloneCounts(this);
+
+ if (this->GetProtocolFamily() == AF_INET)
+ {
+ delete (sockaddr_in*)ip;
+ }
+#ifdef SUPPORT_IP6LINKS
+ else
+ {
+ delete (sockaddr_in6*)ip;
+ }
+#endif
+ }
+
+ ServerInstance->uuidlist->erase(uuid);
+}
+
+char* User::MakeHost()
+{
+ if (this->cached_makehost)
+ return this->cached_makehost;
+
+ char nhost[MAXBUF];
+ /* This is much faster than snprintf */
+ char* t = nhost;
+ for(char* n = ident; *n; n++)
+ *t++ = *n;
+ *t++ = '@';
+ for(char* n = host; *n; n++)
+ *t++ = *n;
+ *t = 0;
+
+ this->cached_makehost = strdup(nhost);
+
+ return this->cached_makehost;
+}
+
+char* User::MakeHostIP()
+{
+ if (this->cached_hostip)
+ return this->cached_hostip;
+
+ char ihost[MAXBUF];
+ /* This is much faster than snprintf */
+ char* t = ihost;
+ for(char* n = ident; *n; n++)
+ *t++ = *n;
+ *t++ = '@';
+ for(const char* n = this->GetIPString(); *n; n++)
+ *t++ = *n;
+ *t = 0;
+
+ this->cached_hostip = strdup(ihost);
+
+ return this->cached_hostip;
+}
+
+void User::CloseSocket()
+{
+ ServerInstance->SE->Shutdown(this, 2);
+ ServerInstance->SE->Close(this);
+}
+
+char* User::GetFullHost()
+{
+ if (this->cached_fullhost)
+ return this->cached_fullhost;
+
+ char result[MAXBUF];
+ char* t = result;
+ for(char* n = nick; *n; n++)
+ *t++ = *n;
+ *t++ = '!';
+ for(char* n = ident; *n; n++)
+ *t++ = *n;
+ *t++ = '@';
+ for(char* n = dhost; *n; n++)
+ *t++ = *n;
+ *t = 0;
+
+ this->cached_fullhost = strdup(result);
+
+ return this->cached_fullhost;
+}
+
+char* User::MakeWildHost()
+{
+ static char nresult[MAXBUF];
+ char* t = nresult;
+ *t++ = '*'; *t++ = '!';
+ *t++ = '*'; *t++ = '@';
+ for(char* n = dhost; *n; n++)
+ *t++ = *n;
+ *t = 0;
+ return nresult;
+}
+
+int User::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;
+}
+
+
+char* User::GetFullRealHost()
+{
+ if (this->cached_fullrealhost)
+ return this->cached_fullrealhost;
+
+ char fresult[MAXBUF];
+ char* t = fresult;
+ for(char* n = nick; *n; n++)
+ *t++ = *n;
+ *t++ = '!';
+ for(char* n = ident; *n; n++)
+ *t++ = *n;
+ *t++ = '@';
+ for(char* n = host; *n; n++)
+ *t++ = *n;
+ *t = 0;
+
+ this->cached_fullrealhost = strdup(fresult);
+
+ return this->cached_fullrealhost;
+}
+
+bool User::IsInvited(const irc::string &channel)
+{
+ for (InvitedList::iterator i = invites.begin(); i != invites.end(); i++)
+ {
+ if (channel == *i)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+InvitedList* User::GetInviteList()
+{
+ return &invites;
+}
+
+void User::InviteTo(const irc::string &channel)
+{
+ invites.push_back(channel);
+}
+
+void User::RemoveInvite(const irc::string &channel)
+{
+ for (InvitedList::iterator i = invites.begin(); i != invites.end(); i++)
+ {
+ if (channel == *i)
+ {
+ invites.erase(i);
+ return;
+ }
+ }
+}
+
+bool User::HasPermission(const std::string &command)
+{
+ /*
+ * users on remote servers can completely bypass all permissions based checks.
+ * This prevents desyncs when one server has different type/class tags to another.
+ * That having been said, this does open things up to the possibility of source changes
+ * allowing remote kills, etc - but if they have access to the src, they most likely have
+ * access to the conf - so it's an end to a means either way.
+ */
+ if (!IS_LOCAL(this))
+ return true;
+
+ // are they even an oper at all?
+ if (!IS_OPER(this))
+ {
+ return false;
+ }
+
+ if (!AllowedOperCommands)
+ return false;
+
+ if (AllowedOperCommands->find(command) != AllowedOperCommands->end())
+ return true;
+ else if (AllowedOperCommands->find("*") != AllowedOperCommands->end())
+ return true;
+
+ return false;
+}
+
+/** NOTE: We cannot pass a const reference to this method.
+ * The string is changed by the workings of the method,
+ * so that if we pass const ref, we end up copying it to
+ * something we can change anyway. Makes sense to just let
+ * the compiler do that copy for us.
+ */
+bool User::AddBuffer(std::string a)
+{
+ try
+ {
+ std::string::size_type i = a.rfind('\r');
+
+ while (i != std::string::npos)
+ {
+ a.erase(i, 1);
+ i = a.rfind('\r');
+ }
+
+ if (a.length())
+ recvq.append(a);
+
+ if (this->MyClass && (recvq.length() > this->MyClass->GetRecvqMax()))
+ {
+ this->SetWriteError("RecvQ exceeded");
+ ServerInstance->SNO->WriteToSnoMask('A', "User %s RecvQ of %d exceeds connect class maximum of %d",this->nick,recvq.length(),this->MyClass->GetRecvqMax());
+ return false;
+ }
+
+ return true;
+ }
+
+ catch (...)
+ {
+ ServerInstance->Log(DEBUG,"Exception in User::AddBuffer()");
+ return false;
+ }
+}
+
+bool User::BufferIsReady()
+{
+ return (recvq.find('\n') != std::string::npos);
+}
+
+void User::ClearBuffer()
+{
+ recvq.clear();
+}
+
+std::string User::GetBuffer()
+{
+ try
+ {
+ if (recvq.empty())
+ return "";
+
+ /* Strip any leading \r or \n off the string.
+ * Usually there are only one or two of these,
+ * so its is computationally cheap to do.
+ */
+ 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++)
+ {
+ /* Find the first complete line, return it as the
+ * result, and leave the recvq as whats left
+ */
+ if (*x == '\n')
+ {
+ std::string ret = std::string(recvq.begin(), x);
+ recvq.erase(recvq.begin(), x + 1);
+ return ret;
+ }
+ }
+ return "";
+ }
+
+ catch (...)
+ {
+ ServerInstance->Log(DEBUG,"Exception in User::GetBuffer()");
+ return "";
+ }
+}
+
+void User::AddWriteBuf(const std::string &data)
+{
+ if (*this->GetWriteError())
+ return;
+
+ if (this->MyClass && (sendq.length() + data.length() > this->MyClass->GetSendqMax()))
+ {
+ /*
+ * Fix by brain - Set the error text BEFORE calling, because
+ * if we dont it'll recursively call here over and over again trying
+ * to repeatedly add the text to the sendq!
+ */
+ this->SetWriteError("SendQ exceeded");
+ ServerInstance->SNO->WriteToSnoMask('A', "User %s SendQ of %d exceeds connect class maximum of %d",this->nick,sendq.length() + data.length(),this->MyClass->GetSendqMax());
+ return;
+ }
+
+ try
+ {
+ if (data.length() > MAXBUF - 2) /* MAXBUF has a value of 514, to account for line terminators */
+ sendq.append(data.substr(0,MAXBUF - 4)).append("\r\n"); /* MAXBUF-4 = 510 */
+ else
+ sendq.append(data);
+ }
+ catch (...)
+ {
+ this->SetWriteError("SendQ exceeded");
+ ServerInstance->SNO->WriteToSnoMask('A', "User %s SendQ got an exception",this->nick);
+ }
+}
+
+// send AS MUCH OF THE USERS SENDQ as we are able to (might not be all of it)
+void User::FlushWriteBuf()
+{
+ try
+ {
+ if ((this->fd == FD_MAGIC_NUMBER) || (*this->GetWriteError()))
+ {
+ sendq.clear();
+ }
+ if ((sendq.length()) && (this->fd != FD_MAGIC_NUMBER))
+ {
+ int old_sendq_length = sendq.length();
+ int n_sent = ServerInstance->SE->Send(this, this->sendq.data(), this->sendq.length(), 0);
+
+ if (n_sent == -1)
+ {
+ if (errno == EAGAIN)
+ {
+ /* The socket buffer is full. This isnt fatal,
+ * try again later.
+ */
+ this->ServerInstance->SE->WantWrite(this);
+ }
+ else
+ {
+ /* Fatal error, set write error and bail
+ */
+ this->SetWriteError(errno ? strerror(errno) : "EOF from client");
+ return;
+ }
+ }
+ else
+ {
+ /* advance the queue */
+ if (n_sent)
+ this->sendq = this->sendq.substr(n_sent);
+ /* update the user's stats counters */
+ this->bytes_out += n_sent;
+ this->cmds_out++;
+ if (n_sent != old_sendq_length)
+ this->ServerInstance->SE->WantWrite(this);
+ }
+ }
+ }
+
+ catch (...)
+ {
+ ServerInstance->Log(DEBUG,"Exception in User::FlushWriteBuf()");
+ }
+
+ if (this->sendq.empty())
+ {
+ FOREACH_MOD(I_OnBufferFlushed,OnBufferFlushed(this));
+ }
+}
+
+void User::SetWriteError(const std::string &error)
+{
+ try
+ {
+ // don't try to set the error twice, its already set take the first string.
+ if (this->WriteError.empty())
+ this->WriteError = error;
+ }
+
+ catch (...)
+ {
+ ServerInstance->Log(DEBUG,"Exception in User::SetWriteError()");
+ }
+}
+
+const char* User::GetWriteError()
+{
+ return this->WriteError.c_str();
+}
+
+void User::Oper(const std::string &opertype, const std::string &opername)
+{
+ char* mycmd;
+ char* savept;
+ char* savept2;
+
+ try
+ {
+ this->modes[UM_OPERATOR] = 1;
+ this->WriteServ("MODE %s :+o", this->nick);
+ FOREACH_MOD(I_OnOper, OnOper(this, opertype));
+ ServerInstance->Log(DEFAULT,"OPER: %s!%s@%s opered as type: %s", this->nick, this->ident, this->host, opertype.c_str());
+ strlcpy(this->oper, opertype.c_str(), NICKMAX - 1);
+ ServerInstance->all_opers.push_back(this);
+
+ opertype_t::iterator iter_opertype = ServerInstance->Config->opertypes.find(this->oper);
+ if (iter_opertype != ServerInstance->Config->opertypes.end())
+ {
+
+ if (AllowedOperCommands)
+ AllowedOperCommands->clear();
+ else
+ AllowedOperCommands = new std::map<std::string, bool>;
+
+ char* Classes = strdup(iter_opertype->second);
+ char* myclass = strtok_r(Classes," ",&savept);
+ while (myclass)
+ {
+ operclass_t::iterator iter_operclass = ServerInstance->Config->operclass.find(myclass);
+ if (iter_operclass != ServerInstance->Config->operclass.end())
+ {
+ char* CommandList = strdup(iter_operclass->second);
+ mycmd = strtok_r(CommandList," ",&savept2);
+ while (mycmd)
+ {
+ this->AllowedOperCommands->insert(std::make_pair(mycmd, true));
+ mycmd = strtok_r(NULL," ",&savept2);
+ }
+ free(CommandList);
+ }
+ myclass = strtok_r(NULL," ",&savept);
+ }
+ free(Classes);
+ }
+
+ FOREACH_MOD(I_OnPostOper,OnPostOper(this, opertype, opername));
+ }
+
+ catch (...)
+ {
+ ServerInstance->Log(DEBUG,"Exception in User::Oper()");
+ }
+}
+
+void User::UnOper()
+{
+ try
+ {
+ if (IS_OPER(this))
+ {
+ // unset their oper type (what IS_OPER checks), and remove +o
+ *this->oper = 0;
+ this->modes[UM_OPERATOR] = 0;
+
+ // remove the user from the oper list. Will remove multiple entries as a safeguard against bug #404
+ ServerInstance->all_opers.remove(this);
+
+ if (AllowedOperCommands)
+ {
+ delete AllowedOperCommands;
+ AllowedOperCommands = NULL;
+ }
+ }
+ }
+
+ catch (...)
+ {
+ ServerInstance->Log(DEBUG,"Exception in User::UnOper()");
+ }
+}
+
+void User::QuitUser(InspIRCd* Instance, User *user, const std::string &quitreason, const char* operreason)
+{
+ Instance->Log(DEBUG,"QuitUser: %s '%s'", user->nick, quitreason.c_str());
+ user->Write("ERROR :Closing link (%s@%s) [%s]", user->ident, user->host, *operreason ? operreason : quitreason.c_str());
+ user->quietquit = false;
+ user->quitmsg = quitreason;
+ user->operquitmsg = operreason;
+ Instance->GlobalCulls.AddItem(user);
+}
+
+/* adds or updates an entry in the whowas list */
+void User::AddToWhoWas()
+{
+ Command* whowas_command = ServerInstance->Parser->GetHandler("WHOWAS");
+ if (whowas_command)
+ {
+ std::deque<classbase*> params;
+ params.push_back(this);
+ whowas_command->HandleInternal(WHOWAS_ADD, params);
+ }
+}
+
+/*
+ * Check class restrictions
+ */
+void User::CheckClass()
+{
+ ConnectClass* a = this->MyClass;
+
+ if ((!a) || (a->GetType() == CC_DENY))
+ {
+ User::QuitUser(ServerInstance, this, "Unauthorised connection");
+ return;
+ }
+ else if ((a->GetMaxLocal()) && (ServerInstance->Users->LocalCloneCount(this) > a->GetMaxLocal()))
+ {
+ User::QuitUser(ServerInstance, this, "No more connections allowed from your host via this connect class (local)");
+ ServerInstance->SNO->WriteToSnoMask('A', "WARNING: maximum LOCAL connections (%ld) exceeded for IP %s", a->GetMaxLocal(), this->GetIPString());
+ return;
+ }
+ else if ((a->GetMaxGlobal()) && (ServerInstance->Users->GlobalCloneCount(this) > a->GetMaxGlobal()))
+ {
+ User::QuitUser(ServerInstance, this, "No more connections allowed from your host via this connect class (global)");
+ ServerInstance->SNO->WriteToSnoMask('A', "WARNING: maximum GLOBAL connections (%ld) exceeded for IP %s", a->GetMaxGlobal(), this->GetIPString());
+ return;
+ }
+
+ this->nping = ServerInstance->Time() + a->GetPingTime() + ServerInstance->Config->dns_timeout;
+ this->timeout = ServerInstance->Time() + a->GetRegTimeout();
+ this->MaxChans = a->GetMaxChans();
+}
+
+void User::FullConnect()
+{
+ ServerInstance->stats->statsConnects++;
+ this->idle_lastmsg = ServerInstance->Time();
+
+ /*
+ * You may be thinking "wtf, we checked this in User::AddClient!" - and yes, we did, BUT.
+ * At the time AddClient is called, we don't have a resolved host, by here we probably do - which
+ * may put the user into a totally seperate class with different restrictions! so we *must* check again.
+ * Don't remove this! -- w00t
+ */
+ this->SetClass();
+
+ /* 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->MyClass && !this->MyClass->GetPass().empty() && !this->haspassed)
+ {
+ User::QuitUser(ServerInstance, this, "Invalid password");
+ return;
+ }
+
+ if (!this->exempt)
+ {
+ GLine *r = (GLine *)ServerInstance->XLines->MatchesLine("G", this);
+
+ if (r)
+ {
+ r->Apply(this);
+ return;
+ }
+
+ KLine *n = (KLine *)ServerInstance->XLines->MatchesLine("K", this);
+
+ if (n)
+ {
+ n->Apply(this);
+ return;
+ }
+ }
+
+ this->WriteServ("NOTICE Auth :Welcome to \002%s\002!",ServerInstance->Config->Network);
+ this->WriteServ("001 %s :Welcome to the %s IRC Network %s!%s@%s",this->nick, ServerInstance->Config->Network, this->nick, this->ident, this->host);
+ this->WriteServ("002 %s :Your host is %s, running version %s",this->nick,ServerInstance->Config->ServerName,VERSION);
+ this->WriteServ("003 %s :This server was created %s %s", this->nick, __TIME__, __DATE__);
+ this->WriteServ("004 %s %s %s %s %s %s", this->nick, ServerInstance->Config->ServerName, VERSION, ServerInstance->Modes->UserModeList().c_str(), ServerInstance->Modes->ChannelModeList().c_str(), ServerInstance->Modes->ParaModeList().c_str());
+
+ ServerInstance->Config->Send005(this);
+
+ this->WriteServ("042 %s %s :your unique ID", this->nick, this->uuid);
+
+
+ this->ShowMOTD();
+
+ /* Now registered */
+ if (ServerInstance->unregistered_count)
+ ServerInstance->unregistered_count--;
+
+ /* Trigger LUSERS output, give modules a chance too */
+ int MOD_RESULT = 0;
+ FOREACH_RESULT(I_OnPreCommand, OnPreCommand("LUSERS", NULL, 0, this, true, "LUSERS"));
+ if (!MOD_RESULT)
+ ServerInstance->CallCommandHandler("LUSERS", NULL, 0, this);
+
+ /*
+ * We don't set REG_ALL until triggering OnUserConnect, so some module events don't spew out stuff
+ * for a user that doesn't exist yet.
+ */
+ FOREACH_MOD(I_OnUserConnect,OnUserConnect(this));
+
+ this->registered = REG_ALL;
+
+ FOREACH_MOD(I_OnPostConnect,OnPostConnect(this));
+
+ ServerInstance->SNO->WriteToSnoMask('c',"Client connecting on port %d: %s!%s@%s [%s] [%s]", this->GetPort(), this->nick, this->ident, this->host, this->GetIPString(), this->fullname);
+
+ ServerInstance->Log(DEBUG, "BanCache: Adding NEGATIVE hit for %s", this->GetIPString());
+ ServerInstance->BanCache->AddHit(this->GetIPString(), "", "");
+}
+
+/** User::UpdateNick()
+ * re-allocates a nick in the user_hash after they change nicknames,
+ * returns a pointer to the new user as it may have moved
+ */
+User* User::UpdateNickHash(const char* New)
+{
+ try
+ {
+ //user_hash::iterator newnick;
+ user_hash::iterator oldnick = ServerInstance->clientlist->find(this->nick);
+
+ if (!strcasecmp(this->nick,New))
+ return oldnick->second;
+
+ if (oldnick == ServerInstance->clientlist->end())
+ return NULL; /* doesnt exist */
+
+ User* olduser = oldnick->second;
+ (*(ServerInstance->clientlist))[New] = olduser;
+ ServerInstance->clientlist->erase(oldnick);
+ return olduser;
+ }
+
+ catch (...)
+ {
+ ServerInstance->Log(DEBUG,"Exception in User::UpdateNickHash()");
+ return NULL;
+ }
+}
+
+void User::InvalidateCache()
+{
+ /* Invalidate cache */
+ if (cached_fullhost)
+ free(cached_fullhost);
+ if (cached_hostip)
+ free(cached_hostip);
+ if (cached_makehost)
+ free(cached_makehost);
+ if (cached_fullrealhost)
+ free(cached_fullrealhost);
+ cached_fullhost = cached_hostip = cached_makehost = cached_fullrealhost = NULL;
+}
+
+bool User::ForceNickChange(const char* newnick)
+{
+ try
+ {
+ int MOD_RESULT = 0;
+
+ this->InvalidateCache();
+
+ FOREACH_RESULT(I_OnUserPreNick,OnUserPreNick(this, newnick));
+
+ if (MOD_RESULT)
+ {
+ ServerInstance->stats->statsCollisions++;
+ return false;
+ }
+
+ if (ServerInstance->XLines->MatchesLine("Q",newnick))
+ {
+ ServerInstance->stats->statsCollisions++;
+ return false;
+ }
+
+ if (this->registered == REG_ALL)
+ {
+ std::deque<classbase*> dummy;
+ Command* 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;
+ }
+
+ catch (...)
+ {
+ ServerInstance->Log(DEBUG,"Exception in User::ForceNickChange()");
+ return false;
+ }
+}
+
+void User::SetSockAddr(int protocol_family, const char* ip, int port)
+{
+ switch (protocol_family)
+ {
+#ifdef SUPPORT_IP6LINKS
+ case AF_INET6:
+ {
+ sockaddr_in6* sin = new sockaddr_in6;
+ sin->sin6_family = AF_INET6;
+ sin->sin6_port = port;
+ inet_pton(AF_INET6, ip, &sin->sin6_addr);
+ this->ip = (sockaddr*)sin;
+ }
+ break;
+#endif
+ case AF_INET:
+ {
+ sockaddr_in* sin = new sockaddr_in;
+ sin->sin_family = AF_INET;
+ sin->sin_port = port;
+ inet_pton(AF_INET, ip, &sin->sin_addr);
+ this->ip = (sockaddr*)sin;
+ }
+ break;
+ default:
+ ServerInstance->Log(DEBUG,"Uh oh, I dont know protocol %d to be set on '%s'!", protocol_family, this->nick);
+ break;
+ }
+}
+
+int User::GetPort()
+{
+ if (this->ip == NULL)
+ return 0;
+
+ switch (this->GetProtocolFamily())
+ {
+#ifdef SUPPORT_IP6LINKS
+ case AF_INET6:
+ {
+ sockaddr_in6* sin = (sockaddr_in6*)this->ip;
+ return sin->sin6_port;
+ }
+ break;
+#endif
+ case AF_INET:
+ {
+ sockaddr_in* sin = (sockaddr_in*)this->ip;
+ return sin->sin_port;
+ }
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+int User::GetProtocolFamily()
+{
+ if (this->ip == NULL)
+ return 0;
+
+ sockaddr_in* sin = (sockaddr_in*)this->ip;
+ return sin->sin_family;
+}
+
+/*
+ * XXX the duplication here is horrid..
+ * do we really need two methods doing essentially the same thing?
+ */
+const char* User::GetIPString()
+{
+ static char buf[1024];
+
+ if (this->ip == NULL)
+ return "";
+
+ switch (this->GetProtocolFamily())
+ {
+#ifdef SUPPORT_IP6LINKS
+ case AF_INET6:
+ {
+ static char temp[1024];
+
+ sockaddr_in6* sin = (sockaddr_in6*)this->ip;
+ inet_ntop(sin->sin6_family, &sin->sin6_addr, buf, sizeof(buf));
+ /* IP addresses starting with a : on irc are a Bad Thing (tm) */
+ if (*buf == ':')
+ {
+ strlcpy(&temp[1], buf, sizeof(temp) - 1);
+ *temp = '0';
+ return temp;
+ }
+ return buf;
+ }
+ break;
+#endif
+ case AF_INET:
+ {
+ sockaddr_in* sin = (sockaddr_in*)this->ip;
+ inet_ntop(sin->sin_family, &sin->sin_addr, buf, sizeof(buf));
+ return buf;
+ }
+ break;
+ default:
+ break;
+ }
+ return "";
+}
+
+/** NOTE: We cannot pass a const reference to this method.
+ * The string is changed by the workings of the method,
+ * so that if we pass const ref, we end up copying it to
+ * something we can change anyway. Makes sense to just let
+ * the compiler do that copy for us.
+ */
+void User::Write(std::string text)
+{
+ if (!ServerInstance->SE->BoundsCheckFd(this))
+ return;
+
+ try
+ {
+ /* 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).
+ */
+ text.append("\r\n");
+ }
+ catch (...)
+ {
+ ServerInstance->Log(DEBUG,"Exception in User::Write() std::string::append");
+ return;
+ }
+
+ if (ServerInstance->Config->GetIOHook(this->GetPort()))
+ {
+ 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)
+ {
+ ServerInstance->Log(DEBUG, "%s threw an exception: %s", modexcept.GetSource(), modexcept.GetReason());
+ }
+ }
+ else
+ {
+ this->AddWriteBuf(text);
+ }
+ ServerInstance->stats->statsSent += text.length();
+ this->ServerInstance->SE->WantWrite(this);
+}
+
+/** Write()
+ */
+void User::Write(const char *text, ...)
+{
+ va_list argsPtr;
+ char textbuffer[MAXBUF];
+
+ va_start(argsPtr, text);
+ vsnprintf(textbuffer, MAXBUF, text, argsPtr);
+ va_end(argsPtr);
+
+ this->Write(std::string(textbuffer));
+}
+
+void User::WriteServ(const std::string& text)
+{
+ char textbuffer[MAXBUF];
+
+ snprintf(textbuffer,MAXBUF,":%s %s",ServerInstance->Config->ServerName,text.c_str());
+ this->Write(std::string(textbuffer));
+}
+
+/** WriteServ()
+ * Same as Write(), except `text' is prefixed with `:server.name '.
+ */
+void User::WriteServ(const char* text, ...)
+{
+ va_list argsPtr;
+ char textbuffer[MAXBUF];
+
+ va_start(argsPtr, text);
+ vsnprintf(textbuffer, MAXBUF, text, argsPtr);
+ va_end(argsPtr);
+
+ this->WriteServ(std::string(textbuffer));
+}
+
+
+void User::WriteFrom(User *user, const std::string &text)
+{
+ char tb[MAXBUF];
+
+ snprintf(tb,MAXBUF,":%s %s",user->GetFullHost(),text.c_str());
+
+ this->Write(std::string(tb));
+}
+
+
+/* write text from an originating user to originating user */
+
+void User::WriteFrom(User *user, const char* text, ...)
+{
+ va_list argsPtr;
+ char textbuffer[MAXBUF];
+
+ va_start(argsPtr, text);
+ vsnprintf(textbuffer, MAXBUF, text, argsPtr);
+ va_end(argsPtr);
+
+ this->WriteFrom(user, std::string(textbuffer));
+}
+
+
+/* write text to an destination user from a source user (e.g. user privmsg) */
+
+void User::WriteTo(User *dest, const char *data, ...)
+{
+ char textbuffer[MAXBUF];
+ va_list argsPtr;
+
+ va_start(argsPtr, data);
+ vsnprintf(textbuffer, MAXBUF, data, argsPtr);
+ va_end(argsPtr);
+
+ this->WriteTo(dest, std::string(textbuffer));
+}
+
+void User::WriteTo(User *dest, const std::string &data)
+{
+ dest->WriteFrom(this, data);
+}
+
+
+void User::WriteCommon(const char* text, ...)
+{
+ char textbuffer[MAXBUF];
+ va_list argsPtr;
+
+ if (this->registered != REG_ALL)
+ return;
+
+ va_start(argsPtr, text);
+ vsnprintf(textbuffer, MAXBUF, text, argsPtr);
+ va_end(argsPtr);
+
+ this->WriteCommon(std::string(textbuffer));
+}
+
+void User::WriteCommon(const std::string &text)
+{
+ try
+ {
+ bool sent_to_at_least_one = false;
+ char tb[MAXBUF];
+
+ if (this->registered != REG_ALL)
+ return;
+
+ uniq_id++;
+
+ /* We dont want to be doing this n times, just once */
+ snprintf(tb,MAXBUF,":%s %s",this->GetFullHost(),text.c_str());
+ std::string out = tb;
+
+ for (UCListIter v = this->chans.begin(); v != this->chans.end(); v++)
+ {
+ CUList* ulist = v->first->GetUsers();
+ for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++)
+ {
+ if ((IS_LOCAL(i->first)) && (already_sent[i->first->fd] != uniq_id))
+ {
+ already_sent[i->first->fd] = uniq_id;
+ i->first->Write(out);
+ sent_to_at_least_one = true;
+ }
+ }
+ }
+
+ /*
+ * if the user was not in any channels, no users will receive the text. Make sure the user
+ * receives their OWN message for WriteCommon
+ */
+ if (!sent_to_at_least_one)
+ {
+ this->Write(std::string(tb));
+ }
+ }
+
+ catch (...)
+ {
+ ServerInstance->Log(DEBUG,"Exception in User::WriteCommon()");
+ }
+}
+
+
+/* write a formatted string to all users who share at least one common
+ * channel, NOT including the source user e.g. for use in QUIT
+ */
+
+void User::WriteCommonExcept(const char* text, ...)
+{
+ char textbuffer[MAXBUF];
+ va_list argsPtr;
+
+ va_start(argsPtr, text);
+ vsnprintf(textbuffer, MAXBUF, text, argsPtr);
+ va_end(argsPtr);
+
+ this->WriteCommonExcept(std::string(textbuffer));
+}
+
+void User::WriteCommonQuit(const std::string &normal_text, const std::string &oper_text)
+{
+ char tb1[MAXBUF];
+ char tb2[MAXBUF];
+
+ if (this->registered != REG_ALL)
+ return;
+
+ uniq_id++;
+ snprintf(tb1,MAXBUF,":%s QUIT :%s",this->GetFullHost(),normal_text.c_str());
+ snprintf(tb2,MAXBUF,":%s QUIT :%s",this->GetFullHost(),oper_text.c_str());
+ std::string out1 = tb1;
+ std::string out2 = tb2;
+
+ for (UCListIter v = this->chans.begin(); v != this->chans.end(); v++)
+ {
+ CUList *ulist = v->first->GetUsers();
+ for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++)
+ {
+ if (this != i->first)
+ {
+ if ((IS_LOCAL(i->first)) && (already_sent[i->first->fd] != uniq_id))
+ {
+ already_sent[i->first->fd] = uniq_id;
+ i->first->Write(IS_OPER(i->first) ? out2 : out1);
+ }
+ }
+ }
+ }
+}
+
+void User::WriteCommonExcept(const std::string &text)
+{
+ char tb1[MAXBUF];
+ std::string out1;
+
+ if (this->registered != REG_ALL)
+ return;
+
+ uniq_id++;
+ snprintf(tb1,MAXBUF,":%s %s",this->GetFullHost(),text.c_str());
+ out1 = tb1;
+
+ for (UCListIter v = this->chans.begin(); v != this->chans.end(); v++)
+ {
+ CUList *ulist = v->first->GetUsers();
+ for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++)
+ {
+ if (this != i->first)
+ {
+ if ((IS_LOCAL(i->first)) && (already_sent[i->first->fd] != uniq_id))
+ {
+ already_sent[i->first->fd] = uniq_id;
+ i->first->Write(out1);
+ }
+ }
+ }
+ }
+
+}
+
+void User::WriteWallOps(const std::string &text)
+{
+ if (!IS_OPER(this) && IS_LOCAL(this))
+ return;
+
+ std::string wallop("WALLOPS :");
+ wallop.append(text);
+
+ for (std::vector<User*>::const_iterator i = ServerInstance->local_users.begin(); i != ServerInstance->local_users.end(); i++)
+ {
+ User* t = *i;
+ if (t->IsModeSet('w'))
+ this->WriteTo(t,wallop);
+ }
+}
+
+void User::WriteWallOps(const char* text, ...)
+{
+ char textbuffer[MAXBUF];
+ va_list argsPtr;
+
+ va_start(argsPtr, text);
+ vsnprintf(textbuffer, MAXBUF, text, argsPtr);
+ va_end(argsPtr);
+
+ this->WriteWallOps(std::string(textbuffer));
+}
+
+/* return 0 or 1 depending if users u and u2 share one or more common channels
+ * (used by QUIT, NICK etc which arent channel specific notices)
+ *
+ * The old algorithm in 1.0 for this was relatively inefficient, iterating over
+ * the first users channels then the second users channels within the outer loop,
+ * therefore it was a maximum of x*y iterations (upon returning 0 and checking
+ * all possible iterations). However this new function instead checks against the
+ * channel's userlist in the inner loop which is a std::map<User*,User*>
+ * and saves us time as we already know what pointer value we are after.
+ * Don't quote me on the maths as i am not a mathematician or computer scientist,
+ * but i believe this algorithm is now x+(log y) maximum iterations instead.
+ */
+bool User::SharesChannelWith(User *other)
+{
+ if ((!other) || (this->registered != REG_ALL) || (other->registered != REG_ALL))
+ return false;
+
+ /* Outer loop */
+ for (UCListIter i = this->chans.begin(); i != this->chans.end(); i++)
+ {
+ /* Eliminate the inner loop (which used to be ~equal in size to the outer loop)
+ * by replacing it with a map::find which *should* be more efficient
+ */
+ if (i->first->HasUser(other))
+ return true;
+ }
+ return false;
+}
+
+bool User::ChangeName(const char* gecos)
+{
+ if (!strcmp(gecos, this->fullname))
+ return true;
+
+ if (IS_LOCAL(this))
+ {
+ int MOD_RESULT = 0;
+ FOREACH_RESULT(I_OnChangeLocalUserGECOS,OnChangeLocalUserGECOS(this,gecos));
+ if (MOD_RESULT)
+ return false;
+ FOREACH_MOD(I_OnChangeName,OnChangeName(this,gecos));
+ }
+ strlcpy(this->fullname,gecos,MAXGECOS+1);
+
+ return true;
+}
+
+bool User::ChangeDisplayedHost(const char* host)
+{
+ if (!strcmp(host, this->dhost))
+ return true;
+
+ if (IS_LOCAL(this))
+ {
+ int MOD_RESULT = 0;
+ FOREACH_RESULT(I_OnChangeLocalUserHost,OnChangeLocalUserHost(this,host));
+ if (MOD_RESULT)
+ return false;
+ FOREACH_MOD(I_OnChangeHost,OnChangeHost(this,host));
+ }
+ if (this->ServerInstance->Config->CycleHosts)
+ this->WriteCommonExcept("QUIT :Changing hosts");
+
+ /* Fix by Om: User::dhost is 65 long, this was truncating some long hosts */
+ strlcpy(this->dhost,host,64);
+
+ this->InvalidateCache();
+
+ if (this->ServerInstance->Config->CycleHosts)
+ {
+ for (UCListIter i = this->chans.begin(); i != this->chans.end(); i++)
+ {
+ i->first->WriteAllExceptSender(this, false, 0, "JOIN %s", i->first->name);
+ std::string n = this->ServerInstance->Modes->ModeString(this, i->first);
+ if (n.length() > 0)
+ i->first->WriteAllExceptSender(this, true, 0, "MODE %s +%s", i->first->name, n.c_str());
+ }
+ }
+
+ if (IS_LOCAL(this))
+ this->WriteServ("396 %s %s :is now your displayed host",this->nick,this->dhost);
+
+ return true;
+}
+
+bool User::ChangeIdent(const char* newident)
+{
+ if (!strcmp(newident, this->ident))
+ return true;
+
+ if (this->ServerInstance->Config->CycleHosts)
+ this->WriteCommonExcept("%s","QUIT :Changing ident");
+
+ strlcpy(this->ident, newident, IDENTMAX+2);
+
+ this->InvalidateCache();
+
+ if (this->ServerInstance->Config->CycleHosts)
+ {
+ for (UCListIter i = this->chans.begin(); i != this->chans.end(); i++)
+ {
+ i->first->WriteAllExceptSender(this, false, 0, "JOIN %s", i->first->name);
+ std::string n = this->ServerInstance->Modes->ModeString(this, i->first);
+ if (n.length() > 0)
+ i->first->WriteAllExceptSender(this, true, 0, "MODE %s +%s", i->first->name, n.c_str());
+ }
+ }
+
+ return true;
+}
+
+void User::SendAll(const char* command, char* text, ...)
+{
+ char textbuffer[MAXBUF];
+ char formatbuffer[MAXBUF];
+ va_list argsPtr;
+
+ va_start(argsPtr, text);
+ vsnprintf(textbuffer, MAXBUF, text, argsPtr);
+ va_end(argsPtr);
+
+ snprintf(formatbuffer,MAXBUF,":%s %s $* :%s", this->GetFullHost(), command, textbuffer);
+ std::string fmt = formatbuffer;
+
+ for (std::vector<User*>::const_iterator i = ServerInstance->local_users.begin(); i != ServerInstance->local_users.end(); i++)
+ {
+ (*i)->Write(fmt);
+ }
+}
+
+
+std::string User::ChannelList(User* source)
+{
+ try
+ {
+ std::string list;
+ for (UCListIter i = this->chans.begin(); i != this->chans.end(); i++)
+ {
+ /* If the target is the same as the sender, let them see all their channels.
+ * If the channel is NOT private/secret OR the user shares a common channel
+ * If the user is an oper, and the <options:operspywhois> option is set.
+ */
+ 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(" ");
+ }
+ }
+ return list;
+ }
+ catch (...)
+ {
+ ServerInstance->Log(DEBUG,"Exception in User::ChannelList()");
+ return "";
+ }
+}
+
+void User::SplitChanList(User* dest, const std::string &cl)
+{
+ std::string line;
+ std::ostringstream prefix;
+ std::string::size_type start, pos, length;
+
+ try
+ {
+ prefix << this->nick << " " << dest->nick << " :";
+ line = prefix.str();
+ int namelen = strlen(ServerInstance->Config->ServerName) + 6;
+
+ for (start = 0; (pos = cl.find(' ', start)) != std::string::npos; start = pos+1)
+ {
+ length = (pos == std::string::npos) ? cl.length() : pos;
+
+ if (line.length() + namelen + length - start > 510)
+ {
+ ServerInstance->SendWhoisLine(this, dest, 319, "%s", line.c_str());
+ line = prefix.str();
+ }
+
+ if(pos == std::string::npos)
+ {
+ line.append(cl.substr(start, length - start));
+ break;
+ }
+ else
+ {
+ line.append(cl.substr(start, length - start + 1));
+ }
+ }
+
+ if (line.length())
+ {
+ ServerInstance->SendWhoisLine(this, dest, 319, "%s", line.c_str());
+ }
+ }
+
+ catch (...)
+ {
+ ServerInstance->Log(DEBUG,"Exception in User::SplitChanList()");
+ }
+}
+
+unsigned int User::GetMaxChans()
+{
+ return this->MaxChans;
+}
+
+
+/*
+ * Sets a user's connection class.
+ * If the class name is provided, it will be used. Otherwise, the class will be guessed using host/ip/ident/etc.
+ * NOTE: If the <ALLOW> or <DENY> tag specifies an ip, and this user resolves,
+ * then their ip will be taken as 'priority' anyway, so for example,
+ * <connect allow="127.0.0.1"> will match joe!bloggs@localhost
+ */
+ConnectClass* User::SetClass(const std::string &explicit_name)
+{
+ ConnectClass *found = NULL;
+
+ if (!IS_LOCAL(this))
+ return NULL;
+
+ if (!explicit_name.empty())
+ {
+ for (ClassVector::iterator i = ServerInstance->Config->Classes.begin(); i != ServerInstance->Config->Classes.end(); i++)
+ {
+ ConnectClass* c = *i;
+
+ if (explicit_name == c->GetName() && !c->GetDisabled())
+ {
+ found = c;
+ }
+ }
+ }
+ else
+ {
+ for (ClassVector::iterator i = ServerInstance->Config->Classes.begin(); i != ServerInstance->Config->Classes.end(); i++)
+ {
+ ConnectClass* c = *i;
+
+ if (((match(this->GetIPString(),c->GetHost().c_str(),true)) || (match(this->host,c->GetHost().c_str()))))
+ {
+ if (c->GetPort())
+ {
+ if (this->GetPort() == c->GetPort() && !c->GetDisabled())
+ {
+ found = c;
+ }
+ else
+ continue;
+ }
+ else
+ {
+ if (!c->GetDisabled())
+ found = c;
+ }
+ }
+ }
+ }
+
+ /* ensure we don't fuck things up refcount wise, only remove them from a class if we find a new one :P */
+ if (found)
+ {
+ /* deny change if change will take class over the limit */
+ if (found->limit && (found->RefCount + 1 >= found->limit))
+ {
+ ServerInstance->Log(DEBUG, "OOPS: Connect class limit (%u) hit, denying", found->limit);
+ return this->MyClass;
+ }
+
+ /* should always be valid, but just in case .. */
+ if (this->MyClass)
+ {
+ if (found == this->MyClass) // no point changing this shit :P
+ return this->MyClass;
+ this->MyClass->RefCount--;
+ ServerInstance->Log(DEBUG, "Untying user from connect class -- refcount: %u", this->MyClass->RefCount);
+ }
+
+ this->MyClass = found;
+ this->MyClass->RefCount++;
+ ServerInstance->Log(DEBUG, "User tied to new class -- connect refcount now: %u", this->MyClass->RefCount);
+ }
+
+ return this->MyClass;
+}
+
+/* looks up a users password for their connection class (<ALLOW>/<DENY> tags)
+ * NOTE: If the <ALLOW> or <DENY> tag specifies an ip, and this user resolves,
+ * then their ip will be taken as 'priority' anyway, so for example,
+ * <connect allow="127.0.0.1"> will match joe!bloggs@localhost
+ */
+ConnectClass* User::GetClass()
+{
+ return this->MyClass;
+}
+
+void User::PurgeEmptyChannels()
+{
+ std::vector<Channel*> to_delete;
+
+ // firstly decrement the count on each channel
+ for (UCListIter f = this->chans.begin(); f != this->chans.end(); f++)
+ {
+ f->first->RemoveAllPrefixes(this);
+ if (f->first->DelUser(this) == 0)
+ {
+ /* No users left in here, mark it for deletion */
+ try
+ {
+ to_delete.push_back(f->first);
+ }
+ catch (...)
+ {
+ ServerInstance->Log(DEBUG,"Exception in User::PurgeEmptyChannels to_delete.push_back()");
+ }
+ }
+ }
+
+ for (std::vector<Channel*>::iterator n = to_delete.begin(); n != to_delete.end(); n++)
+ {
+ Channel* thischan = *n;
+ chan_hash::iterator i2 = ServerInstance->chanlist->find(thischan->name);
+ if (i2 != ServerInstance->chanlist->end())
+ {
+ FOREACH_MOD(I_OnChannelDelete,OnChannelDelete(i2->second));
+ delete i2->second;
+ ServerInstance->chanlist->erase(i2);
+ this->chans.erase(*n);
+ }
+ }
+
+ this->UnOper();
+}
+
+void User::ShowMOTD()
+{
+ if (!ServerInstance->Config->MOTD.size())
+ {
+ this->WriteServ("422 %s :Message of the day file is missing.",this->nick);
+ return;
+ }
+ this->WriteServ("375 %s :%s message of the day", this->nick, ServerInstance->Config->ServerName);
+
+ for (file_cache::iterator i = ServerInstance->Config->MOTD.begin(); i != ServerInstance->Config->MOTD.end(); i++)
+ this->WriteServ("372 %s :- %s",this->nick,i->c_str());
+
+ this->WriteServ("376 %s :End of message of the day.", this->nick);
+}
+
+void User::ShowRULES()
+{
+ if (!ServerInstance->Config->RULES.size())
+ {
+ this->WriteServ("434 %s :RULES File is missing",this->nick);
+ return;
+ }
+
+ 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("232 %s :- %s",this->nick,i->c_str());
+
+ this->WriteServ("309 %s :End of RULES command.",this->nick);
+}
+
+void User::HandleEvent(EventType et, int errornum)
+{
+ if (this->quitting) // drop everything, user is due to be quit
+ return;
+
+ /* WARNING: May delete this user! */
+ int thisfd = this->GetFd();
+
+ try
+ {
+ switch (et)
+ {
+ case EVENT_READ:
+ ServerInstance->ProcessUser(this);
+ break;
+ case EVENT_WRITE:
+ this->FlushWriteBuf();
+ break;
+ case EVENT_ERROR:
+ /** This should be safe, but dont DARE do anything after it -- Brain */
+ this->SetWriteError(errornum ? strerror(errornum) : "EOF from client");
+ break;
+ }
+ }
+ catch (...)
+ {
+ ServerInstance->Log(DEBUG,"Exception in User::HandleEvent intercepted");
+ }
+
+ /* If the user has raised an error whilst being processed, quit them now we're safe to */
+ if ((ServerInstance->SE->GetRef(thisfd) == this))
+ {
+ if (!WriteError.empty())
+ {
+ User::QuitUser(ServerInstance, this, GetWriteError());
+ }
+ }
+}
+
+void User::SetOperQuit(const std::string &oquit)
+{
+ operquitmsg = oquit;
+}
+
+const char* User::GetOperQuit()
+{
+ return operquitmsg.c_str();
+}
+
+void User::IncreasePenalty(int increase)
+{
+ this->Penalty += increase;
+}
+
+void User::DecreasePenalty(int decrease)
+{
+ this->Penalty -= decrease;
+}
+
+VisData::VisData()
+{
+}
+
+VisData::~VisData()
+{
+}
+
+bool VisData::VisibleTo(User* user)
+{
+ return true;
+}
+