]> git.netwichtig.de Git - user/henk/code/inspircd.git/blobdiff - src/users.cpp
Fix a crash when the core_oper module is not loaded.
[user/henk/code/inspircd.git] / src / users.cpp
index 5310c7c96bc3f4d8f6e6eda006a182d869b7650f..f11a7a38079e43d3fba28c9fc914f1ce0fcc3deb 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;
 
@@ -354,12 +357,23 @@ CullResult FakeUser::cull()
 void User::Oper(OperInfo* info)
 {
        ModeHandler* opermh = ServerInstance->Modes->FindMode('o', MODETYPE_USER);
-       if (this->IsModeSet(opermh))
-               this->UnOper();
-
-       this->SetMode(opermh, true);
+       if (opermh)
+       {
+               if (this->IsModeSet(opermh))
+                       this->UnOper();
+               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;
@@ -384,7 +398,7 @@ void User::Oper(OperInfo* info)
        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));
@@ -462,7 +476,8 @@ void User::UnOper()
        stdalgo::vector::swaperase(ServerInstance->Users->all_opers, this);
 
        ModeHandler* opermh = ServerInstance->Modes->FindMode('o', MODETYPE_USER);
-       this->SetMode(opermh, false);
+       if (opermh)
+               this->SetMode(opermh, false);
        FOREACH_MOD(OnPostDeoper, (this));
 }
 
@@ -545,42 +560,15 @@ void LocalUser::FullConnect()
        if (quitting)
                return;
 
-       this->WriteNumeric(RPL_WELCOME, InspIRCd::Format("Welcome to the %s IRC Network %s", ServerInstance->Config->Network.c_str(), GetFullRealHost().c_str()));
-       this->WriteNumeric(RPL_YOURHOSTIS, InspIRCd::Format("Your host is %s, running version %s", ServerInstance->Config->ServerName.c_str(), INSPIRCD_BRANCH));
-       this->WriteNumeric(RPL_SERVERCREATED, InspIRCd::TimeString(ServerInstance->startup_time, "This server was created %H:%M:%S %b %d %Y"));
-
-       const TR1NS::array<std::string, 3>& modelist = ServerInstance->Modes->GetModeListFor004Numeric();
-       this->WriteNumeric(RPL_SERVERVERSION, ServerInstance->Config->ServerName, INSPIRCD_BRANCH, modelist[0], modelist[1], modelist[2]);
-
-       ServerInstance->ISupport.SendTo(this);
-
-       /* Now registered */
-       if (ServerInstance->Users->unregistered_count)
-               ServerInstance->Users->unregistered_count--;
-
-       /* Trigger MOTD and LUSERS output, give modules a chance too */
-       ModResult MOD_RESULT;
-       std::string command("LUSERS");
-       CommandBase::Params parameters;
-       FIRST_MOD_RESULT(OnPreCommand, MOD_RESULT, (command, parameters, this, true, command));
-       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));
-       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());
-
        /*
         * We don't set REG_ALL until triggering OnUserConnect, so some module events don't spew out stuff
         * for a user that doesn't exist yet.
         */
        FOREACH_MOD(OnUserConnect, (this));
 
+       /* Now registered */
+       if (ServerInstance->Users->unregistered_count)
+               ServerInstance->Users->unregistered_count--;
        this->registered = REG_ALL;
 
        FOREACH_MOD(OnPostConnect, (this));
@@ -651,7 +639,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;
 
@@ -667,7 +659,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
@@ -730,21 +725,31 @@ irc::sockets::cidr_mask User::GetCIDRMask()
 
 bool User::SetClientIP(const std::string& address, bool recheck_eline)
 {
-       this->InvalidateCache();
-       return irc::sockets::aptosa(address, 0, client_sa);
+       irc::sockets::sockaddrs sa;
+       if (!irc::sockets::aptosa(address, client_sa.port(), sa))
+               return false;
+
+       User::SetClientIP(sa, recheck_eline);
+       return true;
 }
 
 void User::SetClientIP(const irc::sockets::sockaddrs& sa, bool recheck_eline)
 {
-       this->InvalidateCache();
+       const std::string oldip(GetIPString());
        memcpy(&client_sa, &sa, sizeof(irc::sockets::sockaddrs));
+       this->InvalidateCache();
+
+       // If the users hostname was their IP then update it.
+       if (GetRealHost() == oldip)
+               ChangeRealHost(GetIPString(), false);
+       if (GetDisplayedHost() == oldip)
+               ChangeDisplayedHost(GetIPString());
 }
 
 bool LocalUser::SetClientIP(const std::string& address, bool recheck_eline)
 {
        irc::sockets::sockaddrs sa;
-       if (!irc::sockets::aptosa(address, 0, sa))
-               // Invalid
+       if (!irc::sockets::aptosa(address, client_sa.port(), sa))
                return false;
 
        LocalUser::SetClientIP(sa, recheck_eline);
@@ -763,35 +768,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;
+
+               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(), text.c_str());
+               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;
@@ -799,53 +793,47 @@ void LocalUser::Write(const std::string& text)
        this->cmds_out++;
 }
 
-/** Write()
- */
-void LocalUser::Write(const char *text, ...)
+void LocalUser::Send(ClientProtocol::Event& protoev)
 {
-       std::string textbuffer;
-       VAFORMAT(textbuffer, text, text);
-       this->Write(textbuffer);
-}
-
-void User::WriteServ(const std::string& text)
-{
-       this->Write(":%s %s",ServerInstance->Config->ServerName.c_str(),text.c_str());
-}
+       if (!serializer)
+               return;
 
-/** 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);
+       // 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);
+       }
 }
 
-void User::WriteCommand(const char* command, const std::string& text)
+void LocalUser::Send(ClientProtocol::Event& protoev, ClientProtocol::MessageList& msglist)
 {
-       this->WriteServ(command + (this->registered & REG_NICK ? " " + this->nick : " *") + " " + text);
-}
-
-namespace
-{
-       std::string BuildNumeric(const std::string& source, User* targetuser, unsigned int num, const Command::Params& 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));
@@ -853,24 +841,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)
@@ -887,32 +859,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, ...)
-{
-       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)
+void User::WriteCommonRaw(ClientProtocol::Event& protoev, bool include_self)
 {
-       WriteCommonRawHandler handler(line);
+       WriteCommonRawHandler handler(protoev);
        ForEachNeighbor(handler, include_self);
 }
 
@@ -1023,10 +987,11 @@ bool User::ChangeDisplayedHost(const std::string& shost)
        if (GetDisplayedHost() == shost)
                return true;
 
-       if (IS_LOCAL(this))
+       LocalUser* luser = IS_LOCAL(this);
+       if (luser)
        {
                ModResult MOD_RESULT;
-               FIRST_MOD_RESULT(OnChangeLocalUserHost, MOD_RESULT, (IS_LOCAL(this),shost));
+               FIRST_MOD_RESULT(OnPreChangeHost, MOD_RESULT, (luser, shost));
                if (MOD_RESULT == MOD_RES_DENY)
                        return false;
        }
@@ -1199,8 +1164,16 @@ void User::PurgeEmptyChannels()
                ++i;
                c->DelUser(this);
        }
+}
+
+void User::WriteNotice(const std::string& text)
+{
+       LocalUser* const localuser = IS_LOCAL(this);
+       if (!localuser)
+               return;
 
-       this->UnOper();
+       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()