* | Inspire Internet Relay Chat Daemon |
* +------------------------------------+
*
- * Inspire is copyright (C) 2002-2005 ChatSpike-Dev.
+ * InspIRCd is copyright (C) 2002-2006 ChatSpike-Dev.
* E-mail:
* <brain@chatspike.net>
* <Craig@chatspike.net>
class ModuleSpanningTree;
static ModuleSpanningTree* TreeProtocolModule;
+extern ServerConfig* Config;
+
extern std::vector<Module*> modules;
extern std::vector<ircd_module*> factory;
extern int MODCOUNT;
{
VersionString = "";
UserCount = OperCount = 0;
- this->SetNextPingTime(time(NULL) + 60);
+ this->SetNextPingTime(time(NULL) + 120);
this->SetPingFlag();
/* find the 'route' for this server (e.g. the one directly connected
return UserCount;
}
+ void AddUserCount()
+ {
+ UserCount++;
+ }
+
+ void DelUserCount()
+ {
+ UserCount--;
+ }
+
int GetOperCount()
{
return OperCount;
time_t NextPing;
bool LastPingWasGood;
bool bursting;
- AES* ctx;
+ AES* ctx_in;
+ AES* ctx_out;
unsigned int keylength;
public:
{
myhost = host;
this->LinkState = LISTENER;
+ this->ctx_in = NULL;
+ this->ctx_out = NULL;
}
TreeSocket(std::string host, int port, bool listening, unsigned long maxtime, std::string ServerName)
{
myhost = ServerName;
this->LinkState = CONNECTING;
+ this->ctx_in = NULL;
+ this->ctx_out = NULL;
}
/* When a listening socket gives us a new file descriptor,
: InspSocket(newfd, ip)
{
this->LinkState = WAIT_AUTH_1;
+ this->ctx_in = NULL;
+ this->ctx_out = NULL;
this->SendCapabilities();
}
+ ~TreeSocket()
+ {
+ if (ctx_in)
+ delete ctx_in;
+ if (ctx_out)
+ delete ctx_out;
+ }
+
void InitAES(std::string key,std::string SName)
{
if (key == "")
return;
- ctx = new AES();
+ ctx_in = new AES();
+ ctx_out = new AES();
log(DEBUG,"Initialized AES key %s",key.c_str());
// key must be 16, 24, 32 etc bytes (multiple of 8)
keylength = key.length();
else
{
WriteOpers("*** \2AES\2: Initialized %d bit encryption to server %s",keylength*8,SName.c_str());
- ctx->MakeKey(key.c_str(), "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+ ctx_in->MakeKey(key.c_str(), "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+ \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", keylength, keylength);
+ ctx_out->MakeKey(key.c_str(), "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", keylength, keylength);
}
}
{
/* process one channel at a time, applying modes. */
char* usr = (char*)params[usernum].c_str();
- char permissions = *usr;
- switch (permissions)
- {
- case '@':
- usr++;
- mode_users[modectr++] = usr;
- strlcat(modestring,"o",MAXBUF);
- break;
- case '%':
- usr++;
- mode_users[modectr++] = usr;
- strlcat(modestring,"h",MAXBUF);
- break;
- case '+':
- usr++;
- mode_users[modectr++] = usr;
- strlcat(modestring,"v",MAXBUF);
- break;
- }
- who = Srv->FindNick(usr);
- if (who)
- {
- Srv->JoinUserToChannel(who,channel,key);
- if (modectr >= (MAXMODES-1))
- {
- /* theres a mode for this user. push them onto the mode queue, and flush it
- * if there are more than MAXMODES to go.
- */
- if ((ourTS >= TS) || (Srv->IsUlined(who->server)))
+ /* Safety check just to make sure someones not sent us an FJOIN full of spaces
+ * (is this even possible?) */
+ if (usr && *usr)
+ {
+ char permissions = *usr;
+ switch (permissions)
+ {
+ case '@':
+ usr++;
+ mode_users[modectr++] = usr;
+ strlcat(modestring,"o",MAXBUF);
+ break;
+ case '%':
+ usr++;
+ mode_users[modectr++] = usr;
+ strlcat(modestring,"h",MAXBUF);
+ break;
+ case '+':
+ usr++;
+ mode_users[modectr++] = usr;
+ strlcat(modestring,"v",MAXBUF);
+ break;
+ }
+ who = Srv->FindNick(usr);
+ if (who)
+ {
+ Srv->JoinUserToChannel(who,channel,key);
+ if (modectr >= (MAXMODES-1))
{
- /* We also always let u-lined clients win, no matter what the TS value */
- log(DEBUG,"Our our channel newer than theirs, accepting their modes");
- Srv->SendMode(mode_users,modectr,who);
- }
- else
- {
- log(DEBUG,"Their channel newer than ours, bouncing their modes");
- /* bouncy bouncy! */
- std::deque<std::string> params;
- /* modes are now being UNSET... */
- *mode_users[1] = '-';
- for (unsigned int x = 0; x < modectr; x++)
+ /* theres a mode for this user. push them onto the mode queue, and flush it
+ * if there are more than MAXMODES to go.
+ */
+ if ((ourTS >= TS) || (Srv->IsUlined(who->server)))
+ {
+ /* We also always let u-lined clients win, no matter what the TS value */
+ log(DEBUG,"Our our channel newer than theirs, accepting their modes");
+ Srv->SendMode(mode_users,modectr,who);
+ }
+ else
{
- params.push_back(mode_users[x]);
+ log(DEBUG,"Their channel newer than ours, bouncing their modes");
+ /* bouncy bouncy! */
+ std::deque<std::string> params;
+ /* modes are now being UNSET... */
+ *mode_users[1] = '-';
+ for (unsigned int x = 0; x < modectr; x++)
+ {
+ params.push_back(mode_users[x]);
+ }
+ // tell everyone to bounce the modes. bad modes, bad!
+ DoOneToMany(Srv->GetServerName(),"FMODE",params);
}
- // tell everyone to bounce the modes. bad modes, bad!
- DoOneToMany(Srv->GetServerName(),"FMODE",params);
+ strcpy(mode_users[1],"+");
+ modectr = 2;
}
- strcpy(mode_users[1],"+");
- modectr = 2;
}
}
}
}
params[7] = ":" + params[7];
DoOneToAllButSender(source,"NICK",params,source);
+
+ // Increment the Source Servers User Count..
+ TreeServer* SourceServer = FindServer(source);
+ if (SourceServer) {
+ SourceServer->AddUserCount();
+ }
+
return true;
}
log(DEBUG,"Sending FJOINs to other server for %s",c->name);
char list[MAXBUF];
snprintf(list,MAXBUF,":%s FJOIN %s %lu",Srv->GetServerName().c_str(),c->name,(unsigned long)c->age);
- std::vector<char*> *ulist = c->GetUsers();
- for (unsigned int i = 0; i < ulist->size(); i++)
+ std::map<char*,char*> *ulist = c->GetUsers();
+ for (std::map<char*,char*>::iterator i = ulist->begin(); i != ulist->end(); i++)
{
- char* o = (*ulist)[i];
+ char* o = i->second;
userrec* otheruser = (userrec*)o;
strlcat(list," ",MAXBUF);
strlcat(list,cmode(otheruser,c),MAXBUF);
snprintf(data,MAXBUF,":%s FMODE %s +b %s",Srv->GetServerName().c_str(),c->second->name,b->data);
this->WriteLine(data);
}
- FOREACH_MOD OnSyncChannel(c->second,(Module*)TreeProtocolModule,(void*)this);
+ FOREACH_MOD(I_OnSyncChannel,OnSyncChannel(c->second,(Module*)TreeProtocolModule,(void*)this));
list.clear();
c->second->GetExtList(list);
for (unsigned int j = 0; j < list.size(); j++)
{
- FOREACH_MOD OnSyncChannelMetaData(c->second,(Module*)TreeProtocolModule,(void*)this,list[j]);
+ FOREACH_MOD(I_OnSyncChannelMetaData,OnSyncChannelMetaData(c->second,(Module*)TreeProtocolModule,(void*)this,list[j]));
}
}
}
{
this->WriteLine(":"+std::string(u->second->nick)+" OPERTYPE "+std::string(u->second->oper));
}
- FOREACH_MOD OnSyncUser(u->second,(Module*)TreeProtocolModule,(void*)this);
+ FOREACH_MOD(I_OnSyncUser,OnSyncUser(u->second,(Module*)TreeProtocolModule,(void*)this));
list.clear();
u->second->GetExtList(list);
for (unsigned int j = 0; j < list.size(); j++)
{
- FOREACH_MOD OnSyncUserMetaData(u->second,(Module*)TreeProtocolModule,(void*)this,list[j]);
+ FOREACH_MOD(I_OnSyncUserMetaData,OnSyncUserMetaData(u->second,(Module*)TreeProtocolModule,(void*)this,list[j]));
}
}
}
virtual bool OnDataReady()
{
char* data = this->Read();
- if (data)
+ /* Check that the data read is a valid pointer and it has some content */
+ if (data && *data)
{
this->in_buffer += data;
/* While there is at least one new line in the buffer,
std::string ret = "";
while ((*line != '\n') && (strlen(line)))
{
- ret = ret + *line;
+ if ((*line != '\r') && (*line != '\n'))
+ ret = ret + *line;
line++;
}
if ((*line == '\n') || (*line == '\r'))
/* Process this one, abort if it
* didnt return true.
*/
- if (this->ctx)
+ if (this->ctx_in)
{
char out[1024];
char result[1024];
+ memset(result,0,1024);
+ memset(out,0,1024);
log(DEBUG,"Original string '%s'",ret.c_str());
/* ERROR + CAPAB is still allowed unencryped */
if ((ret.substr(0,7) != "ERROR :") && (ret.substr(0,6) != "CAPAB "))
{
int nbytes = from64tobits(out, ret.c_str(), 1024);
- log(DEBUG,"m_spanningtree: decrypt %d bytes",nbytes);
- ctx->Decrypt(out, result, nbytes, 0);
- for (int t = 0; t < nbytes; t++)
- if (result[t] == '\7') result[t] = 0;
- ret = result;
+ if ((nbytes > 0) && (nbytes < 1024))
+ {
+ log(DEBUG,"m_spanningtree: decrypt %d bytes",nbytes);
+ ctx_in->Decrypt(out, result, nbytes, 1);
+ for (int t = 0; t < nbytes; t++)
+ if (result[t] == '\7') result[t] = 0;
+ ret = result;
+ }
}
}
if (!this->ProcessLine(ret))
}
}
}
+ /* EAGAIN returns an empty but non-NULL string, so this
+ * evaluates to TRUE for EAGAIN but to FALSE for EOF.
+ */
return (data != NULL);
}
int WriteLine(std::string line)
{
log(DEBUG,"OUT: %s",line.c_str());
- if (this->ctx)
+ if (this->ctx_out)
{
log(DEBUG,"AES context");
char result[10240];
}
unsigned int ll = line.length();
log(DEBUG,"Plaintext line with padding = %d chars",ll);
- ctx->Encrypt(line.c_str(), result, ll, 0);
+ ctx_out->Encrypt(line.c_str(), result, ll, 1);
log(DEBUG,"Encrypted.");
- to64frombits((unsigned char*)result64,
- (unsigned char*)result,
- ll);
+ to64frombits((unsigned char*)result64,(unsigned char*)result,ll);
line = result64;
log(DEBUG,"Encrypted: %s",line.c_str());
//int from64tobits(char *out, const char *in, int maxlen);
chanrec* c = Srv->FindChannel(params[0]);
if (c)
{
- FOREACH_MOD OnDecodeMetaData(TYPE_CHANNEL,c,params[1],params[2]);
+ FOREACH_MOD(I_OnDecodeMetaData,OnDecodeMetaData(TYPE_CHANNEL,c,params[1],params[2]));
}
}
else
userrec* u = Srv->FindNick(params[0]);
if (u)
{
- FOREACH_MOD OnDecodeMetaData(TYPE_USER,u,params[1],params[2]);
+ FOREACH_MOD(I_OnDecodeMetaData,OnDecodeMetaData(TYPE_USER,u,params[1],params[2]));
}
}
}
if (params.size() == 1)
{
userrec* x = Srv->FindNick(params[0]);
- if (x->fd > -1)
+ if ((x) && (x->fd > -1))
{
userrec* x = Srv->FindNick(params[0]);
log(DEBUG,"Got IDLE");
{
std::string who_did_the_whois = params[0];
userrec* who_to_send_to = Srv->FindNick(who_did_the_whois);
- if (who_to_send_to->fd > -1)
+ if ((who_to_send_to) && (who_to_send_to->fd > -1))
{
log(DEBUG,"Got final IDLE");
// an incoming reply to a whois we sent out
* hasnt bothered to send the AES command before SERVER, then we
* boot them off as we MUST have this connection encrypted.
*/
- if ((x->EncryptionKey != "") && (!this->ctx))
+ if ((x->EncryptionKey != "") && (!this->ctx_in))
{
this->WriteLine("ERROR :This link requires AES encryption to be enabled. Plaintext connection refused.");
Srv->SendOpers("*** Server connection from \2"+servername+"\2 denied, remote server did not enable AES.");
if (line == "")
return true;
Srv->Log(DEBUG,"IN: "+line);
+
std::deque<std::string> params;
this->Split(line,true,params);
std::string command = "";
params.pop_front();
}
- if ((!this->ctx) && (command == "AES"))
+ if ((!this->ctx_in) && (command == "AES"))
{
std::string sserv = params[0];
for (std::vector<Link>::iterator x = LinkBlocks.begin(); x < LinkBlocks.end(); x++)
}
return true;
}
- else if ((this->ctx) && (command == "AES"))
+ else if ((this->ctx_in) && (command == "AES"))
{
WriteOpers("*** \2AES\2: Encryption already enabled on this connection yet %s is trying to enable it twice!",params[0].c_str());
}
{
return this->Capab(params);
}
+ else if ((command == "U") || (command == "S"))
+ {
+ this->WriteLine("ERROR :Cannot use the old-style mesh linking protocol with m_spanningtree.so!");
+ return false;
+ }
else
{
this->WriteLine("ERROR :Invalid command in negotiation phase.");
// silently ignore.
return true;
}
+ else if ((command == "U") || (command == "S"))
+ {
+ this->WriteLine("ERROR :Cannot use the old-style mesh linking protocol with m_spanningtree.so!");
+ return false;
+ }
else if (command == "BURST")
{
this->LinkState = CONNECTED;
if ((!route_back_again) || (route_back_again->GetSocket() != this))
{
if (route_back_again)
- {
- WriteOpers("*** Protocol violation: Fake direction in command '%s' from connection '%s'",line.c_str(),this->GetName().c_str());
- }
- else
- {
- WriteOpers("*** Protocol violation: Invalid source '%s' in command '%s' from connection '%s'",direction.c_str(),line.c_str(),this->GetName().c_str());
- }
-
+ log(DEBUG,"Protocol violation: Fake direction in command '%s' from connection '%s'",line.c_str(),this->GetName().c_str());
return true;
}
+
+ /* Fix by brain:
+ * When there is activity on the socket, reset the ping counter so
+ * that we're not wasting bandwidth pinging an active server.
+ */
+ route_back_again->SetNextPingTime(time(NULL) + 120);
+ route_back_again->SetPingFlag();
}
if (command == "SVSMODE")
// returns a list of DIRECT servernames for a specific channel
void GetListOfServersForChannel(chanrec* c, std::deque<TreeServer*> &list)
{
- std::vector<char*> *ulist = c->GetUsers();
- unsigned int ucount = ulist->size();
- for (unsigned int i = 0; i < ucount; i++)
+ std::map<char*,char*> *ulist = c->GetUsers();
+ for (std::map<char*,char*>::iterator i = ulist->begin(); i != ulist->end(); i++)
{
- char* o = (*ulist)[i];
+ char* o = i->second;
userrec* otheruser = (userrec*)o;
if (otheruser->fd < 0)
{
}
else
{
- log(DEFAULT,"m_spanningtree: Invalid configuration for server '%s', ignored!",L.Name.c_str());
+ if (L.RecvPass == "")
+ {
+ log(DEFAULT,"Invalid configuration for server '%s', recvpass not defined!",L.Name.c_str());
+ }
+ else if (L.SendPass == "")
+ {
+ log(DEFAULT,"Invalid configuration for server '%s', sendpass not defined!",L.Name.c_str());
+ }
+ else if (L.Name == "")
+ {
+ log(DEFAULT,"Invalid configuration, link tag without a name!");
+ }
+ else if (!L.Port)
+ {
+ log(DEFAULT,"Invalid configuration for server '%s', no port specified!",L.Name.c_str());
+ }
}
}
delete Conf;
{
matrix[line][t] = ' ';
}
- strlcpy(&matrix[line][depth],Current->GetName().c_str(),80);
+
+ // For Aligning, we need to work out exactly how deep this thing is, and produce
+ // a 'Spacer' String to compensate.
+ char spacer[40];
+
+ memset(spacer,' ',40);
+ if ((40 - Current->GetName().length() - depth) > 1) {
+ spacer[40 - Current->GetName().length() - depth] = '\0';
+ }
+ else
+ {
+ spacer[5] = '\0';
+ }
+
+ float percent;
+ char text[80];
+ if (clientlist.size() == 0) {
+ // If there are no users, WHO THE HELL DID THE /MAP?!?!?!
+ percent = 0;
+ }
+ else
+ {
+ percent = ((float)Current->GetUserCount() / (float)clientlist.size()) * 100;
+ }
+ snprintf(text, 80, "%s %s%5d [%5.2f%%]", Current->GetName().c_str(), spacer, Current->GetUserCount(), percent);
+ strlcpy(&matrix[line][depth],text,80);
line++;
for (unsigned int q = 0; q < Current->ChildCount(); q++)
{
log(DEBUG,"Splitting server %s",s->GetName().c_str());
WriteOpers("*** SQUIT: Server \002%s\002 removed from network by %s",parameters[0],user->nick);
sock->Squit(s,"Server quit by "+std::string(user->nick)+"!"+std::string(user->ident)+"@"+std::string(user->host));
- sock->Close();
+ Srv->RemoveSocket(sock);
}
else
{
TreeSocket* sock = serv->GetSocket();
if (sock)
{
- if (curtime >= serv->NextPingTime())
- {
- if (serv->AnsweredLastPing())
- {
- sock->WriteLine(":"+Srv->GetServerName()+" PING "+serv->GetName());
- serv->SetNextPingTime(curtime + 60);
- }
- else
- {
- // they didnt answer, boot them
- WriteOpers("*** Server \002%s\002 pinged out",serv->GetName().c_str());
- sock->Squit(serv,"Ping timeout");
- sock->Close();
- return;
- }
- }
+ if (curtime >= serv->NextPingTime())
+ {
+ if (serv->AnsweredLastPing())
+ {
+ sock->WriteLine(":"+Srv->GetServerName()+" PING "+serv->GetName());
+ serv->SetNextPingTime(curtime + 120);
+ }
+ else
+ {
+ // they didnt answer, boot them
+ WriteOpers("*** Server \002%s\002 pinged out",serv->GetName().c_str());
+ sock->Squit(serv,"Ping timeout");
+ Srv->RemoveSocket(sock);
+ return;
+ }
+ }
+
}
}
}
{
std::string Version = found->GetVersion();
WriteServ(user->fd,"351 %s :%s",user->nick,Version.c_str());
+ if (found == TreeRoot)
+ {
+ std::stringstream out(Config->data005);
+ std::string token = "";
+ std::string line5 = "";
+ int token_counter = 0;
+ while (!out.eof())
+ {
+ out >> token;
+ line5 = line5 + token + " ";
+ token_counter++;
+ if ((token_counter >= 13) || (out.eof() == true))
+ {
+ WriteServ(user->fd,"005 %s %s:are supported by this server",user->nick,line5.c_str());
+ line5 = "";
+ token_counter = 0;
+ }
+ }
+ }
}
else
{
return 1;
}
- virtual bool HandleStats(char ** parameters, int pcnt, userrec* user)
+ virtual int OnStats(char statschar, userrec* user)
{
- if (*parameters[0] == 'c')
+ if (statschar == 'c')
{
for (unsigned int i = 0; i < LinkBlocks.size(); i++)
{
- WriteServ(user->fd,"213 %s C *@%s * %s %d 0 %s",user->nick,LinkBlocks[i].IPAddr.c_str(),LinkBlocks[i].Name.c_str(),LinkBlocks[i].Port,(LinkBlocks[i].EncryptionKey != "" ? "es" : " s"));
+ WriteServ(user->fd,"213 %s C *@%s * %s %d 0 %c%c%c",user->nick,LinkBlocks[i].IPAddr.c_str(),LinkBlocks[i].Name.c_str(),LinkBlocks[i].Port,(LinkBlocks[i].EncryptionKey != "" ? 'e' : '-'),(LinkBlocks[i].AutoConnect ? 'a' : '-'),'s');
WriteServ(user->fd,"244 %s H * * %s",user->nick,LinkBlocks[i].Name.c_str());
}
- WriteServ(user->fd,"219 %s %s :End of /STATS report",user->nick,parameters[0]);
- WriteOpers("*** Notice: Stats '%s' requested by %s (%s@%s)",parameters[0],user->nick,user->ident,user->host);
- return true;
+ WriteServ(user->fd,"219 %s %c :End of /STATS report",user->nick,statschar);
+ WriteOpers("*** Notice: Stats '%c' requested by %s (%s@%s)",statschar,user->nick,user->ident,user->host);
+ return 1;
}
- return false;
+ return 0;
}
virtual int OnPreCommand(std::string command, char **parameters, int pcnt, userrec *user, bool validated)
{
return this->HandleSquit(parameters,pcnt,user);
}
- else if (command == "STATS")
- {
- return this->HandleStats(parameters,pcnt,user);
- }
else if (command == "MAP")
{
this->HandleMap(parameters,pcnt,user);
params.push_back(user->ip);
params.push_back(":"+std::string(user->fullname));
DoOneToMany(Srv->GetServerName(),"NICK",params);
+
+ // User is Local, change needs to be reflected!
+ TreeServer* SourceServer = FindServer(user->server);
+ if (SourceServer) {
+ SourceServer->AddUserCount();
+ }
+
}
}
params.push_back(":"+reason);
DoOneToMany(user->nick,"QUIT",params);
}
+ // Regardless, We need to modify the user Counts..
+ TreeServer* SourceServer = FindServer(user->server);
+ if (SourceServer) {
+ SourceServer->DelUserCount();
+ }
+
}
virtual void OnUserPostNick(userrec* user, std::string oldnick)
{
return Version(1,0,0,0,VF_STATIC|VF_VENDOR);
}
+
+ void Implements(char* List)
+ {
+ List[I_OnPreCommand] = List[I_OnGetServerDescription] = List[I_OnUserInvite] = List[I_OnPostLocalTopicChange] = 1;
+ List[I_OnWallops] = List[I_OnUserNotice] = List[I_OnUserMessage] = List[I_OnBackgroundTimer] = 1;
+ List[I_OnUserJoin] = List[I_OnChangeHost] = List[I_OnChangeName] = List[I_OnUserPart] = List[I_OnUserConnect] = 1;
+ List[I_OnUserQuit] = List[I_OnUserPostNick] = List[I_OnUserKick] = List[I_OnRemoteKill] = List[I_OnRehash] = 1;
+ List[I_OnOper] = List[I_OnAddGLine] = List[I_OnAddZLine] = List[I_OnAddQLine] = List[I_OnAddELine] = 1;
+ List[I_OnDelGLine] = List[I_OnDelZLine] = List[I_OnDelQLine] = List[I_OnDelELine] = List[I_ProtoSendMode] = List[I_OnMode] = 1;
+ List[I_OnStats] = List[I_ProtoSendMetaData] = 1;
+ }
+
+ /* It is IMPORTANT that m_spanningtree is the last module in the chain
+ * so that any activity it sees is FINAL, e.g. we arent going to send out
+ * a NICK message before m_cloaking has finished putting the +x on the user,
+ * etc etc.
+ * Therefore, we return PRIORITY_LAST to make sure we end up at the END of
+ * the module call queue.
+ */
+ Priority Prioritize()
+ {
+ return PRIORITY_LAST;
+ }
};