-/* +------------------------------------+
- * | Inspire Internet Relay Chat Daemon |
- * +------------------------------------+
- *
- * InspIRCd: (C) 2002-2007 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.
- *
- * ---------------------------------------------------
- */
-
-#include "inspircd.h"
-#include "configreader.h"
-#include "channels.h"
-#include "users.h"
-#include <stdarg.h>
-#include "socketengine.h"
-#include "wildcard.h"
-#include "xline.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;
-
-bool InitTypes(ServerConfig* conf, const char* tag)
-{
- if (conf->opertypes.size())
- {
- for (opertype_t::iterator n = conf->opertypes.begin(); n != conf->opertypes.end(); n++)
- {
- if (n->second)
- delete[] n->second;
- }
- }
-
- conf->opertypes.clear();
- return true;
-}
-
-bool InitClasses(ServerConfig* conf, const char* tag)
-{
- if (conf->operclass.size())
- {
- for (operclass_t::iterator n = conf->operclass.begin(); n != conf->operclass.end(); n++)
- {
- if (n->second)
- delete[] n->second;
- }
- }
-
- conf->operclass.clear();
- return true;
-}
-
-bool DoType(ServerConfig* conf, const char* tag, char** entries, ValueList &values, int* types)
-{
- const char* TypeName = values[0].GetString();
- const char* Classes = values[1].GetString();
-
- conf->opertypes[TypeName] = strnewdup(Classes);
- return true;
-}
-
-bool DoClass(ServerConfig* conf, const char* tag, char** entries, ValueList &values, int* types)
-{
- const char* ClassName = values[0].GetString();
- const char* CommandList = values[1].GetString();
-
- conf->operclass[ClassName] = strnewdup(CommandList);
- return true;
-}
-
-bool DoneClassesAndTypes(ServerConfig* conf, const char* tag)
-{
- return true;
-}
-
-std::string userrec::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 userrec::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());
- }
-}
-
-UserResolver::UserResolver(InspIRCd* Instance, userrec* user, std::string to_resolve, QueryType qt, bool &cache) :
- Resolver(Instance, to_resolve, qt, cache), bound_user(user)
-{
- this->fwd = (qt == DNS_QUERY_A || qt == DNS_QUERY_AAAA);
- this->bound_fd = user->GetFd();
-}
-
-void UserResolver::OnLookupComplete(const std::string &result, unsigned int ttl, bool cached)
-{
- if ((!this->fwd) && (ServerInstance->SE->GetRef(this->bound_fd) == this->bound_user))
- {
- this->bound_user->stored_host = result;
- try
- {
- /* Check we didnt time out */
- if (this->bound_user->registered != REG_ALL)
- {
- bool cached;
-#ifdef IPV6
- if (this->bound_user->GetProtocolFamily() == AF_INET6)
- {
- /* IPV6 forward lookup (with possibility of 4in6) */
- const char* ip = this->bound_user->GetIPString();
- bound_user->res_forward = new UserResolver(this->ServerInstance, this->bound_user, result, (!strncmp(ip, "0::ffff:", 8) ? DNS_QUERY_A : DNS_QUERY_AAAA), cached);
- }
- else
- /* IPV4 lookup (mixed protocol mode) */
-#endif
- /* IPV4 lookup (ipv4 only mode) */
- bound_user->res_forward = new UserResolver(this->ServerInstance, this->bound_user, result, DNS_QUERY_A, cached);
- this->ServerInstance->AddResolver(bound_user->res_forward, cached);
- }
- }
- catch (CoreException& e)
- {
- ServerInstance->Log(DEBUG,"Error in resolver: %s",e.GetReason());
- }
- }
- else if ((this->fwd) && (ServerInstance->SE->GetRef(this->bound_fd) == this->bound_user))
- {
- /* Both lookups completed */
- std::string result2("0::ffff:");
- result2.append(result);
- if (this->bound_user->GetIPString() == result || this->bound_user->GetIPString() == result2)
- {
- std::string hostname = this->bound_user->stored_host;
- if (hostname.length() < 65)
- {
- /* Check we didnt time out */
- if ((this->bound_user->registered != REG_ALL) && (!this->bound_user->dns_done))
- {
- /* Hostnames starting with : are not a good thing (tm) */
- if (*(hostname.c_str()) == ':')
- hostname.insert(0, "0");
-
- this->bound_user->WriteServ("NOTICE Auth :*** Found your hostname (%s)%s", hostname.c_str(), (cached ? " -- cached" : ""));
- this->bound_user->dns_done = true;
- strlcpy(this->bound_user->dhost, hostname.c_str(),64);
- strlcpy(this->bound_user->host, hostname.c_str(),64);
- /* Invalidate cache */
- this->bound_user->InvalidateCache();
- }
- }
- else
- {
- if (!this->bound_user->dns_done)
- {
- 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());
- this->bound_user->dns_done = true;
- }
- }
- }
- else
- {
- if (!this->bound_user->dns_done)
- {
- 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());
- this->bound_user->dns_done = true;
- }
- }
- }
-}
-
-void UserResolver::OnError(ResolverError e, const std::string &errormessage)
-{
- if (ServerInstance->SE->GetRef(this->bound_fd) == this->bound_user)
- {
- /* Since dns timeout is implemented outside of the resolver, this was a race condition that could result in this message being sent *after*
- * the user was fully connected. This check fixes that issue - Special */
- if (!this->bound_user->dns_done)
- {
- /* Error message here */
- 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());
- this->bound_user->dns_done = true;
- }
- }
-}
-
-
-bool userrec::IsNoticeMaskSet(unsigned char sm)
-{
- return (snomasks[sm-65]);
-}
-
-void userrec::SetNoticeMask(unsigned char sm, bool value)
-{
- snomasks[sm-65] = value;
-}
-
-const char* userrec::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 userrec::IsModeSet(unsigned char m)
-{
- return (modes[m-65]);
-}
-
-void userrec::SetMode(unsigned char m, bool value)
-{
- modes[m-65] = value;
-}
-
-const char* userrec::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 userrec::DecrementModes()
-{
- for (int n = 0; n < 64; n++)
- {
- if (modes[n])
- {
- ModeHandler* mh = ServerInstance->Modes->FindMode(n+65, MODETYPE_USER);
- if (mh)
- mh->ChangeCount(-1);
- }
- }
-}
-
-userrec::userrec(InspIRCd* Instance) : ServerInstance(Instance)
-{
- // the PROPER way to do it, AVOID bzero at *ALL* costs
- *password = *nick = *ident = *host = *dhost = *fullname = *awaymsg = *oper = 0;
- server = (char*)Instance->FindServerNamePtr(Instance->Config->ServerName);
- reset_due = ServerInstance->Time();
- age = ServerInstance->Time(true);
- lines_in = lastping = signon = idle_lastmsg = nping = registered = 0;
- ChannelCount = timeout = flood = bytes_in = bytes_out = cmds_in = cmds_out = 0;
- muted = exempt = haspassed = dns_done = false;
- fd = -1;
- recvq.clear();
- sendq.clear();
- WriteError.clear();
- res_forward = res_reverse = NULL;
- Visibility = NULL;
- ip = NULL;
- chans.clear();
- invites.clear();
- memset(modes,0,sizeof(modes));
- memset(snomasks,0,sizeof(snomasks));
- /* Invalidate cache */
- operquit = cached_fullhost = cached_hostip = cached_makehost = cached_fullrealhost = NULL;
-}
-
-void userrec::RemoveCloneCounts()
-{
- clonemap::iterator x = ServerInstance->local_clones.find(this->GetIPString());
- if (x != ServerInstance->local_clones.end())
- {
- x->second--;
- if (!x->second)
- {
- ServerInstance->local_clones.erase(x);
- }
- }
-
- clonemap::iterator y = ServerInstance->global_clones.find(this->GetIPString());
- if (y != ServerInstance->global_clones.end())
- {
- y->second--;
- if (!y->second)
- {
- ServerInstance->global_clones.erase(y);
- }
- }
-}
-
-userrec::~userrec()
-{
- this->InvalidateCache();
- this->DecrementModes();
- if (operquit)
- free(operquit);
- if (ip)
- {
- this->RemoveCloneCounts();
-
- if (this->GetProtocolFamily() == AF_INET)
- {
- delete (sockaddr_in*)ip;
- }
-#ifdef SUPPORT_IP6LINKS
- else
- {
- delete (sockaddr_in6*)ip;
- }
-#endif
- }
-}
-
-char* userrec::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* userrec::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 userrec::CloseSocket()
-{
- shutdown(this->fd,2);
- close(this->fd);
-}
-
-char* userrec::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* userrec::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 userrec::ReadData(void* buffer, size_t size)
-{
- if (IS_LOCAL(this))
- {
-#ifndef WIN32
- return read(this->fd, buffer, size);
-#else
- return recv(this->fd, (char*)buffer, size, 0);
-#endif
- }
- else
- return 0;
-}
-
-
-char* userrec::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 userrec::IsInvited(const irc::string &channel)
-{
- for (InvitedList::iterator i = invites.begin(); i != invites.end(); i++)
- {
- if (channel == *i)
- {
- return true;
- }
- }
- return false;
-}
-
-InvitedList* userrec::GetInviteList()
-{
- return &invites;
-}
-
-void userrec::InviteTo(const irc::string &channel)
-{
- invites.push_back(channel);
-}
-
-void userrec::RemoveInvite(const irc::string &channel)
-{
- for (InvitedList::iterator i = invites.begin(); i != invites.end(); i++)
- {
- if (channel == *i)
- {
- invites.erase(i);
- return;
- }
- }
-}
-
-bool userrec::HasPermission(const std::string &command)
-{
- char* mycmd;
- char* savept;
- char* savept2;
-
- /*
- * 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))
- {
- opertype_t::iterator iter_opertype = ServerInstance->Config->opertypes.find(this->oper);
- if (iter_opertype != ServerInstance->Config->opertypes.end())
- {
- 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)
- {
- if ((!strcasecmp(mycmd,command.c_str())) || (*mycmd == '*'))
- {
- free(Classes);
- free(CommandList);
- return true;
- }
- mycmd = strtok_r(NULL," ",&savept2);
- }
- free(CommandList);
- }
- myclass = strtok_r(NULL," ",&savept);
- }
- free(Classes);
- }
- }
- 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 userrec::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 (recvq.length() > (unsigned)this->recvqmax)
- {
- this->SetWriteError("RecvQ exceeded");
- ServerInstance->WriteOpers("*** User %s RecvQ of %d exceeds connect class maximum of %d",this->nick,recvq.length(),this->recvqmax);
- return false;
- }
-
- return true;
- }
-
- catch (...)
- {
- ServerInstance->Log(DEBUG,"Exception in userrec::AddBuffer()");
- return false;
- }
-}
-
-bool userrec::BufferIsReady()
-{
- return (recvq.find('\n') != std::string::npos);
-}
-
-void userrec::ClearBuffer()
-{
- recvq.clear();
-}
-
-std::string userrec::GetBuffer()
-{
- try
- {
- if (!recvq.length())
- 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 userrec::GetBuffer()");
- return "";
- }
-}
-
-void userrec::AddWriteBuf(const std::string &data)
-{
- if (*this->GetWriteError())
- return;
-
- if (sendq.length() + data.length() > (unsigned)this->sendqmax)
- {
- /*
- * Fix by brain - Set the error text BEFORE calling writeopers, 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->WriteOpers("*** User %s SendQ of %d exceeds connect class maximum of %d",this->nick,sendq.length() + data.length(),this->sendqmax);
- 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->WriteOpers("*** 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 userrec::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();
-#ifndef WIN32
- int n_sent = write(this->fd, this->sendq.data(), this->sendq.length());
-#else
- int n_sent = send(this->fd, (const char*)this->sendq.data(), this->sendq.length(), 0);
-#endif
- 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(strerror(errno));
- 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 userrec::FlushWriteBuf()");
- }
-
- if (this->sendq.empty())
- {
- FOREACH_MOD(I_OnBufferFlushed,OnBufferFlushed(this));
- }
-}
-
-void userrec::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 userrec::SetWriteError()");
- }
-}
-
-const char* userrec::GetWriteError()
-{
- return this->WriteError.c_str();
-}
-
-void userrec::Oper(const std::string &opertype)
-{
- 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);
- FOREACH_MOD(I_OnPostOper,OnPostOper(this, opertype));
- }
-
- catch (...)
- {
- ServerInstance->Log(DEBUG,"Exception in userrec::Oper()");
- }
-}
-
-void userrec::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 them from the opers list.
- for (std::vector<userrec*>::iterator a = ServerInstance->all_opers.begin(); a < ServerInstance->all_opers.end(); a++)
- {
- if (*a == this)
- {
- ServerInstance->all_opers.erase(a);
- return;
- }
- }
- }
- }
-
- catch (...)
- {
- ServerInstance->Log(DEBUG,"Exception in userrec::UnOper()");
- }
-}
-
-void userrec::QuitUser(InspIRCd* Instance, userrec *user, const std::string &quitreason, const char* operreason)
-{
- user->muted = true;
- Instance->GlobalCulls.AddItem(user, quitreason.c_str(), operreason);
-}
-
-/* adds or updates an entry in the whowas list */
-void userrec::AddToWhoWas()
-{
- command_t* whowas_command = ServerInstance->Parser->GetHandler("WHOWAS");
- if (whowas_command)
- {
- std::deque<classbase*> params;
- params.push_back(this);
- whowas_command->HandleInternal(WHOWAS_ADD, params);
- }
-}
-
-/* add a client connection to the sockets list */
-void userrec::AddClient(InspIRCd* Instance, int socket, int port, bool iscached, int socketfamily, sockaddr* ip)
-{
- std::string tempnick = ConvToStr(socket) + "-unknown";
- user_hash::iterator iter = Instance->clientlist->find(tempnick);
- char ipaddr[MAXBUF];
-#ifdef IPV6
- if (socketfamily == AF_INET6)
- inet_ntop(AF_INET6, &((const sockaddr_in6*)ip)->sin6_addr, ipaddr, sizeof(ipaddr));
- else
-#endif
- inet_ntop(AF_INET, &((const sockaddr_in*)ip)->sin_addr, ipaddr, sizeof(ipaddr));
- userrec* New;
- int j = 0;
-
- Instance->unregistered_count++;
-
- /*
- * fix by brain.
- * as these nicknames are 'RFC impossible', we can be sure nobody is going to be
- * using one as a registered connection. As they are per fd, we can also safely assume
- * that we wont have collisions. Therefore, if the nick exists in the list, its only
- * used by a dead socket, erase the iterator so that the new client may reclaim it.
- * this was probably the cause of 'server ignores me when i hammer it with reconnects'
- * issue in earlier alphas/betas
- */
- if (iter != Instance->clientlist->end())
- {
- userrec* goner = iter->second;
- DELETE(goner);
- Instance->clientlist->erase(iter);
- }
-
- New = new userrec(Instance);
- (*(Instance->clientlist))[tempnick] = New;
- New->fd = socket;
- strlcpy(New->nick,tempnick.c_str(),NICKMAX-1);
-
- New->server = Instance->FindServerNamePtr(Instance->Config->ServerName);
- /* We don't need range checking here, we KNOW 'unknown\0' will fit into the ident field. */
- strcpy(New->ident, "unknown");
-
- New->registered = REG_NONE;
- New->signon = Instance->Time() + Instance->Config->dns_timeout;
- New->lastping = 1;
-
- New->SetSockAddr(socketfamily, ipaddr, port);
-
- /* Smarter than your average bear^H^H^H^Hset of strlcpys. */
- for (const char* temp = New->GetIPString(); *temp && j < 64; temp++, j++)
- New->dhost[j] = New->host[j] = *temp;
- New->dhost[j] = New->host[j] = 0;
-
- Instance->AddLocalClone(New);
- Instance->AddGlobalClone(New);
-
- /*
- * First class check. We do this again in FullConnect after DNS is done, and NICK/USER is recieved.
- * See my note down there for why this is required. DO NOT REMOVE. :) -- w00t
- */
- ConnectClass* i = New->GetClass();
-
- if (!i)
- {
- userrec::QuitUser(Instance, New, "Access denied by configuration");
- return;
- }
-
- New->CheckClass();
-
- New->pingmax = i->GetPingTime();
- New->nping = Instance->Time() + i->GetPingTime() + Instance->Config->dns_timeout;
- New->timeout = Instance->Time() + i->GetRegTimeout();
- New->flood = i->GetFlood();
- New->threshold = i->GetThreshold();
- New->sendqmax = i->GetSendqMax();
- New->recvqmax = i->GetRecvqMax();
-
- Instance->local_users.push_back(New);
-
- if ((Instance->local_users.size() > Instance->Config->SoftLimit) || (Instance->local_users.size() >= MAXCLIENTS))
- {
- Instance->WriteOpers("*** Warning: softlimit value has been reached: %d clients", Instance->Config->SoftLimit);
- userrec::QuitUser(Instance, New,"No more connections allowed");
- return;
- }
-
- /*
- * XXX -
- * this is done as a safety check to keep the file descriptors within range of fd_ref_table.
- * its a pretty big but for the moment valid assumption:
- * file descriptors are handed out starting at 0, and are recycled as theyre freed.
- * therefore if there is ever an fd over 65535, 65536 clients must be connected to the
- * irc server at once (or the irc server otherwise initiating this many connections, files etc)
- * which for the time being is a physical impossibility (even the largest networks dont have more
- * than about 10,000 users on ONE server!)
- */
-#ifndef WINDOWS
- if ((unsigned int)socket >= MAX_DESCRIPTORS)
- {
- userrec::QuitUser(Instance, New, "Server is full");
- return;
- }
-#endif
-
- New->exempt = (Instance->XLines->matches_exception(New) != NULL);
- if (!New->exempt)
- {
- ZLine* r = Instance->XLines->matches_zline(ipaddr);
- if (r)
- {
- char reason[MAXBUF];
- if (*Instance->Config->MoronBanner)
- New->WriteServ("NOTICE %s :*** %s", New->nick, Instance->Config->MoronBanner);
- snprintf(reason,MAXBUF,"Z-Lined: %s",r->reason);
- userrec::QuitUser(Instance, New, reason);
- return;
- }
- }
-
- if (socket > -1)
- {
- if (!Instance->SE->AddFd(New))
- {
- userrec::QuitUser(Instance, New, "Internal error handling connection");
- return;
- }
- }
-
- /* NOTE: even if dns lookups are *off*, we still need to display this.
- * BOPM and other stuff requires it.
- */
- New->WriteServ("NOTICE Auth :*** Looking up your hostname...");
-}
-
-unsigned long userrec::GlobalCloneCount()
-{
- clonemap::iterator x = ServerInstance->global_clones.find(this->GetIPString());
- if (x != ServerInstance->global_clones.end())
- return x->second;
- else
- return 0;
-}
-
-unsigned long userrec::LocalCloneCount()
-{
- clonemap::iterator x = ServerInstance->local_clones.find(this->GetIPString());
- if (x != ServerInstance->local_clones.end())
- return x->second;
- else
- return 0;
-}
-
-/*
- * Check class restrictions
- */
-void userrec::CheckClass()
-{
- ConnectClass* a = this->GetClass();
-
- if ((!a) || (a->GetType() == CC_DENY))
- {
- userrec::QuitUser(ServerInstance, this, "Unauthorised connection");
- return;
- }
- else if ((a->GetMaxLocal()) && (this->LocalCloneCount() > a->GetMaxLocal()))
- {
- userrec::QuitUser(ServerInstance, this, "No more connections allowed from your host via this connect class (local)");
- ServerInstance->WriteOpers("*** WARNING: maximum LOCAL connections (%ld) exceeded for IP %s", a->GetMaxLocal(), this->GetIPString());
- return;
- }
- else if ((a->GetMaxGlobal()) && (this->GlobalCloneCount() > a->GetMaxGlobal()))
- {
- userrec::QuitUser(ServerInstance, this, "No more connections allowed from your host via this connect class (global)");
- ServerInstance->WriteOpers("*** WARNING: maximum GLOBAL connections (%ld) exceeded for IP %s", a->GetMaxGlobal(), this->GetIPString());
- return;
- }
-}
-
-void userrec::FullConnect()
-{
- ServerInstance->stats->statsConnects++;
- this->idle_lastmsg = ServerInstance->Time();
-
- /*
- * You may be thinking "wtf, we checked this in userrec::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->CheckClass();
-
- /* Check the password, if one is required by the user's connect class.
- * This CANNOT be in CheckClass(), because that is called prior to PASS as well!
- */
- if ((!this->GetClass()->GetPass().empty()) && (!this->haspassed))
- {
- userrec::QuitUser(ServerInstance, this, "Invalid password");
- return;
- }
-
- if (!this->exempt)
- {
- GLine* r = ServerInstance->XLines->matches_gline(this);
-
- if (r)
- {
- this->muted = true;
- char reason[MAXBUF];
- if (*ServerInstance->Config->MoronBanner)
- this->WriteServ("NOTICE %s :*** %s", this->nick, ServerInstance->Config->MoronBanner);
- snprintf(reason,MAXBUF,"G-Lined: %s",r->reason);
- ServerInstance->GlobalCulls.AddItem(this, reason);
- return;
- }
-
- KLine* n = ServerInstance->XLines->matches_kline(this);
-
- if (n)
- {
- this->muted = true;
- char reason[MAXBUF];
- if (*ServerInstance->Config->MoronBanner)
- this->WriteServ("NOTICE %s :*** %s", this, ServerInstance->Config->MoronBanner);
- snprintf(reason,MAXBUF,"K-Lined: %s",n->reason);
- ServerInstance->GlobalCulls.AddItem(this, reason);
- 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->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);
-
- /*
- * fix 3 by brain, move registered = 7 below these so that spurious modes and host
- * changes dont go out onto the network and produce 'fake direction'.
- */
- 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);
-}
-
-/** userrec::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
- */
-userrec* userrec::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 */
-
- userrec* olduser = oldnick->second;
- (*(ServerInstance->clientlist))[New] = olduser;
- ServerInstance->clientlist->erase(oldnick);
- return olduser;
- }
-
- catch (...)
- {
- ServerInstance->Log(DEBUG,"Exception in userrec::UpdateNickHash()");
- return NULL;
- }
-}
-
-void userrec::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 userrec::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->matches_qline(newnick))
- {
- ServerInstance->stats->statsCollisions++;
- return false;
- }
-
- if (this->registered == REG_ALL)
- {
- return (ServerInstance->Parser->CallHandler("NICK", &newnick, 1, this) == CMD_SUCCESS);
- }
- return false;
- }
-
- catch (...)
- {
- ServerInstance->Log(DEBUG,"Exception in userrec::ForceNickChange()");
- return false;
- }
-}
-
-void userrec::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,"Ut oh, I dont know protocol %d to be set on '%s'!", protocol_family, this->nick);
- break;
- }
-}
-
-int userrec::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 userrec::GetProtocolFamily()
-{
- if (this->ip == NULL)
- return 0;
-
- sockaddr_in* sin = (sockaddr_in*)this->ip;
- return sin->sin_family;
-}
-
-const char* userrec::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 "";
-}
-
-const char* userrec::GetIPString(char* buf)
-{
- if (this->ip == NULL)
- {
- *buf = 0;
- return buf;
- }
-
- 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';
- strlcpy(buf, temp, sizeof(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 userrec::Write(std::string text)
-{
-#ifdef WINDOWS
- if ((this->fd < 0) || (this->m_internalFd > MAX_DESCRIPTORS))
-#else
- if ((this->fd < 0) || (this->fd > MAX_DESCRIPTORS))
-#endif
- return;
-
- try
- {
- /* ServerInstance->Log(DEBUG,"C[%d] <- %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 userrec::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 userrec::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 userrec::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 userrec::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 userrec::WriteFrom(userrec *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 userrec::WriteFrom(userrec *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 userrec::WriteTo(userrec *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 userrec::WriteTo(userrec *dest, const std::string &data)
-{
- dest->WriteFrom(this, data);
-}
-
-
-void userrec::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 userrec::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 userrec::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 userrec::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 userrec::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 userrec::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 userrec::WriteWallOps(const std::string &text)
-{
- if (!IS_OPER(this) && IS_LOCAL(this))
- return;
-
- std::string wallop("WALLOPS :");
- wallop.append(text);
-
- for (std::vector<userrec*>::const_iterator i = ServerInstance->local_users.begin(); i != ServerInstance->local_users.end(); i++)
- {
- userrec* t = *i;
- if (t->modes[UM_WALLOPS])
- this->WriteTo(t,wallop);
- }
-}
-
-void userrec::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<userrec*,userrec*>
- * 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 userrec::SharesChannelWith(userrec *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 userrec::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 userrec::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: userrec::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 userrec::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 userrec::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<userrec*>::const_iterator i = ServerInstance->local_users.begin(); i != ServerInstance->local_users.end(); i++)
- {
- (*i)->Write(fmt);
- }
-}
-
-
-std::string userrec::ChannelList(userrec* 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->modes[CM_PRIVATE]) && (!i->first->modes[CM_SECRET])) || (i->first->HasUser(source))))
- {
- list.append(i->first->GetPrefixChar(this)).append(i->first->name).append(" ");
- }
- }
- return list;
- }
- catch (...)
- {
- ServerInstance->Log(DEBUG,"Exception in userrec::ChannelList()");
- return "";
- }
-}
-
-void userrec::SplitChanList(userrec* 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 userrec::SplitChanList()");
- }
-}
-
-
-/* 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* userrec::GetClass()
-{
- for (ClassVector::iterator i = ServerInstance->Config->Classes.begin(); i != ServerInstance->Config->Classes.end(); i++)
- {
- if (((match(this->GetIPString(),i->GetHost().c_str(),true)) || (match(this->host,i->GetHost().c_str()))))
- {
- if (i->GetPort())
- {
- if (this->GetPort() == i->GetPort())
- return &(*i);
- else
- continue;
- }
- else
- return &(*i);
- }
- }
- return NULL;
-}
-
-void userrec::PurgeEmptyChannels()
-{
- std::vector<chanrec*> 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 userrec::PurgeEmptyChannels to_delete.push_back()");
- }
- }
- }
-
- for (std::vector<chanrec*>::iterator n = to_delete.begin(); n != to_delete.end(); n++)
- {
- chanrec* 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 userrec::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 userrec::ShowRULES()
-{
- if (!ServerInstance->Config->RULES.size())
- {
- this->WriteServ("NOTICE %s :Rules file is missing.",this->nick);
- return;
- }
- this->WriteServ("NOTICE %s :%s rules",this->nick,ServerInstance->Config->ServerName);
-
- for (file_cache::iterator i = ServerInstance->Config->RULES.begin(); i != ServerInstance->Config->RULES.end(); i++)
- this->WriteServ("NOTICE %s :%s",this->nick,i->c_str());
-
- this->WriteServ("NOTICE %s :End of %s rules.",this->nick,ServerInstance->Config->ServerName);
-}
-
-void userrec::HandleEvent(EventType et, int errornum)
-{
- /* 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 userrec::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())
- {
- userrec::QuitUser(ServerInstance, this, GetWriteError());
- }
- }
-}
-
-void userrec::SetOperQuit(const std::string &oquit)
-{
- if (operquit)
- return;
-
- operquit = strdup(oquit.c_str());
-}
-
-const char* userrec::GetOperQuit()
-{
- return operquit ? operquit : "";
-}
-
-VisData::VisData()
-{
-}
-
-VisData::~VisData()
-{
-}
-
-bool VisData::VisibleTo(userrec* user)
-{
- return true;
-}
-
+/* +------------------------------------+\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