#include "treesocket.h"
#include "treeserver.h"
#include "main.h"
+#include "commands.h"
+
+/**
+ * Creates FMODE messages, used only when syncing channels
+ */
+class FModeBuilder : public CmdBuilder
+{
+ static const size_t maxline = 480;
+ std::string params;
+ unsigned int modes;
+ std::string::size_type startpos;
+
+ public:
+ FModeBuilder(Channel* chan)
+ : CmdBuilder("FMODE"), modes(0)
+ {
+ push(chan->name).push_int(chan->age).push_raw(" +");
+ startpos = str().size();
+ }
+
+ /** Add a mode to the message
+ */
+ void push_mode(const char modeletter, const std::string& mask)
+ {
+ push_raw(modeletter);
+ params.push_back(' ');
+ params.append(mask);
+ modes++;
+ }
+
+ /** Remove all modes from the message
+ */
+ void clear()
+ {
+ content.erase(startpos);
+ params.clear();
+ modes = 0;
+ }
+
+ /** Prepare the message for sending, next mode can only be added after clear()
+ */
+ const std::string& finalize()
+ {
+ return push_raw(params);
+ }
+
+ /** Returns true if the given mask can be added to the message, false if the message
+ * has no room for the mask
+ */
+ bool has_room(const std::string& mask) const
+ {
+ return ((str().size() + params.size() + mask.size() + 2 <= maxline) &&
+ (modes < ServerInstance->Config->Limits.MaxModes));
+ }
+
+ /** Returns true if this message is empty (has no modes)
+ */
+ bool empty() const
+ {
+ return (modes == 0);
+ }
+};
+
+struct TreeSocket::BurstState
+{
+ SpanningTreeProtocolInterface::Server server;
+ BurstState(TreeSocket* sock) : server(sock) { }
+};
/** This function is called when we want to send a netburst to a local
* server. There is a set order we must do this, because for example
*/
void TreeSocket::DoBurst(TreeServer* s)
{
- std::string servername = s->GetName();
ServerInstance->SNO->WriteToSnoMask('l',"Bursting to \2%s\2 (Authentication: %s%s).",
- servername.c_str(),
- capab->auth_fingerprint ? "SSL Fingerprint and " : "",
+ s->GetName().c_str(),
+ capab->auth_fingerprint ? "SSL certificate fingerprint and " : "",
capab->auth_challenge ? "challenge-response" : "plaintext password");
this->CleanNegotiationInfo();
- this->WriteLine(":" + ServerInstance->Config->GetSID() + " BURST " + ConvToStr(ServerInstance->Time()));
- /* send our version string */
- this->WriteLine(":" + ServerInstance->Config->GetSID() + " VERSION :"+ServerInstance->GetVersionString());
- /* Send server tree */
+ this->WriteLine(CmdBuilder("BURST").push_int(ServerInstance->Time()));
+ // Introduce all servers behind us
this->SendServers(Utils->TreeRoot, s);
- /* Send users and their oper status */
- this->SendUsers();
- for (chan_hash::const_iterator i = ServerInstance->chanlist->begin(); i != ServerInstance->chanlist->end(); ++i)
- SyncChannel(i->second);
+ BurstState bs(this);
+ // Introduce all users
+ this->SendUsers(bs);
+
+ // Sync all channels
+ const chan_hash& chans = ServerInstance->GetChans();
+ for (chan_hash::const_iterator i = chans.begin(); i != chans.end(); ++i)
+ SyncChannel(i->second, bs);
+ // Send all xlines
this->SendXLines();
- FOREACH_MOD(I_OnSyncNetwork,OnSyncNetwork(Utils->Creator,(void*)this));
- this->WriteLine(":" + ServerInstance->Config->GetSID() + " ENDBURST");
+ FOREACH_MOD(OnSyncNetwork, (bs.server));
+ this->WriteLine(CmdBuilder("ENDBURST"));
ServerInstance->SNO->WriteToSnoMask('l',"Finished bursting to \2"+ s->GetName()+"\2.");
+
+ this->burstsent = true;
+}
+
+void TreeSocket::SendServerInfo(TreeServer* from)
+{
+ // Send public version string
+ this->WriteLine(CommandSInfo::Builder(from, "version", from->GetVersion()));
+
+ // Send full version string that contains more information and is shown to opers
+ this->WriteLine(CommandSInfo::Builder(from, "fullversion", from->GetFullVersion()));
}
/** Recursively send the server tree.
* (and any of ITS servers too) of what servers we know about.
* If at any point any of these servers already exist on the other
* end, our connection may be terminated.
- * The hopcount parameter (3rd) is deprecated, and is always 0.
*/
void TreeSocket::SendServers(TreeServer* Current, TreeServer* s)
{
- for (unsigned int q = 0; q < Current->ChildCount(); q++)
+ SendServerInfo(Current);
+
+ const TreeServer::ChildServers& children = Current->GetChildren();
+ for (TreeServer::ChildServers::const_iterator i = children.begin(); i != children.end(); ++i)
{
- TreeServer* recursive_server = Current->GetChild(q);
+ TreeServer* recursive_server = *i;
if (recursive_server != s)
{
- this->WriteLine(InspIRCd::Format(":%s SERVER %s * 0 %s :%s", Current->GetID().c_str(),
- recursive_server->GetName().c_str(), recursive_server->GetID().c_str(), recursive_server->GetDesc().c_str()));
- this->WriteLine(":" + recursive_server->GetID() + " VERSION :" + recursive_server->GetVersion());
+ this->WriteLine(CommandServer::Builder(recursive_server));
/* down to next level */
this->SendServers(recursive_server, s);
}
}
/** Send one or more FJOINs for a channel of users.
- * If the length of a single line is more than 480-NICKMAX
- * in length, it is split over multiple lines.
- * Send one or more FMODEs for a channel with the
- * channel bans, if there's any.
+ * If the length of a single line is too long, it is split over multiple lines.
*/
void TreeSocket::SendFJoins(Channel* c)
{
- std::string line(":");
- line.append(ServerInstance->Config->GetSID()).append(" FJOIN ").append(c->name).append(1, ' ').append(ConvToStr(c->age)).append(" +");
- std::string::size_type erase_from = line.length();
- line.append(c->ChanModes(true)).append(" :");
-
- const UserMembList *ulist = c->GetUsers();
+ CommandFJoin::Builder fjoin(c);
- for (UserMembCIter i = ulist->begin(); i != ulist->end(); ++i)
+ const Channel::MemberMap& ulist = c->GetUsers();
+ for (Channel::MemberMap::const_iterator i = ulist.begin(); i != ulist.end(); ++i)
{
- const std::string& modestr = i->second->modes;
- if ((line.length() + modestr.length() + UIDGenerator::UUID_LENGTH + 2) > 480)
+ Membership* memb = i->second;
+ if (!fjoin.has_room(memb))
{
- this->WriteLine(line);
- line.erase(erase_from);
- line.append(" :");
+ // No room for this user, send the line and prepare a new one
+ this->WriteLine(fjoin.finalize());
+ fjoin.clear();
}
- line.append(modestr).append(1, ',').append(i->first->uuid).push_back(' ');
+ fjoin.add(memb);
}
- this->WriteLine(line);
-
- ModeReference ban(NULL, "ban");
- static_cast<ListModeBase*>(*ban)->DoSyncChannel(c, Utils->Creator, this);
+ this->WriteLine(fjoin.finalize());
}
/** Send all XLines we know about */
if (!i->second->IsBurstable())
break;
- this->WriteLine(InspIRCd::Format(":%s ADDLINE %s %s %s %lu %lu :%s",
- ServerInstance->Config->GetSID().c_str(),
- it->c_str(),
- i->second->Displayable().c_str(),
- i->second->source.c_str(),
- (unsigned long)i->second->set_time,
- (unsigned long)i->second->duration,
- i->second->reason.c_str()));
+ this->WriteLine(CommandAddLine::Builder(i->second));
}
}
}
}
-/** Send channel topic, modes and metadata */
-void TreeSocket::SyncChannel(Channel* chan)
+void TreeSocket::SendListModes(Channel* chan)
+{
+ FModeBuilder fmode(chan);
+ const ModeParser::ListModeList& listmodes = ServerInstance->Modes->GetListModes();
+ for (ModeParser::ListModeList::const_iterator i = listmodes.begin(); i != listmodes.end(); ++i)
+ {
+ ListModeBase* mh = *i;
+ ListModeBase::ModeList* list = mh->GetList(chan);
+ if (!list)
+ continue;
+
+ // Add all items on the list to the FMODE, send it whenever it becomes too long
+ const char modeletter = mh->GetModeChar();
+ for (ListModeBase::ModeList::const_iterator j = list->begin(); j != list->end(); ++j)
+ {
+ const std::string& mask = j->mask;
+ if (!fmode.has_room(mask))
+ {
+ // No room for this mask, send the current line as-is then add the mask to a
+ // new, empty FMODE message
+ this->WriteLine(fmode.finalize());
+ fmode.clear();
+ }
+ fmode.push_mode(modeletter, mask);
+ }
+ }
+
+ if (!fmode.empty())
+ this->WriteLine(fmode.finalize());
+}
+
+/** Send channel users, topic, modes and global metadata */
+void TreeSocket::SyncChannel(Channel* chan, BurstState& bs)
{
SendFJoins(chan);
// If the topic was ever set, send it, even if it's empty now
// because a new empty topic should override an old non-empty topic
if (chan->topicset != 0)
- {
- this->WriteLine(InspIRCd::Format(":%s FTOPIC %s %lu %lu %s :%s", ServerInstance->Config->GetSID().c_str(),
- chan->name.c_str(), (unsigned long)chan->age, (unsigned long)chan->topicset,
- chan->setby.c_str(), chan->topic.c_str()));
- }
+ this->WriteLine(CommandFTopic::Builder(chan));
+
+ SendListModes(chan);
for (Extensible::ExtensibleStore::const_iterator i = chan->GetExtList().begin(); i != chan->GetExtList().end(); i++)
{
ExtensionItem* item = i->first;
std::string value = item->serialize(FORMAT_NETWORK, chan, i->second);
if (!value.empty())
- Utils->Creator->ProtoSendMetaData(this, chan, item->name, value);
+ this->WriteLine(CommandMetadata::Builder(chan, item->name, value));
}
- FOREACH_MOD(I_OnSyncChannel,OnSyncChannel(chan, Utils->Creator, this));
+ FOREACH_MOD(OnSyncChannel, (chan, bs.server));
}
-/** send all users and their oper state/modes */
-void TreeSocket::SendUsers()
+void TreeSocket::SyncChannel(Channel* chan)
+{
+ BurstState bs(this);
+ SyncChannel(chan, bs);
+}
+
+/** Send all users and their state, including oper and away status and global metadata */
+void TreeSocket::SendUsers(BurstState& bs)
{
- for (user_hash::iterator u = ServerInstance->Users->clientlist->begin(); u != ServerInstance->Users->clientlist->end(); u++)
+ ProtocolInterface::Server& piserver = bs.server;
+
+ const user_hash& users = ServerInstance->Users->GetUsers();
+ for (user_hash::const_iterator u = users.begin(); u != users.end(); ++u)
{
- if (u->second->registered == REG_ALL)
- {
- TreeServer* theirserver = Utils->FindServer(u->second->server);
- if (theirserver)
- {
- this->WriteLine(InspIRCd::Format(":%s UID %s %lu %s %s %s %s %s %lu +%s :%s",
- theirserver->GetID().c_str(), // Prefix: SID
- u->second->uuid.c_str(), // 0: UUID
- (unsigned long)u->second->age, // 1: TS
- u->second->nick.c_str(), // 2: Nick
- u->second->host.c_str(), // 3: Real host
- u->second->dhost.c_str(), // 4: Display host
- u->second->ident.c_str(), // 5: Ident
- u->second->GetIPString().c_str(), // 6: IP address
- (unsigned long)u->second->signon, // 7: Signon time
- u->second->FormatModes(true), // 8...n: User modes and params
- u->second->fullname.c_str())); // size-1: GECOS
-
- if (u->second->IsOper())
- {
- this->WriteLine(InspIRCd::Format(":%s OPERTYPE :%s", u->second->uuid.c_str(), u->second->oper->name.c_str()));
- }
- if (u->second->IsAway())
- {
- this->WriteLine(InspIRCd::Format(":%s AWAY %ld :%s", u->second->uuid.c_str(), (long)u->second->awaytime,
- u->second->awaymsg.c_str()));
- }
- }
+ User* user = u->second;
+ if (user->registered != REG_ALL)
+ continue;
- for(Extensible::ExtensibleStore::const_iterator i = u->second->GetExtList().begin(); i != u->second->GetExtList().end(); i++)
- {
- ExtensionItem* item = i->first;
- std::string value = item->serialize(FORMAT_NETWORK, u->second, i->second);
- if (!value.empty())
- Utils->Creator->ProtoSendMetaData(this, u->second, item->name, value);
- }
+ this->WriteLine(CommandUID::Builder(user));
+
+ if (user->IsOper())
+ this->WriteLine(CommandOpertype::Builder(user));
+
+ if (user->IsAway())
+ this->WriteLine(CommandAway::Builder(user));
- FOREACH_MOD(I_OnSyncUser,OnSyncUser(u->second,Utils->Creator,this));
+ const Extensible::ExtensibleStore& exts = user->GetExtList();
+ for (Extensible::ExtensibleStore::const_iterator i = exts.begin(); i != exts.end(); ++i)
+ {
+ ExtensionItem* item = i->first;
+ std::string value = item->serialize(FORMAT_NETWORK, u->second, i->second);
+ if (!value.empty())
+ this->WriteLine(CommandMetadata::Builder(user, item->name, value));
}
+
+ FOREACH_MOD(OnSyncUser, (user, piserver));
}
}
-