]> git.netwichtig.de Git - user/henk/code/inspircd.git/blobdiff - src/users.cpp
Add support for the IRCv3 account-tag specification.
[user/henk/code/inspircd.git] / src / users.cpp
index 1e2554107b1905c1d1a9bb1f30918e2c85330ebb..e05ef1853a3a435a9a93153fcdc929edfbc7a916 100644 (file)
@@ -26,6 +26,8 @@
 #include "inspircd.h"
 #include "xline.h"
 
+ClientProtocol::MessageList LocalUser::sendmsglist;
+
 bool User::IsNoticeMaskSet(unsigned char sm)
 {
        if (!isalpha(sm))
@@ -87,6 +89,7 @@ User::User(const std::string& uid, Server* srv, UserType type)
 LocalUser::LocalUser(int myfd, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* servaddr)
        : User(ServerInstance->UIDGen.GetUID(), ServerInstance->FakeClient->server, USERTYPE_LOCAL)
        , eh(this)
+       , serializer(NULL)
        , bytes_in(0)
        , bytes_out(0)
        , cmds_in(0)
@@ -285,7 +288,7 @@ void UserIOHandler::OnDataReady()
                user->bytes_in += qpos;
                user->cmds_in++;
 
-               ServerInstance->Parser.ProcessBuffer(line, user);
+               ServerInstance->Parser.ProcessBuffer(user, line);
                if (user->quitting)
                        return;
 
@@ -315,6 +318,12 @@ void UserIOHandler::AddWriteBuf(const std::string &data)
        WriteData(data);
 }
 
+void UserIOHandler::OnSetEndPoint(const irc::sockets::sockaddrs& server, const irc::sockets::sockaddrs& client)
+{
+       memcpy(&user->server_sa, &server, sizeof(irc::sockets::sockaddrs));
+       user->SetClientIP(client);
+}
+
 void UserIOHandler::OnError(BufferedSocketError)
 {
        ServerInstance->Users->QuitUser(user, getError());
@@ -325,7 +334,7 @@ CullResult User::cull()
        if (!quitting)
                ServerInstance->Users->QuitUser(this, "Culled without QuitUser");
 
-       if (client_sa.sa.sa_family != AF_UNSPEC)
+       if (client_sa.family() != AF_UNSPEC)
                ServerInstance->Users->RemoveCloneCounts(this);
 
        return Extensible::cull();
@@ -353,7 +362,16 @@ void User::Oper(OperInfo* info)
 
        this->SetMode(opermh, true);
        this->oper = info;
-       this->WriteCommand("MODE", "+o");
+
+       LocalUser* localuser = IS_LOCAL(this);
+       if (localuser)
+       {
+               Modes::ChangeList changelist;
+               changelist.push_add(opermh);
+               ClientProtocol::Events::Mode modemsg(ServerInstance->FakeClient, NULL, localuser, changelist);
+               localuser->Send(modemsg);
+       }
+
        FOREACH_MOD(OnOper, (this, info->name));
 
        std::string opername;
@@ -375,11 +393,10 @@ void User::Oper(OperInfo* info)
                nick.c_str(), ident.c_str(), GetRealHost().c_str(), oper->name.c_str(), opername.c_str());
        this->WriteNumeric(RPL_YOUAREOPER, InspIRCd::Format("You are now %s %s", strchr("aeiouAEIOU", oper->name[0]) ? "an" : "a", oper->name.c_str()));
 
-       ServerInstance->Logs->Log("OPER", LOG_DEFAULT, "%s opered as type: %s", GetFullRealHost().c_str(), oper->name.c_str());
        ServerInstance->Users->all_opers.push_back(this);
 
        // Expand permissions from config for faster lookup
-       if (IS_LOCAL(this))
+       if (localuser)
                oper->init();
 
        FOREACH_MOD(OnPostOper, (this, oper->name, opername));
@@ -556,19 +573,22 @@ void LocalUser::FullConnect()
        /* Trigger MOTD and LUSERS output, give modules a chance too */
        ModResult MOD_RESULT;
        std::string command("LUSERS");
-       std::vector<std::string> parameters;
-       FIRST_MOD_RESULT(OnPreCommand, MOD_RESULT, (command, parameters, this, true, command));
+       CommandBase::Params parameters;
+       FIRST_MOD_RESULT(OnPreCommand, MOD_RESULT, (command, parameters, this, true));
        if (!MOD_RESULT)
                ServerInstance->Parser.CallHandler(command, parameters, this);
 
        MOD_RESULT = MOD_RES_PASSTHRU;
        command = "MOTD";
-       FIRST_MOD_RESULT(OnPreCommand, MOD_RESULT, (command, parameters, this, true, command));
+       FIRST_MOD_RESULT(OnPreCommand, MOD_RESULT, (command, parameters, this, true));
        if (!MOD_RESULT)
                ServerInstance->Parser.CallHandler(command, parameters, this);
 
        if (ServerInstance->Config->RawLog)
-               WriteServ("PRIVMSG %s :*** Raw I/O logging is enabled on this server. All messages, passwords, and commands are being recorded.", nick.c_str());
+       {
+               ClientProtocol::Messages::Privmsg rawlogmsg(ServerInstance->FakeClient, this, "*** Raw I/O logging is enabled on this server. All messages, passwords, and commands are being recorded.");
+               this->Send(ServerInstance->GetRFCEvents().privmsg, rawlogmsg);
+       }
 
        /*
         * We don't set REG_ALL until triggering OnUserConnect, so some module events don't spew out stuff
@@ -581,7 +601,7 @@ void LocalUser::FullConnect()
        FOREACH_MOD(OnPostConnect, (this));
 
        ServerInstance->SNO->WriteToSnoMask('c',"Client connecting on port %d (class %s): %s (%s) [%s]",
-               this->GetServerPort(), this->MyClass->name.c_str(), GetFullRealHost().c_str(), this->GetIPString().c_str(), this->fullname.c_str());
+               this->GetServerPort(), this->MyClass->name.c_str(), GetFullRealHost().c_str(), this->GetIPString().c_str(), this->GetRealName().c_str());
        ServerInstance->Logs->Log("BANCACHE", LOG_DEBUG, "BanCache: Adding NEGATIVE hit for " + this->GetIPString());
        ServerInstance->BanCache.AddHit(this->GetIPString(), "", "");
        // reset the flood penalty (which could have been raised due to things like auto +x)
@@ -646,7 +666,11 @@ bool User::ChangeNick(const std::string& newnick, time_t newts)
        }
 
        if (this->registered == REG_ALL)
-               this->WriteCommon("NICK %s", newnick.c_str());
+       {
+               ClientProtocol::Messages::Nick nickmsg(this, newnick);
+               ClientProtocol::Event nickevent(ServerInstance->GetRFCEvents().nick, nickmsg);
+               this->WriteCommonRaw(nickevent, true);
+       }
        const std::string oldnick = nick;
        nick = newnick;
 
@@ -662,7 +686,10 @@ bool User::ChangeNick(const std::string& newnick, time_t newts)
 
 void LocalUser::OverruleNick()
 {
-       this->WriteFrom(this, "NICK %s", this->uuid.c_str());
+       {
+               ClientProtocol::Messages::Nick nickmsg(this, this->uuid);
+               this->Send(ServerInstance->GetRFCEvents().nick, nickmsg);
+       }
        this->WriteNumeric(ERR_NICKNAMEINUSE, this->nick, "Nickname overruled.");
 
        // Clear the bit before calling ChangeNick() to make it NOT run the OnUserPostNick() hook
@@ -703,10 +730,15 @@ const std::string& User::GetRealHost() const
        return realhost;
 }
 
+const std::string& User::GetRealName() const
+{
+       return realname;
+}
+
 irc::sockets::cidr_mask User::GetCIDRMask()
 {
        unsigned char range = 0;
-       switch (client_sa.sa.sa_family)
+       switch (client_sa.family())
        {
                case AF_INET6:
                        range = ServerInstance->Config->c_ipv6_range;
@@ -753,35 +785,24 @@ void LocalUser::SetClientIP(const irc::sockets::sockaddrs& sa, bool recheck_elin
        }
 }
 
-static std::string wide_newline("\r\n");
-
-void User::Write(const std::string& text)
-{
-}
-
-void User::Write(const char *text, ...)
-{
-}
-
-void LocalUser::Write(const std::string& text)
+void LocalUser::Write(const ClientProtocol::SerializedMessage& text)
 {
        if (!SocketEngine::BoundsCheckFd(&eh))
                return;
 
-       // The maximum size of an IRC message minus the terminating CR+LF.
-       const size_t maxmessage = ServerInstance->Config->Limits.MaxLine - 2;
-       if (text.length() > maxmessage)
+       if (ServerInstance->Config->RawLog)
        {
-               // This should happen rarely or never. Crop the string at MaxLine and try again.
-               std::string try_again(text, 0, maxmessage);
-               Write(try_again);
-               return;
-       }
+               if (text.empty())
+                       return;
 
-       ServerInstance->Logs->Log("USEROUTPUT", LOG_RAWIO, "C[%s] O %s", uuid.c_str(), text.c_str());
+               std::string::size_type nlpos = text.find_first_of("\r\n", 0, 2);
+               if (nlpos == std::string::npos)
+                       nlpos = text.length(); // TODO is this ok, test it
+
+               ServerInstance->Logs->Log("USEROUTPUT", LOG_RAWIO, "C[%s] O %.*s", uuid.c_str(), (int) nlpos, text.c_str());
+       }
 
        eh.AddWriteBuf(text);
-       eh.AddWriteBuf(wide_newline);
 
        const size_t bytessent = text.length() + 2;
        ServerInstance->stats.Sent += bytessent;
@@ -789,53 +810,47 @@ void LocalUser::Write(const std::string& text)
        this->cmds_out++;
 }
 
-/** Write()
- */
-void LocalUser::Write(const char *text, ...)
-{
-       std::string textbuffer;
-       VAFORMAT(textbuffer, text, text);
-       this->Write(textbuffer);
-}
-
-void User::WriteServ(const std::string& text)
+void LocalUser::Send(ClientProtocol::Event& protoev)
 {
-       this->Write(":%s %s",ServerInstance->Config->ServerName.c_str(),text.c_str());
-}
-
-/** WriteServ()
- *  Same as Write(), except `text' is prefixed with `:server.name '.
- */
-void User::WriteServ(const char* text, ...)
-{
-       std::string textbuffer;
-       VAFORMAT(textbuffer, text, text);
-       this->WriteServ(textbuffer);
-}
+       if (!serializer)
+               return;
 
-void User::WriteCommand(const char* command, const std::string& text)
-{
-       this->WriteServ(command + (this->registered & REG_NICK ? " " + this->nick : " *") + " " + text);
+       // In the most common case a static LocalUser field, sendmsglist, is passed to the event to be
+       // populated. The list is cleared before returning.
+       // To handle re-enters, if sendmsglist is non-empty upon entering the method then a temporary
+       // list is used instead of the static one.
+       if (sendmsglist.empty())
+       {
+               Send(protoev, sendmsglist);
+               sendmsglist.clear();
+       }
+       else
+       {
+               ClientProtocol::MessageList msglist;
+               Send(protoev, msglist);
+       }
 }
 
-namespace
+void LocalUser::Send(ClientProtocol::Event& protoev, ClientProtocol::MessageList& msglist)
 {
-       std::string BuildNumeric(const std::string& source, User* targetuser, unsigned int num, const std::vector<std::string>& params)
+       // Modules can personalize the messages sent per user for the event
+       protoev.GetMessagesForUser(this, msglist);
+       for (ClientProtocol::MessageList::const_iterator i = msglist.begin(); i != msglist.end(); ++i)
        {
-               const char* const target = (targetuser->registered & REG_NICK ? targetuser->nick.c_str() : "*");
-               std::string raw = InspIRCd::Format(":%s %03u %s", source.c_str(), num, target);
-               if (!params.empty())
-               {
-                       for (std::vector<std::string>::const_iterator i = params.begin(); i != params.end()-1; ++i)
-                               raw.append(1, ' ').append(*i);
-                       raw.append(" :").append(params.back());
-               }
-               return raw;
+               ClientProtocol::Message& curr = **i;
+               ModResult res;
+               FIRST_MOD_RESULT(OnUserWrite, res, (this, curr));
+               if (res != MOD_RES_DENY)
+                       Write(serializer->SerializeForUser(this, curr));
        }
 }
 
 void User::WriteNumeric(const Numeric::Numeric& numeric)
 {
+       LocalUser* const localuser = IS_LOCAL(this);
+       if (!localuser)
+               return;
+
        ModResult MOD_RESULT;
 
        FIRST_MOD_RESULT(OnNumeric, MOD_RESULT, (this, numeric));
@@ -843,24 +858,8 @@ void User::WriteNumeric(const Numeric::Numeric& numeric)
        if (MOD_RESULT == MOD_RES_DENY)
                return;
 
-       const std::string& servername = (numeric.GetServer() ? numeric.GetServer()->GetName() : ServerInstance->Config->ServerName);
-       this->Write(BuildNumeric(servername, this, numeric.GetNumeric(), numeric.GetParams()));
-}
-
-void User::WriteFrom(User *user, const std::string &text)
-{
-       const std::string message = ":" + user->GetFullHost() + " " + text;
-       this->Write(message);
-}
-
-
-/* write text from an originating user to originating user */
-
-void User::WriteFrom(User *user, const char* text, ...)
-{
-       std::string textbuffer;
-       VAFORMAT(textbuffer, text, text);
-       this->WriteFrom(user, textbuffer);
+       ClientProtocol::Messages::Numeric numericmsg(numeric, localuser);
+       localuser->Send(ServerInstance->GetRFCEvents().numeric, numericmsg);
 }
 
 void User::WriteRemoteNotice(const std::string& text)
@@ -877,32 +876,24 @@ namespace
 {
        class WriteCommonRawHandler : public User::ForEachNeighborHandler
        {
-               const std::string& msg;
+               ClientProtocol::Event& ev;
 
                void Execute(LocalUser* user) CXX11_OVERRIDE
                {
-                       user->Write(msg);
+                       user->Send(ev);
                }
 
         public:
-               WriteCommonRawHandler(const std::string& message)
-                       : msg(message)
+               WriteCommonRawHandler(ClientProtocol::Event& protoev)
+                       : ev(protoev)
                {
                }
        };
 }
 
-void User::WriteCommon(const char* text, ...)
+void User::WriteCommonRaw(ClientProtocol::Event& protoev, bool include_self)
 {
-       std::string textbuffer;
-       VAFORMAT(textbuffer, text, text);
-       textbuffer = ":" + this->GetFullHost() + " " + textbuffer;
-       this->WriteCommonRaw(textbuffer, true);
-}
-
-void User::WriteCommonRaw(const std::string &line, bool include_self)
-{
-       WriteCommonRawHandler handler(line);
+       WriteCommonRawHandler handler(protoev);
        ForEachNeighbor(handler, include_self);
 }
 
@@ -990,20 +981,20 @@ bool User::SharesChannelWith(User *other)
        return false;
 }
 
-bool User::ChangeName(const std::string& gecos)
+bool User::ChangeRealName(const std::string& real)
 {
-       if (!this->fullname.compare(gecos))
+       if (!this->realname.compare(real))
                return true;
 
        if (IS_LOCAL(this))
        {
                ModResult MOD_RESULT;
-               FIRST_MOD_RESULT(OnChangeLocalUserGECOS, MOD_RESULT, (IS_LOCAL(this),gecos));
+               FIRST_MOD_RESULT(OnPreChangeRealName, MOD_RESULT, (IS_LOCAL(this), real));
                if (MOD_RESULT == MOD_RES_DENY)
                        return false;
-               FOREACH_MOD(OnChangeName, (this,gecos));
+               FOREACH_MOD(OnChangeRealName, (this, real));
        }
-       this->fullname.assign(gecos, 0, ServerInstance->Config->Limits.MaxGecos);
+       this->realname.assign(real, 0, ServerInstance->Config->Limits.MaxReal);
 
        return true;
 }
@@ -1193,6 +1184,16 @@ void User::PurgeEmptyChannels()
        this->UnOper();
 }
 
+void User::WriteNotice(const std::string& text)
+{
+       LocalUser* const localuser = IS_LOCAL(this);
+       if (!localuser)
+               return;
+
+       ClientProtocol::Messages::Privmsg msg(ClientProtocol::Messages::Privmsg::nocopy, ServerInstance->FakeClient, localuser, text, MSG_NOTICE);
+       localuser->Send(ServerInstance->GetRFCEvents().privmsg, msg);
+}
+
 const std::string& FakeUser::GetFullHost()
 {
        if (!ServerInstance->Config->HideServer.empty())
@@ -1232,7 +1233,7 @@ ConnectClass::ConnectClass(ConfigTag* tag, char t, const std::string& mask, cons
        for (ConfigItems::const_iterator piter = parentkeys.begin(); piter != parentkeys.end(); ++piter)
        {
                // The class name and parent name are not inherited
-               if (piter->first == "name" || piter->first == "parent")
+               if (stdalgo::string::equalsci(piter->first, "name") || stdalgo::string::equalsci(piter->first, "parent"))
                        continue;
 
                // Store the item in the config tag. If this item also